| /* |
| * Copyright 2019 the original author or authors. |
| * |
| * 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.gradle.instantexecution.serialization |
| |
| import org.gradle.instantexecution.extensions.uncheckedCast |
| import org.gradle.internal.classpath.ClassPath |
| import org.gradle.internal.classpath.DefaultClassPath |
| import org.gradle.internal.serialize.BaseSerializerFactory |
| import org.gradle.internal.serialize.Decoder |
| import org.gradle.internal.serialize.Encoder |
| import org.gradle.internal.serialize.Serializer |
| |
| import java.io.File |
| |
| import java.util.ArrayDeque |
| |
| import kotlin.coroutines.Continuation |
| import kotlin.coroutines.CoroutineContext |
| import kotlin.coroutines.coroutineContext |
| import kotlin.coroutines.startCoroutine |
| import kotlin.coroutines.suspendCoroutine |
| |
| |
| internal |
| fun <T> singleton(value: T): Codec<T> = |
| SingletonCodec(value) |
| |
| |
| internal |
| inline fun <reified T> ownerService() = |
| codec<T>({ }, { readOwnerService() }) |
| |
| |
| internal |
| inline fun <reified T : Any> unsupported(): Codec<T> = codec( |
| encode = { value -> |
| logUnsupported(T::class, value.javaClass) |
| }, |
| decode = { |
| logUnsupported(T::class) |
| null |
| } |
| ) |
| |
| |
| internal |
| fun <T> codec( |
| encode: suspend WriteContext.(T) -> Unit, |
| decode: suspend ReadContext.() -> T? |
| ): Codec<T> = object : Codec<T> { |
| override suspend fun WriteContext.encode(value: T) = encode(value) |
| override suspend fun ReadContext.decode(): T? = decode() |
| } |
| |
| |
| private |
| inline fun <reified T> ReadContext.readOwnerService() = |
| isolate.owner.service<T>() |
| |
| |
| internal |
| fun <T : Any> reentrant(codec: Codec<T>): Codec<T> = object : Codec<T> { |
| |
| val encodeStack = ArrayDeque<EncodeFrame<T>>() |
| |
| val decodeStack = ArrayDeque<DecodeFrame<T?>>() |
| |
| override suspend fun WriteContext.encode(value: T) { |
| when { |
| encodeStack.isEmpty() -> { |
| encodeStack.push(EncodeFrame(value, null)) |
| encodeLoop(coroutineContext) |
| } |
| else -> suspendCoroutine<Unit> { k -> |
| encodeStack.push(EncodeFrame(value, k)) |
| } |
| } |
| } |
| |
| override suspend fun ReadContext.decode(): T? = |
| when { |
| decodeStack.isEmpty() -> { |
| decodeStack.push(DecodeFrame(null)) |
| decodeLoop(coroutineContext) |
| } |
| else -> suspendCoroutine { k -> |
| decodeStack.push(DecodeFrame(k)) |
| } |
| } |
| |
| private |
| fun WriteContext.encodeLoop(coroutineContext: CoroutineContext) { |
| do { |
| suspend { |
| codec.run { |
| encode(encodeStack.peek().value) |
| } |
| }.startCoroutine( |
| Continuation(coroutineContext) { |
| when (val k = encodeStack.pop().k) { |
| null -> it.getOrThrow() |
| else -> k.resumeWith(it) |
| } |
| } |
| ) |
| } while (encodeStack.isNotEmpty()) |
| } |
| |
| private |
| fun ReadContext.decodeLoop(coroutineContext: CoroutineContext): T? { |
| var result: T? = null |
| do { |
| suspend { |
| codec.run { |
| decode() |
| } |
| }.startCoroutine( |
| Continuation(coroutineContext) { |
| when (val k = decodeStack.pop().k) { |
| null -> result = it.getOrThrow() |
| else -> k.resumeWith(it) |
| } |
| } |
| ) |
| } while (decodeStack.isNotEmpty()) |
| return result |
| } |
| } |
| |
| |
| @Suppress("experimental_feature_warning") |
| private |
| inline class DecodeFrame<T>(val k: Continuation<T>?) |
| |
| |
| private |
| data class EncodeFrame<T>(val value: T, val k: Continuation<Unit>?) |
| |
| |
| private |
| data class SingletonCodec<T>( |
| private val singleton: T |
| ) : Codec<T> { |
| override suspend fun WriteContext.encode(value: T) = Unit |
| override suspend fun ReadContext.decode(): T? = singleton |
| } |
| |
| |
| internal |
| data class SerializerCodec<T>(val serializer: Serializer<T>) : Codec<T> { |
| override suspend fun WriteContext.encode(value: T) = serializer.write(this, value) |
| override suspend fun ReadContext.decode(): T = serializer.read(this) |
| } |
| |
| |
| internal |
| fun WriteContext.writeClassArray(values: Array<Class<*>>) { |
| writeArray(values) { writeClass(it) } |
| } |
| |
| |
| internal |
| fun ReadContext.readClassArray(): Array<Class<*>> = |
| readArray { readClass() } |
| |
| |
| internal |
| suspend fun ReadContext.readList(): List<Any?> = |
| readList { read() } |
| |
| |
| internal |
| inline fun <T : Any?> ReadContext.readList(readElement: () -> T): List<T> = |
| readCollectionInto({ size -> ArrayList<T>(size) }) { |
| readElement() |
| } |
| |
| |
| internal |
| suspend fun WriteContext.writeCollection(value: Collection<*>) { |
| writeCollection(value) { write(it) } |
| } |
| |
| |
| internal |
| suspend fun <T : MutableCollection<Any?>> ReadContext.readCollectionInto(factory: (Int) -> T): T = |
| readCollectionInto(factory) { read() } |
| |
| |
| internal |
| suspend fun WriteContext.writeMap(value: Map<*, *>) { |
| writeSmallInt(value.size) |
| for (entry in value.entries) { |
| write(entry.key) |
| write(entry.value) |
| } |
| } |
| |
| |
| internal |
| suspend fun <T : MutableMap<Any?, Any?>> ReadContext.readMapInto(factory: (Int) -> T): T { |
| val size = readSmallInt() |
| val items = factory(size) |
| for (i in 0 until size) { |
| val key = read() |
| val value = read() |
| items[key] = value |
| } |
| return items |
| } |
| |
| |
| internal |
| fun Encoder.writeClassPath(classPath: ClassPath) { |
| writeCollection(classPath.asFiles) { |
| writeFile(it) |
| } |
| } |
| |
| |
| internal |
| fun Decoder.readClassPath(): ClassPath = |
| DefaultClassPath.of( |
| readCollectionInto({ size -> LinkedHashSet<File>(size) }) { |
| readFile() |
| } |
| ) |
| |
| |
| internal |
| fun Encoder.writeFile(file: File?) { |
| BaseSerializerFactory.FILE_SERIALIZER.write(this, file) |
| } |
| |
| |
| internal |
| fun Decoder.readFile(): File = |
| BaseSerializerFactory.FILE_SERIALIZER.read(this) |
| |
| |
| internal |
| fun Encoder.writeStrings(strings: List<String>) { |
| writeCollection(strings) { |
| writeString(it) |
| } |
| } |
| |
| |
| internal |
| fun Decoder.readStrings(): List<String> = |
| readCollectionInto({ size -> ArrayList(size) }) { |
| readString() |
| } |
| |
| |
| internal |
| inline fun <T> Encoder.writeCollection(collection: Collection<T>, writeElement: (T) -> Unit) { |
| writeSmallInt(collection.size) |
| for (element in collection) { |
| writeElement(element) |
| } |
| } |
| |
| |
| internal |
| fun Decoder.readCollection(readElement: () -> Unit) { |
| val size = readSmallInt() |
| for (i in 0 until size) { |
| readElement() |
| } |
| } |
| |
| |
| internal |
| inline fun <T, C : MutableCollection<T>> Decoder.readCollectionInto( |
| containerForSize: (Int) -> C, |
| readElement: () -> T |
| ): C { |
| val size = readSmallInt() |
| val container = containerForSize(size) |
| for (i in 0 until size) { |
| container.add(readElement()) |
| } |
| return container |
| } |
| |
| |
| internal |
| inline fun <T : Any?> WriteContext.writeArray(array: Array<T>, writeElement: (T) -> Unit) { |
| writeClass(array.javaClass.componentType) |
| writeSmallInt(array.size) |
| for (element in array) { |
| writeElement(element) |
| } |
| } |
| |
| |
| internal |
| inline fun <T : Any?> ReadContext.readArray(readElement: () -> T): Array<T> { |
| val componentType = readClass() |
| val size = readSmallInt() |
| val array: Array<T> = java.lang.reflect.Array.newInstance(componentType, size).uncheckedCast() |
| for (i in 0 until size) { |
| array[i] = readElement() |
| } |
| return array |
| } |
| |
| |
| fun <E : Enum<E>> WriteContext.writeEnum(value: E) { |
| writeSmallInt(value.ordinal) |
| } |
| |
| |
| inline fun <reified E : Enum<E>> ReadContext.readEnum(): E = |
| readSmallInt().let { ordinal -> enumValues<E>().first { it.ordinal == ordinal } } |