KotlinX serialization plugin applied onto latest master

IntelliJ project, ant task and maven goal for serialization plugin
Serialization IDEA plugin
This commit is contained in:
Leonid Startsev
2017-07-25 21:29:33 +03:00
parent 6b5dcf6ae7
commit 841693643a
30 changed files with 2556 additions and 4 deletions
+43 -4
View File
@@ -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-reflect](reflect), the library for full reflection support
- [kotlin-test](kotlin.test), the library for multiplatform unit testing - [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. - [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.
<!-- - [kotlin-annotations-android](tools/kotlin-annotations-android) --> <!-- - [kotlin-annotations-android](tools/kotlin-annotations-android) -->
These libraries are built as a part of the [root](../) Gradle project. 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.
<!-- TODO: Move to another root --> <!-- TODO: Move to another root -->
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. 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 ### 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: 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. 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). See also [root ReadMe.md, section "Building"](../ReadMe.md#building).
Then you can build maven artifacts with Maven: Then you can build maven artifacts with Maven:
mvn install 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" 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'
```
@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.version>3.0.4</maven.version>
<serialization.src>${basedir}/../../../plugins/kotlin-serialization/kotlin-serialization-compiler/src</serialization.src>
<serialization.gradle.plugin.src>${basedir}/src/main/kotlin</serialization.gradle.plugin.src>
<serialization.gradle.plugin.resources>${basedir}/src/main/resources</serialization.gradle.plugin.resources>
<serialization.target-src>${basedir}/target/src/main/kotlin</serialization.target-src>
<serialization.target-resources>${basedir}/target/resource</serialization.target-resources>
</properties>
<parent>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-project</artifactId>
<version>1.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization</artifactId>
<packaging>jar</packaging>
<description>Serialization plugin for Gradle</description>
<repositories>
<repository>
<id>jetbrains-utils</id>
<url>http://repository.jetbrains.com/utils</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-compiler-embeddable</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-gradle-plugin-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>gradle-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${serialization.target-src}</sourceDirectory>
<resources>
<resource>
<directory>${serialization.target-resources}</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>copy-sources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${serialization.target-src}</outputDirectory>
<resources>
<resource>
<directory>${serialization.src}</directory>
</resource>
<resource>
<directory>${serialization.gradle.plugin.src}</directory>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${serialization.target-resources}/META-INF</outputDirectory>
<resources>
<resource>
<directory>${serialization.src}/META-INF</directory>
</resource>
<resource>
<directory>${serialization.gradle.plugin.resources}/META-INF</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>replace</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>${serialization.target-src}/**</include>
</includes>
<replacements>
<replacement>
<token>(?&lt;!\.)com\.intellij</token>
<value>org.jetbrains.kotlin.com.intellij</value>
</replacement>
</replacements>
</configuration>
</plugin>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${project.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${serialization.target-src}</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -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<Project> {
companion object {
fun isEnabled(project: Project) = project.plugins.findPlugin(SerializationGradleSubplugin::class.java) != null
}
override fun apply(project: Project) {
// nothing here
}
}
class SerializationKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
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<SubpluginOption> {
if (!SerializationGradleSubplugin.isEnabled(project)) return emptyList()
val options = mutableListOf<SubpluginOption>()
// nothing here
return options
}
override fun getGroupName() = SERIALIZATION_GROUP_NAME
override fun getArtifactName() = SERIALIZATION_ARTIFACT_NAME
override fun getCompilerPluginId() = "org.jetbrains.kotlinx.serialization"
}
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin
@@ -0,0 +1 @@
implementation-class=org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin
@@ -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
@@ -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.
@@ -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
}
@@ -0,0 +1,6 @@
#Mon Jul 31 13:14:07 MSK 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-bin.zip
+172
View File
@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
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" "$@"
@@ -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
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="plugin-api" scope="PROVIDED" />
<orderEntry type="module" module-name="frontend" scope="PROVIDED" />
<orderEntry type="module" module-name="frontend.java" scope="PROVIDED" />
<orderEntry type="module" module-name="util" scope="PROVIDED" />
<orderEntry type="module" module-name="backend" scope="PROVIDED" />
</component>
</module>
@@ -0,0 +1 @@
rootProject.name='serialization-plugin'
@@ -0,0 +1,13 @@
<idea-plugin>
<name>Kotlin-serialization</name>
<id>org.jetbrains.kotlinx.serialization</id>
<version>0.1-SNAPSHOT</version>
<depends>org.jetbrains.kotlin</depends>
<idea-version since-build="172"/>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<expressionCodegenExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationCodegenExtension"/>
<syntheticResolveExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationResolveExtension"/>
</extensions>
</idea-plugin>
@@ -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
@@ -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)
}
@@ -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!!)
}
}
@@ -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, "<init>", "(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<SerializableProperty>): 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, "<init>", 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
}
@@ -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<PropertyDescriptor>()
generateFieldsAndSetters(props)
generateConstructor(props)
}
private fun generateFieldsAndSetters(props: List<PropertyDescriptor>) {
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<PropertyDescriptor>) {
val constr = ClassConstructorDescriptorImpl.createSynthesized(
thisClass,
Annotations.EMPTY,
false,
thisClass.source
)
val args = mutableListOf<ValueParameterDescriptor>()
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", "<init>", "()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()
}
}
}
@@ -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<KtProperty>()
.associateBy { classCodegen.bindingContext[BindingContext.VARIABLE, it]!! }
private val paramsToProps: Map<PropertyDescriptor, KtParameter> = 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<KtAnonymousInitializer>()
.mapNotNull { it.body }
.forEach { exprCodegen.gen(it, Type.VOID_TYPE) }
areturn(Type.VOID_TYPE)
}
private fun InstructionAdapter.generateSuperSerializableCall(propStartVar: Int): Pair<Int, Int> {
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, "<init>", "()V", false)
return 0 to propStartVar
}
else {
val superProps = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties
val creator = buildInternalConstructorDesc(propStartVar, 1, classCodegen, superProps)
invokespecial(superType, "<init>", 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)
}
}
@@ -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, "<init>", "(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<ValueArgument> = (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, "<init>", 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<Label>(labeledProperties.size + 2)
labels[0] = readAllLabel // READ_ALL
labels[1] = readEndLabel // READ_DONE
for (i in labeledProperties.indices) {
labels[i + 2] = Label()
}
load(indexVar, Type.INT_TYPE)
// todo: readEnd is currently default, should probably throw exception instead
tableswitch(-2, labeledProperties.size - 1, readEndLabel, *labels)
// readAll: readAll := true
visitLabel(readAllLabel)
iconst(1)
store(readAllVar, Type.BOOLEAN_TYPE)
// loop for all properties
propVar = propsStartVar
var labelNum = 0
for ((index, property) in orderedProperties.withIndex()) {
val propertyType = codegen.typeMapper.mapType(property.type)
if (!property.transient) {
// labelI:
visitLabel(labels[labelNum + 2])
// propX := input.readXxxValue(value)
load(inputVar, kInputType)
load(descVar, descType)
iconst(labelNum)
val sti = getSerialTypeInfo(property, propertyType)
val useSerializer = stackValueSerializerInstance(codegen, sti)
invokevirtual(kInputType.internalName,
"read" + sti.nn + (if (useSerializer) "Serializable" else "") + "ElementValue",
"(" + descType.descriptor + "I" +
(if (useSerializer) kSerialLoaderType.descriptor else "")
+ ")" + (if (sti.unit) "V" else sti.type.descriptor), false)
if (sti.unit) {
StackValue.putUnitInstance(this)
}
else {
StackValue.coerce(sti.type, propertyType, this)
}
store(propVar, propertyType)
// mark read bit in mask
// bitMask = bitMask | 1 << index
val addr = bitMaskOff(index)
load(addr, OPT_MASK_TYPE)
iconst(1 shl (index % OPT_MASK_BITS))
or(OPT_MASK_TYPE)
store(addr, OPT_MASK_TYPE)
// if (readAll == false) goto readElement
load(readAllVar, Type.BOOLEAN_TYPE)
iconst(0)
ificmpeq(readElementLabel)
labelNum++
}
// next
propVar += propertyType.size
}
val resultVar = propVar
// readEnd: input.readEnd(classDesc)
visitLabel(readEndLabel)
load(inputVar, kInputType)
load(descVar, descType)
invokevirtual(kInputType.internalName, "readEnd",
"(" + descType.descriptor + ")V", false)
if (!serializableDescriptor.isInternalSerializable) {
//validate all required (constructor) fields
val nonThrowLabel = Label()
val throwLabel = Label()
for ((i, property) in properties.serializableConstructorProperties.withIndex()) {
if (property.optional || property.transient) {
// todo: Normal reporting of error
if (!property.isConstructorParameterWithDefault)
throw CompilationException("Property ${property.name} was declared as optional/transient but has no default value", null, null)
}
else {
genValidateProperty(i, ::bitMaskOff)
// todo: print name of each variable?
ificmpeq(throwLabel)
}
}
goTo(nonThrowLabel)
// throwing an exception
visitLabel(throwLabel)
genExceptionThrow(serializationExceptionName, "Not all required constructor fields were specified")
visitLabel(nonThrowLabel)
}
// create object with constructor
anew(serializableAsmType)
dup()
val constructorDesc = if (serializableDescriptor.isInternalSerializable)
buildInternalConstructorDesc(propsStartVar, bitMaskBase, codegen, properties.serializableProperties)
else buildExternalConstructorDesc(propsStartVar, bitMaskBase)
invokespecial(serializableAsmType.internalName, "<init>", constructorDesc, false)
if (!serializableDescriptor.isInternalSerializable && !properties.serializableStandaloneProperties.isEmpty()) {
// result := ... <created object>
store(resultVar, serializableAsmType)
// set other properties
propVar = propsStartVar + properties.serializableConstructorProperties.map { codegen.typeMapper.mapType(it.type).size }.sum()
genSetSerializableStandaloneProperties(expressionCodegen, propVar, resultVar, ::bitMaskOff)
// load result
load(resultVar, serializableAsmType)
// will return result
}
// return
areturn(serializableAsmType)
}
}
private fun InstructionAdapter.buildExternalConstructorDesc(propsStartVar: Int, bitMaskBase: Int): String {
val constructorDesc = StringBuilder("(")
var propVar = propsStartVar
for (property in properties.serializableConstructorProperties) {
val propertyType = codegen.typeMapper.mapType(property.type)
constructorDesc.append(propertyType.descriptor)
load(propVar, propertyType)
propVar += propertyType.size
}
if (!properties.primaryConstructorWithDefaults) {
constructorDesc.append(")V")
}
else {
val cnt = properties.serializableConstructorProperties.size.coerceAtMost(32) //only 32 default values are supported
val mask = if (cnt == 32) -1 else ((1 shl cnt) - 1)
load(bitMaskBase, OPT_MASK_TYPE)
iconst(mask)
xor(Type.INT_TYPE)
aconst(null)
constructorDesc.append("ILkotlin/jvm/internal/DefaultConstructorMarker;)V")
}
return constructorDesc.toString()
}
private fun InstructionAdapter.genSetSerializableStandaloneProperties(
expressionCodegen: ExpressionCodegen, propVarStart: Int, resultVar: Int, bitMaskPos: (Int) -> Int) {
var propVar = propVarStart
val offset = properties.serializableConstructorProperties.size
for ((index, property) in properties.serializableStandaloneProperties.withIndex()) {
val i = index + offset
//check if property has been seen and should be set
val nextLabel = Label()
// seen = bitMask & 1 << pos != 0
genValidateProperty(i, bitMaskPos)
if (property.optional) {
// if (seen)
// set
ificmpeq(nextLabel)
}
else {
// if (!seen)
// throw
// set
ificmpne(nextLabel)
genExceptionThrow(serializationExceptionMissingFieldName, property.name)
visitLabel(nextLabel)
}
// generate setter call
val propertyType = codegen.typeMapper.mapType(property.type)
expressionCodegen.intermediateValueForProperty(property.descriptor, false, null,
StackValue.local(resultVar, serializableAsmType)).
store(StackValue.local(propVar, propertyType), this)
propVar += propertyType.size
if (property.optional)
visitLabel(nextLabel)
}
}
// todo: move to StackValue?
private fun InstructionAdapter.stackValueDefault(type: Type) {
when (type.sort) {
BOOLEAN, BYTE, SHORT, CHAR, INT -> iconst(0)
LONG -> lconst(0)
FLOAT -> fconst(0f)
DOUBLE -> dconst(0.0)
else -> aconst(null)
}
}
}
@@ -0,0 +1,30 @@
/*
* 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
// :kludge: for stripped-down version of ASM inside kotlin-compiler-embeddable.jar
const val VOID = 0
const val BOOLEAN = 1
const val CHAR = 2
const val BYTE = 3
const val SHORT = 4
const val INT = 5
const val FLOAT = 6
const val LONG = 7
const val DOUBLE = 8
const val ARRAY = 9
const val OBJECT = 10
@@ -0,0 +1,31 @@
/*
* 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.extensions
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerialInfoCodegenImpl
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCodegenImpl
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl
class SerializationCodegenExtension : ExpressionCodegenExtension {
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
SerialInfoCodegenImpl.generateSerialInfoImplBody(codegen)
SerializableCodegenImpl.generateSerializableExtensions(codegen)
SerializerCodegenImpl.generateSerializerExtensions(codegen)
}
}
@@ -0,0 +1,30 @@
/*
* 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.extensions
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
class SerializationComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
ExpressionCodegenExtension.registerExtension(project, SerializationCodegenExtension())
SyntheticResolveExtension.registerExtension(project, SerializationResolveExtension())
}
}
@@ -0,0 +1,65 @@
/*
* 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.extensions
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
import org.jetbrains.kotlinx.serialization.compiler.resolve.serialInfoFqName
import java.util.*
class SerializationResolveExtension : SyntheticResolveExtension {
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> {
if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName))
return listOf(KSerializerDescriptorResolver.IMPL_NAME)
else
return listOf()
}
override fun generateSyntheticClasses(thisDescriptor: ClassDescriptor, name: Name, ctx: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider, result: MutableSet<ClassDescriptor>) {
if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == KSerializerDescriptorResolver.IMPL_NAME)
result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx))
return
}
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? =
if (thisDescriptor.isInternalSerializable) SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
else null
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes)
KSerializerDescriptorResolver.addSerializableSupertypes(thisDescriptor, supertypes)
KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes)
}
override fun generateSyntheticMethods(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: List<SimpleFunctionDescriptor>, result: MutableCollection<SimpleFunctionDescriptor>) {
KSerializerDescriptorResolver.generateSerializerMethods(thisDescriptor, fromSupertypes, name, result)
}
override fun generateSyntheticProperties(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: ArrayList<PropertyDescriptor>, result: MutableSet<PropertyDescriptor>) {
KSerializerDescriptorResolver.generateDescriptorsForAnnotationImpl(thisDescriptor, name, fromSupertypes, result)
KSerializerDescriptorResolver.generateSerializerProperties(thisDescriptor, fromSupertypes, name, result)
}
}
@@ -0,0 +1,180 @@
/*
* 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.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.*
internal val packageFqName = FqName("kotlinx.serialization")
// ---- kotlin.serialization.KSerializer
internal val kSerializerName = Name.identifier("KSerializer")
internal val kSerializerConstructorMarkerName = Name.identifier("SerializationConstructorMarker")
internal val kSerializerFqName = packageFqName.child(kSerializerName)
fun isKSerializer(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, kSerializerFqName)
fun ClassDescriptor.getKSerializerDescriptor(): ClassDescriptor =
module.findClassAcrossModuleDependencies(ClassId(packageFqName, kSerializerName))!!
fun ClassDescriptor.getKSerializerType(argument: SimpleType): SimpleType {
val projectionType = Variance.INVARIANT
val types = listOf(TypeProjectionImpl(projectionType, argument))
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, getKSerializerDescriptor(), types)
}
// ---- java.io.Serializable
internal val javaIOPackageFqName = FqName("java.io")
internal val javaSerializableName = Name.identifier("Serializable")
internal val javaSerializableFqName = javaIOPackageFqName.child(javaSerializableName)
fun isJavaSerializable(type: KotlinType?): Boolean =
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, javaSerializableFqName)
fun ClassDescriptor.getJavaSerializableDescriptor(): ClassDescriptor =
module.findClassAcrossModuleDependencies(ClassId(javaIOPackageFqName, javaSerializableName))!!
fun ClassDescriptor.getJavaSerializableType(): SimpleType {
return KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, getJavaSerializableDescriptor(), emptyList())
}
// ---- kotlin.serialization.Serializable(with=xxx)
internal val serializableAnnotationFqName = FqName("kotlinx.serialization.Serializable")
internal val Annotations.serializableWith: KotlinType?
get() = findAnnotationValue(serializableAnnotationFqName, "with")
// ---- kotlin.serialization.Serializer(forClass=xxx)
internal val serializerAnnotationFqName = FqName("kotlinx.serialization.Serializer")
internal val Annotations.serializerForClass: KotlinType?
get() = findAnnotationValue(serializerAnnotationFqName, "forClass")
// ---- kotlin.serialization.SerialName(value=xxx)
internal val serialNameAnnotationFqName = FqName("kotlinx.serialization.SerialName")
val Annotations.serialNameValue: String?
get() {
val value = findAnnotationValue<String?>(serialNameAnnotationFqName, "value")
return value
}
// ---- kotlin.serialization.Optional
internal val serialOptionalFqName = FqName("kotlinx.serialization.Optional")
val Annotations.serialOptional: Boolean
get() = hasAnnotation(serialOptionalFqName)
// ---- kotlin.serialization.Transient
internal val serialTransientFqName = FqName("kotlinx.serialization.Transient")
val Annotations.serialTransient: Boolean
get() = hasAnnotation(serialTransientFqName)
internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo")
// ----------------------------------------
val KotlinType?.toClassDescriptor: ClassDescriptor?
@JvmName("toClassDescriptor")
get() = this?.constructor?.declarationDescriptor as? ClassDescriptor
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
get() = annotations.hasAnnotation(serializableAnnotationFqName) && annotations.serializableWith == null
// serializer that was declared for this type
internal val ClassDescriptor?.classSerializer: KotlinType?
get() = this?.let {
// serializer annotation on class?
annotations.serializableWith?.let { return it }
// default serializable?
if (isInternalSerializable) return companionObjectDescriptor?.defaultType
return null
}
// serializer that was declared for this specific type or annotation from a class declaration
val KotlinType?.typeSerializer: KotlinType?
get() = this?.let {
// serializer annotation on this type or from a class
return it.annotations.serializableWith ?: (it.toClassDescriptor).classSerializer
}
// serializer that was declared specifically for this property via its own annotation or via annotation on its type
val PropertyDescriptor.propertySerializer: KotlinType?
get() = annotations.serializableWith ?: type.typeSerializer
fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
val serializerForClass = serializerDescriptor.annotations.serializerForClass
if (serializerForClass != null) return serializerForClass.toClassDescriptor
if (!serializerDescriptor.isCompanionObject) return null
val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.isInternalSerializable) return null
return classDescriptor
}
// todo: serialization: do an actual check
fun ClassDescriptor.checkSerializableClassPropertyResult(type: KotlinType): Boolean = true
// todo: serialization: do an actual check better that just number of parameters
fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 2
fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean =
KotlinBuiltIns.isUnit(type)
// todo: serialization: do an actual check better that just number of parameters
fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
parameters.size == 1
fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean = getSerializableClassDescriptorBySerializer(this)?.defaultType == type
// ----------------
inline fun <reified R> Annotations.findAnnotationValue(annotationFqName: FqName, property: String): R? =
findAnnotation(annotationFqName)?.let { annotation ->
annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value?.value
} as? R
// Search utils
fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
module.findClassAcrossModuleDependencies(ClassId(packageFqName, kSerializerConstructorMarkerName))!!
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
module.findClassAcrossModuleDependencies(ClassId(packageFqName, Name.identifier(classSimpleName)))!!
fun ClassDescriptor.toSimpleType(nullable: Boolean = true) = KotlinTypeFactory.simpleType(Annotations.EMPTY, this.typeConstructor, emptyList(), nullable)
@@ -0,0 +1,287 @@
/*
* 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.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.util.*
object KSerializerDescriptorResolver {
val SERIAL_DESC_FIELD = "serialClassDesc"
val SAVE = "save"
val LOAD = "load"
val SERIAL_DESC_FIELD_NAME = Name.identifier(SERIAL_DESC_FIELD)
val SAVE_NAME = Name.identifier(SAVE)
val LOAD_NAME = Name.identifier(LOAD)
val DUMMY_PARAM_NAME = Name.identifier("serializationConstructorMarker")
val WRITE_SELF_NAME = Name.identifier("write\$Self") //todo: add it as a supertype to synthetic resolving
val IMPL_NAME = Name.identifier("Impl")
fun addSerializableSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) {
supertypes.add(classDescriptor.getJavaSerializableType())
}
}
fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean {
return thisDescriptor.name == IMPL_NAME
&& thisDescriptor.containingDeclaration is LazyClassDescriptor
&& thisDescriptor.containingDeclaration.annotations.hasAnnotation(serialInfoFqName)
}
fun addSerialInfoSuperType(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
if (isSerialInfoImpl(thisDescriptor)) {
supertypes.add((thisDescriptor.containingDeclaration as LazyClassDescriptor).toSimpleType(false))
}
}
fun addSerializerSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
val serializableClassDescriptor = getSerializableClassDescriptorBySerializer(classDescriptor) ?: return
if (supertypes.none(::isKSerializer)) {
supertypes.add(classDescriptor.getKSerializerType(serializableClassDescriptor.defaultType))
}
}
fun addSerialInfoImplClass(interfaceDesc: ClassDescriptor, declarationProvider: ClassMemberDeclarationProvider, ctx: LazyClassContext): ClassDescriptor {
val interfaceDecl = declarationProvider.correspondingClassOrObject!!
val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor)
return SyntheticClassOrObjectDescriptor(ctx,
interfaceDecl,
interfaceDesc,
IMPL_NAME,
interfaceDesc.source,
scope,
Modality.FINAL,
Visibilities.PUBLIC,
Visibilities.PRIVATE,
ClassKind.CLASS,
false)
}
fun generateSerializerProperties(thisDescriptor: ClassDescriptor,
fromSupertypes: ArrayList<PropertyDescriptor>,
name: Name,
result: MutableSet<PropertyDescriptor>) {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
// todo: check if it is already defined
if (name == SERIAL_DESC_FIELD_NAME)
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
}
fun generateSerializerMethods(thisDescriptor: ClassDescriptor,
fromSupertypes: List<SimpleFunctionDescriptor>,
name: Name,
result: MutableCollection<SimpleFunctionDescriptor>) {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
fun shouldAddSerializerFunction(checkParameters: (FunctionDescriptor) -> Boolean): Boolean {
// Add 'save' / 'load' iff there is no such declared member AND there is no such final member in supertypes
return result.none(checkParameters) &&
fromSupertypes.none { checkParameters(it) && it.modality == Modality.FINAL }
}
if (name == SAVE_NAME &&
shouldAddSerializerFunction { classDescriptor.checkSaveMethodParameters(it.valueParameters) }) {
result.add(createSaveFunctionDescriptor(thisDescriptor, classDescriptor))
}
if (name == LOAD_NAME &&
shouldAddSerializerFunction { classDescriptor.checkLoadMethodParameters(it.valueParameters) }) {
result.add(createLoadFunctionDescriptor(thisDescriptor, classDescriptor))
}
}
fun createSerializableClassPropertyDescriptor(companionDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor): PropertyDescriptor =
doCreateSerializerProperty(companionDescriptor, classDescriptor, SERIAL_DESC_FIELD_NAME)
fun createSaveFunctionDescriptor(companionDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateSerializerFunction(companionDescriptor, classDescriptor, SAVE_NAME)
fun createLoadFunctionDescriptor(companionDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor): SimpleFunctionDescriptor =
doCreateSerializerFunction(companionDescriptor, classDescriptor, LOAD_NAME)
private fun doCreateSerializerProperty(
companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor,
name: Name
): PropertyDescriptor {
val typeParam = listOf(createProjection(classDescriptor.defaultType, Variance.INVARIANT, null))
val propertyFromSerializer = companionDescriptor.getKSerializerDescriptor().getMemberScope(typeParam)
.getContributedVariables(name, NoLookupLocation.FROM_BUILTINS).single()
val propertyDescriptor = PropertyDescriptorImpl.create(
companionDescriptor, Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, false, name,
CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source, false, false, false, false, false, false
)
val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call
propertyDescriptor.setType(propertyFromSerializer.type,
propertyFromSerializer.typeParameters,
companionDescriptor.thisAsReceiverParameter,
extensionReceiverParameter)
val propertyGetter = PropertyGetterDescriptorImpl(
propertyDescriptor, Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, false, false, false,
CallableMemberDescriptor.Kind.SYNTHESIZED, null, companionDescriptor.source
)
propertyGetter.initialize(propertyFromSerializer.type)
propertyDescriptor.initialize(propertyGetter, null)
propertyDescriptor.overriddenDescriptors = listOf(propertyFromSerializer)
return propertyDescriptor
}
private fun doCreateSerializerFunction(
companionDescriptor: ClassDescriptor,
classDescriptor: ClassDescriptor,
name: Name
): SimpleFunctionDescriptor {
val functionDescriptor = SimpleFunctionDescriptorImpl.create(
companionDescriptor, Annotations.EMPTY, name, CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source
)
val typeParam = listOf(createProjection(classDescriptor.defaultType, Variance.INVARIANT, null))
val functionFromSerializer = companionDescriptor.getKSerializerDescriptor().getMemberScope(typeParam)
.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
functionDescriptor.initialize(
null,
companionDescriptor.thisAsReceiverParameter,
functionFromSerializer.typeParameters,
functionFromSerializer.valueParameters.map { it.copy(functionDescriptor, it.name, it.index) },
functionFromSerializer.returnType,
Modality.OPEN,
Visibilities.PUBLIC
)
return functionDescriptor
}
fun createLoadConstructorDescriptor(
classDescriptor: ClassDescriptor,
bindingContext: BindingContext
): ClassConstructorDescriptor {
if (!classDescriptor.isInternalSerializable) throw IllegalArgumentException()
val functionDescriptor = ClassConstructorDescriptorImpl.createSynthesized(
classDescriptor,
Annotations.EMPTY,
false,
classDescriptor.source
)
val markerDesc = classDescriptor.getKSerializerConstructorMarker()
val markerType = markerDesc.toSimpleType()
val parameterDescsAsProps = SerializableProperties(classDescriptor, bindingContext).serializableProperties.map { it.descriptor }
var i = 0
val consParams = mutableListOf<ValueParameterDescriptor>()
consParams.add(ValueParameterDescriptorImpl(functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen"), functionDescriptor.builtIns.intType, false,
false, false, null, functionDescriptor.source))
for (prop in parameterDescsAsProps) {
consParams.add(ValueParameterDescriptorImpl(functionDescriptor, null, i++, prop.annotations, prop.name, prop.type.makeNullableIfNotPrimitive(), false, false,
false, null, functionDescriptor.source))
}
consParams.add(ValueParameterDescriptorImpl(functionDescriptor, null, i++, Annotations.EMPTY, DUMMY_PARAM_NAME, markerType, false,
false, false, null, functionDescriptor.source))
functionDescriptor.initialize(
consParams,
Visibilities.PUBLIC
)
functionDescriptor.returnType = classDescriptor.defaultType
return functionDescriptor
}
private fun KotlinType.makeNullableIfNotPrimitive() =
if (KotlinBuiltIns.isPrimitiveType(this)) this
else this.makeNullable()
fun createWriteSelfFunctionDescriptor(thisClass: ClassDescriptor): FunctionDescriptor {
val f = SimpleFunctionDescriptorImpl.create(thisClass, Annotations.EMPTY, WRITE_SELF_NAME, CallableMemberDescriptor.Kind.SYNTHESIZED, thisClass.source)
val returnType = f.builtIns.unitType
val args = mutableListOf<ValueParameterDescriptor>()
var i = 0
args.add(ValueParameterDescriptorImpl(
f,
null,
i++,
Annotations.EMPTY,
Name.identifier("output"),
thisClass.getClassFromSerializationPackage("KOutput").toSimpleType(false),
false,
false,
false,
null,
f.source)
)
args.add(ValueParameterDescriptorImpl(
f,
null,
i++,
Annotations.EMPTY,
Name.identifier("serialDesc"),
thisClass.getClassFromSerializationPackage("KSerialClassDesc").toSimpleType(false),
false,
false,
false,
null,
f.source)
)
f.initialize(
null,
thisClass.thisAsReceiverParameter,
emptyList(),
args,
returnType,
Modality.OPEN,
Visibilities.PUBLIC
)
return f
}
fun generateDescriptorsForAnnotationImpl(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: List<PropertyDescriptor>, result: MutableCollection<PropertyDescriptor>) {
if (isSerialInfoImpl(thisDescriptor)) {
result.add(fromSupertypes[0].copy(thisDescriptor, Modality.FINAL, Visibilities.PUBLIC, CallableMemberDescriptor.Kind.SYNTHESIZED, true) as PropertyDescriptor)
}
}
}
@@ -0,0 +1,74 @@
/*
* 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.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOwnParametersWithDefaultValue
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) {
private val primaryConstructorParameters: List<ValueParameterDescriptor> =
serializableClass.unsubstitutedPrimaryConstructor?.valueParameters ?: emptyList()
private val primaryConstructorProperties: Map<PropertyDescriptor, Boolean> =
primaryConstructorParameters.asSequence()
.map { parameter -> bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter] to parameter.hasDefaultValue() }
.mapNotNull { (a, b) -> if (a == null) null else a to b }
.toMap()
val isExternallySerializable: Boolean =
primaryConstructorParameters.size == primaryConstructorProperties.size
val serializableProperties: List<SerializableProperty> =
serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES)
.asSequence()
.filterIsInstance<PropertyDescriptor>()
.filter { it.kind == CallableMemberDescriptor.Kind.DECLARATION }
.filter(this::isPropSerializable)
.map { prop -> SerializableProperty(prop, primaryConstructorProperties[prop] ?: false) }
.partition { primaryConstructorProperties.contains(it.descriptor) }
.run {
val supers = serializableClass.getSuperClassNotAny()
if (supers == null || !supers.isInternalSerializable)
first + second
else
SerializableProperties(supers, bindingContext).serializableProperties + first + second
}
private fun isPropSerializable(it: PropertyDescriptor) =
if (serializableClass.isInternalSerializable) !it.annotations.serialTransient
else !Visibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.serialTransient) || primaryConstructorProperties.contains(it))
val serializableConstructorProperties: List<SerializableProperty> =
serializableProperties.asSequence()
.filter { primaryConstructorProperties.contains(it.descriptor) }
.toList()
val serializableStandaloneProperties: List<SerializableProperty> =
serializableProperties.minus(serializableConstructorProperties)
val size = serializableProperties.size
val indices = serializableProperties.indices
operator fun get(index: Int) = serializableProperties[index]
operator fun iterator() = serializableProperties.iterator()
val primaryConstructorWithDefaults = serializableClass.unsubstitutedPrimaryConstructor?.hasOwnParametersWithDefaultValue() ?: false
}
@@ -0,0 +1,32 @@
/*
* 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.resolve
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.module
class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructorParameterWithDefault: Boolean) {
val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
val type = descriptor.type
val module = descriptor.module
val serializer = descriptor.propertySerializer
val optional = descriptor.annotations.serialOptional
val transient = descriptor.annotations.serialTransient
val annotations = descriptor.annotations
.mapNotNull { it.type.toClassDescriptor }
.filter { it.annotations.hasAnnotation(serialInfoFqName) }
}