Rewrite Complex class as interface
This change allow ExceptionState to be both: State and Throwable
This commit is contained in:
committed by
TeamCityServer
parent
6808151af7
commit
1d0acedc48
@@ -150,7 +150,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
|||||||
|
|
||||||
internal fun State.wrapAsProxyIfNeeded(calledFromBuiltIns: Boolean = false): Any? {
|
internal fun State.wrapAsProxyIfNeeded(calledFromBuiltIns: Boolean = false): Any? {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is ExceptionState -> this.getThisAsCauseForException()
|
is ExceptionState -> this
|
||||||
is Wrapper -> this.value
|
is Wrapper -> this.value
|
||||||
is Primitive<*> -> this.value
|
is Primitive<*> -> this.value
|
||||||
is Common -> Proxy(this, this@IrInterpreter, calledFromBuiltIns)
|
is Common -> Proxy(this, this@IrInterpreter, calledFromBuiltIns)
|
||||||
@@ -812,9 +812,9 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
|||||||
private fun interpretTry(expression: IrTry): ExecutionResult {
|
private fun interpretTry(expression: IrTry): ExecutionResult {
|
||||||
try {
|
try {
|
||||||
expression.tryResult.interpret().check(ReturnLabel.EXCEPTION) { return it } // if not exception -> return
|
expression.tryResult.interpret().check(ReturnLabel.EXCEPTION) { return it } // if not exception -> return
|
||||||
val exception = stack.peekReturnValue() as ExceptionState
|
val exception = stack.peekReturnValue()
|
||||||
for (catchBlock in expression.catches) {
|
for (catchBlock in expression.catches) {
|
||||||
if (exception.isSubtypeOf(catchBlock.catchParameter.type.classOrNull!!.owner)) {
|
if (exception.isSubtypeOf(catchBlock.catchParameter.type)) {
|
||||||
catchBlock.interpret().check { return it }
|
catchBlock.interpret().check { return it }
|
||||||
return Next
|
return Next
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.ir.interpreter.builtins
|
package org.jetbrains.kotlin.ir.interpreter.builtins
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
||||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
|
||||||
|
|
||||||
/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */
|
/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */
|
||||||
|
|
||||||
@@ -140,9 +139,7 @@ val unaryFunctions = mapOf<CompileTimeFunction, Function1<Any?, Any?>>(
|
|||||||
unaryOperation<Array<Any?>>("iterator", "Array") { a -> a.iterator() },
|
unaryOperation<Array<Any?>>("iterator", "Array") { a -> a.iterator() },
|
||||||
unaryOperation<Any>("hashCode", "Any") { a -> a.hashCode() },
|
unaryOperation<Any>("hashCode", "Any") { a -> a.hashCode() },
|
||||||
unaryOperation<Any>("toString", "Any") { a -> a.toString() },
|
unaryOperation<Any>("toString", "Any") { a -> a.toString() },
|
||||||
unaryOperation<Any?>("CHECK_NOT_NULL", "T0?") { a -> a!! },
|
unaryOperation<Any?>("CHECK_NOT_NULL", "T0?") { a -> a!! }
|
||||||
unaryOperation<ExceptionState>("message", "Throwable") { a -> a.getMessage() },
|
|
||||||
unaryOperation<ExceptionState>("cause", "Throwable") { a -> a.getCause() }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val binaryFunctions = mapOf<CompileTimeFunction, Function2<Any?, Any?, Any?>>(
|
val binaryFunctions = mapOf<CompileTimeFunction, Function2<Any?, Any?, Any?>>(
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
|
|||||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||||
|
|
||||||
internal class Common private constructor(
|
internal class Common private constructor(override val irClass: IrClass, override val fields: MutableList<Variable>) : Complex {
|
||||||
override val irClass: IrClass, override val fields: MutableList<Variable>
|
override var superWrapperClass: Wrapper? = null
|
||||||
) : Complex(irClass, fields) {
|
override val typeArguments: MutableList<Variable> = mutableListOf()
|
||||||
|
override var outerClass: Variable? = null
|
||||||
|
|
||||||
constructor(irClass: IrClass) : this(irClass, mutableListOf())
|
constructor(irClass: IrClass) : this(irClass, mutableListOf())
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -14,16 +14,15 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.defaultType
|
|
||||||
import org.jetbrains.kotlin.ir.types.isAny
|
import org.jetbrains.kotlin.ir.types.isAny
|
||||||
import org.jetbrains.kotlin.ir.types.isNullableAny
|
import org.jetbrains.kotlin.ir.types.isNullableAny
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
internal abstract class Complex(override val irClass: IrClass, override val fields: MutableList<Variable>) : State {
|
internal interface Complex: State {
|
||||||
var superWrapperClass: Wrapper? = null
|
var superWrapperClass: Wrapper?
|
||||||
override val typeArguments: MutableList<Variable> = mutableListOf()
|
override val typeArguments: MutableList<Variable>
|
||||||
var outerClass: Variable? = null
|
var outerClass: Variable?
|
||||||
|
|
||||||
fun irClassFqName(): String {
|
fun irClassFqName(): String {
|
||||||
return irClass.fqNameForIrSerialization.toString()
|
return irClass.fqNameForIrSerialization.toString()
|
||||||
@@ -34,7 +33,11 @@ internal abstract class Complex(override val irClass: IrClass, override val fiel
|
|||||||
val propertySetters = this.declarations.filterIsInstance<IrProperty>().mapNotNull { it.setter }
|
val propertySetters = this.declarations.filterIsInstance<IrProperty>().mapNotNull { it.setter }
|
||||||
val functions = this.declarations.filterIsInstance<IrFunction>()
|
val functions = this.declarations.filterIsInstance<IrFunction>()
|
||||||
return (propertyGetters + propertySetters + functions).firstOrNull {
|
return (propertyGetters + propertySetters + functions).firstOrNull {
|
||||||
if (it is IrSimpleFunction) it.overrides(symbol.owner as IrSimpleFunction) else it == symbol.owner
|
val owner = symbol.owner
|
||||||
|
when {
|
||||||
|
it is IrSimpleFunction && owner is IrSimpleFunction -> it.overrides(owner) || owner.overrides(it)
|
||||||
|
else -> it == symbol.owner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +49,7 @@ internal abstract class Complex(override val irClass: IrClass, override val fiel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getOverridden(owner: IrSimpleFunction): IrSimpleFunction {
|
private fun getOverridden(owner: IrSimpleFunction): IrSimpleFunction {
|
||||||
if (!owner.isFakeOverride || owner.body != null || owner.parentAsClass.defaultType.isAny()) return owner
|
if (!owner.isFakeOverride || owner.body != null || owner.parentAsClass.defaultType.isAny()) return owner
|
||||||
|
|
||||||
val overriddenOwner = owner.overriddenSymbols.singleOrNull { !it.owner.parentAsClass.isInterface }?.owner
|
val overriddenOwner = owner.overriddenSymbols.singleOrNull { !it.owner.parentAsClass.isInterface }?.owner
|
||||||
|
|||||||
+18
-20
@@ -17,7 +17,16 @@ import kotlin.math.min
|
|||||||
|
|
||||||
internal class ExceptionState private constructor(
|
internal class ExceptionState private constructor(
|
||||||
override val irClass: IrClass, override val fields: MutableList<Variable>, stackTrace: List<String>
|
override val irClass: IrClass, override val fields: MutableList<Variable>, stackTrace: List<String>
|
||||||
) : Complex(irClass, fields) {
|
) : Complex, Throwable() {
|
||||||
|
|
||||||
|
override var superWrapperClass: Wrapper? = null
|
||||||
|
override val typeArguments: MutableList<Variable> = mutableListOf()
|
||||||
|
override var outerClass: Variable? = null
|
||||||
|
|
||||||
|
override val message: String?
|
||||||
|
get() = getState(messageProperty.symbol)?.asStringOrNull()
|
||||||
|
override val cause: Throwable?
|
||||||
|
get() = getState(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
|
||||||
|
|
||||||
private lateinit var exceptionFqName: String
|
private lateinit var exceptionFqName: String
|
||||||
private val exceptionHierarchy = mutableListOf<String>()
|
private val exceptionHierarchy = mutableListOf<String>()
|
||||||
@@ -45,6 +54,7 @@ internal class ExceptionState private constructor(
|
|||||||
constructor(
|
constructor(
|
||||||
exception: Throwable, irClass: IrClass, stackTrace: List<String>
|
exception: Throwable, irClass: IrClass, stackTrace: List<String>
|
||||||
) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) {
|
) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) {
|
||||||
|
setCause(null) // TODO check this fact
|
||||||
if (irClass.name.asString() != exception::class.java.simpleName) {
|
if (irClass.name.asString() != exception::class.java.simpleName) {
|
||||||
// ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy
|
// ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy
|
||||||
this.exceptionFqName = exception::class.java.name
|
this.exceptionFqName = exception::class.java.name
|
||||||
@@ -54,18 +64,11 @@ internal class ExceptionState private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ExceptionData(val state: ExceptionState) : Throwable() {
|
|
||||||
override val message: String? = state.getMessage()
|
|
||||||
override fun fillInStackTrace() = this
|
|
||||||
|
|
||||||
override fun toString(): String = state.getMessageWithName()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpCauseIfNeeded(wrapper: Wrapper?) {
|
private fun setUpCauseIfNeeded(wrapper: Wrapper?) {
|
||||||
val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionData
|
val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionState
|
||||||
setCause(cause?.state)
|
setCause(cause)
|
||||||
if (getMessage() == null && cause != null) {
|
if (message == null && cause != null) {
|
||||||
val causeMessage = cause.state.exceptionFqName + (cause.state.getMessage()?.let { ": $it" } ?: "")
|
val causeMessage = cause.exceptionFqName + (cause.message?.let { ": $it" } ?: "")
|
||||||
setMessage(causeMessage)
|
setMessage(causeMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,23 +88,18 @@ internal class ExceptionState private constructor(
|
|||||||
setField(Variable(causeProperty.symbol, causeValue ?: Primitive<Throwable?>(null, causeProperty.getter!!.returnType)))
|
setField(Variable(causeProperty.symbol, causeValue ?: Primitive<Throwable?>(null, causeProperty.getter!!.returnType)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessage(): String? = (getState(messageProperty.symbol) as Primitive<*>).value as String?
|
|
||||||
private fun getMessageWithName(): String = getMessage()?.let { "$exceptionFqName: $it" } ?: exceptionFqName
|
|
||||||
|
|
||||||
fun getCause(): ExceptionState? = getState(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
|
|
||||||
|
|
||||||
fun getFullDescription(): String {
|
fun getFullDescription(): String {
|
||||||
// TODO remainder of the stack trace with "..."
|
// TODO remainder of the stack trace with "..."
|
||||||
val message = getMessage().let { if (it?.isNotEmpty() == true) ": $it" else "" }
|
val message = message.let { if (it?.isNotEmpty() == true) ": $it" else "" }
|
||||||
val prefix = if (stackTrace.isNotEmpty()) "\n\t" else ""
|
val prefix = if (stackTrace.isNotEmpty()) "\n\t" else ""
|
||||||
val postfix = if (stackTrace.size > 10) "\n\t..." else ""
|
val postfix = if (stackTrace.size > 10) "\n\t..." else ""
|
||||||
val causeMessage = getCause()?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: ""
|
val causeMessage = (cause as? ExceptionState)?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: ""
|
||||||
return "Exception $exceptionFqName$message" +
|
return "Exception $exceptionFqName$message" +
|
||||||
stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) +
|
stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) +
|
||||||
causeMessage
|
causeMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getThisAsCauseForException() = ExceptionData(this)
|
override fun toString(): String = message?.let { "$exceptionFqName: $it" } ?: exceptionFqName
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun IrClass.getPropertyByName(name: String): IrProperty {
|
private fun IrClass.getPropertyByName(name: String): IrProperty {
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ internal fun State.asBoolean() = (this as Primitive<*>).value as Boolean
|
|||||||
internal fun State.asString() = (this as Primitive<*>).value.toString()
|
internal fun State.asString() = (this as Primitive<*>).value.toString()
|
||||||
|
|
||||||
internal fun State.asBooleanOrNull() = (this as? Primitive<*>)?.value as? Boolean
|
internal fun State.asBooleanOrNull() = (this as? Primitive<*>)?.value as? Boolean
|
||||||
|
internal fun State.asStringOrNull() = (this as Primitive<*>).value as? String
|
||||||
|
|
||||||
internal fun State.isSubtypeOf(other: IrType): Boolean {
|
internal fun State.isSubtypeOf(other: IrType): Boolean {
|
||||||
if (this is Primitive<*> && this.value == null) return other.isNullable()
|
if (this is Primitive<*> && this.value == null) return other.isNullable()
|
||||||
|
if (this is ExceptionState) return this.isSubtypeOf(other.classOrNull!!.owner)
|
||||||
|
|
||||||
if (this is Primitive<*> && this.type.isArray() && other.isArray()) {
|
if (this is Primitive<*> && this.type.isArray() && other.isArray()) {
|
||||||
val thisClass = this.typeArguments.single().state.irClass.symbol
|
val thisClass = this.typeArguments.single().state.irClass.symbol
|
||||||
|
|||||||
+14
-7
@@ -9,12 +9,13 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.interpreter.*
|
import org.jetbrains.kotlin.ir.interpreter.*
|
||||||
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
|
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
|
||||||
|
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||||
@@ -22,11 +23,17 @@ import java.lang.invoke.MethodHandle
|
|||||||
import java.lang.invoke.MethodHandles
|
import java.lang.invoke.MethodHandles
|
||||||
import java.lang.invoke.MethodType
|
import java.lang.invoke.MethodType
|
||||||
|
|
||||||
internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(irClass, mutableListOf()) {
|
internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex {
|
||||||
|
override val fields: MutableList<Variable> = mutableListOf()
|
||||||
|
|
||||||
|
override var superWrapperClass: Wrapper? = null
|
||||||
|
override val typeArguments: MutableList<Variable> = mutableListOf()
|
||||||
|
override var outerClass: Variable? = null
|
||||||
|
|
||||||
private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe()
|
|
||||||
private val receiverClass = irClass.defaultType.getClass(true)
|
private val receiverClass = irClass.defaultType.getClass(true)
|
||||||
|
|
||||||
|
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? = null
|
||||||
|
|
||||||
fun getMethod(irFunction: IrFunction): MethodHandle? {
|
fun getMethod(irFunction: IrFunction): MethodHandle? {
|
||||||
if (irFunction.getEvaluateIntrinsicValue()?.isEmpty() == true) return null // this method will handle IntrinsicEvaluator
|
if (irFunction.getEvaluateIntrinsicValue()?.isEmpty() == true) return null // this method will handle IntrinsicEvaluator
|
||||||
// if function is actually a getter, then use "get${property.name.capitalize()}" as method name
|
// if function is actually a getter, then use "get${property.name.capitalize()}" as method name
|
||||||
@@ -51,6 +58,10 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val companionObjectValue = mapOf<String, Any>("kotlin.text.Regex\$Companion" to Regex.Companion)
|
private val companionObjectValue = mapOf<String, Any>("kotlin.text.Regex\$Companion" to Regex.Companion)
|
||||||
|
|
||||||
@@ -184,8 +195,4 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "Wrapper(obj='$typeFqName', value=$value)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ fun generateMap(): String {
|
|||||||
p.println("package org.jetbrains.kotlin.ir.interpreter.builtins")
|
p.println("package org.jetbrains.kotlin.ir.interpreter.builtins")
|
||||||
p.println()
|
p.println()
|
||||||
p.println("import org.jetbrains.kotlin.ir.interpreter.IrInterpreter")
|
p.println("import org.jetbrains.kotlin.ir.interpreter.IrInterpreter")
|
||||||
p.println("import org.jetbrains.kotlin.ir.interpreter.state.*")
|
|
||||||
p.println()
|
p.println()
|
||||||
p.println("/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */")
|
p.println("/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */")
|
||||||
p.println()
|
p.println()
|
||||||
@@ -138,10 +137,7 @@ private fun generateUnaryBody(unaryOperationsMap: Map<CallableDescriptor, Pair<S
|
|||||||
val methodName = "${function.name}"
|
val methodName = "${function.name}"
|
||||||
val parentheses = if (function is FunctionDescriptor) "()" else ""
|
val parentheses = if (function is FunctionDescriptor) "()" else ""
|
||||||
" unaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a -> a.$methodName$parentheses }"
|
" unaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a -> a.$methodName$parentheses }"
|
||||||
} +
|
} + " unaryOperation<Any?>(\"${irNullCheck.name}\", \"${irNullCheck.valueParameters.first().type.originalKotlinType}\") { a -> a!! }"
|
||||||
" unaryOperation<Any?>(\"${irNullCheck.name}\", \"${irNullCheck.valueParameters.first().type.originalKotlinType}\") { a -> a!! },\n" +
|
|
||||||
" unaryOperation<ExceptionState>(\"message\", \"Throwable\") { a -> a.getMessage() },\n" +
|
|
||||||
" unaryOperation<ExceptionState>(\"cause\", \"Throwable\") { a -> a.getCause() }"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateBinaryBody(
|
private fun generateBinaryBody(
|
||||||
|
|||||||
Reference in New Issue
Block a user