<commit_before>/*
 * Copyright 2022 The Android Open Source Project
 *
 * 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 androidx.compose.runtime.reflect

import androidx.compose.runtime.Composer
import androidx.compose.runtime.internal.SLOTS_PER_INT
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.lang.reflect.Parameter
import kotlin.math.ceil

private const val BITS_PER_INT = 31

private fun changedParamCount(realValueParams: Int, thisParams: Int): Int {
    if (realValueParams == 0) return 1
    val totalParams = realValueParams + thisParams
    return ceil(
        totalParams.toDouble() / SLOTS_PER_INT.toDouble()
    ).toInt()
}

private fun defaultParamCount(realValueParams: Int): Int {
    return ceil(
        realValueParams.toDouble() / BITS_PER_INT.toDouble()
    ).toInt()
}

/**
 * Structure intended to be used exclusively by [getComposableInfo].
 */
internal data class ComposableInfo(
    val isComposable: Boolean,
    val realParamsCount: Int,
    val changedParams: Int,
    val defaultParams: Int
)

/**
 * Checks whether the method is Composable function and returns result along with the real
 * parameters count and changed parameter count (if composable) and packed in a structure.
 */
private fun Method.getComposableInfo(): ComposableInfo {
    val realParamsCount = parameterTypes.indexOfLast { it == Composer::class.java }
    if (realParamsCount == -1) {
        return ComposableInfo(false, parameterTypes.size, 0, 0)
    }
    val thisParams = if (Modifier.isStatic(this.modifiers)) 0 else 1
    val changedParams = changedParamCount(realParamsCount, thisParams)
    val totalParamsWithoutDefaults = realParamsCount +
        1 + // composer
        changedParams
    val totalParams = parameterTypes.size
    val isDefault = totalParams != totalParamsWithoutDefaults
    val defaultParams = if (isDefault)
        defaultParamCount(realParamsCount)
    else
        0
    return ComposableInfo(
        totalParamsWithoutDefaults + defaultParams == totalParams,
        realParamsCount,
        changedParams,
        defaultParams
    )
}

/**
 * Returns the default value for the [Class] type. This will be 0 for numeric types, false for
 * boolean and null for object references.
 */
private fun Class<*>.getDefaultValue(): Any? = when (name) {
    "int" -> 0.toInt()
    "short" -> 0.toShort()
    "byte" -> 0.toByte()
    "long" -> 0.toLong()
    "double" -> 0.toDouble()
    "float" -> 0.toFloat()
    "boolean" -> false
    "char" -> 0.toChar()
    else -> null
}

/**
 * Represents the @Composable method.
 */
