From 841693643a655af2e2698170e1b1dcfc762f745b Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Tue, 25 Jul 2017 21:29:33 +0300 Subject: [PATCH] KotlinX serialization plugin applied onto latest master IntelliJ project, ant task and maven goal for serialization plugin Serialization IDEA plugin --- libraries/ReadMe.md | 47 ++- libraries/tools/kotlin-serialization/pom.xml | 158 +++++++ .../gradle/SerializationSubplugin.kt | 58 +++ .../kotlin-serialization.properties | 1 + ...ins.kotlin.plugin.serialization.properties | 1 + ...kotlin.gradle.plugin.KotlinGradleSubplugin | 17 + .../kotlin-serialization-compiler/README.md | 30 ++ .../build.gradle | 63 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54783 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../kotlin-serialization-compiler/gradlew | 172 ++++++++ .../kotlin-serialization-compiler/gradlew.bat | 84 ++++ .../kotlin-serialization-compiler.iml | 16 + .../settings.gradle | 1 + .../src/META-INF/plugin.xml | 13 + ....kotlin.compiler.plugin.ComponentRegistrar | 17 + .../backend/common/SerializableCodegen.kt | 55 +++ .../backend/common/SerializerCodegen.kt | 91 ++++ .../compiler/backend/jvm/JVMCodegenUtil.kt | 283 +++++++++++++ .../backend/jvm/SerialInfoCodegenImpl.kt | 103 +++++ .../backend/jvm/SerializableCodegenImpl.kt | 219 ++++++++++ .../backend/jvm/SerializerCodegenImpl.kt | 396 ++++++++++++++++++ .../compiler/backend/jvm/Types.kt | 30 ++ .../SerializationCodegenExtension.kt | 31 ++ .../SerializationComponentRegistrar.kt | 30 ++ .../SerializationResolveExtension.kt | 65 +++ .../compiler/resolve/KSerializationUtil.kt | 180 ++++++++ .../resolve/KSerializerDescriptorResolver.kt | 287 +++++++++++++ .../resolve/SerializableProperties.kt | 74 ++++ .../compiler/resolve/SerializableProperty.kt | 32 ++ 30 files changed, 2556 insertions(+), 4 deletions(-) create mode 100644 libraries/tools/kotlin-serialization/pom.xml create mode 100644 libraries/tools/kotlin-serialization/src/main/kotlin/org/jetbrains/kotlinx/serialization/gradle/SerializationSubplugin.kt create mode 100644 libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/kotlin-serialization.properties create mode 100644 libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.serialization.properties create mode 100644 libraries/tools/kotlin-serialization/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/README.md create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/gradle/wrapper/gradle-wrapper.jar create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/gradle/wrapper/gradle-wrapper.properties create mode 100755 plugins/kotlin-serialization/kotlin-serialization-compiler/gradlew create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/gradlew.bat create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/settings.gradle create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/Types.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationCodegenExtension.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt diff --git a/libraries/ReadMe.md b/libraries/ReadMe.md index 3492aaebe1f..c74443858b1 100644 --- a/libraries/ReadMe.md +++ b/libraries/ReadMe.md @@ -6,7 +6,7 @@ This part of the project contains the sources of the following libraries: - [kotlin-reflect](reflect), the library for full reflection support - [kotlin-test](kotlin.test), the library for multiplatform unit testing - [kotlin-annotations-jvm](tools/kotlin-annotations-jvm), the annotations to improve types in the Java code to look better when being consumed in the Kotlin code. - + These libraries are built as a part of the [root](../) Gradle project. @@ -16,13 +16,13 @@ These libraries are built as a part of the [root](../) Gradle project. -This area of the project is the root for Maven build. +This area of the project is the root for Maven build. You can work with the maven modules of this maven project in IDEA from the [root IDEA project](../ReadMe.md#working-in-idea). After importing you'll be able to explore maven projects and run goals directly from IDEA with the instruments on the right sidebar. ### Building -You need to install a recent (at least 3.3) [Maven](http://maven.apache.org/) distribution. +You need to install a recent (at least 3.3) [Maven](http://maven.apache.org/) distribution. Before building this Maven project you need to build and install the required artifacts built with Gradle to the local maven repository, by issuing the following command in the root project: @@ -33,7 +33,7 @@ Before building this Maven project you need to build and install the required ar This command assembles and puts the artifacts to the local maven repository to be used by the subsequent maven build. See also [root ReadMe.md, section "Building"](../ReadMe.md#building). - + Then you can build maven artifacts with Maven: mvn install @@ -42,3 +42,42 @@ If your maven build is failing with Out-Of-Memory errors, set JVM options for ma MAVEN_OPTS="-Xmx2G" + + +## Kotlin serialization Gradle Plugin + +To build it, first **build all the above** and then, `cd` to `tools/kotlin-serialization` +and run `mvn install` + +So, all build sequence will look like: + +```bash +# Assuming you are in the kotlin/libraries folder +./gradlew build install +mvn install +cd tools/gradle-tools +./gradlew clean install +cd ../kotlin-serialization +mvn install +``` + +When it is installed in local maven repository, you can add it as a dependency in buildscript classpath and apply it: + +``` +buildscript { + + repositories { + mavenLocal() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1-SNAPSHOT" + classpath "org.jetbrains.kotlinx:kotlinx-serialization:1.1-SNAPSHOT" + } +} + +apply plugin: 'kotlin' +apply plugin: 'kotlin-serialization' + +``` diff --git a/libraries/tools/kotlin-serialization/pom.xml b/libraries/tools/kotlin-serialization/pom.xml new file mode 100644 index 00000000000..1daf15055a5 --- /dev/null +++ b/libraries/tools/kotlin-serialization/pom.xml @@ -0,0 +1,158 @@ + + + + 4.0.0 + + 3.0.4 + ${basedir}/../../../plugins/kotlin-serialization/kotlin-serialization-compiler/src + ${basedir}/src/main/kotlin + ${basedir}/src/main/resources + ${basedir}/target/src/main/kotlin + ${basedir}/target/resource + + + + org.jetbrains.kotlin + kotlin-project + 1.1-SNAPSHOT + ../../pom.xml + + + org.jetbrains.kotlinx + kotlinx-serialization + jar + + Serialization plugin for Gradle + + + + jetbrains-utils + http://repository.jetbrains.com/utils + + + + + + org.jetbrains.kotlin + kotlin-stdlib + ${project.version} + + + org.jetbrains.kotlin + kotlin-compiler-embeddable + ${project.version} + + + org.jetbrains.kotlin + kotlin-gradle-plugin-api + ${project.version} + provided + + + org.jetbrains.kotlin + gradle-api + 2.2 + provided + + + + + ${serialization.target-src} + + + ${serialization.target-resources} + + + + + + maven-resources-plugin + 3.0.0 + + + copy-sources + validate + + copy-resources + + + ${serialization.target-src} + + + ${serialization.src} + + + ${serialization.gradle.plugin.src} + + + + + + copy-resources + validate + + copy-resources + + + ${serialization.target-resources}/META-INF + + + ${serialization.src}/META-INF + + + ${serialization.gradle.plugin.resources}/META-INF + + + + + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + generate-sources + + replace + + + + + + ${serialization.target-src}/** + + + + (?<!\.)com\.intellij + org.jetbrains.kotlin.com.intellij + + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${project.version} + + + + compile + compile + + compile + + + + ${serialization.target-src} + + + + + + + + diff --git a/libraries/tools/kotlin-serialization/src/main/kotlin/org/jetbrains/kotlinx/serialization/gradle/SerializationSubplugin.kt b/libraries/tools/kotlin-serialization/src/main/kotlin/org/jetbrains/kotlinx/serialization/gradle/SerializationSubplugin.kt new file mode 100644 index 00000000000..1a8bf46916f --- /dev/null +++ b/libraries/tools/kotlin-serialization/src/main/kotlin/org/jetbrains/kotlinx/serialization/gradle/SerializationSubplugin.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.gradle + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.internal.AbstractTask +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.compile.AbstractCompile +import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin +import org.jetbrains.kotlin.gradle.plugin.SubpluginOption + +class SerializationGradleSubplugin : Plugin { + companion object { + fun isEnabled(project: Project) = project.plugins.findPlugin(SerializationGradleSubplugin::class.java) != null + } + + override fun apply(project: Project) { + // nothing here + } +} + +class SerializationKotlinGradleSubplugin : KotlinGradleSubplugin { + companion object { + val SERIALIZATION_GROUP_NAME = "org.jetbrains.kotlinx" + val SERIALIZATION_ARTIFACT_NAME = "kotlinx-serialization" + } + + override fun isApplicable(project: Project, task: AbstractCompile) = SerializationGradleSubplugin.isEnabled(project) + + override fun apply( + project: Project, kotlinCompile: AbstractCompile, javaCompile: AbstractCompile, variantData: Any?, androidProjectHandler: Any?, javaSourceSet: SourceSet?) + : List { + if (!SerializationGradleSubplugin.isEnabled(project)) return emptyList() + val options = mutableListOf() + // nothing here + return options + } + + override fun getGroupName() = SERIALIZATION_GROUP_NAME + override fun getArtifactName() = SERIALIZATION_ARTIFACT_NAME + override fun getCompilerPluginId() = "org.jetbrains.kotlinx.serialization" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/kotlin-serialization.properties b/libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/kotlin-serialization.properties new file mode 100644 index 00000000000..63de8aedfc5 --- /dev/null +++ b/libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/kotlin-serialization.properties @@ -0,0 +1 @@ +implementation-class=org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin \ No newline at end of file diff --git a/libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.serialization.properties b/libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.serialization.properties new file mode 100644 index 00000000000..63de8aedfc5 --- /dev/null +++ b/libraries/tools/kotlin-serialization/src/main/resources/META-INF/gradle-plugins/org.jetbrains.kotlin.plugin.serialization.properties @@ -0,0 +1 @@ +implementation-class=org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin \ No newline at end of file diff --git a/libraries/tools/kotlin-serialization/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin b/libraries/tools/kotlin-serialization/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin new file mode 100644 index 00000000000..29016e56c80 --- /dev/null +++ b/libraries/tools/kotlin-serialization/src/main/resources/META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin @@ -0,0 +1,17 @@ +# +# Copyright 2010-2017 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. +# + +org.jetbrains.kotlinx.serialization.gradle.SerializationKotlinGradleSubplugin \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/README.md b/plugins/kotlin-serialization/kotlin-serialization-compiler/README.md new file mode 100644 index 00000000000..fde63f60ace --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/README.md @@ -0,0 +1,30 @@ +# Kotlin serialization IDEA plugin + +Kotlin serialization plugin consists of three parts: a gradle compiler plugin, an IntelliJ plugin and a runtime library. +This is the IDEA plugin. Gradle plugin can be found in `libraries` folder. + +Please note that this plugin currently works only for highlighting and resolving symbols, and it doesn't work with embedded IDEA compiler. +To build any project with serialization within IDEA, you'll need to delegate all build actions to gradle: +`File - Settings - Build, Execution, Deployment - Build Tools - Gradle - Runner -` tick `Delegate IDE build/run actions to gradle`. +Maven and IntelliJ projects currently are not supported. + +## Building and usage + +### Prerequisites: +Before all, follow the instructions from root README.md to download dependencies and build Kotlin compiler. (`ant -f update_dependencies.xml && ant dist`) + +**Plugin works only with IntelliJIDEA 2017.2 and higher.** + +Make sure you have latest dev version of Kotlin plugin installed: +Open `Settings - Plugins - Browse Repositories... - Manage repositories` and add `https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_master_CompilerAndPlugin_NoTests/.lastSuccessful/updatePlugins-IJ2017.2.xml`. +Update Kotlin plugin from new repository. + +### With gradle: + +Run `./gradlew buildPlugin`. +In IDEA, open `Settings - Plugins - Install plugin from disk...` and choose `build/distributions/Kotlin-serialization-0.1-SNAPSHOT.zip` + +### From within IDE (for development): + +Open whole Kotlin project. Choose run configuration `IDEA` and run it. You'll get a fresh copy of IDEA with Kotlin and Kotlin-serialization plugins built from sources. + diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle new file mode 100644 index 00000000000..2cdb3a7e0db --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle @@ -0,0 +1,63 @@ +buildscript { + ext { + kotlin_root = "$rootDir/../../../" + distDir = file("$kotlin_root/dist") + bootstrapCompilerFile = file("$distDir/kotlin-compiler-for-maven.jar") + kotlin_version = "1.1.3" + } + + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" + } +} + +plugins { + id "org.jetbrains.intellij" version "0.2.15" +} + +group 'org.jetbrains.kotlinx' +version '0.1-SNAPSHOT' + + +apply plugin: 'kotlin' +apply plugin: 'maven-publish' + +repositories { + mavenCentral() +} + +dependencies { + compileOnly "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}" + compileOnly files(bootstrapCompilerFile.toString()) +} + +sourceSets { + main { + java { + srcDir 'src' + } + } +} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile) { + compilerJarFile = bootstrapCompilerFile +} + +jar { + from ('./src') { + include 'META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar' + include 'META-INF/plugin.xml' + } +} + + +apply plugin: 'org.jetbrains.intellij' + +intellij { + localPath "$kotlin_root/ideaSDK" + pluginName 'Kotlin-serialization' + downloadSources false +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/gradle/wrapper/gradle-wrapper.jar b/plugins/kotlin-serialization/kotlin-serialization-compiler/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9553fe277ab3ec3e955db42990f8f3afbe60585e GIT binary patch literal 54783 zcmafaW0WS*vSoGIwr!)!wr%4p+g6utqszAKsxI5MZBNhK_h#nax$n)7$jp^1Vx1G2 zC(qu2RFDP%MFj$agaiTt68tMbK*0a&2m}Q6_be-_B1k7GC&mB*r0`FQu26lR{C^cx z{>oqT|Dz}?C?_cuFbIhy@Hlls4PVE#kL z%+b)q8t~t$qWrU}o1>w6dSEU{WQ11MaYRHV`^W006GEHNkKbo3<`>slS- z^Iau?J5(A*RcG;?9caykA`<#qy1~O zV;;PYMn6SI$q}ds#zKhlt{2DkLyA|tPj@5nHw|TfoB{R9AOtjRH|~!gjc7>@`h6hQ zNQ|Ch4lR}rT_GI4eQoy|sMheUuhTnv@_rRPV^^6SNCY zJt~}LH52Y+RK{G^aZh@qG*^+5XM={Yu0CS=<}foB$I}fd5f&atxdLYMbAT-oGoKoE zEX@l(|ILgqD&rTwS4@T(du@BzN3(}du%3WCtJ*e1WJ5HWPNihA7O65R=Zp&IHPQn{ zTJ{$GYURp`Lr$UQ$ZDoj)1f(fN-I+C0)PVej&x_8WZUodh~2t5 z^<=jtVQnpoH>x5ncT0H=^`9-~oCmK=MD#4qnx+7-E-_n^0{2wjL2YV;WK(U;%aCN} zTPh334F$MTbxR7|7mEtX3alSAz|G)I+eFvQnY}XldO7I7$ z2-ZeSVckL<)N1tQ)M6@8uW;`pybJ4+Zf4&;=27ShUds^TB8DN4y^x=7xslL*1%HX_ zT(iSMx?g}!7jTEjX@&lI{{ifXnD}tWA8x4A3#o?GX9GMQHc-%WBBl|UlS|HYNH}JU z?I48Qizg+VWgSZ#zW<;tMruWI@~tW~X_GT(Me0(X0+ag8b-P6vA(1q165LJLl%zIl z?Ef?_&y7e?U@PK^nTSGu!90^0wjPY}`1@cng< z8p@n!$bcZvs3dwYo!t+cpq=9n`6Gi|V&v32g3zJV>ELG|eijj@>UQ8n)?`HPYai20W!}g}CSvAyisSPm0W|p?*Zq_r(%nCY8@}OXs2pS4# zI*)S^UFi`&zltazAxB2B_Gt7iX?Y25?B#w+-*y#dJIH(fIA<(GUhfiupc!IVAu&vF zg3#yzI2SrRpMSxpF*`0Ngul=!@E0Li|35w|ING^;2)a0%18kiwj18Ub{sSbEm38fq z1yOlHl7;{l4yv_FQZ`n><+LwoaKk|cGBRNnN;XDstie!~t5 z#ZWz9*3qvR2XkNZYI0db?t^(lG-Q8*4Jd6Q44rT71}NCQ2nryz(Btr|?2oa(J1`cn z`=-|7k;Q^9=GaCmyu(!&8QJRv=P5M#yLAL|6t%0+)fBn2AnNJg%86562VaB+9869& zfKkJa)8)BQb}^_r0pA1u)W$O`Y~Lenzyv>;CQ_qcG5Z_x^0&CP8G*;*CSy7tBVt|X zt}4Ub&av;8$mQk7?-2%zmOI4Ih72_?WgCq|eKgY~1$)6q+??Qk1DCXcQ)yCix5h#g z4+z7=Vn%$srNO52mlyjlwxO^ThKBz@(B8WGT`@!?Jhu^-9P1-ptx_hfbCseTj{&h}=7o5m0k)+Xx7D&2Vh zXAY*n|A~oM|4%rftd%$BM_6Pd7YVSA4iSzp_^N|raz6ODulPeY4tHN5j$0K9Y4=_~ z)5Wy%A)jp0c+415T7Q#6TZsvYF`adD%0w9Bl2Ip`4nc7h{42YCdZn};GMG+abcIR0 z+z0qSe?+~R5xbD^KtQ;-KtM$Q{Q~>PCzP!TWq`Wu@s-oq!GawPuO?AzaAVX9nLRvg z0P`z82q=Iw2tAw@bDiW;LQ7-vPeX(M#!~eD43{j*F<;h#Tvp?i?nMY1l-xxzoyGi8 zS7x(hY@=*uvu#GsX*~Jo*1B-TqL>Tx$t3sJ`RDiZ_cibBtDVmo3y^DgBsg-bp#dht zV(qiVs<+rrhVdh`wl^3qKC2y!TWM_HRsVoYaK2D|rkjeFPHSJ;xsP^h-+^8{chvzq z%NIHj*%uoS!;hGN?V;<@!|l{bf|HlP0RBOO(W6+vy(ox&e=g>W@<+P$S7%6hcjZ0< z><8JG)PTD4M^ix6OD5q$ZhUD>4fc!nhc4Y0eht6>Y@bU zmLTGy0vLkAK|#eZx+rXpV>6;v^fGXE^CH-tJc zmRq+7xG6o>(>s}bX=vW3D52ec1U(ZUk;BEp2^+#cz4vt zSe}XptaaZGghCACN5JJ^?JUHI1t^SVr`J&d_T$bcou}Q^hyiZ;ca^Um>*x4Nk?)|a zG2)e+ndGq9E%aKORO9KVF|T@a>AUrPhfwR%6uRQS9k!gzc(}9irHXyl5kc_2QtGAV7-T z+}cdnDY2687mXFd$5-(sHg|1daU)2Bdor`|(jh6iG{-)1q_;6?uj!3+&2fLlT~53- zMCtxe{wjPX}Ob$h2R9#lbdl0*UM_FN^C4C-sf3ZMoOAuq>-k+&K%!%EYYHMOTN~TB z8h5Ldln5sx_H3FoHrsaR`sGaGoanU7+hXf<*&v4>1G-8v;nMChKkZnVV#Q_LB{FXS ziG89d+p+9(ZVlc1+iVQy{*5{)+_JMF$Dr+MWjyO@Irs}CYizTI5puId;kL>fM6T(3 zat^8C6u0Ck1cUR%D|A<;uT&cM%DAXq87C~FJsgGMKa_FN#bq2+u%B!_dKbw7csI=V z-PtpPOv<q}F zS)14&NI3JzYKX?>aIs;lf)TfO3W;n+He)p5YGpQ;XxtY_ixQr7%nFT0Cs28c3~^`d zgzu42up|`IaAnkM;*)A~jUI%XMnD_u4rZwwdyb0VKbq@u?!7aQCP@t|O!1uJ8QmAS zPoX9{rYaK~LTk%3|5mPHhXV<}HSt4SG`E!2jk0-C6%B4IoZlIrbf92btI zCaKuXl=W0C`esGOP@Mv~A!Bm6HYEMqjC`?l1DeW&(2&E%R>yTykCk*2B`IcI{@l^| z8E%@IJt&TIDxfFhN_3ja(PmnPFEwpn{b`A z`m$!H=ek)46OXllp+}w6g&TscifgnxN^T{~JEn{A*rv$G9KmEqWt&Ab%5bQ*wbLJ+ zr==4do+}I6a37u_wA#L~9+K6jL)lya!;eMg5;r6U>@lHmLb(dOah&UuPIjc?nCMZ)6b+b4Oel?vcE5Q4$Jt71WOM$^`oPpzo_u; zu{j5ys?ENRG`ZE}RaQpN;4M`j@wA|C?oOYYa;Jja?j2?V@ zM97=sn3AoB_>P&lR zWdSgBJUvibzUJhyU2YE<2Q8t=rC`DslFOn^MQvCquhN~bFj?HMNn!4*F?dMkmM)## z^$AL9OuCUDmnhk4ZG~g@t}Im2okt9RDY9Q4dlt~Tzvhtbmp8aE8;@tupgh-_O-__) zuYH^YFO8-5eG_DE2!~ZSE1lLu9x-$?i*oBP!}0jlk4cy5^Q;{3E#^`3b~Su_bugsj zlernD@6h~-SUxz4fO+VEwbq+_`W{#bG{UOrU;H)z%W0r-mny1sm#O@gvwE72c^im)UrJnQgcB_HxILh!9fPQ);whe*(eIUjA(t{8iI(?NY<5^SGOr;vrcKpedfTu zWCTHMK16<@(tI%`NxN3xW6nKX{JW=77{~yR$t1$xwKUm7UJmOrnI4Z zajmwO&zZ8PhJ6FNRjID+@QZ8fz%%f2c{Xh*BWDIK zXrFxswPdd;(i}fLsNVb(sx-hMJ>IQ0QvH^z3= zc;TX|YE>HpO6-C5=g{+l3U6fF`AXJM6@kcoWLQXxiNiXab#!P8ozeR^oy#PfdS#aj zUDKKNx>5&v%k*OBF;-)X5Afpd60K{FTH@1|)>M!!F)jb))f&{UY-rcR>h z`~9|W#a`Yw7fD~{3`rktJC|L46-(sRaa~hM-d#KSG6@_*&+pnNYQ2JSy@BNg_Tx7< zB-vhG+{d^*zIH!;2M7O`_S{?EKffQ02;N>=2!3JqQX(M_Aj#}dCfdb?yGH%tk^_Zf zAtZ5!rnq4(WSd!_GfuPp4uDd2(8%>)Iu6z=XjRQLi2_RBg97~ zr$zf>FNkUG3~bp6#hl^3HSA2*SS-DT_QkX#QNcG2?8&Cm6Sj#}yaqEhjq1GabS)ZwBhcKc;52~Qc*Z@=jRjfqZO1%y?*D(iB&EE z-Aln~CD}?DqVGGB``Q@F-TY|Fj7)4D28@Z-@a-A4(KC*}W4*2l?E>!wviGFcB*Dc3z50hH^i0Y`j zip{Em#(a42NnOEvkU+6SfAkEzO$ z*j*3sOP4y2W@t7)nbi9Dcj|9Bw}z)VzKuAx4<&3`!gMhuW5&4%F@_!ZKBoaBHYwcn3WcL^0l zkdkY#l8~$5UazRWOJo32=kA|tKs!Y_vX=+xrA3Mwd45^vZe02+dI_r|rmO-`>l0$i zEB%YFf8ecv=Q@YPntwR)df$>p+zI@!1-aj13HMYz5$QWWp$U&Z(I?C5rYl8S=m|d!*(Y&`gzl zu00=P^fRg?$GE2+$)wr(ohep`G%yKT(qdGmR!M45W`~K4bC@YwX{J;T@dq=$9o>;L zz%NIUoFhZxHIjtR1kdw5V7u=4{!3oQc;za?0UQVj5f%uD<=^`&>TYc9;$-0p5VNob z2pSvzby?QX*3j%fJx*5BcET~k^5xT{iQin-qP*nWQ9THOA69^wDN5utzTj#~upjf}CtShX9;wdXE35EVlzWqIGJ z)io1?vG_sea+iQjU%m@q)4(=eS5zC1h|!bCE~d9gvl{7)!IScau*OTR`)!Mhr`mdX zlhmcf-Ms-t;DYx9o2z=q68Nm{ zOF;j&-eqWvD}_5X8`^t48wcrR%*&RycEe!J5nJguNo~cP6)1|!4@Jb2YL6IYdyrH8 zI$W1D+$LRa4*EC=4Cr)=0Qap5g}M^+jyvlDE}G8-wsVQYX&UXR#=~{XZLTPY`=3=N zkvaUS+4ofuBn|356>5pTPX|r)^QG(R2d$TX>Krwf&QVgVCM9zP64l%Z8B=2RYP%{E zaKc@qdtK`R({$|K`t5>0?KorZI1)6`9@|#O>v1WK@3bbLFtGM4gd98X0(-9{W{NiN zIuG0D%0l5WhXSRNbfROzH6w*YO&2Xpx5amm%+T4$qtvPDK+eUjfs$g@<`DBwNH1(33NhDKwO*I9E z$bW{D7h4@U~&K4klFtk`+Smzy>$vNph6hQsYQ1QF(- zHK>f)>|MT%=q)(U-3br5R4KIE!FeeTP`{-^wpgKJzcOqD?!&-6Yf7fd<^40T$r z{@91>s^KAH@mw(72{v#n4rzh?z_qh-AL;FAt==sT(BFv)(FXSoKd)RMA40`^)3^+Z zwdPe9j*t}}%!Fk@58lX}s`NX-7M;>k)w7j1`*~g_dAMDLsOq`@C>D(lreX%!c_OjX zTP$xDO*C|S27Hd)6?;6;Y`P3$%YFG)9y2H0Yuw;6Z2{^y2YvKP`V&OVi;L`j{L;jL zvz-omEQby(t)f?-HssRfTDYnS`=UG{>1Y)Dh(Xb>WU++>XOoF@TR;-#<1E+1AqPdk=H6)VQ32z zLdHM3uv~8{(>v|*O>k2VTW}=fw~%fuNfyf6FMaEXzdHB?tnHs6%)R(k_^``|IN|L# zV&QQG*x~n}a?;|la|TQD383!6WOfCv9V@-(g`ab3{CgpIjQ zGyCjpiIaK${m-Zd;m*k+7;?~M6)Wqb>yI*k`=@zOr%NjIs(C?BUqCq8^ zsi_)Bk)kyU`NL<6nholj+3Xs*E%vZ2H<};VoFCvMFLYwFg-gi8C%2@0gH#_lU>~8E z?>!v9-YFw6r=Z{xMI59a3J6_y8&}4UeEr?9w($B){={R9reR;r4Jgl?G)eMv=EOsc zckWsS;fuDu;l?Dgzgyhj^H>RMJs^*kzUfB#Ax}fqmj?Eb#G1W$J(4a)qfI(k=2*_Y zqr3?H*#`c8owZQ>48MUl@A(yQxuXBM2|bdy`x=bcfHc~8b9#odFy|NGMC(oMC%C+$ zi;L=xaJ%=;6Qf)kX-netDG|g#BZrnfdTm79e(Px7oy)wLHNB^EUMI7snGBJIuq*RP z@Xv@1TIRW_^S82~__wm~U(}t&|5uS))d}DzVP^x7v9q&svHy>{v$D24wjk=4SiJ7i zqf#YhQ?sQusP?MXrRx0PczL)ABq5Z%NibA3eTRvr^@n;Fsio!I2;YM^8}EP;&7WT# zqivIJ-A+dn6W9FwzQ7v&<$;P5qwe`TR5_AiRFDRGVmdG3h+?&byKRASKwXHQiegIU zvi;If(y)ozZ%=Q6)cR|q)pkV>bAocyDX#Om&LQ?^D;#XBhNC;^+80{v1k1(4X1RWKo4Onb+)A zp&OGpq39Ss9Do68%xbC+SH>N@bhr?aF^3ARMK)^mWxfuvt|?ucl0$sf){gT9_b~^# z3>QnE)-@zE%xH=ax{R1+8?7wHJFQhqx1xirV(lZN0HU=>7ODhQ5k^5BK973IumdDP z(oUtiC^Ya#Q@9^~vNuH)*L|F$!0eySLZ_2FYGn%S71MQAFrHK4i#UwxjM0gxL;pC#^nGA?B0S zjI>+f^}Ik10y+Dkm{%iS3&XUVZ;GCHpJ5Re31~x@7X68v;(n<6>>q?g=^VldiKw#@ zEOQ_*7zX;nDQmDM597=8yqlznk7 z+#rTK!TN>LKK0vPkO?^!tGYfh{PQwx2{$;;hXw+o#{4V)o@o7JnX3Pzzv6$kNc=~k zLIc7ZWf|+6KhEdwl_w5PEQknl2TTo9GE7ziZ{5ESq%({Nit}IqJ>FT2iz#C<-kH>9 zZ7#i0)@|N7p)q-r1L{;J^UC?UYp(10rKh8TRyy>yhJWXD>$&^W=lZ>SB=Othg$XEg z5FL%%z9nMPJzPhRIyIGwqaa@*F!II`tmbAv*|$^bO0Q~(jj|aJj5BP6N%o zi>Fh52P_qg$2UE^&NabtBe|(p{jB`_nxYv`c#kx>LN*OSN+N zU4?c;6AYnTgQjgGHWamUI~Jj|bO=J#gpsI+{P2#bjpt${i6FN0W?!+*Po|F(Ep~r^ znlCW6`~{P*dJn~2sE-28TWaVhPubr5OB6wFGHdSr{ylUzA%71gLT*B+enM2v-TrvO ztop}Gd0>sC_EpOG@@K2?m+wHVUHJ=ochwHJueUm~pZw7CElAsk!cgpuF&clLJlcoM z5RfmuLPJGOQ&+|Qje(!|_U>laCSIu5Go16&6C`MR%qhi#y^MTR$a|FuE7KaW!jdVu zQc6y3$b-fjA|zT|iyLgCtE)?+*{ez$14G@qDry0u%fYe=m_L9 zcpCG?q=Z0|3N5rQ75C6%&qtH`V%gd}#f)a{GqGaN!;vg5_;5m_q=-%TK(QnPrSGBM zJR)n3VvZ+adg)`v(iogiMOEgsJRqsAT%F)$7q%>N z+>ypdC#5P+#5I)8tD%Jz_C$CkQ4(v+;XO+*-@Vqfr%y4;NXBbf)IKJp+YrDNXQtxD zPjcXDE`uD{H50-$)3Jxd>X|xN$u3~#ft_j`y+MY-5bs>?@)We6Dr$y%FUB(3ui3I# z7^>}aXe=hA%0I;(8>2ca-1`OXuRv5Kv8h?&2rUu>D9D7L@V+srE z;`vC7L`JG;GbZ`e$0uDdeHVMFNI+5qBQG04|Ejy-g zBlav6v%&NUA^JNO?bO@ZQP|(AT!lFEgBu*fg)=wOA5wiaY#-n~WK#|S`TM7(g1I)Y z{MElhws)Vgzx?^BUlK$3_Zei$(_xyl<)dBB_p!esdMsYJzw(HJx!JOYS=cmMrTh5V zK48AlHI8<>h)vH(Dt}CkO2SPKUCu>*r(ZT(MEJC`EoDeyIjAiZ z4!$#Bv;#Ha|50x!E~2$H@qVM*{HX?6=U`;C_*DY9J?+_ zE_1(oZky$GE>%urwl$tN$r2Q;P6h=-(#J>KqL@4-5)GJp?Lnl!QHTV56UmG?h?t2t z8N0+xSbWmtk1G4%6cSek>wX?&<^~ckAjopL$THKk$l^NQSZr`^P^wN!3f97?2^9l& zo!!HDu5GNryHQMMV&*B02#4$-Kd86@R8@jPjIwC0qR`5yN~0wFF<)(m`Oe--meLR- zQ^9g0Oe9t;I$nX*0sl)jqI6z_x7yg_iIO2oCo`RV(;7kceK2{MG}=Z%q=5WqSafGh zp!GmTD`*RiQDP@S%N*1(9eILhgEc~3nujB!gK^;UZ?|@f%BqT7`F*;dx;_lgxCloE zv)sDk$CT1t^!Ia2yo(vQvLn$!E<}s<-iI>wtXvs#cScn-lpVpte^S&<NYtNP%9=Z+{&Er+rD=2JmitU_vutwn0S4Po2dU$b)6jiBdJ_5VEwz9fT28%;c zk9W8e_B3!WT3Yoz&l)@3uIZ7)GxE z4Xl;;y6~Y|bC|KGj+Bzc?zL66dWH|!>z2pjQuj2bzisLrIDXD?MOOKv{oZumqO&Tt z(~hW<7OR@y^~R0RadKcc}NKI%CiV=eeh%``Vo-RnrvWK(sOydLoK zU$2g-d)ye45;H0P3=L^>a&{%W>(CZNGqYdWEauKGS;tJg%qiCob8E(^&Ltqv)pJgJ z&&ALyxTw~=UZJ1wWa6FTSiq|!=(n^Uh6myUWeNhp4XN3+{UOy#Ftu8-K`^nJ>flFd zrY{FgM8K$1LqQ75sR1Gihk}T(Mj6_MzTTVM8c=aWC@_Nbl|mSZWE8KFmDj4&kDogj zSUoIBdvUaPo-Qjs?4qPLIBoTo}E0mu%O#i zjm2g)0K=|B!>PrQU6C)*{U!S_iH;eR(+_BcTepYExFxn8!O{tLGH>!>zj_IE7r)%$ z?Kj)U{L~DD5_u&9xkDs~GuDvcMA#7<3~M4F-;4 zX{_?jDjL0nedG#Aj2fZRjuBw*dG&M}z$K~y`=~0SC{f_vKrGD^_#{2q!p2xg1IciZ z;6wviQw)Z0Hz~1MKn_K-%}1{7iCGmZyCb`R?p&CxP^!0b{>qsgub#@fpls6(4F0Qt6oWd-ZU(qRseeZ6RRT3Iw%y-mKV?})8V^t>+XKZ0#Gsb%{m&C+Up z{YiPA(cio~45i}`!<+#^hh^P^Ax*|;Uv#Z_fvLAL!yjHjeiP+X&0K}j`c_F-kh6dt(*W7~Cd0 z!!{rP?PE89LfP-8j=XH)`|5V2_sAlez76p+Ax{`9SgVx3_Iv1IRK>q9QHADt#*Y!6r?w zJ5bTiaP7*l{|Znqg@Z$x7oV~vxDJT69J;^p?pH^8117H{G^OIb5#ko3+BjY7nwHaj zt0PiK=(W2l&_CZ%!Nyr& zk;xb^^2gea?J8Y4B6V6KpAUV5{4>)%zR++g|I2XK{|fQHXS$OA+0XV5hAa9vXWGvQ z8}dDIdW4G939a{NblX`04I-%Upx46uQ;Pe{nJ*K9pf?nmI~fadH1*^4-g}b(2>rzC z#1j(IH=l-#O&&7wl>AtIDv5H{5F=QBj8)rADX4*jNMqATF)3Zm41sst%ZI71^f^ed z@k4X+T)1B&GpQ(qLaBD_CLb|`4ZHuwn4wK-^(iT`l{D(B;7B=Cz+M5OEeKs_+(z2v za^=DLy4UYtJk74ad|CLLJpGCAUwdln3G6T`G}oWeH@cHs@7q zZ;{{rJ#XqSrPu5YnVZ%rkVhU*S)AM6sn6cq+}oTU@7p!q;08Ef&9K@xt*``1yTZ(v z%rc{K^2CvW;4I;wa+Z|j@gjog^LHj>_EJal#C3qQ_`di)StH~kQa)IQfO-k@l#<%^?z_se2)nkaRm+p zPBWe7uN31~FEskXR3)9XAlHgFJv&e3NX2J-cgVY#7?_b>+!ly6f_$nIfQU#xA z)62KU z9-k;5Ns8x>h4*lKw`SPB)%zGPMKSuj^&x*-(Xe}F9l#p6%3I3~#%Xiyjwj*-4 z0~Yjnt=EbfR5^w@kvUvtQg^rxvBzS5v7#6s+?%HBy3@SdU!}ZTW!kVhx|rdZMRylS zPGddO{_KC~f7)30WFCU)mud)b&HQbnKg_k(OrbtShyJUPo>I6flvXul0WOo zW2?G$1Uv2>>~5z@7{AQS`WcR|NK6bR_;sX1TdBR4HIPQ|DWOhW7ypB95P59D(C&M? zRyztK7nufK3Uj?YTb74wuIqBT@@h!Q(R7V6Hskn&_zYAT@5l$Z;abhWF*eh-9wum8 z_WpLonUYWAz1wt9i7`t!CUb`e%cm&*bV4YBo( z58L?ql-giN`#~)zhh5Di5A(0|5>v+e9az(x%FcH27o0(St?R>iBxiyBPNoJAbZVz- zS}tavhAJ0kgd+tZjT;&?Bc%%F3vsl#+)G2N?I|@T%6`h|7*kwkGqLte^qR*n0c>>{# z-gTbvExPb@9s2(0T|wq12+Oma8+`3o#BvN+W|Q7o0p`?NLu*jCe4%a&DjmuyCl!0} z)T$0ghCzsXXT$P*~yojBLuRMs-L)E+45g0MNcMtTz>~WZ3Eud|o zf=UioWFpEiNfFa|W_xpfdNm#~s<&6v75(lXw}-{(>=qfJ=7WlEcCAs3Z&jRxGctHA zZmsbixM5%p#!f2}I@{dw5xVdzM2kMSR-8{HvT~QixsE1tq#i1Sp~a*5#|QXg@VbV{ z+l52hbp+qNh+n~mP52NCG@b03k5R zC8cEEGUo2RP-wCS{xX60P~KP3;tdynQ8QG+Bh3&#P#3%$p-jg&JZP~`lZjy-ruMup zxin_e3%MS~+@&N_lp5}Miq9Jn3IW%TuVqgu%fG%ueu!E8J<+ktfppS?F!Jjabc>)f za}Xj8`o>RnXqxrq{a^B2;5Gyqcz=Hxx}X9ABK$AV{~wt6zuR!VRSui@DOl3E({%_z zg)oTn`%0kcqqzPOFmvo_sGCzBbx)~6PT^gT9~qPTAUb1!ALaXwua$Ad zN*U$e)koOD$L}5i{V;&xe4xqwp}C&HY3ai@nL%FV;VEbZrsX$}HXikZ+tp6y-s79L zADxR-ozw#3y)ed)bF32cl&ESj!S^4XVxAeOeEPf7FKw&SRz(G50>^h;7E2H>z+1oV zt^Aj6-1+U2j>#>`fjiS%D82LgZI~_o-o9-HYPu1HwnI>;xUt!d{OlCwqmM6^GNco* z*{HS`_iuLS$Q|%q`rM$pb3Jrm$H`wT^4+4E4ueEd7&{N2QcSYVU3V?;)u*R002cF3_eFPTkdWg8D0NlE3DW8Y&l zLU9lkf8tPHl}rp2GpuEgek$~~Vhi=KV?dlcPe|`3yW84AG4T| z?>>1gRzk%lb(s>@r8GOn<9X419ydKlrh;BfB~LXh?nQvf+c3Fs1c{h-jV`hlKR9C= zznFgMZ)QnZBBWp&3nQiCAWj4!wVxAN0zAT4Wfrklj?4Xq)D?F9+M^wdt}{`YHnBOp zbKaxDALj*|g~Ged`KrVnRM9=l$lNG$tOd97ux9ljHfr-X)pox68%w2U=(bcoe7TO5 zQI^7v~qkOC9lph+Umgo3Oo#A}sib7A3lAmsx47{b#ifMtPr{^E3FN@Dnx2o=3 zK0K0Zj(MT|1o^s4@8G-(#`O1a>UatC%i3UqR#H{Jp#9LOO{~JqZFQB^gNa3VYsxxP zdtyqba^lb`2!*C;yc5UR@9C(w$6Cs~x&IQ)Jv|mm?~<|Y9lLUGjBDjr+ivj;FV${& z)>i#Ph!dL&;DJbXQsWe)MV8f!(}a8LV4>AuA#*)RBRxvoWt2RP4d}d&MphE^Iit@s zQ=^7xY2XTYwqn<gekKI^&oubIG!&M(Ua%z=;PCjAK8WP*cFqgoJZzsP4M z8~$oUsx7G6u+aQmIpAc1J-dp=*ekVHLO=1t>wfADn^aA)&}=8++o`xr*lcWERK6-w zHDoIgG2LU4rZ0t-W@&_`b5B|mi&^~DTH&scMO|Iw1{g;c?D}>#m}vZrV=dchn8!2+ z+Qv8GTIZe{$2hfQAuSh6T+7fxb2uz0%n?+)-LzU-C<}5CX#k7CplPZW{u%53Y#e(1 zgo)6_A*#Y+z6NE-9Bf{3Ib1TSl+kG;W`d(aNY+)<5Vum3Zq+4a9Ms|}*jn0;WCC64Pc1Az`CY0=-k z$5a8Mp&njQt{&nuwl|_^xS}rh< z(#wu{IlD&m3s~${!pJ`S3NM_=xyK-}pyn&Oh^$|V(F+2YB!gTUyrPQIL|pi2e$ECE65#dDJO6vV9H15{cjs1lOB zC^?*8U0M?f<}yYxI}B({nHh1AN$&YvA!~An1b64q-x7xe_c+wwLED2GHOk=SAL!pI zhb^yo3%{$IVx@YHbE!U@lDE;EKLWR4BEXg&hQdUmZ;zv#9@HatIge>B;(iwog{ZTBnlla=sVbuf&Zl_nR7(b-rg z9Cs#mA_^>qksL|9ffWG?>_CfSGLl?|b9Bx;%i*&nSc>sV96|2Ns!^cD!)+3LFN#k#g)ns{t5+U&%Ms}^M73|+A zbWC=7VIOTijqqmt0>=9~FF@Ie5_RS<=8*6W`wp5_0kSict0+sfRDLtNy$cv};X8D6 zi8u-2BrJ(O(rI=>%dq+>sL4Ou_9jF3rBWAdMgne-xyMf(JuN<0Uen)`$M(<9es0W={!<7Cdyoqp$s1~=0VWo7)M2Q_`Crm z`oa}e<}MB-F0%@=Pim~>2T3HQQ{A!KB%cbH{Rwzii0h}n&xs~)G+h&<*(YX6^pV=s z=iXu02VzEU0VUl$ZK+5C>&y56V|tytXc6IdgI|zZm{UBTgU`AKia^r1B=hbN*uCZr%c0{KFd=ZsujjZ?ux22_|-_1O^t2p9#E6B~q%zEOKL{Mp4_~2@Bhs2G?54*u@?wnOT4m3FhA`7miQhSWp_ECr)&nUh}!LD^_-DaYi;4 z7EIO+2I&@VZMks~2k)A9dz3Nt13U1+_DqiN>UIGoMR685eoV{4@BJDUod46Rv~* z;2Yc>fggVa2`16!1Q-I6)rc(qUG(9A9h(~7wDsG~AKJ?4kg04b^vgkT8&TGl2H`ER zEg4PqmkO(Za!%2nxY(#BINrEm8*;tctaEwD!MzRVGRFq9V|8K8te!-YwAt+PDY*jF zj8Qw*)1!e6=cZ7LaKq`$J$yS#!_f@v8~B#@gKXuK(V?!!ulw=>1ok`z|M+w068yZK zHKL3qH71F9Z64_^6qpk#KO5V4b~A#>Qs^W2nW&;I;%nWJFD0yrM^wSl^!HdF4Nidu z%e=#jWYSo4V!xT^i7r+@Vmz3)h>yr>E}@deBd~jL^O$GbF$8L`dx(<K}aSo)AW*O~MMc&DIKo;eE; zmpQTpQE-=efHT$a5)gC6^`LBp8|2FF|H0Thz}D7p>%-kOcWv9YZQHhOW7oEA+vcuq z+jhI#em(cR7w5g_|K%pD$x2q!q-%~j#~9D=0hq{G!M!=ersQ*+ZsJtxBS$-~h`^xU zBG3a~VJcsT885b&cEJYYLzv_T_6nUStVtHnd@F+}-P9+DrI zIsn5g30?!p%oU)QM;Q(a8mNb)$UF)rnpF>WfUrZY0}QuBjQ`gDiLy1N*tGtG(fRjK zK%SKy3=(8%xCo`BtHUnF+_Xi(|M7>@3?86PPjXja2&F5(X)+>OxXQXsxyrgbS5>KO z(mN3aDm&RNW@c_THOr9mP=c;A{SH1R0X~jjXg>|^Q!8{E;9}cs#1Gb+!r)c{JU&Lu ztzQSkpTUA`h&%2M7&u+mLFZTjP)i_tpYROxc4p%VZ(G&CgP^ly3E6* zY`KA{1$@?y_E&kh1M1RSK=%&~AI`EQ{%yoYf{<@n14#UK4c5~nRmP6A+_}li5eh|- zCj3$h|BmJfR%p`C8-?5tA5Jk+MG$U5(K;UryU)s~_S2iw=bL28eq*Fc$=6v}i@mPQ z$mh)Lfs@y6>owe+Yj%$<@sd9{tp|Bugm`CG2jPN(N*gNjtq!qM>f_XcPBt0W=H-_6 zNYw%7kmtK>FEx42u^3r@nlWBssyVNJa$rNqpyxBwsVMHg0zIJHGvNR&aPe6_&!6F2 zm}BNUTQm56;Azu|VG=1e8uSfo2v4+>RV{r1B7-IMPySp8{9O96RuAGXjL`p!`rSNy zz=cxhK5IEb1E8bc>S$e*F{Q6R;?@DY9Th(x7BA-aJ^cYZm=&rb{aT0qho@fMd+q5) z3_9!_fsi-#QH{Vv3t_(}{P8kgw=JL4wcsF^9~m0}2W;O~%+3eB+8dpLA-EkEBwjbz z&d1MMgzYDQ%&yR3)DvN~4-6|_+S&1)))139O22&E4JnT#oxl`JbJCAkosbmV{tevO zm|52qAJ2i{CsFiiUm@N)Zr-r1!RxH%VA~l@mPW?|2FfOTo1v6mAC28;LZ{J!LKrzu zM`8UDfM1SRC0f_~(|uAW$ZK5DfV|UlNV(P&a)cOC_GE=_6-?P%bpsTlHsgw3IDUx% zlg7v{TuS?SHIJ2<>S5A5jSiSPNsOp~x`78tFb6-!94&v2_bf=+x%Y91J)J5m?ut{#oW zReUZ~yW+En!(CwK%dB3vV;MP1daw|2W4g5^>PKe%+#qaGtTR&}$CW=};G@rdn8g29 z|8ZLr4uhW7^E1c;0C&wLfxm%{BD9h|&$EHOjOIExebr?Iozk2>tlRQ`%?i$#ak9|O z%bX>DK;z*`XghIR63)B<4V~ihpTd?7 ze1dD>7F547l6gmZy~(B#F`=$sf<0iaxNtVFZW}ZezI35;UV&6*MH$kTLS8_|X86LE zC8NH}wIN|LF<}j+YK!2W){|D@^5YfV<|oZsj@h1VA$MFzv!K z8LGBZ(&N`oXh3-6cB3>#S)2D7A_<=(ZPz|YcOaGLD^0I-vaP@(kC$&%oYn<0_$Bcb z2N{RKWvo(7MB+ME&e(?^HS`6cJwo%8wXxUJ$2YaNri5^_dKmIT7me(L@LKT&(Tz%H}F0D{FH@c0}ar2*hV4 zOnWnJf9fb<)7>=>BkrEzaFd= zxzn|){KI|-1ONc{-$QFswx<8Z%m0<|ZaXK3G}4nYLQz9MY$uh9m<1`U8f;5X5^Mwk zj|*W!@?MpgQ7vhnhZOY{?)wX4Xb|@g(4T_H<7OBHwT9U2Z?6RQoO=r2&(AlQ9XQzp zu^kh@6gx`)^->b~Kq?{aP)>o3Bs)C*xEa0Bm=aJ|^c9GKHO2vkjbrG#Gx5t*9c#~C z^m^@qy_%8%9@nih?*ti^j^^U@k#a+DPPWLllHs7dg(ht6S!`!Lhr@z`Xps&1_U3BG zk|8)|>#RJv%j_~-r6DD1?bEhs{Zr~VIgGnep~Ws}%AZO(e(FHM!vK zW>FnpNBi>3Bdx_#2<0gu57L7;pt3awsigs|8nPhvnQ6GTC8kz9l&jU4gS@vpG_M;* zJ|)`a^b6Aa17arkbQNj8&{rh$0eVT?WRyc7$cIni6M`hg2k$Pa5}ZY>no#17!C-|% z0-k;Pt}`qdj7wV1JZnV&U#}ZFRsEHdASdomu$g!83PUR}gz;PrjbDSKU9wCww;ep^ zj~8Wtsn?xE*yx^=9;!Ubpl%ubcc_yMtgHcKiK~L~9~uQTh7VKkCy{(9uBK|5zf>V~ z2*ox7$9-0?vSD`w*1xBi>}FAo1xYvR&XhUmISY_8-CYp8D}^sSh2FgI{^GPnJUb!<{nOTy(0iZ)#rCY;+H`JYU<>l;lSM#&7(Eg6l;l6^}2|z6z5d9q}d6CwG&_ z+l#Br#TYzS3g@+w=J-zIxH8^@>I=|0RKY%>R|O6$EB!EmHSOK`AW!mQ&HOt?DTi+R zBs_;eMZL2I;nioOoKpJc&XBqE0*(bE?P?I4dMzx{*L?O`65AL4^>#}S&vR19V%Qy5 zsr)V`sO#+ER(y8U>OOX7slJ(rib;ur7sgY%tOo)Vp|j6NG7OJDQc=(jo^(+)aX^u~k!yL=7&U^A=1Sb_7jZ|ng7f{+RXEp(CNnyzZbP2U=s8g) z+$u{efG`(0oE~>CmI=^H>SG#)GwEVS*U*y+5!Ky5)59kW)|0SPBvUNBQQkwe(&xWitYBBIS^b07@gud1z97M}3~EN1OCDCHGwWvvJhnKk;r)R z0T}dbRr$nAX>~OU3Hm|3-!kfjsQI51$Sw)lCcVzI=8L~#!4c&{NC%REU(nUC=9lt@Qe^8F=Mj2W*{uDvl zj@;9v_rlzUKc*GE-6ZQKCDm2A^+x8Ev$JY%tVSi39%-6v3b#zA0?}BihxW`b<&54X zV{>-*v2yURa5mSs@Od1wvaxX1x98z>ROk143-(c*Mslu*RnPrVL07(WBQ)xuwds)Z zXfPyaXJq5^6jl~C^j1a)qB)HkMLbellgJ`Gz-pMx5R)MsNJ0>ko_wmKFq4g?r2>~u zc39@(wAL7zHg=S*PkUx5EcgfN#dwp&7~3j%116#Ly+qOlf4^gFqyEuhwU*Jby@P(Z zl%>pkezxwwXL;|^tk3TGzAoL$_?+C=q;YvtU}#C$)#--1>t|<}-L92)4KfJzWTR6l zUVAa;a3qb8$UW0}1hz}rAf1(O(HO24$eeORr5?-c(M4Avo2HRY)yfcMdjo$M*4vyQ zb!Q`&m)pD@R+pYsI>>-M^24h{be&F}v@2)A`aA36faQ9%lIePrJqV;BSKY|j!cx2Z z&zCT^Y$%c?78Xg?s50v1TCA9(*u%PlSQui-sep<1%tx@_)B}@LlcuoX>L*(D5sw7j zHPZXW#oGLlA|q+|F(03St7b~RVhCe_P(|TgHor+Iy>(%tenY?%xG4>Q*~<@6Vvu|v za4+992A9xP;76G29CRf!{{eSp;sVQ3ZATw+8=^Xb(Hw{oJ|=x3M;|qNNvjmOb%g1G zJ56aV*!ja*V^?=eiQKb97pT5R^4WP@!H^;uS9-?s4^;TRZE9htX$m+(ZeJ% z_*4;@+P{6{3gdd49$YTurMltF!paB3ykU43I5ixhs?Ufyn$aBYYv!hnKo_pPlx_5B z5KxpvmnAghu|=^-kUFR-FP0OfXR>UAcHRjO+cP;nIxyOIWWlwyusGa>aW2tZd1i9R zUK3BaH#SCz=A-G#K}LQmXJd}v8fcnN4}%yH;R1vb zHGEEmee)pe6{_Cc3{C9^Xg1?hW+S=+V>tFlF*O^Ohm0cZ#76N;>Roy)v!zTl-;;1~ zk%DgpglRdXpZ?TiV|TXa1XzzSvv}(qUm!Fb+u#Bip_{%aJ7w$YU7idRwgP}$AD6?3 zSM%1IX6?mz$2uf>T18;t?w@sKB2Voq!HiX8pAkpXPx0XjxWVD(7rsio&<(Ri_}}*S z?k^y1rlN@z=?ZENjKTK<@)ijMxr2XX7bSGN=!p~g6XTK4p|AX*gy%_)RU$-XgoDq{D&edOtM`1#ah zPHtb$2z5kNVRQFN3`U#t(ar;IH`RzNkWE5F7GHWsaHYQ%bqyKUiMw$D|6Ods{>lYhrVQ6hvI3jaqrn%5w zAnsG&H52g-7NYCcK=PgSLLH178pM`8t?Qf2Osue+_7E@!rxk8S zAzSVawk`yM{4I<(4zO}JJJObjL5V-mjEi5vrmxV7pVi(QQTAA(V1`#l_3x*zRNheC z&-9<*9`qqGH$q^qX(NDjnMIwU#I)&g9B=Sco+s-E#IUhElGfxc)lPq`kbzwJ85HLmGYR(_vcH0So3HYqa38r!7u5QcYkt3;!oAd&QM-8j9uaKA z7w_vW;^DwrLqCJ!Rvj9Ei6KQtN0UsoH;XJxSlMsf`Yj>5X$hOHk7Z@g=C531z@$TP zORK)?D!%hYoQ)_#GJk7?99V;w-X77M<-~PZ#Zh#!f9k166YNSv&EGXBsz$0aYjpL^ z+(IKJl!+G{Qb5S_*)!^gO?o#h^X=35ml0Z&il(BbGSVlDI2%6JSQnF+ zW?@s1rUI=PaU%s15i%e#c#+N-ekMssu;bpS_z&C1Hw|4Z)3ZR^pHpm83n_HJBfXzR z%eG|*4wlA@>Yvsuy*)3RdYYDHKHuJBcz<+;+IpW16$X&wp3$8SI7?Bc-u4kj*}mrL zsmKs0bmZ+=gE&GSd7JeYqRO+=h}Dq|N#iO}iMv(8kGqw?Q>rEHC2t%QqgwK840kAW zk`BEiyzvuW?FfRT2RQpTuV`4gdwfpq&Gi!uJxCp(L^)=xc~d9OO$d=4tpulmLorFK zn+(rNnF>o9JNv&u3@~L{0#^6-hWmMrt>rekPtiS^xmaqqq%=Jy(gdp8Q#a+W24|v1 z*^rtW0S6ybal%Witcgg#TCZzxRITT&*bL9MpjbyBj?6GNq>HyqBCR2|E1n{=;gS_v zs^y^*7KMO8&Q}^13fya?pLYh28lJ2r`}II$($A}x><~!N)lCul8tHqGR+nH8Fq}GW z&by+EH6X51Z#s>!Yp886?EjQ^9v1eGj{hKxwy}&RPT)=A8B@2B7Ia?&j1nHCX-Jk* z!5K)QVShYDc&5kHKPB7uWc|QBE;#%_`YrdiZX5Q4p(oV0kXbT`JT-On-b?LHO={Zr z@DI%{QQ{&?DQ^u$1=fgpPFrLUzbeA3HUQGvmXCn&uP#y25b3NS@GpcE9JZ;EcksX3 zA55t)Hnch=o~j;Gls1W42)2RJN^Q0tzuJ^JGqD|;V>vnJuGYNPK5|eVBDoTeQ>X(` zBrz%z+b0BR4u{49QAd8xt5_NSNh@*`nwuM-jf}gGh@7*>h@7+UA5MEy6i}n&6=e$y zD!ZisNS&0T#z$QgWo?60L%IHktVIHHuuKCMl(Deejkv+%ZL74`U4qL{r{dw|jLBWqd_=(ISPa+|r4rV*cEnvn&Z41dC{lx_5rd0XXAh}QQU&gmD+)aH+@`xny&p}cjE28nLTL3@)+j! zfo;l}VLy02&^A5g?qx?+dH!Ta^MFQuJrRu!1G8u6eWMSyXPP5~#TDi}RClxgIeAc* z1pPLui>rQqY#Q1K%pNU|NlLAc&=3y4(#V5X0E_+z_No60QnRBPc_gl7(8%M2fP6rs z{{ZKjwkGI=xGL&l-5H*8!$7`h7f303O5D^KZU3-ms?}#n^$T~~ahXn%PM%7p&oybS z$?J!1$&-kV=l$PI6eeJFMB=`Iir4Rb;Qt}X{7dB~Xlr9)ZtCoy|KF=%RD!iEB0t>7 z*ZT2NAWwi_em=n^erE0tBLu86y)rbin3rI+T{7We^oBO`t)e*r{p~N@URdMIF3sG^ z^+8s~2FClGk4vrh_vvX}fTJ6-5Xsb0J(dWpNa!nj-jPWz*5@|&-bn$B2y-r@nI~)B zn+p}zTI~@1T6;4e2AC1Z$g0W566jxBZ{eq!&_$&sh8)%f;>;z~&s~gxK*4!iO832) zx@uM~F=%tT7yD)iG5K2yjO%rQ#KCS&&6BZe&d+7pwky$(&7KSOozEr}h+CIeX<63u z4X^4%h<*N-j0+gm%PeczZQFH`)7kD`R_?O1Lt-qEpx0 zLP=(=rJ;iJmmZ!=P#M=gN=-ZJpBOO6(6c(aHZ(QNXC0c8Z%0=ZQLN4|fxj7{Gkx$s zDQ}sPVwdIiiYKCif4~TDu|4MKCRKCj?unewtU=NJ_zVG12)zwM8hW|RqXpMR>L&7H ze*n_U%(ZMZhB>f8B0dX= z*hXjt)qs<4JOjF3CVknPZw%0gV`1Y1>REss_liH3y}dbw<3SuYUGcQ?pQmh~NA+^Y+;VUat~1>!z=hJ}812t|fL%&6Fw4k_vaLl%5P zaF}0KrvAe`GL@YpmT|#qECE!XTQ;nsjIkQ`z{$2-uKwZ@2%kzWw}ffj5=~v0Q(2V? zAO79<4!;m$do&EO4zVRU4p)ITMVaP!{G0(g;zAMXgTk{gJ=r826SDLO>2>v>ATV;q zS`5P4Re?-@C7y1y<2Hw%LDpk z6&-~5NU<3R7l-(;5UVYfO|%IN!F@3D;*`RvRZ)7G9*m5gAmlD5WOu}MUH`S>dfWJ! z{0&B@N*{cuMxXoxgB}fx{3zJ^< z9z}XHhNqMGvg?N2zH&FBf5?M)DPN#Sg;5Og|0wru-#o*8=I!LXqyz~9i6{|yJw)0_ zi{j3jT#nPCG)D52S+165KRchAq|514-eM$YPimg2%X+16RCArIZtlDbDJO9=_XyMD zoC^b@fUv711vit4&lIo~XncD2uCrfuKH8E``e;Wk&{8k);EWqCUZY4dFLKdmDl2_o zMP+GW-dzpwsUA(^%gsgRdYf#-3OCJUsgmJ`fGQap4~PuIKu)ZT(CxOSpRyUl=$|t1 z@@9CcP9_@rSKUF|;BN%KHC+N7d4VZ(4JNDI)}~sZv2!hs#<)>M(?2^H1`Nah~_taU^n*CbZH+v)kdrHiM?!|KO#%*anDcA zed#~O%=w^jdIN>J!b>@<2;X8ubcCH!LUaV3T0*)*P6lv1xM#U>JO~Lka?P=Kai~qs z)|hDVH@#0tM}OqE%ga*c8vmF(0X!4gj}tZqMuEekF6fS&$@If4oJH9PLW&Ca2CqS! zfkAWlfh!<(6MyR-lrwS$!W1cT&?~9N)lQb(4OtXPysW0aAuCFVGK)qU3A{G5JDcRR z0l*vGOmm7i3SwqTqa#ANOHJHqtXj*J-5DUpWe*|^!LSE7MH;VKN8ppjX3R8gSfnPR za?2F6Xxunau(+jZc-<7%)%3K*{j}AElzPIow3=~#ISC_ByScS)c5RK|nL(TH%;(lK z^u*J*<(dfJ;}Uiev!~7#lDhATnmpSY)w#;Y`=iAW#6`}@HGaXSeT;jsEvDL&Rwu?g zwa+JW;0MPS06x|r$VLq6$(ka8!;gGb1K<%MqGP+vDZWZJpLjKUgN0dK?p3C{D&tcv z?8!@{Tp?UxYWG0JfVo|U^rKmRPEB&^qgnQp(hU_Mp`Hw%ZX8fw*h*4tt04)@@mcJ_ zE;fJG*eg~9`F2+PL4%?p8fN*l|`>hNJhPR@f<$JH}SDGe|xPodBc@ z>*Gnzv5JtD8GN(Z%CmDFt?t%9F3^cpug_(Pj_XoBpS6RydL6+wWw4E%2-C%D)4a@G z7Mm4d{CY9S+M^0d1mLZT+oHVm5%c>in{0}!k>iT1C7#O+0_1Gclk$8$rnAyl`57^B zo9|71ttYuJ?CCDp$oK~e9lPh*aS!gBLQ1$o0w|uluKHCle;NYURgv7Cg;E*M8+;83~Kx>BJqZ=o*mJS9Hxp=bp~uQ+Q%iUB!>h> zOs3rb^x>b}>%7ncd=$S7FEv%w)~kN!oh)w>XYRbU2#{7MtEP=KR`!!n z@c6cm$`qZ86iAb-P2zW?ffg_?Xz?EWLv+Pnv)j_^g>gIsDw>%z=48xXs ztXy*AgZ}XryXSSAq;ZyAo)P&1<{h#o+VX1pS&x;c*LB2ys@g^|Ne^e&u(F($VQFzr2N;Uxpn0XHISA zuG$StIAZ#%^;gdx$;F0uJ&fE3FfcOV5yV(?_06FH)#7uOG>hC+zoVY1>30J3Ep>V)`nJL7 zk-AP2lh7;4f1R`YHyo;x@iS6P1L=R_8g$rKjBniGG z7Wy?lA+#98cwsLqlOX_;2mj}QgJ00aae3PBZO))?g054Gt?|`89P}ud8M2P~c zY2m?A{f&}{PvB%59$#`Yk6F9}LtTVLr4`_vUk1t5EDB5ygR+ri}TnuVxHj)IP*)IkApp`A~+v|BqN+W)Eh{|~%!crx)V;Kr^+pMkH z-VRyWpnOF)zmUX=sW=EW7Sdz15#ID+-r^V11Ir+;p$0yW;Ox4TAr-xrzn_b`k?bky zeItAr-#I&+|GRSkvlRau-}`?TWtEDiE56bAOSC zXcKZ(B?@}6N2NN5qNO?(71~?1N_iSEI}#5>GtgSGfksdS;%*IxVesnmc|!B7!#As( zgkcT^N*WT)relVUBm%nwL7Ks$StYuLd{O9NFq1)*nGAwTTHGTa$A)1vhix>~^ zwI|7g-%^M18t{Wp1E^%KnR)wZ~8RVWvNJrwz|vlMs7BF=)# z!#!W^ejQa>_i{U|rv{Nps!~_x?0z#}RB!+F_*)hdG!fagq+6O|;|V>DK|}OwLHM{7 zc|Q4JDqZH(nqF#j77OTDd%tU=1^eF_*XUDD zLzIL8?i~Il6q-m+m~@v*S2Gf6MH<43mrr3PsXp3Gc@CI9CsQ(oIsNyL`y-30TZ)y2 zYC@-4t+WFJjTIFKG{Ik_q1EU8u@@uFmb&W$L!V4#wKElaN{V~n%%E8S=L#i)yK!!&}msL1A@L^Cvs!?xT_*E3Wy+?&!bM>&BX0zj}N zWsjWwc*VWfRRw=egZ{i2*C%@Q6@@{UL*b;Ww9X^`b!$qP0Sy zC~!r#ku$&SkWCvn zA%wXT{U&rse)rLT(?kEqV~XFw)Y(gt1=pD3_FfE4BEggPx@1S6tDZ0ZScD8*)IFipTitfM{x-f+_9Ia~$WY){ z?tP3Z{DseC&$!T-VRNexl=}yi$sykaFt&Eqqf_>L$NZHPzs|)+crni^~2>p+%^0$d5N?uxWfDg`lerb52rkr$|fC*BhMw(nq9tjW< zVyoq}-AbIbelzit1@;rbH?dVZ4>&;pH95<@;rcru?D+W{vzL1c+X*`pA(KcEsv0J5 z8>+;r?@uE6ZVy`ZD%&AHgeSJFy8&PgBs@pVc#tnfT3K5lV*sXjUg{__>Bb@itc03T zqY?ocs6Ce36GFD9e(^6_ri{W3S%uRcdhX){d6o=%W{9G-wuW=;LYD68tlaYm5QL(>p!s%^L(DaS;O>oUeRK;kuUa~kLY$|&( zd(+mnhx-oK_v;PQFXh%6i<6GnkRzH!%2|(d>!cUjnvoBDg#=J!3L2v*2pgtSQ*Gu z=RCC%>XTs;O!aDy!=X%QiK8w96-@&t*Yed=2*U&LS z0^$6&T~hZC?1Fp>6%{d~fV|qvj(ms2(Ua!9Dg4-@-?flR%5sI9p(hOK^Qdv5}Xb=$>(jo4>I*u7NUC zyw$-D1RDY8JH4QF@IEYTf;JSon$LXTqQLj_Eo^HoZr>5s!0W2;3#ol30_UhcLoGP$ zkgJGZqf;mXnmRac=Q{0!EA1#l)h_iV6jGE9xOGkji}=nk5xH7<(w?_Ql{_mq#X^Ps zDrl19$7P*mtYZXO;`>IfGU<6IfHEoJLRWA?c7mlA2snEJa+2G{F|z9-5Lc$X_M_6I zS7rTj8iq>V>2qDS!$9X$3AkeoqYUrRvZZlu5AXhe&-qj7DINRpJ=$nbm&yJUL zcJ@H|>CqgW{xwFY`cv)wN}Xp%GW9wd!vU)01INOK@s$_sz16F3W2^K@64nUUezH@@ zQJiU(N4T!2=C0~dhUNu;Y&_yVmEn~^nk$dh5N)a%9~XmIbR7Nc8u%miPwioLEmHR* zySN?!T9C0CcZeao2$y3m!0*@y+9t(59hZ=ALbQ%d^GQ)E#qI^ctA?{nKcx$+W2A#j zcLQb5NUIbd)gvB~QWr^1ng{>h?Ow+v4w|%dqIcC-N&%ap_Fz6b`6n}Ti zlkcCu9o78psV=AQ@NEwJpC&!OBKiLjt|$Cu)}#UDa@ZbfDL5^M1T5T#IOtMJZ4M~@ zXh*~47lNRu)o#ag&x>oab^hT7_!}++Tu>Kp?ES&$NgZ=ft z@|%3a9wO!rj!ufs27i70Pfq5L%DKY49NedjCV1fw36Mcf1LIukMiBT~H*#ef1u`|^ zS>3!r3^IrW&|73LfNdaCC%H8HKgW?VdxC6N;*dy^8U1woISrmJ&t9gk4IS(~pI+}j z@q&fnCqtR$5RhjBLdEL&X@l(~du#pHwHPS`dQ<&40f&X%>}7*O-vM#J#po6?Y!?LZ z#%8kSqO^!ie^^+#kQpbo(yAwf6w+F9{5 zxr2E+g=yfXY^^*w^#T)dy*>{ssx02%=D=Iv@JdTqIii;(pCh3`y+{r`Qlv~G#KJ6+ zr-QLYiWxU8f%SEPjUe~u6gi2Y>}jl6O(nUyc^qx33sm-56?`f42*06OBLegREfmbNUvvR#>{W&4DL|NPV+As&($WF)rTOnFv3La3jr4-Hn6zUC4{4}gS4p|j| zXte{N$&J}b9RjH;Wk(fQ8MEm5MeheCL`nuU`LK6JG^(7x%thc4+P}<4YJm2`*J22c zv@7LA`$kj)8W9K8B&?Wg?{7p1U09yEf`82HVE-#!;om=j{^PFv=Zxw2&%3cI$y#>) zTgCC!f_Z)dib)na4Hdu#m6(?wN-ysPJ}QLh6xK=aYKgsA&Fm_COZcMgg&!u7ANCJQ z1XoK%L48~Ry|l+P`}4*&`|+0JdQMOG2Y}pgI4JTwMt$ljskkbA1%8w}3<-)-qB0f3 z!I@9PD0ju48_R&(5GqUqe(T|y$)@uJsaB(vrSrDwFMP-G+sqx7fdi-dcc~=&t}{(w zTCssQmj;uFlFp-e(*|_9ORZHD~t<;{*$w zNUR8S5`2=qbMkY8gr1sJ%pa)y>%Zw3wB3ic9p(>p1~$Nh_L)^oSkM);n2a2>6QF^* zQ3Xp|`{@>v*X7L_axqvuV?75YX!0YdpSNS~reC+(uRqF2o>f6zJr|R)XmP}cltJk# zzZLEYqldM~iCG}86pT_>#t?zcyS5SSAH8u^^lOKVv=I}8A)Q{@;{~|s;l#m*LT`-M zO~*a=9+_J!`icz0&d98HYQxgOZHA9{0~hwqIr_IRoBXV7?yBg;?J^Iw_Y}mh^j;^6 z=U;jHdsQzrr{AWZm=o0JpE7uENgeA?__+QQ5)VTY0?l8w7v%A8xxaY`#{tY?#TCsa zPOV_WZM^s`Qj|afA8>@iRhDK(&Sp}70j`RyUyQ$kuX_#J_V>n2b8p4{#gt6qsS?m=-0u0 zD_Y*Q2(x9pg_p3%c8P^UFocmhWpeovzNNK;JPHra?NwY%WX^09ckLz+dUvRC>Zu(= zE0Rq{;x~uY#ED&tU6>T)#7Tw%8ai&-9Amoh5O$^)1VfT3Kefm=*Pq?2=Wn~J;4I3~ z*>@-M`i4Ha{(pDXzdDhCv5Bq2ceu#EZAI3Kh^k0FHuZM)4Q666NzE%_fqXjP{1tp~ zQ1Gz`Vb+N(D=pG$^NU8yt5)T{dAxaF{ZoyB$z@NPrf)@G1-$w5j;@B_B(;6^#kyDH zZPVPxZPVGFPoIz1wzL3+_PWFB6IuBtIwEL}Sm@{oD8^Jf8UT{5Q@3HMRF0M4D=_E` zD(p+3wNv(r!=OA#^r6zxnUQeKY+Tj~-6J`c$SGNlHTst`!>PT8oP64JwLJ zo0&FdEy@+u>gWQrXTdhK^p&z61G=JYN1H5KCKeg|W9c0j1L*oI77G&T&Z5-HqX=VZ z#!c;28ttj9QSrIsa5}SB8OhDXn$8_FWX#?SWSGHu>Z|1%HI~2`_eAKIXQ46}WVn1C zq4Vx2!Tj@NE9J(=xU22vc3x9-2hp2qjb;foS)&_3k6_Ho%25*KdYbL>qfQ#don@{s zBtLx?%fU}M{>-*8VsnKZ{M-OZKZ2E3>;ko6$FWGD*p9T!CSb=4~c)rOoo5E`K0Ic^_ULF141!8WqUJpg$IH=MuWY`+G@#?Hu#}$j zDKKwbn1(V+u}fexB}_7WjyMn97x-r)1;@-dW1ka*LV~~`ZMXb5jwOa|#_kzpH|1;~ ziM0Z(3(i51hF699k}j_R#YEPp?^MUV~lprsYT9X z&C;nR9aPs;069~kp*WuEUfXSpQ>RR&>8I-|<=)3VsPW4F^3DhBOV6Nm<{%}(LoVbz zXCz2qe&_se*qqX*hi8u%6IS!95}mLi-(R#SvKM_{jFaAOIcxIBVb0D z#mxPNiCzQf@=e5;1EQ@f4{xlXGooG1uw`hnwcHQZLq7i3=x>PAecmrXKu~j`52SO| zuM4u^mx46I<`|*yI_~W;eFi6u51dm-AEW(@z|V9K4!C*wD{)wHI{4e}Yx$lynI|S; zXE2fV%8_->;1VDQXej!4Ogi*7WK5aj-uw@PdJ{y%P__4KNhoh}7HN zTe+&l792&XU2;`=>;_P>=;%@BAP49r&lpXeMrS1>Y4#0|J+jcu^7t0z?)9^Ups(Gfh^lT~da7_I!7SQqo`ayuRhc*HoBNP@sr{-|^8? zZO2pGuK$RS-u}UK!vzE+%OG}2?9bhm2&3fGYLRQRQ|9j-Y$VA}!DbMeL`e#L+sv5= zjj4V3+jU-C*JC8#R*`7i8LXcNK6~z+3=NitB4?Lh^QC_OW$sovcgmRdCXvymBY|-@ ztoIRZB6?q}#u{onCGn>H+{4iFA}o)(%D;-LUnYogL75kPIz`7E<~wT?Er_#ySf|aC zV(OPMl&RHZ+~lEHks$k(dahPU-n%*=RWxi_LmoyHn%Xhs`}=1Z7VzX@sL658PZ~r~ z)3-wXUIRX{mgZLx#p(P9TE1W>*(hvysV0P~9&Kj~vh_DYUCXw2!u+v^jWX6)+e922 z{j!a28HTt%W<)TvR5oDpvGZ2HbW+w{5yIjn=VP345an~xUsRw6M+E0>Yj z%L(l~15e>#g<$DAx#;2NC*lZ!Jgj5+uyjAGo%6HAIU}fGaKp}2Z)gwfjLfCa@MQNm zUXQT+U=H$fAjHv#W5BUVGinxT;W*b`BL}CX-fvd}$ZO!aei6wM4lvTSq1US%r@>b| zHOqrj9@-~x$+*(lL$$zA$oA?3M4-C&!c#q~H_=hl2;2n*%pNDN!M=<)zCx^9IzRus{1_>%iAM{3Q?s zIu~?m^B-?+TrwsWeuO-)?BonmXlc;AmRzV&e%-Hz{5S3_UfzCZXlx032W zT&r`5@e2?Q5v0)Z)gs03?%Z{(bg*=^ie<&oU=0QO;nA0ON})kq=^uX4b*uT)?v6`2 zwMgyt^sjpoc_|NjcyUL18e0u`Gj#jg-i@{xeM{f;`>%s*lDfN-MdsW+>!Zi)m`c6hL;eALmV6u+0aZrzWGeL zICYR@_=fPc)$s3}jn}?$32DP;h@$A-Dh)QEg%wTMGpnZ9g|~Vmf}-KiC~PcId9XNZ zNfy2&CwYf7*;g?iVuUU64A`Gq4f)XA$s!mbc;a*a8f(A3e`wySVO-;*M7dXh*>sRtw$iRxXe?7VPx z)^wzvs)QWJUcB_?N2d^{Z9KKssXr9v`3(mV1I4$q{RMlfp4q-Bxf@St-Pw3Q*Ef!$ z!{NR<=B)=|K&A(zG8TQxik5kFerKk^W(N6`tJ(+C8ka{3yfhI~zuw$buwnXgvJB~x zC)%fCrD})mLbehXLw+LA62K1)!9-)D$dTZJ8+OY7(gHj(3BjTIp;EQ9$l+|UF^9d_ zsI|CwwV*tyG>^V5@L|uh|BTI1`Tte+6;OF3Y1agIcZcBa?(T%(8r)2gLm1+c^ zF&AsjmD{avGrXl=zeaw_TKjkqz`rgOdHi^$aKiNYam-b;L$rme%A2UCA};#Re$5-LOr|W;3ud&1mY7pTQ#SM`Vt5Cd zYEBoHXs+%h(5*H!JlUzQkBSOQ6Lk`ou!6@mc-nlq%Y${&A$i4?KLoNyyM5fPPJlIG z$G9~@>H1RG-a*Ro`uS9+1cbU)O~xfHts+ENu#IO2urHiJn7 zdbB5vQBTpglO%Jn1jYCPvBhc_(>gL#ci(@;{t_0 z*^kA(phCrIXxmdUdiv};x}upJK!~nM@Tr%%y0SG*e6pAwGTuAJc~XAGUc@zzkfKvD z=sLfn^M2BD-OKDK>-N~)U%KjZO-u;d$4`MK7+kj`hxj6tdcFmwo2XJb`r!E!oPoh6 zZ`gv)Os7!uOwUm}C%%uPs)&?AQD>f#tcdK#N11l>yi(^0r8>oA3yp1*N)sw~#f{j2 zx9ZQN5jy-(4TV^S+$^_5RhSA{Gwm$2NYhh|rKKGx$MUL!(`{ zGHkktj35?Uc-7&ovz}hFGC2bAvyV`8HfcR6$gt5_b%o3g^ZodU@`A|nv6qF_>S=;>pi4y^+co?x!?qait97Ph0;&YL(~eiz_LI zZgkKuIc=oA>rvjEHK%g&gv)lP!G3vbNsu$)giUbeI~F9DXX9J;*n~6 zilm|`+CJlNTxoHKlUk_Y@cq%}R^OaHD|Xf6HhW+A)b+r-_krCakhjl`J~x9C)MPjUhAS*_Xx&3;)AU->jXas zQ1$dhp9i!%PGH`sFM%sSm59?qR?F6q!0Xpm7dLS!X|sXY84D&6oYo$l?lGwsuOZGf zoMfrJ(SzO)45AzlfL62(QPqQu(4F3g2rgV_;QLOh&RM^XSrHWeaq1d{=n3Zl)5r_T zx{#i#OHF>Y((l=fJGf8i>}A5=TOlaCBEWq18juk4Ti?X8W)2R38SEc(1i(uBpC$2tOJQuoLTL_=fTQtnoGAIm~IQ%Z%LlwcxC zIU>S{)58khv)}|LEyCExa4{d7X}dUa2$%SoE8t21a#@GDX>%98OY3V*X-~-rb%D+j zW5h;^a;cpHC63VbBUz+C0_9*R5$rX3mry-{fORZUjj=LCrl474o9g21VWH<@UhH~J zUDt@LHO^ZE_}51;0h4%ch8&SZC46V~+HfTr2N|N`@>HbCn>Gzjla_Nn;tDm}i4SP> zDOyTlI2E$iLrt{73=O4BUtZH=#Uz*tS~>|h`d-aF29nOw+F?>nFs;tWHMt;6VG|mC zAdCH+BUESNkgA|%0@QWh7QC`txJ4_rI}+X!k4YA}*_man=5ZD2Jr)(lOBtj=z2>qX}Gvx+3nNSd%coGbmrl!6@g%xYi9--d;<>eT!UbT zM&T0n6K%yrJ>wc)`ta{{`jrJRiA2jISY~&=Jw&QzYH|T2U__imTv)`1d}c)MWM>CJ z*`Dj)xKE*w=r!5HY0GS8d0;*d3XgBBFCN@kW0-mFJhLhIBUb9l>ICMAgTb#aThHL`*qIO_6R3STm?k%hlNH9KkrA=o^1S%YhbwBSwWb1&RBJJD44Hp+DQ)7gg<0j%>3|V(LAuKB6}9-^Ii`#nd=vz(21+P^Z`Xnv?-7NX(*m|@2ll?QF?|24{l-j1kT$+7ZcDf`yM|>4LV-Y zoHwQ)gH3C8`l*qKrP!=wjsvg`sENhA2`1zPX^RrhXb>Vs2z!hfg{ont^waepLpjVFoy8I%b%d`K^XW<`R{$( z%Y^a2#eQpSW_F9s?I)ahzyrIB`W!@fLeu;D%xI(5tErs<>3H0qeo`9;{2IFm*Ni;H z++0Pw+&xlnCd@B_P_c<^bEP9%=R7nW+Z%d)vD2w!yq6XXN(uhPv09xP)oq6C7}#*P z0^3RYJcTDW{u!@s53As=5*FuFeSyTh$(%hsrOn`^zTY`8b7vZwX;<|GTobt-OXYYT zSukNSl^Vxt?LiTFg^FSw&uM!^h|#hqn8vI^ryd}=0yoAn!ENxdhY&SoeITRdIP^Un^qPcuL~zsqLYN&WuQYP4YP`HRUa6~k#ncCSuhxW&)No9T!SN1cmidtmZz+B~d9 zcmg~ZLTAH3F&HVv(?L5vHsdmcC^TI_ete8ATZ58ZI_J#L5BDtG)!!ufkZi@Z{OQDz zU_huvLVipu$0Q+xnW8XS6&Jy`T-Qtnk@GxXGA0oA1~aJJq83_jRLya;=2ezx5=kfS zSCszy8?)h?eH#36E^mkzni zv@~A1RQFbhoj8NUpQ3L>Ekv-$jUHK$JqDnKaNYF%D_?H&Aa^uZmuDHp^fxisktTTF zIIM3g7sblgWYS;IE)7IA0@~gLujedyZmqf@+M$8d=H^51D^ib!bZVV$DaQ|cO5iAL zE0TntRCNIbFMl^Y>}p;PWG-V=dJ2(^&^Q%v>B+B^rfl29oriKr5}XwdS~tB@We zyft}3>ELdiuoE!{W5x9-WQm(d-8<oH@dv z(XOT5=w&oH4Wc)hh~5?0Ux!EW3FCtp!0?y`i19N2{_yxmX!nm;S-c`(9ss!SQwa5O zb=rf86e<473$#X*8|3eMu~A;XLV*Q;hC5VCuyjdX?%B<~0ey-WA)M%jILQuoDWDcw z1if+CS>bj#;@uhjmx1rAqK&VF6cI|_N|Q+;`Fccfa7Jb&lQadksu|jo zw(q(?f;p~JVVmlBpQ1J4T~cR_cNeW4lJ(jxLh5&7KIwmUZX9ILJm8&6rL5u0Hc`w~LnJB|qQSIGsC@8$&;*E8qb!tTP+r5^(egeo^JT>F87 z=P)MS_%cgUL=UF?S(MGtv7j+J7-3J{wLt~5n`;G5G)`l_-$TpVxZitgx72Qr_jyna zOTUlQ!{oBZjQ$oKNdv2|Q*7#5W`IQvi?W=bdAukme8ZKYtK0`C!&ns>xt}z75|?dH z`5m?wusf04BL|;pIE|m#eMpqkIyV(>4_@dfq=TD#$p~}bp(+3RV$kg~?GpIJp-UDy znpr{dx3SG&p2tSm$5~9-xL!#eIl1K7yupa^RGfU%)Etg-^>a+SPoD1sIb9%~v$71J z6SA&313{-4`->=sbkojm;bBo4!71eH^#)@j^tYz0M6CFVFx42pypEQ}*IR=r@D^I+ z6O#%Kh%>3hC3-}QeP#*D`G{9b6r0^FDEjV2u6RqCEYlAigaAMn|Nofu{(AKyU1WD+PxIuG`w(cYcqHzG}H?tD^`aH=|Ol72C{GEaivVl zqE?l+#$2$NI|uvpdPvnngfpHfe-0BdVgLTNC#E@Y=@|$s_{yjYNl@`a%_V)wh`_=( z0@8-&p!tHgy={vZ=Cd%x)~w4j){=Nqgnov=^3@dv0qbt>B8}EHKS$bdw!v-{&+y_; zc_Ef;=7N$%8B1vIj8xfmnKi#Hqebs>rLV)pzxo1|g1Kmq-Jy3AY>I7sm)^m|D-Y z(mwkH?~eG9j$!`$?Ip97ZlQV%g^D?+2bZ@b4f#kw zf9RDy$x))hIJ7S*v7oTyGO&I3WX6;wNlA*bf-*+%_$<)X9H;59NPEYByAejlcLQ+) zQ2Vv|Y8U`&HkmcI9j+6MlDWwnQGTpbr$;?D4c}9_~^5_V{Zan5~y@^OpO0cFt zfKm5X_CaC0(v*eqa68B~%@&>4EZ{vlTJ?>*+c_d6Zc+0-3oP#$S*A#^4Di4VGqo+C zw>3iYS6}}^4f3eQZ~^HG|985TvbDuOtgf{x554^{W8fVi(PI*TL_7_8PceW9xt>k@o;W{W z8C4^wMt6=xadX!&I7KGz-N`#G3AB$?7~a0mYrS)w5jgD4RV}>9&`3h0cI)3(jux-e z+PscNwjPfgj)Au9GzDQdp}80)1WVHxVmr5bI^z7zhDGdHLoHzKziwvW_uvXYAh`@R ze?m8f=QKRTP}pE3KMS(4)s>7OQheE;@7{ zow783vfzmCTWSKG&K7E8-Ke(WL4fYOhk1+yi2T7${rRMK<92f7V`3U@oGo%v#;Vco zikFU#eT0-`1Mge*^siY}28zM^<=+=qVxl~l(#;y=Lk$QfSIn}k7sDBKHecHhuGBa( z>a%0EpM2+Msd95(v8k!$rIgW0qSPzE64Q7!H8h(RsyTj~oby<0z=^UELFGO{KHL&& zO`>!`SS%!%7^(*=R-Ls$hMwEl!*D$|)xj`>x(GBqMVMeEZg{uOh7qdAvTV*AoC5bs zn4BK{*xG~c-HuAo)ni0&-K&{uYDN))PWU^u;3!nMY~{DL{nyi^XGjt`UM$s*wbT=I zGHve+Afjrkc>`+?@H-c7@VD@|qN0dX{biV+keKd4gS^Dp@BR6%&6=dG$nAp)Za!KnLO0LhS8wj||^M|61DQ&zKKi0P@^;+h$JU+Z!(OmSf@mO@X(A@iSm7F{*15b9f zxp~0rQQ11Xuzz}7=d$u}(}W_z5+?s`68_HT_Cb`7U|^>b`;j*BNseHDD_fE4P*iy_o)k;TqsOvp< z)EGQx)ycApkQ;K%maA)#c#m$;B2swX11;^;{Kgplya{{myJIzjR5UZ?)K zR3+8U6iug}g-W0La~b!5ffBY;>sP3hrCwdl`EVD}>aOkNPhkg{M9fPnM$BrBOvqNH z=3yzcM5QI1sAWqt>gXE0lg! zYV$>we+W>Cvylog^ZNyxwmPu3Jhr#aF_g@xFZPSPUH-%na{ibj`a zgQ>%A=Bn42s1t%zpng4KY?_V*H+bBg#l3W=0!eu9&`DD)_(|Ac~MZGwip`reT6VppQJCzLFk^9xf^u|4BWPA`aKAvB zFy42LenUHNdrX$s%yGf?8Hi*_=0d*P64Wk%jNs!$Rzja|PA4jVKykWP0YiS1gNE*#xeqWW&^@ChwUHkI3fZ0aH= zTQCb1OkngV8H9RgXwV_}ll_~C7{h7-wJlQb;kP9;n0#G=JwY}|yN5zTHsOwCB)VAB1)lBz)Bi8|SbdM`#?%>S{qD zaDT&h4TK26=9-OV(MM*t!M#fX<6_pbxMtlpnH93fSg?^D86bEN-HZLs=mdX-8kYZc zW@&Os2+tMnzJ|J2MDIOpTSDcO>z9tq?R~f>+*P2op-9>Qn=O#{0ina5Nr!ZsxqYY(9f>!R7-Hd&RY@JnosgJb>&m&9?@-5qt&|djw;sxjL{Aljky;cd?Uv{${i9^U?m zaba!e20Y;Y)(tYbqU8b@m{LWH1_qXo$c&>GD}vQqVfTw4F=@FN$U=QG5$b1cJdQHO zAP#eSkNqCzf<9wZ2!kGDNXYR??InL=Ld!a(P;~ka$zRM)8c1-=zYp7TAk(vHYJ|Qv zvyd%=BNtDxN>N)1?+tSS+dS+6EIcV;JHjlNoDPA1>8vz=NHOfbl)(uZsOCV1cfw#7 z@3dpa49-bEG{#)~0G=9_y0~6#9H<6+N?D`y$w+LQ9K7t%GvXcl4V;3Z&_F@H2b+_Q zG7j)+D~@7oC~fMH6r6V~Qj216IH?x=C|-v)-N$Kt0-C>>WTOxAR$`$X#6X*Ft~FOV+9vZ8gEY4bpgpXyRODSa=I1`M1eNZx z%-;5XOg`+Q2n=qM0{9PQF0<8a(GH@7jlzIp7wMFQ_(nar#g-7}UBOm7Jn9_ETi7lK z0;4?HWSj9iX(1=NV)RllS~CeA?re+5j@@8)SYE^wy@q5=ighQbYT?Z;${NI8j)8D- zNPvGhZUcd?m@#pd)%batngdIc=iz=FdH5|n8&*Ml%VN2;QARXK>}7#k-|JUUUv8!? zLUT?9AGq!#dcrF3_^zl2+F=CUAz@$b@6l7Dq%nCA&hwOKS76p+9~#Qy=5>WHci*@B z%ja7dzv0e#tyNL_oyj;vMdLK{)FlYD0-zV+<*R^4)ho7Eie_EUWNAoW*?>XoxTcl4 z#ZDEG`Y-~SG^yX?uTVWyoP@r{MZ+h-)uNaJ#)e+_0J+Np~bY&M&qmeU=a z7a{(fyQ-T}@)FmD31Pwm^lp8&{fnqIo_K=X_B#avUWn9daz-%)-puH7w(eHnCn$GD zKQ0GGVyw#eCZYmMAV|x#W*jM8DG)kZFfE95^(|O^J9@RHopybKm`Bo7u+P~|!PT<< zIIXo=Z7tawB6W+(hm>ciO$0(x=x)Yyt{?+TB?BGK7~jV49`U=6>4Cj!-xp_hf}>z- zMz_cG_%rz7YuIm1-y4B2a)Qm05hf$lCxd<-7SV*GFY_aq?Ha>bW1nzAd0>UI)AbM) zC-Ffk)oMROzC$-%QWBV2&m^zvV$E_**6Enn7pS-y&gUShc0g$@shSGu_kmj(lXM%z zcN4}smA$LCLYkvG0ZBR2e5|bnIJAy}dCmf>B(lB&+jrk<2@XYVyPB|1%E=rJ*`q76 zQ>Cz&2I@k-;-G8|6l`Hjg$Wvgu2PVAAO3}6U`wAt{loCAC0*0g5@@2+q_jXS9pT$U zZ{LC&FFDL9@k?FjKr#dwc}yO!?+RZktJXQn{ZWgmH0Il++(NJQCZXqa+YiSs_q`lH zNbi~u6K-Xh#_@(Z>p+3$FO%$%)=q`;_{-1qnOW6AtP@Go;kD#3*BPd(Wq+J4-IlsZ z_Ujy4qJN_p3v4>w9KlaT4xK3U`K!d*3?vMS8ID-Wkx+_M!ZJd+9O1 z8FKZ)2A~hC>z?kZJL4|5T6g$!6W2`@C7ChJ16Yeh0;3J7L)dv9Ka$ zeQ*GFS1iRWnZjvm#p{zWdAJkqG>jS_O!fed$Mt> zhvcor7OmE&nzE1e1sG&3yb4Lvu4MQHOF*L!-!5Nq9%$t40M|S~X4HYV=f9m)x$q++ zb|A?^AFq6c{|ukyMj&vWNdfi@6TfI<%ITHWQVqHQ`o{W^`Vr^qn>hcgI}R$J2&X*v zp?R#eawl5axqGz)=xlec-D7pg?{IbP>T0QuDl3@k4F3=8lo|AgrsGvT9fioB1X z6<^NKt_=n8lbi>G3vIYP3^VU22jjfx*k+NW(7kCKruYnFY5**v>6oBTrN1-AfoHzI zGdp}B5>>@A2V7x()}sjYf;>sqBl?Z1Ju&%eh|KMA_numi3eRhFXZ{Rj6V<}>F#J0L zz4txO(qCNE4hHEa(=nbq6zoS&NS+BEC-fK*gqaR53#ZGAG)533_ye3T-k_=-@d;Va zF)OKHa?72XP+ZiO5P^pVns4>I0xgUMJdxG12Uz2c zU*Ops@xMr5*L1Z`iUS0B`;dMas`^)a%JAQL>OZ9W{=EXM`qc+V741oEv42veTDdQB znM`?#Bf*F>m}K0wIBb(l7I%@-PSREb8pVQGKFTC+qW^=YlyvrZP<}pGN`e=2m|q?X z6d8X4`_1Vnj!nynDqBO^UCK+W%(d&kqHX_EixzKNVsR!5C1&+AQftW4=FG%?`CQ!V67fM(zr`6+N4 zVqX|jThdxJJ?VI4qM;|JpEahPr)bN&$W63UZ)MXKGC6P750&dRYTnvFp2EHRb}i8Y z2yMyTsjkwyp3*#RwD>jyH<5vd#$N%(>-L6jv-OU-{prPF-($Gc>MkeOYBlhq$C*ko z;ASc`QUYTm-ia?8NwS6p(6#r+nV~_1C1_BX1)P(5o!HHkmi4R+tWmb zBXKI%*M=I;AF1R|+>{wBZg=#i0|mfG<@@YQ^Nk}&eAF`4a(LIp=)qT4G6`;d`rUY- zWirq2gT@h8SA@YNjx3W^S``X*90EQUM@JuO!$FM}ugy+MwXRtAV2 zMRevB!{h|S5;!ucoJJX`7C|1BIyae1@0xSj=9MHur%05SsYdRp1ZhliMt{vZ0j73z z9BTG^s;4Q6*8n)bvsqkBq|SoPlr~LS7e*7}wFpeJY>r33ZDN0tH7P2=bBoGncjc_ZmXA4)GHOELF6Y z02d=q)sE{EFEIXg1X9DJZ|uzVtSYDvko*a!C~VzgM@A1y9b+dBP!O$l9BF&STg(v{ z(VypBH9xe1w=3?@*F>QWuf_SJJ@~7Rktqhai1OvGCElQgh*kPi0xUs*ZXa1yj2^@~ z$QcXQ8gB5!-Il|33sGm@j+ExAeq;R@l3pR7ypkbh)5~xuBlx*}7v^5Hm#)9DV2w)! zttF%o^#Q+hWQ&cFoG1;=DcU+iA+4*~rLI~DJEh=gP@4VNUd{e)I7$;Dy&R7*O>~pN z*cOW2c??GiieaVA`As)DnswJ~1|2E| zTWJD>6IF9^9})2=bUXy80Ii@CqKdD(Pj`fKA#7zoPKZcu`ND|?yL?%E84QnFVm^aW zy5M9tX+o|F1nS4>AAoN;G0Ig3>2H9?JX*XVk=dE6lI>JZ%ZrLRQMJr$ictAp3nvb4 zP23vnK>{oOIVVF$Od?YM-j?Q?k6639q95Bi(9U^#^oz<-i%a@wAYRj&SpE?grY#S~5BCgV+Evv!^c&tSfv`GclDRx~X|KLYtm}mn1s6_)iJve&^(B#R za6}^7#N42`#yR76_N7-63BHQlz+98~h$PwxH3+?WG@2*BAt{xH>lq{D>8}zf=hOW9 zirjHD$RS+1Ehzny0}>G#9wSwEBjU4fg#~;#d0cJ&u zyJQX5a^k&OPa&`Uc|*Pm3!NoJ9Jxb$@VskFop)l(TsdrM&T%R|nIBC5vSYLHWWl$5 zpFX%NjLS)~E1H0oXi|=6INn9#mFDD}hAL-olEO;HgAQs^4e?Piv}RHam+5t~;v&cKw6(EHuKQ&2OQIV76e(Hqdr{BFSmBu{^CVnM5>r_}y5v;(F)2qCpgGZ_x( z&UA6yKCUH@HWaJjYs__$w0od=SB3);J^2-|D@mU1?eDT}U?5(gi;8nZ%XRQ(nFH0# z_${HPhE9N+&{gUU%_WgjRA~LSI)vmPzq0j0Eol^~HWeN!AjgQ=d->}2=we;5?E0=5 zjXU$o>=Bk!$64nAEhjK2(o~r1GrP!+M&Y?n*XZJIvh;Bn$IKEv^cA}MbIiNSg<2Fn zKiL9>HUV-BTykbQ-E=?}Gi805X)EIPdD3+fwqp`VPL3p7y-0BdX%2oCj;Nn}gxE3# z{$))OB%)Zf(WTaER6CRO{abBdv{gYc(LO;2pk-l}7SJQ>H6yV&?UiIxFBZnCBbz}e z4Vg5m!pWFSTH+gLXYMHp`#h+=9aIfGsqU{~H)SUGaAQ|*e5{=BE@R6}37VOzg0jG|UdJ9?$U;&p9um^_b?+dtpd$0eo#qW=|dxWCqzqY*8H_#pu=WC$~Qeb4$ z4u)d1yeS1`Br@yH0}rqf=2j2>vA>HJ$p?CjtnDW2gH)7=*AEZi1G;=vNzdeT&inLZ z&s9$5FVCLOuXK^9DGlGeQIH|>7ecsW$jsRe4o#J$$&2#M=%uA0v(xlGE^4Iya=lfP zx6u{~0gdc2Y-y(|SWq)`m9%meSgabjPfNX94Be%#GqetE-0Yp`|7n0mxY9kcDtwzM zpG2w?Sw~%v<=wmzqGsKG+>$C1y3d5UrNa)|A{%8)`8=z!HIsP117^4zw}0Fgb-lrE zaMyRTIg5hvcT9cTm&%_fbXBz9TCwozX)4i~+1e;#zhOwCPfHZRmqQdu2|EpH#vl&x<*fdf`tvTSeu&=iG-`MLeOzu_c9 zpdJ`Vp}8t<$f5KA_FeRhE#4<+GP%-x@QIt`k6mgQLEPDQ#DDNV1Qd)Y6n0+544qbYFn_hddWTgkGoUhy75a65*EVCGUVmX=!_r9f|#<^IF@dqiYMlgIZNbJ3ZkN8^O z!ATJR{9-3qm>{dACt#;53}B}#*Dsy|dmD2j1IOQy15S^TmhTim4H^Rj74hVM)0K13 z4vLN9wE0>goH~aD^IDiYfzc`i76j^9N|lW6;nfqxCTa=eyvQca(Ow$o>V@3|<5LwNia0`+~QW+JYjFz_nM=R+OOjQ4e@q^}!i@Ss=g3gFa~S5B~1TJgej zm&_RO8GZUK;5s{e;3I4~0@7U|G^$rQlo(8o__49xT<8^yrY2j(g^wQc24oBiLxU-P zLUF)@DL^lLl^cfQ2{pv1^>O_|Zlnl|K54+~TDaoSLP(`;cN}ztIPa%>( ztpseD_?N@>zxC_^o6-MS%YOT%f;}W8Bv6;xvlIa5{|YAHOy9}O(va5P$iT?V*747( z_|mjgqr+q8)FZM|v^A`=%s-^?As}bnCT#$s0ziOoV!;1j0iyx|Y6pM@0ulrqKgIC@ zqHiyXtf-P8t)!e7Koak-mpMaqxc}Hlr35&B{M4W2hp_DbT_!6iCn+YXs6;0#_7dU8 z6@V`PuL9WY_otEqIJdDk`5WhtKl#rU09Bwrl{Ucnk1PJI>iB4_I z4*ZSz*j*#>7*NR>KnwkFBKuiz0Z;BP0EBF89DmpZ*vjgeSp%{zf0_fltedI7p>Zw% zZw7F@{)r1XQvoFW`~q48Ao^fwqi6UZnZcKUgEm|a2!Li10MwV|FMwQtxX3>N0^S?0 zZh}sZrT{r+GlQRkCodV=Q$yDw0shRm0KW#Jzc8c%+UFk_{+N!vgqxP5U(N==3<3_u zzrbx^{39Hoz9#lY4h}K^A8;e<|F|!_gg2N!O0fepB_iw}2?9SECIOGqFKP==qPBE$ z0LZ_HnOPbM8W;c=Wc94|O#T}IXtDdU7NEfu01f`v&JjoOO9F9wJ%9U3N$@bSx)9^ueKL;8Jhzd|sz}~Ar6>j2xfK~-`Jb>ZnkG}V7+?@k8hY|qVBH#c3 z{i%=we!(x=MDjnq?$>zfrI|tq+4+5v1S&p40=t zU_$t(+5sLB)_=nLb?x%q7@Ac8HwXyu?EttVe=6P*zeJOi=JC0A9ksRIvXE?`r)|@GqvyFJWH_xBrBlxA`a7KSvo}V!o6O{)t)P^iP<7 zY@z*cBjZbf-k+!`&cA{B-{pE=R{y2u=uaXB_unP@qj~Cv4rNHA)0ury^AovHl z$CpemW#E1?nfm<(({G8wz2tl;=Jk^kJ@7X;|4G{GCDlt+ke^f>;lDxk4;mpanO>?d z{AA+#_!~@rbO*s-YB9W|dYSe9lZrOxx2XW*i|wD4`(Bc~OdS46A|Lp9J)PPpKC#o1bH}L;CYv<*Ud70MmldLM|f0O-slJ#=^`6s4k-QO_()f|6cjec3r zm$C1kn9QyJ8}pyuQ7_|(Kan}x|Bn3o(Z#?0f)_EapFD7#f8%*M?fg@Z{U5LKmvNV$ zG;3Y|H>LY;QJDYK`9Cq2KL<^I#9V$B#_s \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/gradlew.bat b/plugins/kotlin-serialization/kotlin-serialization-compiler/gradlew.bat new file mode 100644 index 00000000000..e95643d6a2c --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml b/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml new file mode 100644 index 00000000000..0946ca030d9 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/kotlin-serialization-compiler.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/settings.gradle b/plugins/kotlin-serialization/kotlin-serialization-compiler/settings.gradle new file mode 100644 index 00000000000..82367d8bce4 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/settings.gradle @@ -0,0 +1 @@ +rootProject.name='serialization-plugin' diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml new file mode 100644 index 00000000000..79a80811a0d --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml @@ -0,0 +1,13 @@ + + Kotlin-serialization + org.jetbrains.kotlinx.serialization + 0.1-SNAPSHOT + + org.jetbrains.kotlin + + + + + + + \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar new file mode 100644 index 00000000000..1fb46ede97c --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar @@ -0,0 +1,17 @@ +# +# Copyright 2010-2017 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. +# + +org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationComponentRegistrar \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt new file mode 100644 index 00000000000..028ebf1ae87 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.compiler.backend.common + +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties +import org.jetbrains.kotlin.psi.KtPureClassOrObject +import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor +import org.jetbrains.kotlin.resolve.BindingContext + +/** + * @author Leonid Startsev + * sandwwraith@gmail.com + */ + +abstract class SerializableCodegen(declaration: KtPureClassOrObject, private val bindingContext: BindingContext) { + protected val serializableDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext) + protected val properties = SerializableProperties(serializableDescriptor, bindingContext) + + fun generate() { + generateSyntheticInternalConstructor() + generateSyntheticMethods() + } + + private fun generateSyntheticInternalConstructor() { + val constrDesc = KSerializerDescriptorResolver.createLoadConstructorDescriptor(serializableDescriptor, bindingContext) + generateInternalConstructor(constrDesc) + } + + private fun generateSyntheticMethods() { + val func = KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(serializableDescriptor) + generateWriteSelfMethod(func) + } + + protected abstract fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) + + protected abstract fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt new file mode 100644 index 00000000000..732adf7bcc6 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.compiler.backend.common + +import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberToGenerate +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtPureClassOrObject +import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties + +abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContext: BindingContext) { + protected val serializerDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext) + protected val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorBySerializer(serializerDescriptor)!! + protected val serialName: String = serializableDescriptor.fqNameUnsafe.asString() + protected val properties = SerializableProperties(serializableDescriptor, bindingContext) + protected val orderedProperties = properties.serializableProperties + + fun generate() { + check(properties.isExternallySerializable) { "Class ${serializableDescriptor.name} is not externally serializable" } + generateSerializableClassPropertyIfNeeded() + val save = generateSaveIfNeeded() + val load = generateLoadIfNeeded() + if (save || load) + generateSerialDesc() + } + + protected abstract fun generateSerialDesc() + + protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor) + + protected abstract fun generateSave(function: FunctionDescriptor) + + protected abstract fun generateLoad(function: FunctionDescriptor) + + private fun generateSerializableClassPropertyIfNeeded() { + val property = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD, + serializerDescriptor::checkSerializableClassPropertyResult) + ?: return + generateSerializableClassProperty(property) + } + + private fun generateSaveIfNeeded(): Boolean { + val function = getMemberToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SAVE, + serializerDescriptor::checkSaveMethodResult, serializerDescriptor::checkSaveMethodParameters) + ?: return false + generateSave(function) + return true + } + + private fun generateLoadIfNeeded(): Boolean { + val function = getMemberToGenerate(serializerDescriptor, KSerializerDescriptorResolver.LOAD, + serializerDescriptor::checkLoadMethodResult, serializerDescriptor::checkLoadMethodParameters) + ?: return false + generateLoad(function) + return true + } + + fun getPropertyToGenerate( + classDescriptor: ClassDescriptor, + name: String, + isReturnTypeOk: (KotlinType) -> Boolean + ): PropertyDescriptor? = + classDescriptor.unsubstitutedMemberScope.getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_BACKEND) + .singleOrNull { property -> + property.kind.let { kind -> kind == CallableMemberDescriptor.Kind.SYNTHESIZED || kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE } && + property.modality != Modality.FINAL && + property.returnType != null && + isReturnTypeOk(property.returnType!!) + } +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt new file mode 100644 index 00000000000..539e866fc99 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -0,0 +1,283 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.compiler.backend.jvm + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty +import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable +import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.typeSerializer +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +/** + * @author Leonid Startsev + * sandwwraith@gmail.com + */ + +internal val internalPackageFqName = FqName("kotlinx.serialization.internal") +internal val descType = Type.getObjectType("kotlinx/serialization/KSerialClassDesc") +internal val descImplType = Type.getObjectType("kotlinx/serialization/internal/SerialClassDescImpl") +internal val kOutputType = Type.getObjectType("kotlinx/serialization/KOutput") +internal val kInputType = Type.getObjectType("kotlinx/serialization/KInput") + + +internal val kSerialSaverType = Type.getObjectType("kotlinx/serialization/KSerialSaver") +internal val kSerialLoaderType = Type.getObjectType("kotlinx/serialization/KSerialLoader") +internal val kSerializerType = Type.getObjectType("kotlinx/serialization/KSerializer") +internal val kSerializerArrayType = Type.getObjectType("[Lkotlinx/serialization/KSerializer;") + +internal val serializationExceptionName = "kotlinx/serialization/SerializationException" +internal val serializationExceptionMissingFieldName = "kotlinx/serialization/MissingFieldException" + +val OPT_MASK_TYPE: Type = Type.INT_TYPE +val OPT_MASK_BITS = 32 + +// compare with zero. if result == 0, property was not seen. +internal fun InstructionAdapter.genValidateProperty(index: Int, bitMaskPos: (Int) -> Int) { + val addr = bitMaskPos(index) + load(addr, OPT_MASK_TYPE) + iconst(1 shl (index % OPT_MASK_BITS)) + and(OPT_MASK_TYPE) + iconst(0) +} + +internal fun InstructionAdapter.genExceptionThrow(exceptionClass: String, message: String) { + anew(Type.getObjectType(exceptionClass)) + dup() + aconst(message) + invokespecial(exceptionClass, "", "(Ljava/lang/String;)V", false) + checkcast(Type.getObjectType("java/lang/Throwable")) + athrow() +} + +fun InstructionAdapter.genKOutputMethodCall(property: SerializableProperty, classCodegen: ImplementationBodyCodegen, expressionCodegen: ExpressionCodegen, propertyOwnerType: Type, ownerVar: Int) { + val propertyType = classCodegen.typeMapper.mapType(property.type) + val sti = getSerialTypeInfo(property, propertyType) + val useSerializer = stackValueSerializerInstance(classCodegen, sti) + if (!sti.unit) classCodegen.genPropertyOnStack(this, expressionCodegen.context, property.descriptor, propertyOwnerType, ownerVar) + invokevirtual(kOutputType.internalName, + "write" + sti.nn + (if (useSerializer) "Serializable" else "") + "ElementValue", + "(" + descType.descriptor + "I" + + (if (useSerializer) kSerialSaverType.descriptor else "") + + (if (sti.unit) "" else sti.type.descriptor) + ")V", false) +} + +internal fun InstructionAdapter.buildInternalConstructorDesc(propsStartVar: Int, bitMaskBase: Int, codegen: ClassBodyCodegen, args: List): String { + val constructorDesc = StringBuilder("(I") + load(bitMaskBase, OPT_MASK_TYPE) + var propVar = propsStartVar + for (property in args) { + val propertyType = codegen.typeMapper.mapType(property.type) + constructorDesc.append(propertyType.descriptor) + load(propVar, propertyType) + propVar += propertyType.size + } + constructorDesc.append("Lkotlinx/serialization/SerializationConstructorMarker;)V") + aconst(null) + return constructorDesc.toString() +} + +internal fun ImplementationBodyCodegen.generateMethod(function: FunctionDescriptor, + block: InstructionAdapter.(JvmMethodSignature, ExpressionCodegen) -> Unit) { + this.functionCodegen.generateMethod(OtherOrigin(this.myClass.psiOrParent, function), function, + object : FunctionGenerationStrategy.CodegenBased(this.state) { + override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { + codegen.v.block(signature, codegen) + } + }) +} + + +internal val enumSerializerId = ClassId(internalPackageFqName, Name.identifier("EnumSerializer")) +internal val polymorphicSerializerId = ClassId(internalPackageFqName, Name.identifier("PolymorphicSerializer")) +internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier("ReferenceArraySerializer")) + +// returns false is property should not use serializer +internal fun InstructionAdapter.stackValueSerializerInstance(codegen: ClassBodyCodegen, sti: SerialTypeInfo): Boolean { + val serializer = sti.serializer ?: return false + return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this) +} + +// returns false is cannot not use serializer +// use iv == null to check only (do not emit serializer onto stack) +internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, serializer: ClassDescriptor, + iv: InstructionAdapter?): Boolean { + if (serializer.kind == ClassKind.OBJECT) { + // singleton serializer -- just get it + if (iv != null) + StackValue.singleton(serializer, codegen.typeMapper).put(kSerializerType, iv) + return true + } + // serializer is not singleton object and shall be instantiated + val argSerializers = kType.arguments.map { projection -> + // bail out from stackValueSerializerInstance if any type argument is not serializable + val argSerializer = findTypeSerializer(module, projection.type, codegen.typeMapper.mapType(projection.type)) ?: return false + // check if it can be properly serialized with its args recursively + if (!stackValueSerializerInstance(codegen, module, projection.type, argSerializer, null)) + return false + Pair(projection.type, argSerializer) + } + // new serializer if needed + iv?.apply { + val serializerType = codegen.typeMapper.mapClass(serializer) + // todo: support static factory methods for serializers for shorter bytecode + anew(serializerType) + dup() + // instantiate all arg serializers on stack + val signature = StringBuilder("(") + when (serializer.classId) { + enumSerializerId -> { + // a special way to instantiate enum -- need a enum KClass reference + aconst(codegen.typeMapper.mapType(kType)) + AsmUtil.wrapJavaClassIntoKClass(this) + signature.append(AsmTypes.K_CLASS_TYPE.descriptor) + } + referenceArraySerializerId -> { + // a special way to instantiate reference array serializer -- need an element java.lang.Class reference + aconst(codegen.typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT)) + signature.append("Ljava/lang/Class;") + } + } + // all serializers get arguments with serializers of their generic types + argSerializers.forEach { (argType, argSerializer) -> + assert(stackValueSerializerInstance(codegen, module, argType, argSerializer, this)) + // wrap into nullable serializer if argType is nullable + if (argType.isMarkedNullable) { + invokestatic("kotlinx/serialization/internal/BuiltinSerializersKt", "makeNullable", + "(" + kSerializerType.descriptor + ")" + kSerializerType.descriptor, false) + + } + signature.append(kSerializerType.descriptor) + } + signature.append(")V") + // invoke constructor + invokespecial(serializerType.internalName, "", signature.toString(), false) + } + return true +} + +// +// ======= Serializers Resolving ======= +// + + +class SerialTypeInfo( + val property: SerializableProperty, + val type: Type, + val nn: String, + val serializer: ClassDescriptor? = null, + val unit: Boolean = false +) + +fun getSerialTypeInfo(property: SerializableProperty, type: Type): SerialTypeInfo { + when (type.sort) { + BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, CHAR -> { + val name = type.className + return SerialTypeInfo(property, type, Character.toUpperCase(name[0]) + name.substring(1)) + } + ARRAY -> { + // check for explicit serialization annotation on this property + var serializer = property.serializer.toClassDescriptor + if (serializer == null) { + // no explicit serializer for this property. Select strategy by element type + when (type.elementType.sort) { + OBJECT, ARRAY -> { + // reference elements + serializer = property.module.findClassAcrossModuleDependencies(referenceArraySerializerId) + } + // primitive elements are not supported yet + } + } + return SerialTypeInfo(property, Type.getType("Ljava/lang/Object;"), + if (property.type.isMarkedNullable) "Nullable" else "", serializer) + } + OBJECT -> { + // no explicit serializer for this property. Check other built in types + if (KotlinBuiltIns.isString(property.type)) + return SerialTypeInfo(property, Type.getType("Ljava/lang/String;"), "String") + if (KotlinBuiltIns.isUnit(property.type)) + return SerialTypeInfo(property, Type.getType("Lkotlin/Unit;"), "Unit", unit = true) + // todo: more efficient enum support here, but only for enums that don't define custom serializer + // otherwise, it is a serializer for some other type + val serializer = findTypeSerializer(property.module, property.type, type) + return SerialTypeInfo(property, Type.getType("Ljava/lang/Object;"), + if (property.type.isMarkedNullable) "Nullable" else "", serializer) + } + else -> throw AssertionError("Unexpected sort for $type") // should not happen + } +} + +fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType, asmType: Type): ClassDescriptor? { + return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module) + else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type + ?: findStandardAsmTypeSerializer(module, asmType) // otherwise see if there is a standard serializer + ?: findStandardKotlinTypeSerializer(module, kType) +} + +fun KotlinType.requiresPolymorphism(): Boolean { + return this.toClassDescriptor?.getSuperClassNotAny()?.isInternalSerializable == true + || this.toClassDescriptor?.modality == Modality.OPEN + || this.containsTypeProjectionsInTopLevelArguments() // List<*> +} + +fun findPolymorphicSerializer(module: ModuleDescriptor): ClassDescriptor { + return requireNotNull(module.findClassAcrossModuleDependencies(polymorphicSerializerId)) { "Can't locate polymorphic serializer definition" } +} + +fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { + val classDescriptor = kType.constructor.declarationDescriptor as? ClassDescriptor ?: return null + return if (classDescriptor.kind == ClassKind.ENUM_CLASS) module.findClassAcrossModuleDependencies(enumSerializerId) else null +} + +fun findStandardAsmTypeSerializer(module: ModuleDescriptor, asmType: Type): ClassDescriptor? { + val name = asmType.standardSerializer ?: return null + return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier(name))) +} + +internal val org.jetbrains.org.objectweb.asm.Type.standardSerializer: String? get() = when (this.descriptor) { + "Lkotlin/Unit;" -> "UnitSerializer" + "Z", "Ljava/lang/Boolean;" -> "BooleanSerializer" + "B", "Ljava/lang/Byte;" -> "ByteSerializer" + "S", "Ljava/lang/Short;" -> "ShortSerializer" + "I", "Ljava/lang/Integer;" -> "IntSerializer" + "J", "Ljava/lang/Long;" -> "LongSerializer" + "F", "Ljava/lang/Float;" -> "FloatSerializer" + "D", "Ljava/lang/Double;" -> "DoubleSerializer" + "C", "Ljava/lang/Character;" -> "CharSerializer" + "Ljava/lang/String;" -> "StringSerializer" + "Ljava/util/Collection;", "Ljava/util/List;", "Ljava/util/ArrayList;" -> "ArrayListSerializer" + "Ljava/util/Set;", "Ljava/util/LinkedHashSet;" -> "LinkedHashSetSerializer" + "Ljava/util/HashSet;" -> "HashSetSerializer" + "Ljava/util/Map;", "Ljava/util/LinkedHashMap;" -> "LinkedHashMapSerializer" + "Ljava/util/HashMap;" -> "HashMapSerializer" + "Ljava/util/Map\$Entry;" -> "MapEntrySerializer" + else -> null +} diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt new file mode 100644 index 00000000000..cf23f80708b --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerialInfoCodegenImpl.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.compiler.backend.jvm + +import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type + +/** + * @author Leonid Startsev + * sandwwraith@gmail.com + */ + +class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClass: ClassDescriptor, val bindingContext: BindingContext) { + val thisAsmType = codegen.typeMapper.mapClass(thisClass) + + fun generate() { + val props = thisClass.unsubstitutedMemberScope.getDescriptorsFiltered().filterIsInstance() + generateFieldsAndSetters(props) + generateConstructor(props) + } + + private fun generateFieldsAndSetters(props: List) { + props.forEach { prop -> + val propType = codegen.typeMapper.mapType(prop.type) + val propFieldName = "_" + prop.name.identifier + codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL or Opcodes.ACC_SYNTHETIC, + propFieldName, propType.descriptor, null, null) + val f = SimpleFunctionDescriptorImpl.create(thisClass, Annotations.EMPTY, prop.name, CallableMemberDescriptor.Kind.SYNTHESIZED, thisClass.source) + f.initialize(null, thisClass.thisAsReceiverParameter, emptyList(), emptyList(), prop.type, Modality.FINAL, Visibilities.PUBLIC) + codegen.generateMethod(f, { _, _ -> + load(0, thisAsmType) + getfield(thisAsmType.internalName, propFieldName, propType.descriptor) + areturn(propType) + }) + } + } + + private fun generateConstructor(props: List) { + val constr = ClassConstructorDescriptorImpl.createSynthesized( + thisClass, + Annotations.EMPTY, + false, + thisClass.source + ) + val args = mutableListOf() + var i = 0 + props.forEach { prop -> + args.add(ValueParameterDescriptorImpl(constr, null, i++, Annotations.EMPTY, prop.name, prop.type, false, false, false, null, constr.source)) + } + constr.initialize( + args, + Visibilities.PUBLIC + ) + + constr.returnType = thisClass.defaultType + + codegen.generateMethod(constr, { _, _ -> + load(0, thisAsmType) + invokespecial("java/lang/Object", "", "()V", false) + props.forEachIndexed { index, prop -> + val propType = codegen.typeMapper.mapType(prop.type) + val propFieldName = "_" + prop.name.identifier + load(0, thisAsmType) + load(index + 1, propType) + putfield(thisAsmType.internalName, propFieldName, propType.descriptor) + } + areturn(Type.VOID_TYPE) + }) + } + + companion object { + fun generateSerialInfoImplBody(codegen: ImplementationBodyCodegen) { + val thisClass = codegen.descriptor + if (KSerializerDescriptorResolver.isSerialInfoImpl(thisClass)) + SerialInfoCodegenImpl(codegen, thisClass, codegen.bindingContext).generate() + } + } +} + diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt new file mode 100644 index 00000000000..13214487fdd --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -0,0 +1,219 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.compiler.backend.jvm + +import org.jetbrains.kotlin.codegen.CompilationException +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen +import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.psi.KtAnonymousInitializer +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty +import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +/** + * @author Leonid Startsev + * sandwwraith@gmail.com + */ + +class SerializableCodegenImpl( + private val classCodegen: ImplementationBodyCodegen, + serializableClass: ClassDescriptor +) : SerializableCodegen(classCodegen.myClass, classCodegen.bindingContext) { + + private val thisAsmType = classCodegen.typeMapper.mapClass(serializableDescriptor) + + companion object { + fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) { + val serializableClass = codegen.descriptor + if (serializableClass.isInternalSerializable) + SerializableCodegenImpl(codegen, serializableClass).generate() + } + } + + private val descToProps = classCodegen.myClass.declarations + .asSequence() + .filterIsInstance() + .associateBy { classCodegen.bindingContext[BindingContext.VARIABLE, it]!! } + + private val paramsToProps: Map = classCodegen.myClass.primaryConstructorParameters + .asSequence() + .filter { it.hasValOrVar() } + .associateBy { classCodegen.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! } + + private fun getProp(prop: SerializableProperty) = descToProps[prop.descriptor] + private fun getParam(prop: SerializableProperty) = paramsToProps[prop.descriptor] + private val SerializableProperty.asmType get() = classCodegen.typeMapper.mapType(this.type) + + override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) { + classCodegen.generateMethod(constructorDescriptor, { sig, expr -> doGenerateConstructorImpl(expr) }) + } + + override fun generateWriteSelfMethod(methodDescriptor: FunctionDescriptor) { + classCodegen.generateMethod(methodDescriptor, { sig, expr -> doGenerateWriteSelf(expr, sig) }) + } + + private fun InstructionAdapter.doGenerateWriteSelf(exprCodegen: ExpressionCodegen, signature: JvmMethodSignature) { + val thisI = 0 + val outputI = 1 + val serialDescI = 2 +// val offsetI = 3 + + val superClass = serializableDescriptor.getSuperClassOrAny() + val myPropsStart: Int + if (superClass.isInternalSerializable) { + myPropsStart = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties.size + //super.writeSelf(output, serialDesc) + load(thisI, thisAsmType) + load(outputI, kOutputType) + load(serialDescI, descType) + invokespecial(classCodegen.typeMapper.mapType(superClass).internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false) + } + else { + myPropsStart = 0 + // offset = 0 +// iconst(0) + } + + for (i in myPropsStart until properties.serializableProperties.size) { + val property = properties[i] + // output.writeXxxElementValue (desc, index, value) + load(outputI, kOutputType) + load(serialDescI, descType) + iconst(i) + genKOutputMethodCall(property, classCodegen, exprCodegen, thisAsmType, thisI) + } + + areturn(Type.VOID_TYPE) + } + + private fun InstructionAdapter.doGenerateConstructorImpl(exprCodegen: ExpressionCodegen) { + val seenMask = 1 +// var propOffset = 2 + var (propIndex, propOffset) = generateSuperSerializableCall(2) + for (i in propIndex until properties.serializableProperties.size) { + val prop = properties[i] + if (prop.transient) { + if (!needInitProperty(prop)) throw CompilationException("transient without default value", null, null) + exprCodegen.genInitProperty(prop) + propOffset += prop.asmType.size + continue + } + val propType = prop.asmType + if (!prop.optional) { + // primary were validated before constructor call + genValidateProperty(i) { seenMask } + val nonThrowLabel = Label() + ificmpne(nonThrowLabel) + genExceptionThrow(serializationExceptionMissingFieldName, prop.name) + visitLabel(nonThrowLabel) + // setting field + load(0, thisAsmType) + load(propOffset, propType) + putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor) + } + else { + genValidateProperty(i) { seenMask } + val setLbl = Label() + val nextLabel = Label() + ificmpeq(setLbl) + // setting field + // todo: validate nullability + load(0, thisAsmType) + load(propOffset, propType) + putfield(thisAsmType.internalName, prop.descriptor.name.asString(), propType.descriptor) + goTo(nextLabel) + visitLabel(setLbl) + // setting defaultValue + exprCodegen.genInitProperty(prop) + visitLabel(nextLabel) + } + propOffset += prop.asmType.size + } + + // these properties required to be manually invoked, because they are not in serializableProperties + val serializedProps = properties.serializableProperties.map { it.descriptor } + + (descToProps - serializedProps) + .forEach { _, prop -> classCodegen.initializeProperty(exprCodegen, prop) } + (paramsToProps - serializedProps) + .forEach { t, u -> exprCodegen.genInitParam(t, u) } + + // init blocks + // todo: proper order with other initializers + classCodegen.myClass.declarations + .asSequence() + .filterIsInstance() + .mapNotNull { it.body } + .forEach { exprCodegen.gen(it, Type.VOID_TYPE) } + areturn(Type.VOID_TYPE) + } + + private fun InstructionAdapter.generateSuperSerializableCall(propStartVar: Int): Pair { + val superClass = serializableDescriptor.getSuperClassOrAny() + val superType = classCodegen.typeMapper.mapType(superClass).internalName + + load(0, thisAsmType) + + if (!superClass.isInternalSerializable) { + require(superClass.constructors.firstOrNull { it.valueParameters.isEmpty() } != null) { "Non-serializable parent of serializable class must have no arg constructor" } + + // call + invokespecial(superType, "", "()V", false) + return 0 to propStartVar + } + else { + val superProps = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties + val creator = buildInternalConstructorDesc(propStartVar, 1, classCodegen, superProps) + invokespecial(superType, "", creator, false) + return superProps.size to propStartVar + superProps.sumBy { it.asmType.size } + } + } + + private fun needInitProperty(prop: SerializableProperty) = getProp(prop)?.let { classCodegen.shouldInitializeProperty(it) } + ?: getParam(prop)?.hasDefaultValue() ?: throw IllegalStateException() + + private fun ExpressionCodegen.genInitProperty(prop: SerializableProperty) + = getProp(prop)?.let { + classCodegen.initializeProperty(this, it) + } + ?: getParam(prop)?.let { + this.v.load(0, thisAsmType) + this.gen(it.defaultValue, prop.asmType) + this.v.putfield(thisAsmType.internalName, prop.name, prop.asmType.descriptor) + } + ?: throw IllegalStateException() + + private fun ExpressionCodegen.genInitParam(prop: PropertyDescriptor, param: KtParameter) { + this.v.load(0, thisAsmType) + val mapType = classCodegen.typeMapper.mapType(prop.type) + this.gen(param.defaultValue, mapType) + this.v.putfield(thisAsmType.internalName, prop.name.asString(), mapType.descriptor) + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt new file mode 100644 index 00000000000..831ca90ac62 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -0,0 +1,396 @@ +/* + * Copyright 2010-2017 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.kotlinx.serialization.compiler.backend.jvm + +import org.jetbrains.kotlin.codegen.CompilationException +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer +import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter + +class SerializerCodegenImpl( + private val codegen: ImplementationBodyCodegen, + serializableClass: ClassDescriptor +) : SerializerCodegen(codegen.myClass, codegen.bindingContext) { + + + private val serialDescField = "\$\$serialDesc" + + private val serializerAsmType = codegen.typeMapper.mapClass(codegen.descriptor) + private val serializableAsmType = codegen.typeMapper.mapClass(serializableClass) + + companion object { + fun generateSerializerExtensions(codegen: ImplementationBodyCodegen) { + val serializableClass = getSerializableClassDescriptorBySerializer(codegen.descriptor) ?: return + SerializerCodegenImpl(codegen, serializableClass).generate() + } + } + + override fun generateSerialDesc() { + codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_STATIC or ACC_FINAL or ACC_SYNTHETIC, + serialDescField, descType.descriptor, null, null) + // todo: lazy initialization of $$serialDesc that is performed only when save/load is invoked first time + val expr = codegen.createOrGetClInitCodegen() + with(expr.v) { + val classDescVar = 0 + anew(descImplType) + dup() + aconst(serialName) + invokespecial(descImplType.internalName, "", "(Ljava/lang/String;)V", false) + store(classDescVar, descImplType) + for (property in orderedProperties) { + if (property.transient) continue + load(classDescVar, descImplType) + aconst(property.name) + invokevirtual(descImplType.internalName, "addElement", "(Ljava/lang/String;)V", false) + // pushing annotations + for (annotationClass in property.annotations) { + load(classDescVar, descImplType) + val implType = codegen.typeMapper.mapType(annotationClass).internalName + "\$" + KSerializerDescriptorResolver.IMPL_NAME.identifier + // new Annotation$Impl(...) + anew(Type.getObjectType(implType)) + dup() + val sb = StringBuilder("(") + val args: List = (property.descriptor.annotations.findAnnotation(annotationClass.fqNameSafe) as? LazyAnnotationDescriptor)?.annotationEntry?.valueArguments.orEmpty() + val consParams = annotationClass.unsubstitutedPrimaryConstructor?.valueParameters.orEmpty() + if (args.size != consParams.size) throw IllegalArgumentException("Can't use arguments with defaults for serializable annotations yet") + for (i in consParams.indices) { + val decl = args[i] + val desc = consParams[i] + val valAsmType = codegen.typeMapper.mapType(desc.type) + expr.gen(decl.getArgumentExpression(), valAsmType) + sb.append(valAsmType.descriptor) + } + sb.append(")V") + invokespecial(implType, "", sb.toString(), false) + // serialDesc.pushAnnotation(..) + invokevirtual(descImplType.internalName, "pushAnnotation", "(Ljava/lang/annotation/Annotation;)V", false) + } + } + load(classDescVar, descImplType) + putstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) + } + } + + private fun InstructionAdapter.serialCLassDescToLocalVar(classDescVar: Int) { + getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) + store(classDescVar, descType) + } + + override fun generateSerializableClassProperty(property: PropertyDescriptor) { + codegen.generateMethod(property.getter!!) { signature, expressionCodegen -> + getstatic(serializerAsmType.internalName, serialDescField, descType.descriptor) + areturn(descType) + } + } + + override fun generateSave( + function: FunctionDescriptor + ) { + codegen.generateMethod(function) { signature, expressionCodegen -> + // fun save(output: KOutput, obj : T) + val outputVar = 1 + val objVar = 2 + val descVar = 3 + serialCLassDescToLocalVar(descVar) + val objType = signature.valueParameters[1].asmType + // output = output.writeBegin(classDesc, new KSerializer[0]) + load(outputVar, kOutputType) + load(descVar, descType) + iconst(0) + newarray(kSerializerType) // todo: use some predefined empty array + invokevirtual(kOutputType.internalName, "writeBegin", + "(" + descType.descriptor + kSerializerArrayType.descriptor + + ")" + kOutputType.descriptor, false) + store(outputVar, kOutputType) + if (serializableDescriptor.isInternalSerializable) { + // call obj.write$Self(output, classDesc) + load(objVar, objType) + load(outputVar, kOutputType) + load(descVar, descType) + invokevirtual(objType.internalName, KSerializerDescriptorResolver.WRITE_SELF_NAME.asString(), + "(${kOutputType.descriptor}${descType.descriptor})V", false) + } + else { + // loop for all properties + val labeledProperties = orderedProperties.filter { !it.transient } + for (index in labeledProperties.indices) { + val property = labeledProperties[index] + if (property.transient) continue + // output.writeXxxElementValue(classDesc, index, value) + load(outputVar, kOutputType) + load(descVar, descType) + iconst(index) + genKOutputMethodCall(property, codegen, expressionCodegen, objType, objVar) + } + } + // output.writeEnd(classDesc) + load(outputVar, kOutputType) + load(descVar, descType) + invokevirtual(kOutputType.internalName, "writeEnd", + "(" + descType.descriptor + ")V", false) + // return + areturn(Type.VOID_TYPE) + } + } + + override fun generateLoad( + function: FunctionDescriptor + ) { + codegen.generateMethod(function) { signature, expressionCodegen -> + // fun load(input: KInput): T + val inputVar = 1 + val descVar = 2 + val indexVar = 3 + val readAllVar = 4 + val bitMaskBase = 5 + val blocksCnt = orderedProperties.size / OPT_MASK_BITS + 1 + fun bitMaskOff(i: Int) = bitMaskBase + (i / OPT_MASK_BITS) * OPT_MASK_TYPE.size + val propsStartVar = bitMaskBase + OPT_MASK_TYPE.size * blocksCnt + serialCLassDescToLocalVar(descVar) + // boolean readAll = false + iconst(0) + store(readAllVar, Type.BOOLEAN_TYPE) + // initialize bit mask + for (i in 0 until blocksCnt) { + //int bitMaskN = 0 + iconst(0) + store(bitMaskBase + i * OPT_MASK_TYPE.size, OPT_MASK_TYPE) + } + // initialize all prop vars + var propVar = propsStartVar + for (property in orderedProperties) { + val propertyType = codegen.typeMapper.mapType(property.type) + stackValueDefault(propertyType) + store(propVar, propertyType) + propVar += propertyType.size + } + // input = input.readBegin(classDesc, new KSerializer[0]) + load(inputVar, kInputType) + load(descVar, descType) + iconst(0) + newarray(kSerializerType) // todo: use some predefined empty array + invokevirtual(kInputType.internalName, "readBegin", + "(" + descType.descriptor + kSerializerArrayType.descriptor + + ")" + kInputType.descriptor, false) + store(inputVar, kInputType) + // readElement: int index = input.readElement(classDesc) + val readElementLabel = Label() + visitLabel(readElementLabel) + load(inputVar, kInputType) + load(descVar, descType) + invokevirtual(kInputType.internalName, "readElement", + "(" + descType.descriptor + ")I", false) + store(indexVar, Type.INT_TYPE) + // switch(index) + val labeledProperties = orderedProperties.filter { !it.transient } + val readAllLabel = Label() + val readEndLabel = Label() + val labels = arrayOfNulls