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:
@@ -42,3 +42,42 @@ If your maven build is failing with Out-Of-Memory errors, set JVM options for ma
|
|||||||
|
|
||||||
MAVEN_OPTS="-Xmx2G"
|
MAVEN_OPTS="-Xmx2G"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Kotlin serialization Gradle Plugin
|
||||||
|
|
||||||
|
To build it, first **build all the above** and then, `cd` to `tools/kotlin-serialization`
|
||||||
|
and run `mvn install`
|
||||||
|
|
||||||
|
So, all build sequence will look like:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Assuming you are in the kotlin/libraries folder
|
||||||
|
./gradlew build install
|
||||||
|
mvn install
|
||||||
|
cd tools/gradle-tools
|
||||||
|
./gradlew clean install
|
||||||
|
cd ../kotlin-serialization
|
||||||
|
mvn install
|
||||||
|
```
|
||||||
|
|
||||||
|
When it is installed in local maven repository, you can add it as a dependency in buildscript classpath and apply it:
|
||||||
|
|
||||||
|
```
|
||||||
|
buildscript {
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.1-SNAPSHOT"
|
||||||
|
classpath "org.jetbrains.kotlinx:kotlinx-serialization:1.1-SNAPSHOT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'kotlin-serialization'
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<properties>
|
||||||
|
<maven.version>3.0.4</maven.version>
|
||||||
|
<serialization.src>${basedir}/../../../plugins/kotlin-serialization/kotlin-serialization-compiler/src</serialization.src>
|
||||||
|
<serialization.gradle.plugin.src>${basedir}/src/main/kotlin</serialization.gradle.plugin.src>
|
||||||
|
<serialization.gradle.plugin.resources>${basedir}/src/main/resources</serialization.gradle.plugin.resources>
|
||||||
|
<serialization.target-src>${basedir}/target/src/main/kotlin</serialization.target-src>
|
||||||
|
<serialization.target-resources>${basedir}/target/resource</serialization.target-resources>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-project</artifactId>
|
||||||
|
<version>1.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>org.jetbrains.kotlinx</groupId>
|
||||||
|
<artifactId>kotlinx-serialization</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<description>Serialization plugin for Gradle</description>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>jetbrains-utils</id>
|
||||||
|
<url>http://repository.jetbrains.com/utils</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-stdlib</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-compiler-embeddable</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-gradle-plugin-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>gradle-api</artifactId>
|
||||||
|
<version>2.2</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>${serialization.target-src}</sourceDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${serialization.target-resources}</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-sources</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${serialization.target-src}</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${serialization.src}</directory>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>${serialization.gradle.plugin.src}</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${serialization.target-resources}/META-INF</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${serialization.src}/META-INF</directory>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>${serialization.gradle.plugin.resources}/META-INF</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||||
|
<artifactId>replacer</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>replace</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<includes>
|
||||||
|
<include>${serialization.target-src}/**</include>
|
||||||
|
</includes>
|
||||||
|
<replacements>
|
||||||
|
<replacement>
|
||||||
|
<token>(?<!\.)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