class ComposableMethod internal constructor(
    private val method: Method,
    private val composableInfo: ComposableInfo
    ) {
    /**
     * Returns the backing [Method].
     */
    fun asMethod() = method

    /**
     * Returns the count of method parameters excluding the utility Compose-specific parameters.
     */
    val parameterCount
        get() = composableInfo.realParamsCount

    /**
     * Returns method parameters excluding the utility Compose-specific parameters.
     */
    val parameters: Array<Parameter>
        @Suppress("ClassVerificationFailure", "NewApi")
        get() = method.parameters.copyOfRange(0, composableInfo.realParamsCount)

    /**
     * Returns method parameters types excluding the utility Compose-specific parameters.
     */
    val parameterTypes: Array<Class<*>>
        get() = method.parameterTypes.copyOfRange(0, composableInfo.realParamsCount)

    /**
     * Calls the Composable method on the given [instance]. If the method accepts default values,
     * this function will call it with the correct options set.
     */
    @Suppress("BanUncheckedReflection", "ListIterator")
    operator fun invoke(composer: Composer, instance: Any?, vararg args: Any?): Any? {
        val (_, realParamsCount, changedParams, defaultParams) = composableInfo

        val totalParams = method.parameterTypes.size
        val changedStartIndex = realParamsCount + 1
        val defaultStartIndex = changedStartIndex + changedParams

        val defaultsMasks = Array(defaultParams) { index ->
            val start = index * BITS_PER_INT
            val end = minOf(start + BITS_PER_INT, realParamsCount)
            val useDefault =
                (start until end).map { if (it >= args.size || args[it] == null) 1 else 0 }
            val mask = useDefault.foldIndexed(0) { i, mask, default -> mask or (default shl i) }
            mask
        }

        val arguments = Array(totalParams) { idx ->
            when (idx) {
                // pass in "empty" value for all real parameters since we will be using defaults.
                in 0 until realParamsCount -> args.getOrElse(idx) {
                    method.parameterTypes[idx].getDefaultValue()
                }
                // the composer is the first synthetic parameter
                realParamsCount -> composer
                // since this is the root we don't need to be anything unique. 0 should suffice.
                // changed parameters should be 0 to indicate "uncertain"
                changedStartIndex -> 1
                in changedStartIndex + 1 until defaultStartIndex -> 0<commit_msg>Make first changed param 0<commit_after>/*
 * Copyright 2022 The Android Open Source Project
 *
 * 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 androidx.compose.runtime.reflect

import androidx.compose.runtime.Composer
import androidx.compose.runtime.internal.SLOTS_PER_INT
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.lang.reflect.Parameter
import kotlin.math.ceil

private const val BITS_PER_INT = 31

private fun changedParamCount(realValueParams: Int, thisParams: Int): Int {
    if (realValueParams == 0) return 1
    val totalParams = realValueParams + thisParams
    return ceil(
        totalParams.toDouble() / SLOTS_PER_INT.toDouble()
    ).toInt()
}

private fun defaultParamCount(realValueParams: Int): Int {
    return ceil(
        realValueParams.toDouble() / BITS_PER_INT.toDouble()
    ).toInt()
}

/**
 * Structure intended to be used exclusively by [getComposableInfo].
 */
internal data class ComposableInfo(
    val isComposable: Boolean,
    val realParamsCount: Int,
    val changedParams: Int,
    val defaultParams: Int
)

/**
 * Checks whether the method is Composable function and returns result along with the real
 * parameters count and changed parameter count (if composable) and packed in a structure.
 */
private fun Method.getComposableInfo(): ComposableInfo {
    val realParamsCount = parameterTypes.indexOfLast { it == Composer::class.java }
    if (realParamsCount == -1) {
        return ComposableInfo(false, parameterTypes.size, 0, 0)
    }
    val thisParams = if (Modifier.isStatic(this.modifiers)) 0 else 1
    val changedParams = changedParamCount(realParamsCount, thisParams)
    val totalParamsWithoutDefaults = realParamsCount +
        1 + // composer
        changedParams
    val totalParams = parameterTypes.size
    val isDefault = totalParams != totalParamsWithoutDefaults
    val defaultParams = if (isDefault)
        defaultParamCount(realParamsCount)
    else
        0
    return ComposableInfo(
        totalParamsWithoutDefaults + defaultParams == totalParams,
        realParamsCount,
        changedParams,
        defaultParams
    )
}

/**
 * Returns the default value for the [Class] type. This will be 0 for numeric types, false for
 * boolean and null for object references.
 */
private fun Class<*>.getDefaultValue(): Any? = when (name) {
    "int" -> 0.toInt()
    "short" -> 0.toShort()
    "byte" -> 0.toByte()
    "long" -> 0.toLong()
    "double" -> 0.toDouble()
    "float" -> 0.toFloat()
    "boolean" -> false
    "char" -> 0.toChar()
    else -> null
}

/**
 * Represents the @Composable method.
 */
class ComposableMethod internal constructor(
    private val method: Method,
    private val composableInfo: ComposableInfo
    ) {
    /**
     * Returns the backing [Method].
     */
    fun asMethod() = method

    /**
     * Returns the count of method parameters excluding the utility Compose-specific parameters.
     */
    val parameterCount
        get() = composableInfo.realParamsCount

    /**
     * Returns method parameters excluding the utility Compose-specific parameters.
     */
    val parameters: Array<Parameter>
        @Suppress("ClassVerificationFailure", "NewApi")
        get() = method.parameters.copyOfRange(0, composableInfo.realParamsCount)

    /**
     * Returns method parameters types excluding the utility Compose-specific parameters.
     */
    val parameterTypes: Array<Class<*>>
        get() = method.parameterTypes.copyOfRange(0, composableInfo.realParamsCount)

    /**
     * Calls the Composable method on the given [instance]. If the method accepts default values,
     * this function will call it with the correct options set.
     */
    @Suppress("BanUncheckedReflection", "ListIterator")
    operator fun invoke(composer: Composer, instance: Any?, vararg args: Any?): Any? {
        val (_, realParamsCount, changedParams, defaultParams) = composableInfo

        val totalParams = method.parameterTypes.size
        val changedStartIndex = realParamsCount + 1
        val defaultStartIndex = changedStartIndex + changedParams

        val defaultsMasks = Array(defaultParams) { index ->
            val start = index * BITS_PER_INT
            val end = minOf(start + BITS_PER_INT, realParamsCount)
            val useDefault =
                (start until end).map { if (it >= args.size || args[it] == null) 1 else 0 }
            val mask = useDefault.foldIndexed(0) { i, mask, default -> mask or (default shl i) }
            mask
        }

        val arguments = Array(totalParams) { idx ->
            when (idx) {
                // pass in "empty" value for all real parameters since we will be using defaults.
                in 0 until realParamsCount -> args.getOrElse(idx) {
                    method.parameterTypes[idx].getDefaultValue()
                }
                // the composer is the first synthetic parameter
                realParamsCount -> composer
                // since this is the root we don't need to be anything unique. 0 should suffice.
                // changed parameters should be 0 to indicate "uncertain"
                changedStartIndex -> 0
                in changedStartIndex + 1 until defaultStartIndex -> 0
{'fraction_non_alphanumeric': 0.087509805630611, 'fraction_numerical': 0.006885731717946483, 'mean_word_length': 2.8361751922433966, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4159198', 'n_tokens_mistral': 3112, 'n_tokens_neox': 2976, 'n_words': 1033}