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:
@@ -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