diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 81acc665be3..d5615a1cfa9 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* @@ -688,7 +689,7 @@ open class KotlinFileExtractor( "", listOf(), pluginContext.irBuiltIns.unitType, - extensionReceiverParameter = null, + extensionParamType = null, functionTypeParameters = listOf(), classTypeArgsIncludingOuterClasses = listOf(), overridesCollectionsMethod = false, @@ -832,10 +833,117 @@ open class KotlinFileExtractor( null else { forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses).also { + // The defaults-forwarder function is a static utility, not a member, so we only need to extract this for the unspecialised instance of this class. + if (classTypeArgsIncludingOuterClasses.isNullOrEmpty()) + extractDefaultsFunction(f, parentId, extractBody) extractGeneratedOverloads(f, parentId, null, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses) } } + private fun extractDefaultsFunction(f: IrFunction, parentId: Label, extractBody: Boolean) { + if (f.valueParameters.none { it.defaultValue != null }) + return + + val id = getDefaultsMethodLabel(f) + val locId = getLocation(f, null) + val extReceiver = f.extensionReceiverParameter + val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter + val parameterTypes = listOfNotNull(extReceiver?.let { erase(it.type) }) + getDefaultsMethodArgTypes(f) + val allParamTypeResults = parameterTypes.mapIndexed { i, paramType -> + val paramId = tw.getLabelFor(getValueParameterLabel(id, i)) + extractValueParameter(paramId, paramType, "p$i", locId, id, i, paramId, isVararg = false, syntheticParameterNames = true, isCrossinline = false, isNoinline = false).also { + extractTypeAccess(useType(paramType), locId, paramId, -1) + } + } + val paramsSignature = allParamTypeResults.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature } + val shortName = getDefaultsMethodName(f) + + if (f.symbol is IrConstructorSymbol) { + val constrId = id.cast() + extractConstructor(constrId, shortName, paramsSignature, parentId, constrId) + } else { + val methodId = id.cast() + extractMethod(methodId, locId, shortName, erase(f.returnType), paramsSignature, parentId, methodId, origin = null, extractTypeAccess = true) + addModifiers(id, "static") + } + tw.writeHasLocation(id, locId) + addModifiers(id, "public") + tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind) + + if (extractBody) { + val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters + // This stack entry represents as if we're extracting the 'real' function `f`, giving the indices of its non-synthetic parameters + // such that when we extract the default expressions below, any reference to f's nth parameter will resolve to f$default's + // n + o'th parameter, where `o` is the parameter offset caused by adding any dispatch receiver to the parameter list. + // Note we don't need to add the extension receiver here because `useValueParameter` always assumes an extension receiver + // will be prepended if one exists. + DeclarationStackAdjuster(f, OverriddenFunctionAttributes(id, id, locId, nonSyntheticParams)).use { + val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2) + val intType = pluginContext.irBuiltIns.intType + val paramIdxOffset = listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null } + extractBlockBody(id, locId).also { blockId -> + var nextStmt = 0 + // For each parameter with a default, sub in the default value if the caller hasn't supplied a value: + f.valueParameters.forEachIndexed { paramIdx, param -> + val defaultVal = param.defaultValue + if (defaultVal != null) { + extractIfStmt(locId, blockId, nextStmt++, id).also { ifId -> + // if (realParams & thisParamBit == 0) ... + extractEqualsExpression(locId, ifId, 0, id, ifId).also { eqId -> + extractAndbitExpression(intType, locId, eqId, 0, id, ifId).also { opId -> + extractConstantInteger(1 shl paramIdx, locId, opId, 0, id, ifId) + extractVariableAccess(tw.getLabelFor(realParamsVarId), intType, locId, opId, 1, id, ifId) + } + extractConstantInteger(0, locId, eqId, 1, id, ifId) + } + // thisParamVar = defaultExpr... + extractExpressionStmt(locId, ifId, 1, id).also { exprStmtId -> + extractAssignExpr(param.type, locId, exprStmtId, 0, id, exprStmtId).also { assignId -> + extractVariableAccess(tw.getLabelFor(getValueParameterLabel(id, paramIdx + paramIdxOffset)), param.type, locId, assignId, 0, id, exprStmtId) + extractExpressionExpr(defaultVal.expression, id, assignId, 1, exprStmtId) + } + } + } + } + } + // Now call the real function: + val realFunctionId = useFunction(f) + if (f is IrConstructor) { + tw.getFreshIdLabel().also { thisCallId -> + tw.writeStmts_constructorinvocationstmt(thisCallId, blockId, nextStmt++, id) + tw.writeHasLocation(thisCallId, locId) + f.valueParameters.forEachIndexed { idx, param -> + extractVariableAccess(tw.getLabelFor(getValueParameterLabel(id, idx)), param.type, locId, thisCallId, idx, id, thisCallId) + } + tw.writeCallableBinding(thisCallId, realFunctionId) + } + } else { + tw.getFreshIdLabel().also { returnId -> + tw.writeStmts_returnstmt(returnId, blockId, nextStmt++, id) + tw.writeHasLocation(returnId, locId) + extractMethodAccessWithoutArgs(f.returnType, locId, id, returnId, 0, returnId, realFunctionId).also { thisCallId -> + val realFnIdxOffset = if (f.extensionReceiverParameter != null) 1 else 0 + val paramMappings = f.valueParameters.mapIndexed { idx, param -> Triple(param.type, idx + paramIdxOffset, idx + realFnIdxOffset) } + + listOfNotNull( + dispatchReceiver?.let { Triple(it.type, realFnIdxOffset, -1) }, + extReceiver?.let { Triple(it.type, 0, 0) } + ) + paramMappings.forEach { (type, fromIdx, toIdx) -> + extractVariableAccess(tw.getLabelFor(getValueParameterLabel(id, fromIdx)), type, locId, thisCallId, toIdx, id, returnId) + } + if (f.shouldExtractAsStatic) + extractStaticTypeAccessQualifier(f, thisCallId, locId, id, returnId) + else if (f.isLocalFunction()) { + extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(f), thisCallId, locId, id, returnId) + } + } + } + } + } + } + } + } + private val jvmOverloadsFqName = FqName("kotlin.jvm.JvmOverloads") private fun extractGeneratedOverloads(f: IrFunction, parentId: Label, maybeSourceParentId: Label?, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) { @@ -932,6 +1040,29 @@ open class KotlinFileExtractor( } } + private fun extractConstructor(id: Label, shortName: String, paramsSignature: String, parentId: Label, sourceDeclaration: Label) { + val unitType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN) + tw.writeConstrs(id, shortName, "$shortName$paramsSignature", unitType.javaResult.id, parentId, sourceDeclaration) + tw.writeConstrsKotlinType(id, unitType.kotlinResult.id) + } + + private fun extractMethod(id: Label, locId: Label, shortName: String, returnType: IrType, paramsSignature: String, parentId: Label, sourceDeclaration: Label, origin: IrDeclarationOrigin?, extractTypeAccess: Boolean) { + val returnTypeResults = useType(returnType, TypeContext.RETURN) + tw.writeMethods(id, shortName, "$shortName$paramsSignature", returnTypeResults.javaResult.id, parentId, sourceDeclaration) + tw.writeMethodsKotlinType(id, returnTypeResults.kotlinResult.id) + when (origin) { + IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> + tw.writeCompiler_generated(id, CompilerGeneratedKinds.GENERATED_DATA_CLASS_MEMBER.kind) + IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR -> + tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_PROPERTY_ACCESSOR.kind) + IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER -> + tw.writeCompiler_generated(id, CompilerGeneratedKinds.ENUM_CLASS_SPECIAL_MEMBER.kind) + } + if (extractTypeAccess) { + extractTypeAccessRecursive(returnType, locId, id, -1) + } + } + private fun forceExtractFunction(f: IrFunction, parentId: Label, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?, extractOrigin: Boolean = true, overriddenAttributes: OverriddenFunctionAttributes? = null): Label { with("function", f) { DeclarationStackAdjuster(f, overriddenAttributes).use { @@ -976,42 +1107,23 @@ open class KotlinFileExtractor( val locId = overriddenAttributes?.sourceLoc ?: getLocation(f, classTypeArgsIncludingOuterClasses) if (f.symbol is IrConstructorSymbol) { - val unitType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN) val shortName = when { adjustedReturnType.isAnonymous -> "" typeSubstitution != null -> useType(substReturnType).javaResult.shortName else -> adjustedReturnType.classFqName?.shortName()?.asString() ?: f.name.asString() } - val constrId = id.cast() - tw.writeConstrs(constrId, shortName, "$shortName$paramsSignature", unitType.javaResult.id, parentId, sourceDeclaration.cast()) - tw.writeConstrsKotlinType(constrId, unitType.kotlinResult.id) + extractConstructor(id.cast(), shortName, paramsSignature, parentId, sourceDeclaration.cast()) } else { - val returnType = useType(substReturnType, TypeContext.RETURN) - val shortName = getFunctionShortName(f) + val shortNames = getFunctionShortName(f) val methodId = id.cast() - tw.writeMethods(methodId, shortName.nameInDB, "${shortName.nameInDB}$paramsSignature", returnType.javaResult.id, parentId, sourceDeclaration.cast()) - tw.writeMethodsKotlinType(methodId, returnType.kotlinResult.id) - if (extractOrigin) { - when (f.origin) { - IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> - tw.writeCompiler_generated(methodId, CompilerGeneratedKinds.GENERATED_DATA_CLASS_MEMBER.kind) - IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR -> - tw.writeCompiler_generated(methodId, CompilerGeneratedKinds.DEFAULT_PROPERTY_ACCESSOR.kind) - IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER -> - tw.writeCompiler_generated(methodId, CompilerGeneratedKinds.ENUM_CLASS_SPECIAL_MEMBER.kind) - } - } + extractMethod(methodId, locId, shortNames.nameInDB, substReturnType, paramsSignature, parentId, sourceDeclaration.cast(), if (extractOrigin) f.origin else null, extractMethodAndParameterTypeAccesses) - if (extractMethodAndParameterTypeAccesses) { - extractTypeAccessRecursive(substReturnType, locId, id, -1) - } - - if (shortName.nameInDB != shortName.kotlinName) { - tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName) + if (shortNames.nameInDB != shortNames.kotlinName) { + tw.writeKtFunctionOriginalNames(methodId, shortNames.kotlinName) } if (f.hasInterfaceParent() && f.body != null) { - addModifiers(id, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting. + addModifiers(methodId, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting. } } @@ -1209,14 +1321,18 @@ open class KotlinFileExtractor( } } + private fun extractBlockBody(callable: Label, locId: Label) = + tw.getFreshIdLabel().also { + tw.writeStmts_block(it, callable, 0, callable) + tw.writeHasLocation(it, locId) + } + private fun extractBlockBody(b: IrBlockBody, callable: Label) { with("block body", b) { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(b) - tw.writeStmts_block(id, callable, 0, callable) - tw.writeHasLocation(id, locId) - for ((sIdx, stmt) in b.statements.withIndex()) { - extractStatement(stmt, callable, id, sIdx) + extractBlockBody(callable, tw.getLocation(b)).also { + for ((sIdx, stmt) in b.statements.withIndex()) { + extractStatement(stmt, callable, it, sIdx) + } } } } @@ -1294,6 +1410,12 @@ open class KotlinFileExtractor( } } + private fun extractIfStmt(locId: Label, parent: Label, idx: Int, callable: Label) = + tw.getFreshIdLabel().also { + tw.writeStmts_ifstmt(it, parent, idx, callable) + tw.writeHasLocation(it, locId) + } + private fun extractStatement(s: IrStatement, callable: Label, parent: Label, idx: Int) { with("statement", s) { when(s) { @@ -1521,6 +1643,147 @@ open class KotlinFileExtractor( extractTypeAccessRecursive(pluginContext.irBuiltIns.anyType, locId, idNewexpr, -3, enclosingCallable, enclosingStmt) } + fun extractMethodAccessWithoutArgs( + returnType: IrType, + locId: Label, + enclosingCallable: Label, + callsiteParent: Label, + childIdx: Int, + enclosingStmt: Label, + methodLabel: Label? + ) = tw.getFreshIdLabel().also { id -> + val type = useType(returnType) + + tw.writeExprs_methodaccess(id, type.javaResult.id, callsiteParent, childIdx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + tw.writeHasLocation(id, locId) + tw.writeCallableEnclosingExpr(id, enclosingCallable) + tw.writeStatementEnclosingExpr(id, enclosingStmt) + + // The caller should have warned about this before, so we don't repeat the warning here. + if (methodLabel != null) + tw.writeCallableBinding(id, methodLabel) + } + + private val defaultConstructorMarkerClass by lazy { + val result = pluginContext.referenceClass(FqName("kotlin.jvm.internal.DefaultConstructorMarker"))?.owner + result?.let { extractExternalClassLater(it) } + result + } + + private val defaultConstructorMarkerType by lazy { + defaultConstructorMarkerClass?.typeWith() + } + + private fun getDefaultsMethodLastArgType(f: IrFunction) = + ( + if (f is IrConstructor) + defaultConstructorMarkerType + else + null + ) ?: pluginContext.irBuiltIns.anyType + + private fun getDefaultsMethodArgTypes(f: IrFunction) = + // The $default method has type ([extensionReceiver], [dispatchReciever], paramTypes..., int, Object) + // All parameter types are erased. The trailing int is a mask indicating which parameter values are real + // and which should be replaced by defaults. The final Object parameter is apparently always null. + ( + listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) + + f.valueParameters.map { it.type } + + listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f)) + ).map { erase(it) } + + private fun getDefaultsMethodName(f: IrFunction) = + if (f is IrConstructor) { + f.returnType.let { + when { + it.isAnonymous -> "" + else -> it.classFqName?.shortName()?.asString() ?: f.name.asString() + } + } + } else { + f.name.asString() + "\$default" + } + + private fun getDefaultsMethodLabel(f: IrFunction): Label { + val defaultsMethodName = getDefaultsMethodName(f) + val normalArgTypes = getDefaultsMethodArgTypes(f) + val extensionParamType = f.extensionReceiverParameter?.let { erase(it.type) } + + val defaultMethodLabelStr = getFunctionLabel( + f.parent, + maybeParentId = null, + defaultsMethodName, + normalArgTypes, + erase(f.returnType), + extensionParamType, + listOf(), + classTypeArgsIncludingOuterClasses = null, + overridesCollectionsMethod = false, + javaSignature = null, + addParameterWildcardsByDefault = false + ) + + return tw.getLabelFor(defaultMethodLabelStr) + } + + private fun extractsDefaultsCall( + syntacticCallTarget: IrFunction, + locId: Label, + callsite: IrCall, + enclosingCallable: Label, + callsiteParent: Label, + childIdx: Int, + enclosingStmt: Label, + valueArguments: List, + dispatchReceiver: IrExpression?, + extensionReceiver: IrExpression? + ) { + val callTarget = syntacticCallTarget.target.realOverrideTarget + val defaultMethodLabel = getDefaultsMethodLabel(callTarget) + val id = extractMethodAccessWithoutArgs(callsite.type, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel) + + if (callTarget.isLocalFunction()) { + extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(callTarget), id, locId, enclosingCallable, enclosingStmt) + } else { + extractStaticTypeAccessQualifierUnchecked(callTarget.parent, id, locId, enclosingCallable, enclosingStmt) + } + + extractDefaultsCallArguments(id, callTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver) + } + + private fun extractDefaultsCallArguments( + id: Label, + callTarget: IrFunction, + enclosingCallable: Label, + enclosingStmt: Label, + valueArguments: List, + dispatchReceiver: IrExpression?, + extensionReceiver: IrExpression? + ) { + var nextIdx = 0 + if (extensionReceiver != null) { + extractExpressionExpr(extensionReceiver, enclosingCallable, id, nextIdx++, enclosingStmt) + } + if (dispatchReceiver != null && !callTarget.shouldExtractAsStatic) { + extractExpressionExpr(dispatchReceiver, enclosingCallable, id, nextIdx++, enclosingStmt) + } + + val valueArgsWithDummies = valueArguments.zip(callTarget.valueParameters).map { + (expr, param) -> expr ?: IrConstImpl.defaultValueForType(0, 0, param.type) + } + + var realParamsMask = 0 + valueArguments.forEachIndexed { index, arg -> if (arg != null) realParamsMask = realParamsMask or (1 shl index) } + + val extraArgs = listOf( + IrConstImpl.int(0, 0, pluginContext.irBuiltIns.intType, realParamsMask), + IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget)) + ) + + extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx) + } + fun extractRawMethodAccess( syntacticCallTarget: IrFunction, callsite: IrCall, @@ -1537,24 +1800,38 @@ open class KotlinFileExtractor( val locId = tw.getLocation(callsite) - extractRawMethodAccess( - syntacticCallTarget, - locId, - callsite.type, - enclosingCallable, - callsiteParent, - childIdx, - enclosingStmt, - valueArguments.size, - { argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, enclosingStmt, enclosingCallable, idxOffset) }, - dispatchReceiver?.type, - dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } }, - extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } }, - typeArguments, - extractClassTypeArguments, - superQualifierSymbol - ) - + if (valueArguments.any { it == null }) { + extractsDefaultsCall( + syntacticCallTarget, + locId, + callsite, + enclosingCallable, + callsiteParent, + childIdx, + enclosingStmt, + valueArguments, + dispatchReceiver, + extensionReceiver + ) + } else { + extractRawMethodAccess( + syntacticCallTarget, + locId, + callsite.type, + enclosingCallable, + callsiteParent, + childIdx, + enclosingStmt, + valueArguments.size, + { argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, enclosingStmt, enclosingCallable, idxOffset) }, + dispatchReceiver?.type, + dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } }, + extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } }, + typeArguments, + extractClassTypeArguments, + superQualifierSymbol + ) + } } private fun getFunctionInvokeMethod(typeArgs: List): IrFunction? { @@ -1634,26 +1911,16 @@ open class KotlinFileExtractor( superQualifierSymbol: IrClassSymbol? = null) { val callTarget = syntacticCallTarget.target.realOverrideTarget - val id = tw.getFreshIdLabel() - val type = useType(returnType) + val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments) + if (methodId == null) { + logger.warn("No method to bind call to for raw method access") + } - tw.writeExprs_methodaccess(id, type.javaResult.id, callsiteParent, childIdx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - tw.writeHasLocation(id, locId) - tw.writeCallableEnclosingExpr(id, enclosingCallable) - tw.writeStatementEnclosingExpr(id, enclosingStmt) + val id = extractMethodAccessWithoutArgs(returnType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, methodId) // type arguments at index -2, -3, ... extractTypeArguments(typeArguments, locId, id, enclosingCallable, enclosingStmt, -2, true) - val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments) - - if (methodId == null) { - logger.warn("No method to bind call to for raw method access") - } else { - tw.writeCallableBinding(id, methodId) - } - if (callTarget.isLocalFunction()) { extractNewExprForLocalFunction(getLocallyVisibleFunctionLabels(callTarget), id, locId, enclosingCallable, enclosingStmt) } else if (callTarget.shouldExtractAsStatic) { @@ -1683,16 +1950,19 @@ open class KotlinFileExtractor( extractValueArguments(argParent, idxOffset) } + private fun extractStaticTypeAccessQualifierUnchecked(parent: IrDeclarationParent, parentExpr: Label, locId: Label, enclosingCallable: Label, enclosingStmt: Label) { + if (parent is IrClass) { + extractTypeAccessRecursive(parent.toRawType(), locId, parentExpr, -1, enclosingCallable, enclosingStmt) + } else if (parent is IrFile) { + extractTypeAccess(useFileClassType(parent), locId, parentExpr, -1, enclosingCallable, enclosingStmt) + } else { + logger.warnElement("Unexpected static type access qualifier ${parent.javaClass}", parent) + } + } + private fun extractStaticTypeAccessQualifier(target: IrDeclaration, parentExpr: Label, locId: Label, enclosingCallable: Label, enclosingStmt: Label) { if (target.shouldExtractAsStatic) { - val parent = target.parent - if (parent is IrClass) { - extractTypeAccessRecursive(parent.toRawType(), locId, parentExpr, -1, enclosingCallable, enclosingStmt) - } else if (parent is IrFile) { - extractTypeAccess(useFileClassType(parent), locId, parentExpr, -1, enclosingCallable, enclosingStmt) - } else { - logger.warnElement("Unexpected static type access qualifer ${parent.javaClass}", target) - } + extractStaticTypeAccessQualifierUnchecked(target.parent, parentExpr, locId, enclosingCallable, enclosingStmt) } } @@ -2666,14 +2936,22 @@ open class KotlinFileExtractor( useType(eType) } val locId = tw.getLocation(e) - val id = extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt) + val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) } + val anyDefaultArgs = valueArgs.any { it == null } + val id = if (anyDefaultArgs) { + extractNewExpr(getDefaultsMethodLabel(e.symbol.owner).cast(), type, locId, parent, idx, callable, enclosingStmt).also { + extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null) + } + } else { + extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt).also { + extractCallValueArguments(it, e, enclosingStmt, callable, 0) + } + } if (isAnonymous) { tw.writeIsAnonymClass(type.javaResult.id.cast(), id) } - extractCallValueArguments(id, e, enclosingStmt, callable, 0) - val dr = e.dispatchReceiver if (dr != null) { extractExpressionExpr(dr, callable, id, -2, enclosingStmt) @@ -2872,6 +3150,12 @@ open class KotlinFileExtractor( return false } + private fun extractExpressionStmt(locId: Label, parent: Label, idx: Int, callable: Label) = + tw.getFreshIdLabel().also { + tw.writeStmts_exprstmt(it, parent, idx, callable) + tw.writeHasLocation(it, locId) + } + private fun extractExpressionStmt(e: IrExpression, callable: Label, parent: Label, idx: Int) { extractExpression(e, callable, StmtParent(parent, idx)) } @@ -2880,6 +3164,45 @@ open class KotlinFileExtractor( extractExpression(e, callable, ExprParent(parent, idx, enclosingStmt)) } + private fun extractExprContext(id: Label, locId: Label, callable: Label, enclosingStmt: Label) { + tw.writeHasLocation(id, locId) + tw.writeCallableEnclosingExpr(id, callable) + tw.writeStatementEnclosingExpr(id, enclosingStmt) + } + + private fun extractEqualsExpression(locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) = + tw.getFreshIdLabel().also { + val type = useType(pluginContext.irBuiltIns.booleanType) + tw.writeExprs_eqexpr(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + } + + private fun extractAndbitExpression(type: IrType, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) = + tw.getFreshIdLabel().also { + val typeResults = useType(type) + tw.writeExprs_andbitexpr(it, typeResults.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, typeResults.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + } + + private fun extractConstantInteger(v: Int, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) = + tw.getFreshIdLabel().also { + val type = useType(pluginContext.irBuiltIns.intType) + tw.writeExprs_integerliteral(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + tw.writeNamestrings(v.toString(), v.toString(), it) + extractExprContext(it, locId, callable, enclosingStmt) + } + + private fun extractAssignExpr(type: IrType, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) = + tw.getFreshIdLabel().also { + val typeResults = useType(type) + tw.writeExprs_assignexpr(it, typeResults.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, typeResults.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + } + private fun extractExpression(e: IrExpression, callable: Label, parent: StmtExprParent) { with("expression", e) { when(e) { @@ -3130,7 +3453,7 @@ open class KotlinFileExtractor( val exprParent = parent.expr(e, callable) val owner = e.symbol.owner if (owner is IrValueParameter && owner.index == -1 && !owner.isExtensionReceiver()) { - extractThisAccess(e, exprParent, callable) + extractThisAccess(e, owner.parent, exprParent, callable) } else { val isAnnotationClassParameter = ((owner as? IrValueParameter)?.parent as? IrConstructor)?.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS val extractType = if (isAnnotationClassParameter) kClassToJavaClass(e.type) else e.type @@ -3482,7 +3805,7 @@ open class KotlinFileExtractor( tw.writeStatementEnclosingExpr(it, enclosingStmt) } - private fun extractThisAccess(e: IrGetValue, exprParent: ExprParent, callable: Label) { + private fun extractThisAccess(e: IrGetValue, thisParamParent: IrDeclarationParent, exprParent: ExprParent, callable: Label) { val containingDeclaration = declarationStack.peek().first val locId = tw.getLocation(e) @@ -3494,6 +3817,20 @@ open class KotlinFileExtractor( extractStaticTypeAccessQualifier(containingDeclaration, varAccessId, locId, callable, exprParent.enclosingStmt) } } else { + if (thisParamParent is IrFunction) { + val overriddenAttributes = declarationStack.findOverriddenAttributes(thisParamParent) + val replaceWithParamIdx = overriddenAttributes?.valueParameters?.indexOf(e.symbol.owner) + if (replaceWithParamIdx != null && replaceWithParamIdx != -1) { + // Use of 'this' in a function where the dispatch receiver is passed like an ordinary parameter, + // such as a `$default` static function that substitutes in default arguments as needed. + val paramDeclarerId = overriddenAttributes.id ?: useDeclarationParent(thisParamParent, false) + val extensionParamOffset = if (thisParamParent.extensionReceiverParameter != null) 1 else 0 + val replacementParamId = tw.getLabelFor(getValueParameterLabel(paramDeclarerId, replaceWithParamIdx + extensionParamOffset)) + extractVariableAccess(replacementParamId, e.type, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt) + return + } + } + val id = extractThisAccess(e.type, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt, locId) fun extractTypeAccess(parent: IrClass) { @@ -5048,5 +5385,6 @@ open class KotlinFileExtractor( DELEGATED_PROPERTY_SETTER(7), JVMSTATIC_PROXY_METHOD(8), JVMOVERLOADS_METHOD(9), + DEFAULT_ARGUMENTS_METHOD(10) } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 957450474c5..5873b06c280 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -1043,9 +1043,9 @@ open class KotlinUsesExtractor( f.parent, maybeParentId, getFunctionShortName(f).nameInDB, - maybeParameterList ?: f.valueParameters, + (maybeParameterList ?: f.valueParameters).map { it.type }, getAdjustedReturnType(f), - f.extensionReceiverParameter, + f.extensionReceiverParameter?.type, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses, overridesCollectionsMethodWithAlteredParameterTypes(f), @@ -1067,12 +1067,12 @@ open class KotlinUsesExtractor( maybeParentId: Label?, // The name of the function; normally f.name.asString(). name: String, - // The value parameters that the functions takes; normally f.valueParameters. - parameters: List, + // The types of the value parameters that the functions takes; normally f.valueParameters.map { it.type }. + parameterTypes: List, // The return type of the function; normally f.returnType. returnType: IrType, - // The extension receiver of the function, if any; normally f.extensionReceiverParameter. - extensionReceiverParameter: IrValueParameter?, + // The extension receiver of the function, if any; normally f.extensionReceiverParameter?.type. + extensionParamType: IrType?, // The type parameters of the function. This does not include type parameters of enclosing classes. functionTypeParameters: List, // The type arguments of enclosing classes of the function. @@ -1089,11 +1089,7 @@ open class KotlinUsesExtractor( prefix: String = "callable" ): String { val parentId = maybeParentId ?: useDeclarationParent(parent, false, classTypeArgsIncludingOuterClasses, true) - val allParams = if (extensionReceiverParameter == null) { - parameters - } else { - listOf(extensionReceiverParameter) + parameters - } + val allParamTypes = if (extensionParamType == null) parameterTypes else listOf(extensionParamType) + parameterTypes val substitutionMap = classTypeArgsIncludingOuterClasses?.let { notNullArgs -> if (notNullArgs.isEmpty()) { @@ -1103,11 +1099,11 @@ open class KotlinUsesExtractor( enclosingClass?.let { notNullClass -> makeTypeGenericSubstitutionMap(notNullClass, notNullArgs) } } } - val getIdForFunctionLabel = { it: IndexedValue -> + val getIdForFunctionLabel = { it: IndexedValue -> // Kotlin rewrites certain Java collections types adding additional generic constraints-- for example, // Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe. // If this has happened, erase the type again to get the correct Java signature. - val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type + val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value, name, it.index) else it.value // Add any wildcard types that the Kotlin compiler would add in the Java lowering of this function: val withAddedWildcards = addJavaLoweringWildcards(maybeAmendedForCollections, addParameterWildcardsByDefault, javaSignature?.let { sig -> getJavaValueParameterType(sig, it.index) }) // Now substitute any class type parameters in: @@ -1117,7 +1113,7 @@ open class KotlinUsesExtractor( val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed) "{${useType(maybeErased).javaResult.id}}" } - val paramTypeIds = allParams.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel) + val paramTypeIds = allParamTypes.withIndex().joinToString(separator = ",", transform = getIdForFunctionLabel) val labelReturnType = if (name == "") pluginContext.irBuiltIns.unitType @@ -1551,7 +1547,7 @@ open class KotlinUsesExtractor( * Note that `Array` is retained (with `T` itself erased) because these are expected to be lowered to Java * arrays, which are not generic. */ - private fun erase (t: IrType): IrType { + fun erase (t: IrType): IrType { if (t is IrSimpleType) { val classifier = t.classifier val owner = classifier.owner @@ -1578,6 +1574,8 @@ open class KotlinUsesExtractor( private fun eraseTypeParameter(t: IrTypeParameter) = erase(t.superTypes[0]) + fun getValueParameterLabel(parentId: Label?, idx: Int) = "@\"params;{$parentId};$idx\"" + /** * Gets the label for `vp` in the context of function instance `parent`, or in that of its declaring function if * `parent` is null. @@ -1607,7 +1605,7 @@ open class KotlinUsesExtractor( logger.error("Unexpected negative index for parameter") } - return "@\"params;{$parentId};$idx\"" + return getValueParameterLabel(parentId, idx) } @@ -1669,7 +1667,7 @@ open class KotlinUsesExtractor( val returnType = getter?.returnType ?: setter?.valueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType val typeParams = getFunctionTypeParameters(func) - getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, javaSignature = null, addParameterWildcardsByDefault = false, prefix = "property") + getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext.type, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false, javaSignature = null, addParameterWildcardsByDefault = false, prefix = "property") } } diff --git a/java/ql/lib/semmle/code/java/Element.qll b/java/ql/lib/semmle/code/java/Element.qll index d2cbfbc120e..0120cdc158f 100644 --- a/java/ql/lib/semmle/code/java/Element.qll +++ b/java/ql/lib/semmle/code/java/Element.qll @@ -65,6 +65,8 @@ class Element extends @element, Top { i = 8 and result = "Proxy static method for a @JvmStatic-annotated function or property" or i = 9 and result = "Forwarder for a @JvmOverloads-annotated function" + or + i = 10 and result = "Forwarder for Kotlin calls that need default arguments filling in" ) } } diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected new file mode 100644 index 00000000000..068b34f79b4 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected @@ -0,0 +1,849 @@ +test.kt: +# 0| [CompilationUnit] test +# 0| 1: [Class] TestKt +# 1| 1: [Method] sink +# 1| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 1| 0: [Parameter] a +# 1| 0: [TypeAccess] Object +# 1| 5: [BlockStmt] { ... } +# 3| 2: [Class] TestMember +# 3| 1: [Constructor] TestMember +# 3| 5: [BlockStmt] { ... } +# 3| 0: [SuperConstructorInvocationStmt] super(...) +# 3| 1: [BlockStmt] { ... } +# 5| 2: [Method] f +# 5| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 5| 0: [Parameter] x +# 5| 0: [TypeAccess] String +# 5| 1: [Parameter] y +# 5| 0: [TypeAccess] String +# 5| 2: [Parameter] z +# 5| 0: [TypeAccess] String +# 5| 5: [BlockStmt] { ... } +# 6| 0: [ExprStmt] ; +# 6| 0: [MethodAccess] sink(...) +# 6| -1: [TypeAccess] TestKt +# 6| 0: [VarAccess] y +# 5| 3: [Method] f$default +# 5| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 5| 0: [Parameter] p0 +# 5| 0: [TypeAccess] TestMember +# 5| 1: [Parameter] p1 +# 5| 0: [TypeAccess] String +# 5| 2: [Parameter] p2 +# 5| 0: [TypeAccess] String +# 5| 3: [Parameter] p3 +# 5| 0: [TypeAccess] String +# 5| 4: [Parameter] p4 +# 5| 0: [TypeAccess] int +# 5| 5: [Parameter] p5 +# 5| 0: [TypeAccess] Object +# 5| 5: [BlockStmt] { ... } +# 5| 0: [IfStmt] if (...) +# 5| 0: [EQExpr] ... == ... +# 5| 0: [AndBitwiseExpr] ... & ... +# 5| 0: [IntegerLiteral] 2 +# 5| 1: [VarAccess] p4 +# 5| 1: [IntegerLiteral] 0 +# 5| 1: [ExprStmt] ; +# 5| 0: [AssignExpr] ...=... +# 5| 0: [VarAccess] p2 +# 5| 1: [VarAccess] p1 +# 5| 1: [IfStmt] if (...) +# 5| 0: [EQExpr] ... == ... +# 5| 0: [AndBitwiseExpr] ... & ... +# 5| 0: [IntegerLiteral] 4 +# 5| 1: [VarAccess] p4 +# 5| 1: [IntegerLiteral] 0 +# 5| 1: [ExprStmt] ; +# 5| 0: [AssignExpr] ...=... +# 5| 0: [VarAccess] p3 +# 5| 1: [StringLiteral] hello world +# 5| 2: [ReturnStmt] return ... +# 5| 0: [MethodAccess] f(...) +# 5| -1: [VarAccess] p0 +# 5| 0: [VarAccess] p1 +# 5| 1: [VarAccess] p2 +# 5| 2: [VarAccess] p3 +# 9| 4: [Method] user +# 9| 3: [TypeAccess] Unit +# 9| 5: [BlockStmt] { ... } +# 10| 0: [ExprStmt] ; +# 10| 0: [MethodAccess] f$default(...) +# 10| -1: [TypeAccess] TestMember +# 10| 0: [ThisAccess] this +# 10| 1: [StringLiteral] member sunk +# 1| 2: [NullLiteral] null +# 1| 3: [NullLiteral] null +# 1| 4: [IntegerLiteral] 1 +# 1| 5: [NullLiteral] null +# 11| 1: [ExprStmt] ; +# 11| 0: [MethodAccess] f$default(...) +# 11| -1: [TypeAccess] TestMember +# 11| 0: [ThisAccess] this +# 11| 1: [StringLiteral] member sunk fp +# 11| 2: [StringLiteral] member sunk 2 +# 1| 3: [NullLiteral] null +# 1| 4: [IntegerLiteral] 3 +# 1| 5: [NullLiteral] null +# 12| 2: [ExprStmt] ; +# 12| 0: [MethodAccess] f(...) +# 12| -1: [ThisAccess] this +# 12| 0: [StringLiteral] not sunk +# 12| 1: [StringLiteral] member sunk 3 +# 12| 2: [StringLiteral] not sunk +# 17| 3: [Class] TestExtensionMember +# 17| 1: [Constructor] TestExtensionMember +# 17| 5: [BlockStmt] { ... } +# 17| 0: [SuperConstructorInvocationStmt] super(...) +# 17| 1: [BlockStmt] { ... } +# 19| 2: [ExtensionMethod] f +# 19| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 19| 0: [Parameter] +# 19| 0: [TypeAccess] String +# 19| 1: [Parameter] x +# 19| 0: [TypeAccess] String +# 19| 2: [Parameter] y +# 19| 0: [TypeAccess] String +# 19| 3: [Parameter] z +# 19| 0: [TypeAccess] String +# 19| 5: [BlockStmt] { ... } +# 20| 0: [ExprStmt] ; +# 20| 0: [MethodAccess] sink(...) +# 20| -1: [TypeAccess] TestKt +# 20| 0: [ExtensionReceiverAccess] this +# 21| 1: [ExprStmt] ; +# 21| 0: [MethodAccess] sink(...) +# 21| -1: [TypeAccess] TestKt +# 21| 0: [VarAccess] y +# 19| 3: [Method] f$default +# 19| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 19| 0: [Parameter] p0 +# 19| 0: [TypeAccess] String +# 19| 1: [Parameter] p1 +# 19| 0: [TypeAccess] TestExtensionMember +# 19| 2: [Parameter] p2 +# 19| 0: [TypeAccess] String +# 19| 3: [Parameter] p3 +# 19| 0: [TypeAccess] String +# 19| 4: [Parameter] p4 +# 19| 0: [TypeAccess] String +# 19| 5: [Parameter] p5 +# 19| 0: [TypeAccess] int +# 19| 6: [Parameter] p6 +# 19| 0: [TypeAccess] Object +# 19| 5: [BlockStmt] { ... } +# 19| 0: [IfStmt] if (...) +# 19| 0: [EQExpr] ... == ... +# 19| 0: [AndBitwiseExpr] ... & ... +# 19| 0: [IntegerLiteral] 2 +# 19| 1: [VarAccess] p5 +# 19| 1: [IntegerLiteral] 0 +# 19| 1: [ExprStmt] ; +# 19| 0: [AssignExpr] ...=... +# 19| 0: [VarAccess] p3 +# 19| 1: [VarAccess] p2 +# 19| 1: [IfStmt] if (...) +# 19| 0: [EQExpr] ... == ... +# 19| 0: [AndBitwiseExpr] ... & ... +# 19| 0: [IntegerLiteral] 4 +# 19| 1: [VarAccess] p5 +# 19| 1: [IntegerLiteral] 0 +# 19| 1: [ExprStmt] ; +# 19| 0: [AssignExpr] ...=... +# 19| 0: [VarAccess] p4 +# 19| 1: [StringLiteral] hello world +# 19| 2: [ReturnStmt] return ... +# 19| 0: [MethodAccess] f(...) +# 19| -1: [VarAccess] p1 +# 19| 0: [VarAccess] p0 +# 19| 1: [VarAccess] p2 +# 19| 2: [VarAccess] p3 +# 19| 3: [VarAccess] p4 +# 24| 4: [Method] user +# 24| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 24| 0: [Parameter] sunk +# 24| 0: [TypeAccess] String +# 24| 5: [BlockStmt] { ... } +# 25| 0: [ExprStmt] ; +# 25| 0: [MethodAccess] f$default(...) +# 25| -1: [TypeAccess] TestExtensionMember +# 25| 0: [VarAccess] sunk +# 25| 1: [ThisAccess] this +# 25| 2: [StringLiteral] extension sunk +# 1| 3: [NullLiteral] null +# 1| 4: [NullLiteral] null +# 1| 5: [IntegerLiteral] 1 +# 1| 6: [NullLiteral] null +# 26| 1: [ExprStmt] ; +# 26| 0: [MethodAccess] f$default(...) +# 26| -1: [TypeAccess] TestExtensionMember +# 26| 0: [VarAccess] sunk +# 26| 1: [ThisAccess] this +# 26| 2: [StringLiteral] extension sunk fp +# 26| 3: [StringLiteral] extension sunk 2 +# 1| 4: [NullLiteral] null +# 1| 5: [IntegerLiteral] 3 +# 1| 6: [NullLiteral] null +# 27| 2: [ExprStmt] ; +# 27| 0: [MethodAccess] f(...) +# 27| -1: [ThisAccess] this +# 27| 0: [VarAccess] sunk +# 27| 1: [StringLiteral] not sunk +# 27| 2: [StringLiteral] extension sunk 3 +# 27| 3: [StringLiteral] not sunk +# 32| 4: [Class] TestStaticMember +# 32| 1: [Constructor] TestStaticMember +# 32| 5: [BlockStmt] { ... } +# 32| 0: [SuperConstructorInvocationStmt] super(...) +# 32| 1: [BlockStmt] { ... } +# 34| 2: [Method] f +# 34| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 34| 0: [Parameter] x +# 34| 0: [TypeAccess] String +# 34| 1: [Parameter] y +# 34| 0: [TypeAccess] String +# 34| 2: [Parameter] z +# 34| 0: [TypeAccess] String +# 34| 5: [BlockStmt] { ... } +# 35| 0: [ExprStmt] ; +# 35| 0: [MethodAccess] sink(...) +# 35| -1: [TypeAccess] TestKt +# 35| 0: [VarAccess] y +# 34| 3: [Method] f$default +# 34| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 34| 0: [Parameter] p0 +# 34| 0: [TypeAccess] String +# 34| 1: [Parameter] p1 +# 34| 0: [TypeAccess] String +# 34| 2: [Parameter] p2 +# 34| 0: [TypeAccess] String +# 34| 3: [Parameter] p3 +# 34| 0: [TypeAccess] int +# 34| 4: [Parameter] p4 +# 34| 0: [TypeAccess] Object +# 34| 5: [BlockStmt] { ... } +# 34| 0: [IfStmt] if (...) +# 34| 0: [EQExpr] ... == ... +# 34| 0: [AndBitwiseExpr] ... & ... +# 34| 0: [IntegerLiteral] 2 +# 34| 1: [VarAccess] p3 +# 34| 1: [IntegerLiteral] 0 +# 34| 1: [ExprStmt] ; +# 34| 0: [AssignExpr] ...=... +# 34| 0: [VarAccess] p1 +# 34| 1: [VarAccess] p0 +# 34| 1: [IfStmt] if (...) +# 34| 0: [EQExpr] ... == ... +# 34| 0: [AndBitwiseExpr] ... & ... +# 34| 0: [IntegerLiteral] 4 +# 34| 1: [VarAccess] p3 +# 34| 1: [IntegerLiteral] 0 +# 34| 1: [ExprStmt] ; +# 34| 0: [AssignExpr] ...=... +# 34| 0: [VarAccess] p2 +# 34| 1: [StringLiteral] hello world +# 34| 2: [ReturnStmt] return ... +# 34| 0: [MethodAccess] f(...) +# 34| -1: [TypeAccess] TestStaticMember +# 34| 0: [VarAccess] p0 +# 34| 1: [VarAccess] p1 +# 34| 2: [VarAccess] p2 +# 38| 4: [Method] user +# 38| 3: [TypeAccess] Unit +# 38| 5: [BlockStmt] { ... } +# 39| 0: [ExprStmt] ; +# 39| 0: [MethodAccess] f$default(...) +# 39| -1: [TypeAccess] TestStaticMember +# 39| 0: [StringLiteral] static sunk +# 1| 1: [NullLiteral] null +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 1 +# 1| 4: [NullLiteral] null +# 40| 1: [ExprStmt] ; +# 40| 0: [MethodAccess] f$default(...) +# 40| -1: [TypeAccess] TestStaticMember +# 40| 0: [StringLiteral] static sunk fp +# 40| 1: [StringLiteral] static sunk 2 +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 3 +# 1| 4: [NullLiteral] null +# 41| 2: [ExprStmt] ; +# 41| 0: [MethodAccess] f(...) +# 41| -1: [TypeAccess] TestStaticMember +# 41| 0: [StringLiteral] not sunk +# 41| 1: [StringLiteral] static sunk 3 +# 41| 2: [StringLiteral] not sunk +# 46| 5: [Class] ExtendMe +# 46| 1: [Constructor] ExtendMe +# 46| 5: [BlockStmt] { ... } +# 46| 0: [SuperConstructorInvocationStmt] super(...) +# 46| 1: [BlockStmt] { ... } +# 48| 2: [Method] f +# 48| 3: [TypeAccess] String +#-----| 4: (Parameters) +# 48| 0: [Parameter] x +# 48| 0: [TypeAccess] String +# 48| 5: [BlockStmt] { ... } +# 48| 0: [ReturnStmt] return ... +# 48| 0: [VarAccess] x +# 52| 6: [Class] TestReceiverReferences +# 52| 1: [Constructor] TestReceiverReferences +# 52| 5: [BlockStmt] { ... } +# 52| 0: [SuperConstructorInvocationStmt] super(...) +# 52| 1: [BlockStmt] { ... } +# 54| 2: [Method] g +# 54| 3: [TypeAccess] String +#-----| 4: (Parameters) +# 54| 0: [Parameter] x +# 54| 0: [TypeAccess] String +# 54| 5: [BlockStmt] { ... } +# 54| 0: [ReturnStmt] return ... +# 54| 0: [VarAccess] x +# 56| 3: [ExtensionMethod] test +# 56| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 56| 0: [Parameter] +# 56| 0: [TypeAccess] ExtendMe +# 56| 1: [Parameter] x +# 56| 0: [TypeAccess] String +# 56| 2: [Parameter] y +# 56| 0: [TypeAccess] String +# 56| 3: [Parameter] z +# 56| 0: [TypeAccess] String +# 56| 5: [BlockStmt] { ... } +# 57| 0: [ExprStmt] ; +# 57| 0: [MethodAccess] sink(...) +# 57| -1: [TypeAccess] TestKt +# 57| 0: [VarAccess] y +# 56| 4: [Method] test$default +# 56| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 56| 0: [Parameter] p0 +# 56| 0: [TypeAccess] ExtendMe +# 56| 1: [Parameter] p1 +# 56| 0: [TypeAccess] TestReceiverReferences +# 56| 2: [Parameter] p2 +# 56| 0: [TypeAccess] String +# 56| 3: [Parameter] p3 +# 56| 0: [TypeAccess] String +# 56| 4: [Parameter] p4 +# 56| 0: [TypeAccess] String +# 56| 5: [Parameter] p5 +# 56| 0: [TypeAccess] int +# 56| 6: [Parameter] p6 +# 56| 0: [TypeAccess] Object +# 56| 5: [BlockStmt] { ... } +# 56| 0: [IfStmt] if (...) +# 56| 0: [EQExpr] ... == ... +# 56| 0: [AndBitwiseExpr] ... & ... +# 56| 0: [IntegerLiteral] 2 +# 56| 1: [VarAccess] p5 +# 56| 1: [IntegerLiteral] 0 +# 56| 1: [ExprStmt] ; +# 56| 0: [AssignExpr] ...=... +# 56| 0: [VarAccess] p3 +# 56| 1: [MethodAccess] f(...) +# 56| -1: [VarAccess] p0 +# 56| 0: [MethodAccess] g(...) +# 56| -1: [VarAccess] p1 +# 56| 0: [VarAccess] p2 +# 56| 1: [IfStmt] if (...) +# 56| 0: [EQExpr] ... == ... +# 56| 0: [AndBitwiseExpr] ... & ... +# 56| 0: [IntegerLiteral] 4 +# 56| 1: [VarAccess] p5 +# 56| 1: [IntegerLiteral] 0 +# 56| 1: [ExprStmt] ; +# 56| 0: [AssignExpr] ...=... +# 56| 0: [VarAccess] p4 +# 56| 1: [StringLiteral] hello world +# 56| 2: [ReturnStmt] return ... +# 56| 0: [MethodAccess] test(...) +# 56| -1: [VarAccess] p1 +# 56| 0: [VarAccess] p0 +# 56| 1: [VarAccess] p2 +# 56| 2: [VarAccess] p3 +# 56| 3: [VarAccess] p4 +# 60| 5: [Method] user +# 60| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 60| 0: [Parameter] t +# 60| 0: [TypeAccess] ExtendMe +# 60| 5: [BlockStmt] { ... } +# 61| 0: [ExprStmt] ; +# 61| 0: [MethodAccess] test$default(...) +# 61| -1: [TypeAccess] TestReceiverReferences +# 61| 0: [VarAccess] t +# 61| 1: [ThisAccess] this +# 61| 2: [StringLiteral] receiver refs sunk +# 1| 3: [NullLiteral] null +# 1| 4: [NullLiteral] null +# 1| 5: [IntegerLiteral] 1 +# 1| 6: [NullLiteral] null +# 62| 1: [ExprStmt] ; +# 62| 0: [MethodAccess] test$default(...) +# 62| -1: [TypeAccess] TestReceiverReferences +# 62| 0: [VarAccess] t +# 62| 1: [ThisAccess] this +# 62| 2: [StringLiteral] receiver refs sunk fp +# 62| 3: [StringLiteral] receiver refs sunk 2 +# 1| 4: [NullLiteral] null +# 1| 5: [IntegerLiteral] 3 +# 1| 6: [NullLiteral] null +# 63| 2: [ExprStmt] ; +# 63| 0: [MethodAccess] test(...) +# 63| -1: [ThisAccess] this +# 63| 0: [VarAccess] t +# 63| 1: [StringLiteral] not sunk +# 63| 2: [StringLiteral] receiver refs sunk 3 +# 63| 3: [StringLiteral] not sunk +# 68| 7: [Class] TestConstructor +# 68| 1: [Constructor] TestConstructor +#-----| 4: (Parameters) +# 68| 0: [Parameter] x +# 68| 0: [TypeAccess] String +# 68| 1: [Parameter] y +# 68| 0: [TypeAccess] String +# 68| 2: [Parameter] z +# 68| 0: [TypeAccess] String +# 68| 5: [BlockStmt] { ... } +# 68| 0: [SuperConstructorInvocationStmt] super(...) +# 68| 1: [BlockStmt] { ... } +# 71| 0: [ExprStmt] ; +# 71| 0: [MethodAccess] sink(...) +# 71| -1: [TypeAccess] TestKt +# 71| 0: [VarAccess] y +# 68| 2: [Constructor] TestConstructor +#-----| 4: (Parameters) +# 68| 0: [Parameter] p0 +# 68| 0: [TypeAccess] String +# 68| 1: [Parameter] p1 +# 68| 0: [TypeAccess] String +# 68| 2: [Parameter] p2 +# 68| 0: [TypeAccess] String +# 68| 3: [Parameter] p3 +# 68| 0: [TypeAccess] int +# 68| 4: [Parameter] p4 +# 68| 0: [TypeAccess] DefaultConstructorMarker +# 68| 5: [BlockStmt] { ... } +# 68| 0: [IfStmt] if (...) +# 68| 0: [EQExpr] ... == ... +# 68| 0: [AndBitwiseExpr] ... & ... +# 68| 0: [IntegerLiteral] 2 +# 68| 1: [VarAccess] p3 +# 68| 1: [IntegerLiteral] 0 +# 68| 1: [ExprStmt] ; +# 68| 0: [AssignExpr] ...=... +# 68| 0: [VarAccess] p1 +# 68| 1: [VarAccess] p0 +# 68| 1: [IfStmt] if (...) +# 68| 0: [EQExpr] ... == ... +# 68| 0: [AndBitwiseExpr] ... & ... +# 68| 0: [IntegerLiteral] 4 +# 68| 1: [VarAccess] p3 +# 68| 1: [IntegerLiteral] 0 +# 68| 1: [ExprStmt] ; +# 68| 0: [AssignExpr] ...=... +# 68| 0: [VarAccess] p2 +# 68| 1: [StringLiteral] hello world +# 68| 2: [ThisConstructorInvocationStmt] this(...) +# 68| 0: [VarAccess] p0 +# 68| 1: [VarAccess] p1 +# 68| 2: [VarAccess] p2 +# 74| 3: [Method] user +# 74| 3: [TypeAccess] Unit +# 74| 5: [BlockStmt] { ... } +# 75| 0: [ExprStmt] ; +# 75| 0: [ImplicitCoercionToUnitExpr] +# 75| 0: [TypeAccess] Unit +# 75| 1: [ClassInstanceExpr] new TestConstructor(...) +# 75| -3: [TypeAccess] TestConstructor +# 75| 0: [StringLiteral] constructor sunk +# 1| 1: [NullLiteral] null +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 1 +# 1| 4: [NullLiteral] null +# 76| 1: [ExprStmt] ; +# 76| 0: [ImplicitCoercionToUnitExpr] +# 76| 0: [TypeAccess] Unit +# 76| 1: [ClassInstanceExpr] new TestConstructor(...) +# 76| -3: [TypeAccess] TestConstructor +# 76| 0: [StringLiteral] constructor sunk fp +# 76| 1: [StringLiteral] constructor sunk 2 +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 3 +# 1| 4: [NullLiteral] null +# 77| 2: [ExprStmt] ; +# 77| 0: [ImplicitCoercionToUnitExpr] +# 77| 0: [TypeAccess] Unit +# 77| 1: [ClassInstanceExpr] new TestConstructor(...) +# 77| -3: [TypeAccess] TestConstructor +# 77| 0: [StringLiteral] not sunk +# 77| 1: [StringLiteral] constructor sunk 3 +# 77| 2: [StringLiteral] not sunk +# 82| 8: [Class] TestLocal +# 82| 1: [Constructor] TestLocal +# 82| 5: [BlockStmt] { ... } +# 82| 0: [SuperConstructorInvocationStmt] super(...) +# 82| 1: [BlockStmt] { ... } +# 84| 2: [Method] enclosing +# 84| 3: [TypeAccess] Unit +# 84| 5: [BlockStmt] { ... } +# 86| 0: [LocalTypeDeclStmt] class ... +# 86| 0: [LocalClass] +# 86| 1: [Constructor] +# 86| 5: [BlockStmt] { ... } +# 86| 0: [SuperConstructorInvocationStmt] super(...) +# 86| 2: [Method] f +# 86| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 86| 0: [Parameter] x +# 86| 0: [TypeAccess] String +# 86| 1: [Parameter] y +# 86| 0: [TypeAccess] String +# 86| 2: [Parameter] z +# 86| 0: [TypeAccess] String +# 86| 5: [BlockStmt] { ... } +# 87| 0: [ExprStmt] ; +# 87| 0: [MethodAccess] sink(...) +# 87| -1: [TypeAccess] TestKt +# 87| 0: [VarAccess] y +# 86| 3: [Method] f$default +# 86| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 86| 0: [Parameter] p0 +# 86| 0: [TypeAccess] String +# 86| 1: [Parameter] p1 +# 86| 0: [TypeAccess] String +# 86| 2: [Parameter] p2 +# 86| 0: [TypeAccess] String +# 86| 3: [Parameter] p3 +# 86| 0: [TypeAccess] int +# 86| 4: [Parameter] p4 +# 86| 0: [TypeAccess] Object +# 86| 5: [BlockStmt] { ... } +# 86| 0: [IfStmt] if (...) +# 86| 0: [EQExpr] ... == ... +# 86| 0: [AndBitwiseExpr] ... & ... +# 86| 0: [IntegerLiteral] 2 +# 86| 1: [VarAccess] p3 +# 86| 1: [IntegerLiteral] 0 +# 86| 1: [ExprStmt] ; +# 86| 0: [AssignExpr] ...=... +# 86| 0: [VarAccess] p1 +# 86| 1: [VarAccess] p0 +# 86| 1: [IfStmt] if (...) +# 86| 0: [EQExpr] ... == ... +# 86| 0: [AndBitwiseExpr] ... & ... +# 86| 0: [IntegerLiteral] 4 +# 86| 1: [VarAccess] p3 +# 86| 1: [IntegerLiteral] 0 +# 86| 1: [ExprStmt] ; +# 86| 0: [AssignExpr] ...=... +# 86| 0: [VarAccess] p2 +# 86| 1: [StringLiteral] hello world +# 86| 2: [ReturnStmt] return ... +# 86| 0: [MethodAccess] f(...) +# 86| -1: [ClassInstanceExpr] new (...) +# 86| -3: [TypeAccess] Object +# 86| 0: [VarAccess] p0 +# 86| 1: [VarAccess] p1 +# 86| 2: [VarAccess] p2 +# 90| 1: [LocalTypeDeclStmt] class ... +# 90| 0: [LocalClass] +# 90| 1: [Constructor] +# 90| 5: [BlockStmt] { ... } +# 90| 0: [SuperConstructorInvocationStmt] super(...) +# 90| 2: [Method] user +# 90| 3: [TypeAccess] Unit +# 90| 5: [BlockStmt] { ... } +# 91| 0: [ExprStmt] ; +# 91| 0: [MethodAccess] f$default(...) +# 91| -1: [ClassInstanceExpr] new (...) +# 91| -3: [TypeAccess] Object +# 91| 0: [StringLiteral] local sunk +# 1| 1: [NullLiteral] null +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 1 +# 1| 4: [NullLiteral] null +# 92| 1: [ExprStmt] ; +# 92| 0: [MethodAccess] f$default(...) +# 92| -1: [ClassInstanceExpr] new (...) +# 92| -3: [TypeAccess] Object +# 92| 0: [StringLiteral] local sunk fp +# 92| 1: [StringLiteral] local sunk 2 +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 3 +# 1| 4: [NullLiteral] null +# 93| 2: [ExprStmt] ; +# 93| 0: [MethodAccess] f(...) +# 93| -1: [ClassInstanceExpr] new (...) +# 93| -3: [TypeAccess] Object +# 93| 0: [StringLiteral] not sunk +# 93| 1: [StringLiteral] local sunk 3 +# 93| 2: [StringLiteral] not sunk +# 100| 9: [Class] TestLocalClass +# 100| 1: [Constructor] TestLocalClass +# 100| 5: [BlockStmt] { ... } +# 100| 0: [SuperConstructorInvocationStmt] super(...) +# 100| 1: [BlockStmt] { ... } +# 102| 2: [Method] enclosingFunction +# 102| 3: [TypeAccess] Unit +# 102| 5: [BlockStmt] { ... } +# 104| 0: [LocalTypeDeclStmt] class ... +# 104| 0: [LocalClass] EnclosingLocalClass +# 104| 1: [Constructor] EnclosingLocalClass +# 104| 5: [BlockStmt] { ... } +# 104| 0: [SuperConstructorInvocationStmt] super(...) +# 104| 1: [BlockStmt] { ... } +# 106| 2: [Method] f +# 106| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 106| 0: [Parameter] x +# 106| 0: [TypeAccess] String +# 106| 1: [Parameter] y +# 106| 0: [TypeAccess] String +# 106| 2: [Parameter] z +# 106| 0: [TypeAccess] String +# 106| 5: [BlockStmt] { ... } +# 107| 0: [ExprStmt] ; +# 107| 0: [MethodAccess] sink(...) +# 107| -1: [TypeAccess] TestKt +# 107| 0: [VarAccess] y +# 106| 3: [Method] f$default +# 106| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 106| 0: [Parameter] p0 +# 106| 0: [TypeAccess] EnclosingLocalClass +# 106| 1: [Parameter] p1 +# 106| 0: [TypeAccess] String +# 106| 2: [Parameter] p2 +# 106| 0: [TypeAccess] String +# 106| 3: [Parameter] p3 +# 106| 0: [TypeAccess] String +# 106| 4: [Parameter] p4 +# 106| 0: [TypeAccess] int +# 106| 5: [Parameter] p5 +# 106| 0: [TypeAccess] Object +# 106| 5: [BlockStmt] { ... } +# 106| 0: [IfStmt] if (...) +# 106| 0: [EQExpr] ... == ... +# 106| 0: [AndBitwiseExpr] ... & ... +# 106| 0: [IntegerLiteral] 2 +# 106| 1: [VarAccess] p4 +# 106| 1: [IntegerLiteral] 0 +# 106| 1: [ExprStmt] ; +# 106| 0: [AssignExpr] ...=... +# 106| 0: [VarAccess] p2 +# 106| 1: [VarAccess] p1 +# 106| 1: [IfStmt] if (...) +# 106| 0: [EQExpr] ... == ... +# 106| 0: [AndBitwiseExpr] ... & ... +# 106| 0: [IntegerLiteral] 4 +# 106| 1: [VarAccess] p4 +# 106| 1: [IntegerLiteral] 0 +# 106| 1: [ExprStmt] ; +# 106| 0: [AssignExpr] ...=... +# 106| 0: [VarAccess] p3 +# 106| 1: [StringLiteral] hello world +# 106| 2: [ReturnStmt] return ... +# 106| 0: [MethodAccess] f(...) +# 106| -1: [VarAccess] p0 +# 106| 0: [VarAccess] p1 +# 106| 1: [VarAccess] p2 +# 106| 2: [VarAccess] p3 +# 110| 4: [Method] user +# 110| 3: [TypeAccess] Unit +# 110| 5: [BlockStmt] { ... } +# 111| 0: [ExprStmt] ; +# 111| 0: [MethodAccess] f$default(...) +# 111| -1: [TypeAccess] EnclosingLocalClass +# 111| 0: [ThisAccess] this +# 111| 1: [StringLiteral] local sunk +# 1| 2: [NullLiteral] null +# 1| 3: [NullLiteral] null +# 1| 4: [IntegerLiteral] 1 +# 1| 5: [NullLiteral] null +# 112| 1: [ExprStmt] ; +# 112| 0: [MethodAccess] f$default(...) +# 112| -1: [TypeAccess] EnclosingLocalClass +# 112| 0: [ThisAccess] this +# 112| 1: [StringLiteral] local sunk fp +# 112| 2: [StringLiteral] local sunk 2 +# 1| 3: [NullLiteral] null +# 1| 4: [IntegerLiteral] 3 +# 1| 5: [NullLiteral] null +# 113| 2: [ExprStmt] ; +# 113| 0: [MethodAccess] f(...) +# 113| -1: [ThisAccess] this +# 113| 0: [StringLiteral] not sunk +# 113| 1: [StringLiteral] local sunk 3 +# 113| 2: [StringLiteral] not sunk +# 122| 10: [Class,GenericType,ParameterizedType] TestGeneric +#-----| -2: (Generic Parameters) +# 122| 0: [TypeVariable] T +# 122| 1: [Constructor] TestGeneric +# 122| 5: [BlockStmt] { ... } +# 122| 0: [SuperConstructorInvocationStmt] super(...) +# 122| 1: [BlockStmt] { ... } +# 124| 2: [Method] f +# 124| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 124| 0: [Parameter] x +# 124| 0: [TypeAccess] T +# 124| 1: [Parameter] y +# 124| 0: [TypeAccess] T +# 124| 2: [Parameter] z +# 124| 0: [TypeAccess] T +# 124| 5: [BlockStmt] { ... } +# 125| 0: [ExprStmt] ; +# 125| 0: [MethodAccess] sink(...) +# 125| -1: [TypeAccess] TestKt +# 125| 0: [VarAccess] y +# 124| 3: [Method] f$default +# 124| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 124| 0: [Parameter] p0 +# 124| 0: [TypeAccess] TestGeneric<> +# 124| 1: [Parameter] p1 +# 124| 0: [TypeAccess] Object +# 124| 2: [Parameter] p2 +# 124| 0: [TypeAccess] Object +# 124| 3: [Parameter] p3 +# 124| 0: [TypeAccess] Object +# 124| 4: [Parameter] p4 +# 124| 0: [TypeAccess] int +# 124| 5: [Parameter] p5 +# 124| 0: [TypeAccess] Object +# 124| 5: [BlockStmt] { ... } +# 124| 0: [IfStmt] if (...) +# 124| 0: [EQExpr] ... == ... +# 124| 0: [AndBitwiseExpr] ... & ... +# 124| 0: [IntegerLiteral] 2 +# 124| 1: [VarAccess] p4 +# 124| 1: [IntegerLiteral] 0 +# 124| 1: [ExprStmt] ; +# 124| 0: [AssignExpr] ...=... +# 124| 0: [VarAccess] p2 +# 124| 1: [VarAccess] p1 +# 124| 1: [IfStmt] if (...) +# 124| 0: [EQExpr] ... == ... +# 124| 0: [AndBitwiseExpr] ... & ... +# 124| 0: [IntegerLiteral] 4 +# 124| 1: [VarAccess] p4 +# 124| 1: [IntegerLiteral] 0 +# 124| 1: [ExprStmt] ; +# 124| 0: [AssignExpr] ...=... +# 124| 0: [VarAccess] p3 +# 124| 1: [NullLiteral] null +# 124| 2: [ReturnStmt] return ... +# 124| 0: [MethodAccess] f(...) +# 124| -1: [VarAccess] p0 +# 124| 0: [VarAccess] p1 +# 124| 1: [VarAccess] p2 +# 124| 2: [VarAccess] p3 +# 128| 4: [Method] user +# 128| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 128| 0: [Parameter] tgs +# 128| 0: [TypeAccess] TestGeneric +# 128| 0: [TypeAccess] String +# 128| 1: [Parameter] tcs +# 128| 0: [TypeAccess] TestGeneric +# 128| 0: [TypeAccess] CharSequence +# 128| 5: [BlockStmt] { ... } +# 129| 0: [ExprStmt] ; +# 129| 0: [MethodAccess] f$default(...) +# 129| -1: [TypeAccess] TestGeneric<> +# 129| 0: [VarAccess] tgs +# 129| 1: [StringLiteral] generic sunk +# 1| 2: [NullLiteral] null +# 1| 3: [NullLiteral] null +# 1| 4: [IntegerLiteral] 1 +# 1| 5: [NullLiteral] null +# 130| 1: [ExprStmt] ; +# 130| 0: [MethodAccess] f$default(...) +# 130| -1: [TypeAccess] TestGeneric<> +# 130| 0: [VarAccess] tcs +# 130| 1: [StringLiteral] generic sunk fp +# 130| 2: [StringLiteral] generic sunk 2 +# 1| 3: [NullLiteral] null +# 1| 4: [IntegerLiteral] 3 +# 1| 5: [NullLiteral] null +# 131| 2: [ExprStmt] ; +# 131| 0: [MethodAccess] f(...) +# 131| -1: [VarAccess] tgs +# 131| 0: [StringLiteral] not sunk +# 131| 1: [StringLiteral] generic sunk 3 +# 131| 2: [StringLiteral] not sunk +# 132| 3: [ExprStmt] ; +# 132| 0: [MethodAccess] f(...) +# 132| -1: [VarAccess] tcs +# 132| 0: [StringLiteral] not sunk +# 132| 1: [StringLiteral] generic sunk 3 +# 132| 2: [StringLiteral] not sunk +# 135| 5: [Method] testReturn +# 135| 3: [TypeAccess] T +#-----| 4: (Parameters) +# 135| 0: [Parameter] t1 +# 135| 0: [TypeAccess] T +# 135| 1: [Parameter] t2 +# 135| 0: [TypeAccess] T +# 135| 5: [BlockStmt] { ... } +# 135| 0: [ReturnStmt] return ... +# 135| 0: [VarAccess] t1 +# 135| 6: [Method] testReturn$default +# 135| 3: [TypeAccess] Object +#-----| 4: (Parameters) +# 135| 0: [Parameter] p0 +# 135| 0: [TypeAccess] TestGeneric<> +# 135| 1: [Parameter] p1 +# 135| 0: [TypeAccess] Object +# 135| 2: [Parameter] p2 +# 135| 0: [TypeAccess] Object +# 135| 3: [Parameter] p3 +# 135| 0: [TypeAccess] int +# 135| 4: [Parameter] p4 +# 135| 0: [TypeAccess] Object +# 135| 5: [BlockStmt] { ... } +# 135| 0: [IfStmt] if (...) +# 135| 0: [EQExpr] ... == ... +# 135| 0: [AndBitwiseExpr] ... & ... +# 135| 0: [IntegerLiteral] 2 +# 135| 1: [VarAccess] p3 +# 135| 1: [IntegerLiteral] 0 +# 135| 1: [ExprStmt] ; +# 135| 0: [AssignExpr] ...=... +# 135| 0: [VarAccess] p2 +# 135| 1: [NullLiteral] null +# 135| 1: [ReturnStmt] return ... +# 135| 0: [MethodAccess] testReturn(...) +# 135| -1: [VarAccess] p0 +# 135| 0: [VarAccess] p1 +# 135| 1: [VarAccess] p2 +# 137| 7: [Method] testReturnUser +# 137| 3: [TypeAccess] Unit +#-----| 4: (Parameters) +# 137| 0: [Parameter] tgs +# 137| 0: [TypeAccess] TestGeneric +# 137| 0: [TypeAccess] String +# 137| 5: [BlockStmt] { ... } +# 138| 0: [ExprStmt] ; +# 138| 0: [MethodAccess] sink(...) +# 138| -1: [TypeAccess] TestKt +# 138| 0: [MethodAccess] testReturn$default(...) +# 138| -1: [TypeAccess] TestGeneric<> +# 138| 0: [VarAccess] tgs +# 138| 1: [StringLiteral] sunk return value +# 1| 2: [NullLiteral] null +# 1| 3: [IntegerLiteral] 1 +# 1| 4: [NullLiteral] null diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref new file mode 100644 index 00000000000..c7fd5faf239 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/java/PrintAst.ql \ No newline at end of file diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected new file mode 100644 index 00000000000..37b80612273 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.expected @@ -0,0 +1,2 @@ +shouldBeSunkButIsnt +shouldntBeSunkButIs diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql new file mode 100644 index 00000000000..28151ecdc85 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/parameter-defaults/flowTest.ql @@ -0,0 +1,34 @@ +import java +import semmle.code.java.dataflow.DataFlow + +class ShouldNotBeSunk extends StringLiteral { + ShouldNotBeSunk() { this.getValue().matches("%not sunk%") } +} + +class ShouldBeSunk extends StringLiteral { + ShouldBeSunk() { + this.getValue().matches("%sunk%") and + not this instanceof ShouldNotBeSunk + } +} + +class Config extends DataFlow::Configuration { + Config() { this = "Config" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr() instanceof ShouldBeSunk or + n.asExpr() instanceof ShouldNotBeSunk + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().getName() = "sink" + } +} + +predicate isSunk(StringLiteral sl) { + exists(Config c, DataFlow::Node source | c.hasFlow(source, _) and sl = source.asExpr()) +} + +query predicate shouldBeSunkButIsnt(ShouldBeSunk src) { not isSunk(src) } + +query predicate shouldntBeSunkButIs(ShouldNotBeSunk src) { isSunk(src) } diff --git a/java/ql/test/kotlin/library-tests/parameter-defaults/test.kt b/java/ql/test/kotlin/library-tests/parameter-defaults/test.kt new file mode 100644 index 00000000000..144664f0a07 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/parameter-defaults/test.kt @@ -0,0 +1,141 @@ +fun sink(a: Any?) { } + +class TestMember { + + fun f(x: String, y: String = x, z: String = "hello world") { + sink(y) + } + + fun user() { + f("member sunk") + f("member sunk fp", "member sunk 2") + f("not sunk", "member sunk 3", "not sunk") + } + +} + +class TestExtensionMember { + + fun String.f(x: String, y: String = x, z: String = "hello world") { + sink(this) + sink(y) + } + + fun user(sunk: String) { + sunk.f("extension sunk") + sunk.f("extension sunk fp", "extension sunk 2") + sunk.f("not sunk", "extension sunk 3", "not sunk") + } + +} + +object TestStaticMember { + + @JvmStatic fun f(x: String, y: String = x, z: String = "hello world") { + sink(y) + } + + fun user() { + f("static sunk") + f("static sunk fp", "static sunk 2") + f("not sunk", "static sunk 3", "not sunk") + } + +} + +class ExtendMe { + + fun f(x: String) = x + +} + +class TestReceiverReferences { + + fun g(x: String) = x + + fun ExtendMe.test(x: String, y: String = this.f(this@TestReceiverReferences.g(x)), z: String = "hello world") { + sink(y) + } + + fun user(t: ExtendMe) { + t.test("receiver refs sunk") + t.test("receiver refs sunk fp", "receiver refs sunk 2") + t.test("not sunk", "receiver refs sunk 3", "not sunk") + } + +} + +class TestConstructor(x: String, y: String = x, z: String = "hello world") { + + init { + sink(y) + } + + fun user() { + TestConstructor("constructor sunk") + TestConstructor("constructor sunk fp", "constructor sunk 2") + TestConstructor("not sunk", "constructor sunk 3", "not sunk") + } + +} + +class TestLocal { + + fun enclosing() { + + fun f(x: String, y: String = x, z: String = "hello world") { + sink(y) + } + + fun user() { + f("local sunk") + f("local sunk fp", "local sunk 2") + f("not sunk", "local sunk 3", "not sunk") + } + + } + +} + +class TestLocalClass { + + fun enclosingFunction() { + + class EnclosingLocalClass { + + fun f(x: String, y: String = x, z: String = "hello world") { + sink(y) + } + + fun user() { + f("local sunk") + f("local sunk fp", "local sunk 2") + f("not sunk", "local sunk 3", "not sunk") + } + + } + + } + +} + +class TestGeneric { + + fun f(x: T, y: T = x, z: T? = null) { + sink(y) + } + + fun user(tgs: TestGeneric, tcs: TestGeneric) { + tgs.f("generic sunk") + tcs.f("generic sunk fp", "generic sunk 2") + tgs.f("not sunk", "generic sunk 3", "not sunk") + tcs.f("not sunk", "generic sunk 3", "not sunk") + } + + fun testReturn(t1: T, t2: T? = null) = t1 + + fun testReturnUser(tgs: TestGeneric) { + sink(tgs.testReturn("sunk return value")) + } + +}