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:
+43
-4
@@ -6,7 +6,7 @@ This part of the project contains the sources of the following libraries:
|
||||
- [kotlin-reflect](reflect), the library for full reflection support
|
||||
- [kotlin-test](kotlin.test), the library for multiplatform unit testing
|
||||
- [kotlin-annotations-jvm](tools/kotlin-annotations-jvm), the annotations to improve types in the Java code to look better when being consumed in the Kotlin code.
|
||||
|
||||
|
||||
<!-- - [kotlin-annotations-android](tools/kotlin-annotations-android) -->
|
||||
|
||||
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 -->
|
||||
|
||||
This area of the project is the root for Maven build.
|
||||
This area of the project is the root for Maven build.
|
||||
|
||||
You can work with the maven modules of this maven project in IDEA from the [root IDEA project](../ReadMe.md#working-in-idea). After importing you'll be able to explore maven projects and run goals directly from IDEA with the instruments on the right sidebar.
|
||||
|
||||
### Building
|
||||
|
||||
You need to install a recent (at least 3.3) [Maven](http://maven.apache.org/) distribution.
|
||||
You need to install a recent (at least 3.3) [Maven](http://maven.apache.org/) distribution.
|
||||
|
||||
Before building this Maven project you need to build and install the required artifacts built with Gradle to the local maven repository, by issuing the following command in the root project:
|
||||
|
||||
@@ -33,7 +33,7 @@ Before building this Maven project you need to build and install the required ar
|
||||
This command assembles and puts the artifacts to the local maven repository to be used by the subsequent maven build.
|
||||
See also [root ReadMe.md, section "Building"](../ReadMe.md#building).
|
||||
|
||||
|
||||
|
||||
Then you can build maven artifacts with Maven:
|
||||
|
||||
mvn install
|
||||
@@ -42,3 +42,42 @@ If your maven build is failing with Out-Of-Memory errors, set JVM options for ma
|
||||
|
||||
MAVEN_OPTS="-Xmx2G"
|
||||
|
||||
|
||||
|
||||
## Kotlin serialization Gradle Plugin
|
||||
|
||||
To build it, first **build all the above** and then, `cd` to `tools/kotlin-serialization`
|
||||
and run `mvn install`
|
||||
|
||||
So, all build sequence will look like:
|
||||
|
||||
```bash
|
||||
# Assuming you are in the kotlin/libraries folder
|
||||
./gradlew build install
|
||||
mvn install
|
||||
cd tools/gradle-tools
|
||||
./gradlew clean install
|
||||
cd ../kotlin-serialization
|
||||
mvn install
|
||||
```
|
||||
|
||||
When it is installed in local maven repository, you can add it as a dependency in buildscript classpath and apply it:
|
||||
|
||||
```
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1-SNAPSHOT"
|
||||
classpath "org.jetbrains.kotlinx:kotlinx-serialization:1.1-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-serialization'
|
||||
|
||||
```
|
||||
|
||||
@@ -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>(?<!\.)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>
|
||||
+58
@@ -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"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin
|
||||
+17
@@ -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
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+6
@@ -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
@@ -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
|
||||
+16
@@ -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>
|
||||
+17
@@ -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
|
||||
+55
@@ -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)
|
||||
}
|
||||
+91
@@ -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!!)
|
||||
}
|
||||
}
|
||||
+283
@@ -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
|
||||
}
|
||||
+103
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+219
@@ -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)
|
||||
}
|
||||
}
|
||||
+396
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+30
@@ -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
|
||||
+31
@@ -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)
|
||||
}
|
||||
}
|
||||
+30
@@ -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())
|
||||
}
|
||||
}
|
||||
+65
@@ -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)
|
||||
}
|
||||
}
|
||||
+180
@@ -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)
|
||||
+287
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -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
|
||||
}
|
||||
+32
@@ -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) }
|
||||
}
|
||||
Reference in New Issue
Block a user