diff --git a/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt index 8cd58d90736..4004f6f040c 100644 --- a/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor2/src/main/kotlin/KotlinFileExtractor.kt @@ -1,17 +1,14 @@ package com.github.codeql -import com.github.codeql.utils.isInterfaceLike import com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.analysis.api.components.KaDiagnosticCheckerFilter import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.types.KaType import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.resolution.* import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.parsing.parseNumericLiteral -import java.io.Closeable -import java.util.* /* OLD: KE1 @@ -2474,73 +2471,6 @@ OLD: KE1 } */ - fun extractBody(b: KtExpression, callable: Label) { - with("body", b) { - when (b) { - is KtBlockExpression -> extractBlockBody(b, callable) - /* - OLD: KE1 - is IrSyntheticBody -> extractSyntheticBody(b, callable) - */ - else -> extractExpressionBody(b, callable) - } - } - } - - // TODO: Can this be inlined? - 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: KtBlockExpression, callable: Label) { - with("block body", b) { - extractBlockBody(callable, tw.getLocation(b)).also { - for ((sIdx, stmt) in b.statements.withIndex()) { - extractExpression(stmt, callable, StmtParent(it, sIdx)) - } - } - } - } - - /* - OLD: KE1 - private fun extractSyntheticBody(b: IrSyntheticBody, callable: Label) { - with("synthetic body", b) { - val kind = b.kind - when { - kind == IrSyntheticBodyKind.ENUM_VALUES -> tw.writeKtSyntheticBody(callable, 1) - kind == IrSyntheticBodyKind.ENUM_VALUEOF -> tw.writeKtSyntheticBody(callable, 2) - kind == kind_ENUM_ENTRIES -> tw.writeKtSyntheticBody(callable, 3) - else -> { - logger.errorElement("Unhandled synthetic body kind " + kind, b) - } - } - } - } - */ - - private fun extractExpressionBody(e: KtExpression, callable: Label) { - with("expression body", e) { - val locId = tw.getLocation(e) - extractExpressionBody(callable, locId).also { returnId -> - extractExpression(e, callable, ExprParent(returnId, 0, returnId)) - } - } - } - - // TODO: Inline this? It used to be public - private fun extractExpressionBody( - callable: Label, - locId: Label - ): Label { - val blockId = extractBlockBody(callable, locId) - return tw.getFreshIdLabel().also { returnId -> - tw.writeStmts_returnstmt(returnId, blockId, 0, callable) - tw.writeHasLocation(returnId, locId) - } - } /* OLD: KE1 @@ -5023,1726 +4953,6 @@ OLD: KE1 } } - /* - OLD: KE1 - private fun getStatementOriginOperator(origin: IrStatementOrigin?) = - when (origin) { - IrStatementOrigin.PLUSEQ -> "plus" - IrStatementOrigin.MINUSEQ -> "minus" - IrStatementOrigin.MULTEQ -> "times" - IrStatementOrigin.DIVEQ -> "div" - IrStatementOrigin.PERCEQ -> "rem" - else -> null - } - - private fun getUpdateInPlaceRHS( - origin: IrStatementOrigin?, - isExpectedLhs: (IrExpression?) -> Boolean, - updateRhs: IrExpression - ): IrExpression? { - // Check for a desugared in-place update operator, such as "v += e": - return getStatementOriginOperator(origin)?.let { - if (updateRhs is IrCall && isNumericFunction(updateRhs.symbol.owner, it)) { - // Check for an expression like x = get(x).op(e): - val opReceiver = updateRhs.dispatchReceiver - if (isExpectedLhs(opReceiver)) { - updateRhs.getValueArgument(0) - } else null - } else null - } - } - - private fun writeUpdateInPlaceExpr( - origin: IrStatementOrigin - ): (( - tw: TrapWriter, - id: Label, - type: Label, - exprParent: Label, - index: Int - ) -> Unit)? { - when (origin) { - IrStatementOrigin.PLUSEQ -> - return { - tw: TrapWriter, - id: Label, - type: Label, - exprParent: Label, - index: Int -> - tw.writeExprs_assignaddexpr(id.cast(), type, exprParent, index) - } - IrStatementOrigin.MINUSEQ -> - return { - tw: TrapWriter, - id: Label, - type: Label, - exprParent: Label, - index: Int -> - tw.writeExprs_assignsubexpr(id.cast(), type, exprParent, index) - } - IrStatementOrigin.MULTEQ -> - return { - tw: TrapWriter, - id: Label, - type: Label, - exprParent: Label, - index: Int -> - tw.writeExprs_assignmulexpr(id.cast(), type, exprParent, index) - } - IrStatementOrigin.DIVEQ -> - return { - tw: TrapWriter, - id: Label, - type: Label, - exprParent: Label, - index: Int -> - tw.writeExprs_assigndivexpr(id.cast(), type, exprParent, index) - } - IrStatementOrigin.PERCEQ -> - return { - tw: TrapWriter, - id: Label, - type: Label, - exprParent: Label, - index: Int -> - tw.writeExprs_assignremexpr(id.cast(), type, exprParent, index) - } - else -> return null - } - } - - /** - * This method tries to extract a block as an enhanced for loop. It returns true if it succeeds, - * and false otherwise. - */ - private fun tryExtractForLoop( - e: IrContainerExpression, - callable: Label, - parent: StmtExprParent - ): Boolean { - /* - * We're expecting the pattern - * { - * val iterator = [expr].iterator() - * while (iterator.hasNext()) { - * val [loopVar] = iterator.next() - * [block] - * } - * } - */ - - if (e.origin != IrStatementOrigin.FOR_LOOP || e.statements.size != 2) { - return false - } - - val iteratorVariable = e.statements[0] as? IrVariable - val innerWhile = e.statements[1] as? IrWhileLoop - - if ( - iteratorVariable == null || - iteratorVariable.origin != IrDeclarationOrigin.FOR_LOOP_ITERATOR || - innerWhile == null || - innerWhile.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE - ) { - return false - } - - val initializer = iteratorVariable.initializer as? IrCall - if ( - initializer == null || - initializer.origin != IrStatementOrigin.FOR_LOOP_ITERATOR || - initializer.symbol.owner.name.asString() != "iterator" - ) { - return false - } - - val expr = initializer.dispatchReceiver - val cond = innerWhile.condition as? IrCall - val body = innerWhile.body as? IrBlock - - if ( - expr == null || - cond == null || - cond.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT || - (cond.dispatchReceiver as? IrGetValue)?.symbol?.owner != iteratorVariable || - body == null || - body.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE || - body.statements.size < 2 - ) { - return false - } - - val loopVar = body.statements[0] as? IrVariable - val nextCall = loopVar?.initializer as? IrCall - - if ( - loopVar == null || - !(loopVar.origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE || - loopVar.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE) || - nextCall == null || - nextCall.origin != IrStatementOrigin.FOR_LOOP_NEXT || - (nextCall.dispatchReceiver as? IrGetValue)?.symbol?.owner != iteratorVariable - ) { - return false - } - - val id = - extractLoop(innerWhile, null, parent, callable) { p, idx -> - tw.getFreshIdLabel().also { - tw.writeStmts_enhancedforstmt(it, p, idx, callable) - } - } - - extractVariableExpr(loopVar, callable, id, 0, id, extractInitializer = false) - extractExpressionExpr(expr, callable, id, 1, id) - val block = body.statements[1] as? IrBlock - if (body.statements.size == 2 && block != null) { - // Extract the body that was given to us by the compiler - extractExpressionStmt(block, callable, id, 2) - } else { - // Extract a block with all but the first (loop variable declaration) statement - extractBlock(body, body.statements.takeLast(body.statements.size - 1), id, 2, callable) - } - - return true - } - - /** - * This tried to extract a block as an array update. It returns true if it succeeds, and false - * otherwise. - */ - private fun tryExtractArrayUpdate( - e: IrContainerExpression, - callable: Label, - parent: StmtExprParent - ): Boolean { - /* - * We're expecting the pattern - * { - * val array = e1 - * val idx = e2 - * array.set(idx, array.get(idx).op(e3)) - * } - * - * If we find it, we'll extract e1[e2] op= e3 (op is +, -, ...) - */ - if (e.statements.size != 3) return false - (e.statements[0] as? IrVariable)?.let { arrayVarDecl -> - arrayVarDecl.initializer?.let { arrayVarInitializer -> - (e.statements[1] as? IrVariable)?.let { indexVarDecl -> - indexVarDecl.initializer?.let { indexVarInitializer -> - (e.statements[2] as? IrCall)?.let { arraySetCall -> - if ( - isFunction( - arraySetCall.symbol.owner, - "kotlin", - "(some array type)", - { isArrayType(it) }, - "set" - ) - ) { - val updateRhs0 = arraySetCall.getValueArgument(1) - if (updateRhs0 == null) { - logger.errorElement("Update RHS not found", e) - return false - } - getUpdateInPlaceRHS( - e - .origin, // Using e.origin not arraySetCall.origin here - // distinguishes a compiler-generated block - // from a user manually code that looks the - // same. - { oldValue -> - oldValue is IrCall && - isFunction( - oldValue.symbol.owner, - "kotlin", - "(some array type)", - { typeName -> isArrayType(typeName) }, - "get" - ) && - (oldValue.dispatchReceiver as? IrGetValue)?.let { - receiverVal -> - receiverVal.symbol.owner == - arrayVarDecl.symbol.owner - } ?: false - }, - updateRhs0 - ) - ?.let { updateRhs -> - val origin = e.origin - if (origin == null) { - logger.errorElement("No origin found", e) - return false - } - val writeUpdateInPlaceExprFun = - writeUpdateInPlaceExpr(origin) - if (writeUpdateInPlaceExprFun == null) { - logger.errorElement("Unexpected origin", e) - return false - } - - // Create an assignment skeleton _ op= _ - val exprParent = parent.expr(e, callable) - val assignId = tw.getFreshIdLabel() - val type = useType(arrayVarInitializer.type) - val locId = tw.getLocation(e) - tw.writeExprsKotlinType(assignId, type.kotlinResult.id) - extractExprContext( - assignId, - locId, - callable, - exprParent.enclosingStmt - ) - - writeUpdateInPlaceExprFun( - tw, - assignId, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - - // Extract e1[e2] - val lhsId = tw.getFreshIdLabel() - val elementType = useType(updateRhs.type) - tw.writeExprs_arrayaccess( - lhsId, - elementType.javaResult.id, - assignId, - 0 - ) - tw.writeExprsKotlinType(lhsId, elementType.kotlinResult.id) - extractExprContext( - lhsId, - locId, - callable, - exprParent.enclosingStmt - ) - extractExpressionExpr( - arrayVarInitializer, - callable, - lhsId, - 0, - exprParent.enclosingStmt - ) - extractExpressionExpr( - indexVarInitializer, - callable, - lhsId, - 1, - exprParent.enclosingStmt - ) - - // Extract e3 - extractExpressionExpr( - updateRhs, - callable, - assignId, - 1, - exprParent.enclosingStmt - ) - - return true - } - } - } - } - } - } - } - - 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) - } - - /* - OLD: KE1 - private fun extractExpressionStmt( - e: IrExpression, - callable: Label, - parent: Label, - idx: Int - ) { - extractExpression(e, callable, StmtParent(parent, idx)) - } - - fun extractExpressionExpr( - e: IrExpression, - callable: Label, - parent: Label, - idx: Int, - enclosingStmt: Label - ) { - extractExpression(e, callable, ExprParent(parent, idx, enclosingStmt)) - } - */ - - private fun extractExprContext( - id: Label, - locId: Label, - callable: Label?, - enclosingStmt: Label? - ) { - tw.writeHasLocation(id, locId) - callable?.let { tw.writeCallableEnclosingExpr(id, it) } - enclosingStmt?.let { tw.writeStatementEnclosingExpr(id, it) } - } - - /* - OLD: KE1 - 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( - text: String, - t: KaType, - v: Number, - locId: Label, - parent: Label, - idx: Int, - callable: Label?, - enclosingStmt: Label?, - /* - OLD: KE1 - overrideId: Label? = null - */ - ) = - // OLD: KE1: Was: exprIdOrFresh(overrideId).also { - tw.getFreshIdLabel().also { - val type = useType(t) - tw.writeExprs_integerliteral(it, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(it, type.kotlinResult.id) - tw.writeNamestrings(text, v.toString(), it) - extractExprContext(it, locId, callable, enclosingStmt) - } - - /* - OLD: KE1 - private fun extractNull( - t: IrType, - locId: Label, - parent: Label, - idx: Int, - callable: Label?, - enclosingStmt: Label?, - overrideId: Label? = null - ) = - exprIdOrFresh(overrideId).also { - val type = useType(t) - tw.writeExprs_nullliteral(it, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(it, type.kotlinResult.id) - 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: KtExpression, - callable: Label, - parent: StmtExprParent - ) { - with("expression", e) { - when (e) { - /* - OLD: KE1 - is IrDelegatingConstructorCall -> { - val stmtParent = parent.stmt(e, callable) - - val irCallable = declarationStack.peek().first - - val delegatingClass = e.symbol.owner.parent - val currentClass = irCallable.parent - - if (delegatingClass !is IrClass) { - logger.warnElement( - "Delegating class isn't a class: " + delegatingClass.javaClass, - e - ) - } - if (currentClass !is IrClass) { - logger.warnElement( - "Current class isn't a class: " + currentClass.javaClass, - e - ) - } - - val id: Label - if (delegatingClass != currentClass) { - id = tw.getFreshIdLabel() - tw.writeStmts_superconstructorinvocationstmt( - id, - stmtParent.parent, - stmtParent.idx, - callable - ) - } else { - id = tw.getFreshIdLabel() - tw.writeStmts_constructorinvocationstmt( - id, - stmtParent.parent, - stmtParent.idx, - callable - ) - } - - val locId = tw.getLocation(e) - val methodId = useFunction(e.symbol.owner) - if (methodId == null) { - logger.errorElement("Cannot get ID for delegating constructor", e) - } else { - tw.writeCallableBinding(id.cast(), methodId) - } - - tw.writeHasLocation(id, locId) - extractCallValueArguments(id, e, id, callable, 0) - val dr = e.dispatchReceiver - if (dr != null) { - extractExpressionExpr(dr, callable, id, -1, id) - } - - // todo: type arguments at index -2, -3, ... - } - is IrThrow -> { - val stmtParent = parent.stmt(e, callable) - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - tw.writeStmts_throwstmt(id, stmtParent.parent, stmtParent.idx, callable) - tw.writeHasLocation(id, locId) - extractExpressionExpr(e.value, callable, id, 0, id) - } - is IrBreak -> { - val stmtParent = parent.stmt(e, callable) - val id = tw.getFreshIdLabel() - tw.writeStmts_breakstmt(id, stmtParent.parent, stmtParent.idx, callable) - extractBreakContinue(e, id) - } - is IrContinue -> { - val stmtParent = parent.stmt(e, callable) - val id = tw.getFreshIdLabel() - tw.writeStmts_continuestmt(id, stmtParent.parent, stmtParent.idx, callable) - extractBreakContinue(e, id) - } - */ - is KtReturnExpression -> { - val stmtParent = parent.stmt(e, callable) - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - tw.writeStmts_returnstmt(id, stmtParent.parent, stmtParent.idx, callable) - tw.writeHasLocation(id, locId) - val returned = e.getReturnedExpression() - if (returned != null) { - extractExpression(returned, callable, ExprParent(id, 0, id)) - } - // TODO: e.getLabeledExpression() - } - /* - OLD: KE1 - is IrTry -> { - val stmtParent = parent.stmt(e, callable) - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - tw.writeStmts_trystmt(id, stmtParent.parent, stmtParent.idx, callable) - tw.writeHasLocation(id, locId) - extractExpressionStmt(e.tryResult, callable, id, -1) - val finallyStmt = e.finallyExpression - if (finallyStmt != null) { - extractExpressionStmt(finallyStmt, callable, id, -2) - } - for ((catchIdx, catchClause) in e.catches.withIndex()) { - val catchId = tw.getFreshIdLabel() - tw.writeStmts_catchclause(catchId, id, catchIdx, callable) - val catchLocId = tw.getLocation(catchClause) - tw.writeHasLocation(catchId, catchLocId) - extractTypeAccessRecursive( - catchClause.catchParameter.type, - tw.getLocation(catchClause.catchParameter), - catchId, - -1, - callable, - catchId - ) - extractVariableExpr( - catchClause.catchParameter, - callable, - catchId, - 0, - catchId - ) - extractExpressionStmt(catchClause.result, callable, catchId, 1) - } - } - is IrContainerExpression -> { - if ( - !tryExtractArrayUpdate(e, callable, parent) && - !tryExtractForLoop(e, callable, parent) - ) { - - extractBlock(e, e.statements, parent, callable) - } - } - is IrWhileLoop -> { - extractLoopWithCondition(e, parent, callable) - } - is IrDoWhileLoop -> { - extractLoopWithCondition(e, parent, callable) - } - is IrInstanceInitializerCall -> { - val irConstructor = declarationStack.peek().first as? IrConstructor - if (irConstructor == null) { - logger.errorElement("IrInstanceInitializerCall outside constructor", e) - return - } - if (needsObinitFunction(irConstructor.parentAsClass)) { - val exprParent = parent.expr(e, callable) - val id = tw.getFreshIdLabel() - val type = useType(pluginContext.irBuiltIns.unitType) - val locId = tw.getLocation(e) - val parentClass = irConstructor.parentAsClass - val parentId = useDeclarationParentOf(irConstructor, false, null, true) - if (parentId == null) { - logger.errorElement("Cannot get parent ID for obinit", e) - return - } - val methodLabel = getObinitLabel(parentClass, parentId) - val methodId = tw.getLabelFor(methodLabel) - tw.writeExprs_methodaccess( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - tw.writeCallableBinding(id, methodId) - } else { - val stmtParent = parent.stmt(e, callable) - extractInstanceInitializerBlock(stmtParent, irConstructor) - } - } - is IrConstructorCall -> { - val exprParent = parent.expr(e, callable) - extractConstructorCall( - e, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - } - is IrEnumConstructorCall -> { - val exprParent = parent.expr(e, callable) - extractConstructorCall( - e, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - } - is IrCall -> { - extractCall(e, callable, parent) - } - is IrStringConcatenation -> { - val exprParent = parent.expr(e, callable) - val id = tw.getFreshIdLabel() - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_stringtemplateexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - e.arguments.forEachIndexed { i, a -> - extractExpressionExpr(a, callable, id, i, exprParent.enclosingStmt) - } - } - */ - is KtConstantExpression -> { - val exprParent = parent.expr(e, callable) - extractConstant( - e, - callable, - exprParent.parent, - exprParent.idx, - exprParent.enclosingStmt - ) - } - /* - OLD: KE1 - is IrGetValue -> { - val exprParent = parent.expr(e, callable) - val owner = e.symbol.owner - if ( - owner is IrValueParameter && - owner.index == -1 && - !owner.isExtensionReceiver() - ) { - 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 - extractVariableAccess( - useValueDeclaration(owner), - extractType, - tw.getLocation(e), - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - } - } - is IrGetField -> { - val exprParent = parent.expr(e, callable) - val owner = tryReplaceAndroidSyntheticField(e.symbol.owner) - val locId = tw.getLocation(e) - val fieldType = - if (isAnnotationClassField(owner)) kClassToJavaClass(e.type) else e.type - extractVariableAccess( - useField(owner), - fieldType, - locId, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - .also { id -> - val receiver = e.receiver - if (receiver != null) { - extractExpressionExpr( - receiver, - callable, - id, - -1, - exprParent.enclosingStmt - ) - } else if (owner.isStatic) { - extractStaticTypeAccessQualifier( - owner, - id, - locId, - callable, - exprParent.enclosingStmt - ) - } - } - } - is IrGetEnumValue -> { - val exprParent = parent.expr(e, callable) - extractEnumValue( - e, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - } - is IrSetValue, - is IrSetField -> { - val exprParent = parent.expr(e, callable) - val id = tw.getFreshIdLabel() - val type = useType(e.type) - val rhsValue = - when (e) { - is IrSetValue -> e.value - is IrSetField -> e.value - else -> { - logger.errorElement("Unhandled IrSet* element.", e) - return - } - } - // The set operation's location as actually that of its LHS. Hence, the - // assignment spans the - // set op plus its RHS, while the varAccess takes its location from `e`. - val locId = tw.getLocation(e.startOffset, rhsValue.endOffset) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - - val lhsId = tw.getFreshIdLabel() - val lhsLocId = tw.getLocation(e) - extractExprContext(lhsId, lhsLocId, callable, exprParent.enclosingStmt) - - when (e) { - is IrSetValue -> { - // Check for a desugared in-place update operator, such as "v += e": - val inPlaceUpdateRhs = - getUpdateInPlaceRHS( - e.origin, - { it is IrGetValue && it.symbol.owner == e.symbol.owner }, - rhsValue - ) - if (inPlaceUpdateRhs != null) { - val origin = e.origin - if (origin == null) { - logger.errorElement("No origin for set-value", e) - return - } else { - val writeUpdateInPlaceExprFun = writeUpdateInPlaceExpr(origin) - if (writeUpdateInPlaceExprFun == null) { - logger.errorElement("Unexpected origin for set-value", e) - return - } - writeUpdateInPlaceExprFun( - tw, - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - } - } else { - tw.writeExprs_assignexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - } - - val lhsType = useType(e.symbol.owner.type) - tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, id, 0) - tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id) - val vId = useValueDeclaration(e.symbol.owner) - if (vId != null) { - tw.writeVariableBinding(lhsId, vId) - } - extractExpressionExpr( - inPlaceUpdateRhs ?: rhsValue, - callable, - id, - 1, - exprParent.enclosingStmt - ) - } - is IrSetField -> { - tw.writeExprs_assignexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - val realField = tryReplaceAndroidSyntheticField(e.symbol.owner) - val lhsType = useType(realField.type) - tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, id, 0) - tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id) - val vId = useField(realField) - tw.writeVariableBinding(lhsId, vId) - extractExpressionExpr( - e.value, - callable, - id, - 1, - exprParent.enclosingStmt - ) - - val receiver = e.receiver - if (receiver != null) { - extractExpressionExpr( - receiver, - callable, - lhsId, - -1, - exprParent.enclosingStmt - ) - } else if (realField.isStatic) { - extractStaticTypeAccessQualifier( - realField, - lhsId, - lhsLocId, - callable, - exprParent.enclosingStmt - ) - } - } - else -> { - logger.errorElement("Unhandled IrSet* element.", e) - } - } - } - is IrWhen -> { - val isAndAnd = e.origin == IrStatementOrigin.ANDAND - val isOrOr = e.origin == IrStatementOrigin.OROR - - if ( - (isAndAnd || isOrOr) && - e.branches.size == 2 && - (e.branches[1].condition as? IrConst<*>)?.value == true && - (e.branches[if (e.origin == IrStatementOrigin.ANDAND) 1 else 0].result - as? IrConst<*>) - ?.value == isOrOr - ) { - - // resugar binary logical operators: - - val exprParent = parent.expr(e, callable) - val type = useType(e.type) - - val id = - if (e.origin == IrStatementOrigin.ANDAND) { - val id = tw.getFreshIdLabel() - tw.writeExprs_andlogicalexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - id - } else { - val id = tw.getFreshIdLabel() - tw.writeExprs_orlogicalexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - id - } - val locId = tw.getLocation(e) - - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - - extractExpressionExpr( - e.branches[0].condition, - callable, - id, - 0, - exprParent.enclosingStmt - ) - - var rhsIdx = if (e.origin == IrStatementOrigin.ANDAND) 0 else 1 - extractExpressionExpr( - e.branches[rhsIdx].result, - callable, - id, - 1, - exprParent.enclosingStmt - ) - - return - } - - val exprParent = parent.expr(e, callable) - val id = tw.getFreshIdLabel() - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_whenexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - if (e.origin == IrStatementOrigin.IF) { - tw.writeWhen_if(id) - } - e.branches.forEachIndexed { i, b -> - val bId = tw.getFreshIdLabel() - val bLocId = tw.getLocation(b) - tw.writeStmts_whenbranch(bId, id, i, callable) - tw.writeHasLocation(bId, bLocId) - extractExpressionExpr(b.condition, callable, bId, 0, bId) - extractExpressionStmt(b.result, callable, bId, 1) - if (b is IrElseBranch) { - tw.writeWhen_branch_else(bId) - } - } - } - is IrGetClass -> { - val exprParent = parent.expr(e, callable) - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_getclassexpr( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 0, exprParent.enclosingStmt) - } - is IrTypeOperatorCall -> { - val exprParent = parent.expr(e, callable) - extractTypeOperatorCall( - e, - callable, - exprParent.parent, - exprParent.idx, - exprParent.enclosingStmt - ) - } - is IrVararg -> { - // There are lowered IR cases when the vararg expression is not within a call, - // such as - // val temp0 = [*expr]. - // This AST element can also occur as a collection literal in an annotation - // class, such as - // annotation class Ann(val strings: Array = []) - val exprParent = parent.expr(e, callable) - extractArrayCreation( - e, - e.type, - e.varargElementType, - true, - e, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - } - is IrGetObjectValue -> { - // For `object MyObject { ... }`, the .class has an - // automatically-generated `public static final MyObject INSTANCE` - // field that we are accessing here. - val exprParent = parent.expr(e, callable) - val c = getBoundSymbolOwner(e.symbol, e) ?: return - - val instance = - if (c.isCompanion) useCompanionObjectClassInstance(c) - else useObjectClassInstance(c) - - if (instance != null) { - val id = tw.getFreshIdLabel() - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_varaccess( - id, - type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) - - tw.writeVariableBinding(id, instance.id) - } - } - is IrFunctionReference -> { - extractFunctionReference(e, parent, callable) - } - is IrFunctionExpression -> { - /* - * Extract generated class: - * ``` - * class C : Any, kotlin.FunctionI { - * constructor() { super(); } - * fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... } - * } - * ``` - * or in case of big arity lambdas - * ``` - * class C : Any, kotlin.FunctionN { - * constructor() { super(); } - * fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... } - * fun invoke(vararg args: Any?): R { - * return invoke(args[0] as T0, args[1] as T1, ..., args[I] as TI) - * } - * } - * ``` - **/ - - val ids = getLocallyVisibleFunctionLabels(e.function) - val locId = tw.getLocation(e) - - val ext = e.function.extensionReceiverParameter - val parameters = - if (ext != null) { - listOf(ext) + e.function.valueParameters - } else { - e.function.valueParameters - } - - var types = parameters.map { it.type } - types += e.function.returnType - - val isBigArity = types.size > BuiltInFunctionArity.BIG_ARITY - if (isBigArity) { - implementFunctionNInvoke(e.function, ids, locId, parameters) - } else { - addModifiers(ids.function, "override") - } - - val exprParent = parent.expr(e, callable) - val idLambdaExpr = tw.getFreshIdLabel() - tw.writeExprs_lambdaexpr( - idLambdaExpr, - ids.type.javaResult.id, - exprParent.parent, - exprParent.idx - ) - tw.writeExprsKotlinType(idLambdaExpr, ids.type.kotlinResult.id) - extractExprContext(idLambdaExpr, locId, callable, exprParent.enclosingStmt) - tw.writeCallableBinding(idLambdaExpr, ids.constructor) - - // todo: fix hard coded block body of lambda - tw.writeLambdaKind(idLambdaExpr, 1) - - val fnInterfaceType = getFunctionalInterfaceType(types) - if (fnInterfaceType == null) { - logger.warnElement( - "Cannot find functional interface type for function expression", - e - ) - } else { - val id = - extractGeneratedClass( - e - .function, // We're adding this function as a member, and - // changing its name to `invoke` to implement - // `kotlin.FunctionX<,,,>.invoke(,,)` - listOf(pluginContext.irBuiltIns.anyType, fnInterfaceType) - ) - - extractTypeAccessRecursive( - fnInterfaceType, - locId, - idLambdaExpr, - -3, - callable, - exprParent.enclosingStmt - ) - - tw.writeIsAnonymClass(id, idLambdaExpr) - } - } - is IrClassReference -> { - val exprParent = parent.expr(e, callable) - extractClassReference( - e, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - } - is IrPropertyReference -> { - extractPropertyReference( - "property reference", - e, - e.getter, - e.setter, - e.field, - parent, - callable - ) - } - is IrLocalDelegatedPropertyReference -> { - extractPropertyReference( - "local delegated property reference", - e, - e.getter, - e.setter, - null, - parent, - callable - ) - } - */ - else -> { - logger.errorElement("Unrecognised KtExpression: " + e.javaClass, e) - } - } - return - } - } - - /* - OLD: KE1 - private fun extractBlock( - e: IrContainerExpression, - statements: List, - parent: StmtExprParent, - callable: Label - ) { - val stmtParent = parent.stmt(e, callable) - extractBlock(e, statements, stmtParent.parent, stmtParent.idx, callable) - } - - private fun extractBlock( - e: IrElement, - statements: List, - parent: Label, - idx: Int, - callable: Label - ) { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - tw.writeStmts_block(id, parent, idx, callable) - tw.writeHasLocation(id, locId) - statements.forEachIndexed { i, s -> extractStatement(s, callable, id, i) } - } - - private inline fun getBoundSymbolOwner( - symbol: IrBindableSymbol, - e: IrExpression - ): B? { - if (symbol.isBound) { - return symbol.owner - } - - logger.errorElement("Unbound symbol found, skipping extraction of expression", e) - return null - } - - private fun extractSuperAccess( - irType: IrType, - callable: Label, - parent: Label, - idx: Int, - enclosingStmt: Label, - locId: Label - ) = - tw.getFreshIdLabel().also { - val type = useType(irType) - tw.writeExprs_superaccess(it, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(it, type.kotlinResult.id) - extractExprContext(it, locId, callable, enclosingStmt) - extractTypeAccessRecursive(irType, locId, it, 0) - } - - private fun extractThisAccess( - type: TypeResults, - callable: Label, - parent: Label, - idx: Int, - enclosingStmt: Label, - locId: Label - ) = - tw.getFreshIdLabel().also { - tw.writeExprs_thisaccess(it, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(it, type.kotlinResult.id) - extractExprContext(it, locId, callable, enclosingStmt) - } - - private fun extractThisAccess( - irType: IrType, - callable: Label, - parent: Label, - idx: Int, - enclosingStmt: Label, - locId: Label - ) = extractThisAccess(useType(irType), callable, parent, idx, enclosingStmt, locId) - - private fun extractThisAccess( - e: IrGetValue, - thisParamParent: IrDeclarationParent, - exprParent: ExprParent, - callable: Label - ) { - val containingDeclaration = declarationStack.peek().first - val locId = tw.getLocation(e) - - if ( - containingDeclaration.shouldExtractAsStatic && - containingDeclaration.parentClassOrNull?.isNonCompanionObject == true - ) { - // Use of `this` in a non-companion object member that will be lowered to a static - // function -- replace with a reference - // to the corresponding static object instance. - val instanceField = useObjectClassInstance(containingDeclaration.parentAsClass) - extractVariableAccess( - instanceField.id, - e.type, - locId, - exprParent.parent, - exprParent.idx, - callable, - exprParent.enclosingStmt - ) - .also { varAccessId -> - 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 replacementParamId = - tw.getLabelFor( - getValueParameterLabel(paramDeclarerId, replaceWithParamIdx) - ) - 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) { - extractTypeAccessRecursive( - parent.typeWith(listOf()), - locId, - id, - 0, - callable, - exprParent.enclosingStmt - ) - } - - val owner = e.symbol.owner - when (val ownerParent = owner.parent) { - is IrFunction -> { - if ( - ownerParent.dispatchReceiverParameter == owner && - ownerParent.extensionReceiverParameter != null - ) { - - val ownerParent2 = ownerParent.parent - if (ownerParent2 is IrClass) { - extractTypeAccess(ownerParent2) - } else { - logger.errorElement("Unhandled qualifier for this", e) - } - } - } - is IrClass -> { - if (ownerParent.thisReceiver == owner) { - extractTypeAccess(ownerParent) - } - } - else -> { - logger.errorElement( - "Unexpected owner parent for this access: " + ownerParent.javaClass, - e - ) - } - } - } - } - - private fun extractVariableAccess( - variable: Label?, - type: TypeResults, - locId: Label, - parent: Label, - idx: Int, - callable: Label, - enclosingStmt: Label - ) = - tw.getFreshIdLabel().also { - tw.writeExprs_varaccess(it, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(it, type.kotlinResult.id) - extractExprContext(it, locId, callable, enclosingStmt) - - if (variable != null) { - tw.writeVariableBinding(it, variable) - } - } - - private fun extractVariableAccess( - variable: Label?, - irType: IrType, - locId: Label, - parent: Label, - idx: Int, - callable: Label, - enclosingStmt: Label - ) = - extractVariableAccess( - variable, - useType(irType), - locId, - parent, - idx, - callable, - enclosingStmt - ) - - private fun extractLoop( - loop: IrLoop, - bodyIdx: Int?, - stmtExprParent: StmtExprParent, - callable: Label, - getId: (Label, Int) -> Label - ): Label { - val stmtParent = stmtExprParent.stmt(loop, callable) - val locId = tw.getLocation(loop) - - val idx: Int - val parent: Label - - val label = loop.label - if (label != null) { - val labeledStmt = tw.getFreshIdLabel() - tw.writeStmts_labeledstmt(labeledStmt, stmtParent.parent, stmtParent.idx, callable) - tw.writeHasLocation(labeledStmt, locId) - - tw.writeNamestrings(label, "", labeledStmt) - idx = 0 - parent = labeledStmt - } else { - idx = stmtParent.idx - parent = stmtParent.parent - } - - val id = getId(parent, idx) - tw.writeHasLocation(id, locId) - - val body = loop.body - if (body != null && bodyIdx != null) { - extractExpressionStmt(body, callable, id, bodyIdx) - } - - return id - } - - private fun extractLoopWithCondition( - loop: IrLoop, - stmtExprParent: StmtExprParent, - callable: Label - ) { - val id = - extractLoop(loop, 1, stmtExprParent, callable) { parent, idx -> - if (loop is IrWhileLoop) { - tw.getFreshIdLabel().also { - tw.writeStmts_whilestmt(it, parent, idx, callable) - } - } else { - tw.getFreshIdLabel().also { - tw.writeStmts_dostmt(it, parent, idx, callable) - } - } - } - extractExpressionExpr(loop.condition, callable, id, 0, id) - } - - private fun exprIdOrFresh(id: Label?) = - id?.cast() ?: tw.getFreshIdLabel() - - private fun extractClassReference( - e: IrClassReference, - parent: Label, - idx: Int, - enclosingCallable: Label?, - enclosingStmt: Label?, - overrideId: Label? = null, - typeAccessOverrideId: Label? = null, - useJavaLangClassType: Boolean = false - ) = - exprIdOrFresh(overrideId).also { id -> - val locId = tw.getLocation(e) - val jlcType = - if (useJavaLangClassType) this.javaLangClass?.let { it.typeWith() } else null - val type = useType(jlcType ?: e.type) - tw.writeExprs_typeliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - - extractTypeAccessRecursive( - e.classType, - locId, - id, - 0, - enclosingCallable, - enclosingStmt, - overrideId = typeAccessOverrideId - ) - } - - private fun extractEnumValue( - e: IrGetEnumValue, - parent: Label, - idx: Int, - enclosingCallable: Label?, - enclosingStmt: Label?, - extractTypeAccess: Boolean = true, - overrideId: Label? = null - ) = - exprIdOrFresh(overrideId).also { id -> - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_varaccess(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - - getBoundSymbolOwner(e.symbol, e)?.let { owner -> - val vId = useEnumEntry(owner) - tw.writeVariableBinding(id, vId) - - if (extractTypeAccess) - extractStaticTypeAccessQualifier( - owner, - id, - locId, - enclosingCallable, - enclosingStmt - ) - } - } - - private fun escapeCharForQuotedLiteral(c: Char) = - when (c) { - '\r' -> "\\r" - '\n' -> "\\n" - '\t' -> "\\t" - '\\' -> "\\\\" - '"' -> "\\\"" - else -> c.toString() - } - - // Render a string literal as it might occur in Kotlin source. Note this is a reasonable guess; - // the real source - // could use other escape sequences to describe the same String. Importantly, this is the same - // guess the Java - // extractor makes regarding string literals occurring within annotations, which we need to - // coincide with to ensure - // database consistency. - private fun toQuotedLiteral(s: String) = - s.toCharArray().joinToString(separator = "", prefix = "\"", postfix = "\"") { c -> - escapeCharForQuotedLiteral(c) - } - */ - - private fun extractConstant( - e: KtConstantExpression, - enclosingCallable: Label, // OLD: KE1: ?, - // TODO: Pass ExprParent rather than these 3? - parent: Label, - idx: Int, - enclosingStmt: Label, // OLD: KE1: ?, - /* - OLD: KE1 - overrideId: Label? = null - */ - ): Label? { - val text = e.text - if (text == null) { - TODO() - } - - val elementType = e.node.elementType - when (elementType) { - KtNodeTypes.INTEGER_CONSTANT -> { - val t = e.expressionType - val i = parseNumericLiteral(text, elementType) - println("=== parsed") - println(text) - println(i) - println(i?.javaClass) - when { - i == null -> { - TODO() - } - - t == null -> { - TODO() - } - - t.isIntType || t.isShortType || t.isByteType -> { - extractConstantInteger( - text, - t, - i, - tw.getLocation(e), - parent, - idx, - enclosingCallable, - enclosingStmt, - /* - OLD: KE1 - overrideId = overrideId - */ - ) - } - - t.isLongType -> { - // OLD: KE1: Was: exprIdOrFresh(overrideId).also { id -> - tw.getFreshIdLabel().also { id -> - if (t == null) { - TODO() - } - val type = useType(t) - val locId = tw.getLocation(e) - tw.writeExprs_longliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - /* - OLD: KE1 - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - */ - tw.writeNamestrings(text, i.toString(), id) - } - } - - else -> { - TODO() - } - } - } - - else -> { - TODO() - } - } - - // TODO: Wrong - return null - /* - OLD: KE1 - val v = e.value - return when { - v is Float -> { - exprIdOrFresh(overrideId).also { id -> - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_floatingpointliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - tw.writeNamestrings(v.toString(), v.toString(), id) - } - } - v is Double -> { - exprIdOrFresh(overrideId).also { id -> - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_doubleliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - tw.writeNamestrings(v.toString(), v.toString(), id) - } - } - v is Boolean -> { - exprIdOrFresh(overrideId).also { id -> - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_booleanliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - tw.writeNamestrings(v.toString(), v.toString(), id) - } - } - v is Char -> { - exprIdOrFresh(overrideId).also { id -> - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_characterliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - tw.writeNamestrings(v.toString(), v.toString(), id) - } - } - v is String -> { - exprIdOrFresh(overrideId).also { id -> - val type = useType(e.type) - val locId = tw.getLocation(e) - tw.writeExprs_stringliteral(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, enclosingCallable, enclosingStmt) - tw.writeNamestrings(toQuotedLiteral(v.toString()), v.toString(), id) - } - } - v == null -> { - extractNull( - e.type, - tw.getLocation(e), - parent, - idx, - enclosingCallable, - enclosingStmt, - overrideId = overrideId - ) - } - else -> { - null.also { logger.errorElement("Unrecognised IrConst: " + v.javaClass, e) } - } - } - */ - } - /* OLD: KE1 private fun IrValueParameter.isExtensionReceiver(): Boolean { @@ -8361,435 +6571,6 @@ OLD: KE1 } } - private fun extractArrayCreationWithInitializer( - parent: Label, - arraySize: Int, - locId: Label, - enclosingCallable: Label, - enclosingStmt: Label - ): Label { - - val arrayCreationId = tw.getFreshIdLabel() - val arrayType = - pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType) - val at = useType(arrayType) - tw.writeExprs_arraycreationexpr(arrayCreationId, at.javaResult.id, parent, 0) - tw.writeExprsKotlinType(arrayCreationId, at.kotlinResult.id) - extractExprContext(arrayCreationId, locId, enclosingCallable, enclosingStmt) - - extractTypeAccessRecursive( - pluginContext.irBuiltIns.anyNType, - locId, - arrayCreationId, - -1, - enclosingCallable, - enclosingStmt - ) - - val initId = tw.getFreshIdLabel() - tw.writeExprs_arrayinit(initId, at.javaResult.id, arrayCreationId, -2) - tw.writeExprsKotlinType(initId, at.kotlinResult.id) - extractExprContext(initId, locId, enclosingCallable, enclosingStmt) - - extractConstantInteger( - arraySize, - locId, - arrayCreationId, - 0, - enclosingCallable, - enclosingStmt - ) - - return initId - } - - private fun extractTypeOperatorCall( - e: IrTypeOperatorCall, - callable: Label, - parent: Label, - idx: Int, - enclosingStmt: Label - ) { - with("type operator call", e) { - when (e.operator) { - IrTypeOperator.CAST -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_castexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) - } - IrTypeOperator.IMPLICIT_CAST -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_implicitcastexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) - } - IrTypeOperator.IMPLICIT_NOTNULL -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_implicitnotnullexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) - } - IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_implicitcoerciontounitexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) - } - IrTypeOperator.SAFE_CAST -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_safecastexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) - } - IrTypeOperator.INSTANCEOF -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_instanceofexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 0, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 1, callable, enclosingStmt) - } - IrTypeOperator.NOT_INSTANCEOF -> { - val id = tw.getFreshIdLabel() - val locId = tw.getLocation(e) - val type = useType(e.type) - tw.writeExprs_notinstanceofexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractExpressionExpr(e.argument, callable, id, 0, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 1, callable, enclosingStmt) - } - IrTypeOperator.SAM_CONVERSION -> { - - /* - The following Kotlin code - - ``` - fun interface IntPredicate { - fun accept(i: Int): Boolean - } - - val x = IntPredicate { it % 2 == 0 } - ``` - - is extracted as - - ``` - interface IntPredicate { - Boolean accept(Integer i); - } - class extends Object implements IntPredicate { - Function1 ; - public (Function1 ) { this. = ; } - public override Boolean accept(Integer i) { return .invoke(i); } - } - - IntPredicate x = (IntPredicate)new (...); - ``` - */ - - val st = e.argument.type as? IrSimpleType - if (st == null) { - logger.errorElement("Expected to find a simple type in SAM conversion.", e) - return - } - - fun IrSimpleType.isKProperty() = - classFqName?.asString()?.startsWith("kotlin.reflect.KProperty") == true - - if ( - !st.isFunctionOrKFunction() && - !st.isSuspendFunctionOrKFunction() && - !st.isKProperty() - ) { - logger.errorElement( - "Expected to find expression with function type in SAM conversion.", - e - ) - return - } - - // Either Function1, ... Function22 or FunctionN type, but not Function23 or - // above. - val functionType = getFunctionalInterfaceTypeWithTypeArgs(st.arguments) - if (functionType == null) { - logger.errorElement("Cannot find functional interface.", e) - return - } - - val invokeMethod = - functionType.classOrNull?.owner?.declarations?.findSubType { - it.name.asString() == OperatorNameConventions.INVOKE.asString() - } - if (invokeMethod == null) { - logger.errorElement( - "Couldn't find `invoke` method on functional interface.", - e - ) - return - } - - val typeOwner = e.typeOperand.classifierOrFail.owner - if (typeOwner !is IrClass) { - logger.errorElement( - "Expected to find SAM conversion to IrClass. Found '${typeOwner.javaClass}' instead. Can't implement SAM interface.", - e - ) - return - } - val samMember = - typeOwner.declarations.findSubType { - it is IrOverridableMember && it.modality == Modality.ABSTRACT - } - if (samMember == null) { - logger.errorElement( - "Couldn't find SAM member in type '${typeOwner.kotlinFqName.asString()}'. Can't implement SAM interface.", - e - ) - return - } - - val javaResult = TypeResult(tw.getFreshIdLabel(), "", "") - val kotlinResult = TypeResult(tw.getFreshIdLabel(), "", "") - tw.writeKt_notnull_types(kotlinResult.id, javaResult.id) - val ids = - LocallyVisibleFunctionLabels( - TypeResults(javaResult, kotlinResult), - constructor = tw.getFreshIdLabel(), - constructorBlock = tw.getFreshIdLabel(), - function = tw.getFreshIdLabel() - ) - - val locId = tw.getLocation(e) - val helper = GeneratedClassHelper(locId, ids) - - val declarationParent = peekDeclStackAsDeclarationParent(e) ?: return - val classId = - extractGeneratedClass( - ids, - listOf(pluginContext.irBuiltIns.anyType, e.typeOperand), - locId, - e, - declarationParent - ) - - // add field - val fieldId = tw.getFreshIdLabel() - extractField( - fieldId, - "", - functionType, - classId, - locId, - DescriptorVisibilities.PRIVATE, - e, - isExternalDeclaration = false, - isFinal = true, - isStatic = false - ) - - // adjust constructor - helper.extractParameterToFieldAssignmentInConstructor( - "", - functionType, - fieldId, - 0, - 1 - ) - - // add implementation function - val classTypeArgs = (e.type as? IrSimpleType)?.arguments - val typeSub = - classTypeArgs?.let { makeGenericSubstitutionFunction(typeOwner, it) } - - fun trySub(t: IrType, context: TypeContext) = - if (typeSub == null) t else typeSub(t, context, pluginContext) - - // Force extraction of this function even if this is a fake override -- - // This happens in the case where a functional interface inherits its only - // abstract member, - // which usually we wouldn't extract, but in this case we're effectively using - // it as a template - // for the real function we're extracting that will implement this interface, - // and it serves fine - // for that purpose. By contrast if we looked through the fake to the underlying - // abstract method - // we would need to compose generic type substitutions -- for example, if we're - // implementing - // T UnaryOperator.apply(T t) here, we would need to compose substitutions so - // we can implement - // the real underlying R Function.apply(T t). - forceExtractFunction( - samMember, - classId, - extractBody = false, - extractMethodAndParameterTypeAccesses = true, - extractAnnotations = false, - typeSub, - classTypeArgs, - overriddenAttributes = - OverriddenFunctionAttributes( - id = ids.function, - sourceLoc = tw.getLocation(e), - modality = Modality.FINAL - ) - ) - - addModifiers(ids.function, "override") - if (st.isSuspendFunctionOrKFunction()) { - addModifiers(ids.function, "suspend") - } - - // body - val blockId = extractBlockBody(ids.function, locId) - - // return stmt - val returnId = tw.getFreshIdLabel() - tw.writeStmts_returnstmt(returnId, blockId, 0, ids.function) - tw.writeHasLocation(returnId, locId) - - // .invoke(vp0, cp1, vp2, vp3, ...) or - // .invoke(new Object[x]{vp0, vp1, vp2, ...}) - - // Call to original `invoke`: - val callId = tw.getFreshIdLabel() - val callType = useType(trySub(samMember.returnType, TypeContext.RETURN)) - tw.writeExprs_methodaccess(callId, callType.javaResult.id, returnId, 0) - tw.writeExprsKotlinType(callId, callType.kotlinResult.id) - extractExprContext(callId, locId, ids.function, returnId) - val calledMethodId = useFunction(invokeMethod, functionType.arguments) - if (calledMethodId == null) { - logger.errorElement("Cannot get ID for called method", invokeMethod) - } else { - tw.writeCallableBinding(callId, calledMethodId) - } - - // access - val lhsId = tw.getFreshIdLabel() - val lhsType = useType(functionType) - tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, callId, -1) - tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id) - extractExprContext(lhsId, locId, ids.function, returnId) - tw.writeVariableBinding(lhsId, fieldId) - - val parameters = mutableListOf() - val extParam = samMember.extensionReceiverParameter - if (extParam != null) { - parameters.add(extParam) - } - parameters.addAll(samMember.valueParameters) - - fun extractArgument( - p: IrValueParameter, - idx: Int, - parent: Label - ) { - val argsAccessId = tw.getFreshIdLabel() - val paramType = useType(trySub(p.type, TypeContext.OTHER)) - tw.writeExprs_varaccess(argsAccessId, paramType.javaResult.id, parent, idx) - tw.writeExprsKotlinType(argsAccessId, paramType.kotlinResult.id) - extractExprContext(argsAccessId, locId, ids.function, returnId) - tw.writeVariableBinding(argsAccessId, useValueParameter(p, ids.function)) - } - - val isBigArity = st.arguments.size > BuiltInFunctionArity.BIG_ARITY - val argParent = - if (isBigArity) { - // .invoke(new Object[x]{vp0, vp1, vp2, ...}) - extractArrayCreationWithInitializer( - callId, - parameters.size, - locId, - ids.function, - returnId - ) - } else { - // .invoke(vp0, cp1, vp2, vp3, ...) or - callId - } - - for ((parameterIdx, vp) in parameters.withIndex()) { - extractArgument(vp, parameterIdx, argParent) - } - - val id = tw.getFreshIdLabel() - val type = useType(e.typeOperand) - tw.writeExprs_castexpr(id, type.javaResult.id, parent, idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, enclosingStmt) - extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) - - val idNewexpr = - extractNewExpr( - ids.constructor, - ids.type, - locId, - id, - 1, - callable, - enclosingStmt - ) - - tw.writeIsAnonymClass( - ids.type.javaResult.id.cast(), - idNewexpr - ) - - extractTypeAccessRecursive( - e.typeOperand, - locId, - idNewexpr, - -3, - callable, - enclosingStmt - ) - - extractExpressionExpr(e.argument, callable, idNewexpr, 0, enclosingStmt) - } - else -> { - logger.errorElement( - "Unrecognised IrTypeOperatorCall for ${e.operator}: " + e.render(), - e - ) - } - } - } - } - - private fun extractBreakContinue(e: IrBreakContinue, id: Label) { - with("break/continue", e) { - val locId = tw.getLocation(e) - tw.writeHasLocation(id, locId) - val label = e.label - if (label != null) { - tw.writeNamestrings(label, "", id) - } - } - } - private val IrType.isAnonymous: Boolean get() = ((this as? IrSimpleType)?.classifier?.owner as? IrClass)?.isAnonymousObject ?: false diff --git a/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt b/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt new file mode 100644 index 00000000000..7f26ca82b35 --- /dev/null +++ b/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt @@ -0,0 +1,2231 @@ +package com.github.codeql + +import com.github.codeql.KotlinFileExtractor.StmtExprParent +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.parsing.parseNumericLiteral +import org.jetbrains.kotlin.psi.* + +context(KaSession) +private fun KotlinFileExtractor.extractExpressionBody(e: KtExpression, callable: Label) { + with("expression body", e) { + val locId = tw.getLocation(e) + extractExpressionBody(callable, locId).also { returnId -> + extractExpression(e, callable, ExprParent(returnId, 0, returnId)) + } + } +} + +// TODO: Inline this? It used to be public +private fun KotlinFileExtractor.extractExpressionBody( + callable: Label, + locId: Label +): Label { + val blockId = extractBlockBody(callable, locId) + return tw.getFreshIdLabel().also { returnId -> + tw.writeStmts_returnstmt(returnId, blockId, 0, callable) + tw.writeHasLocation(returnId, locId) + } +} + +context(KaSession) +fun KotlinFileExtractor.extractBody(b: KtExpression, callable: Label) { + with("body", b) { + when (b) { + is KtBlockExpression -> extractBlockBody(b, callable) + /* + OLD: KE1 + is IrSyntheticBody -> extractSyntheticBody(b, callable) + */ + else -> extractExpressionBody(b, callable) + } + } +} + +// TODO: Can this be inlined? +private fun KotlinFileExtractor.extractBlockBody(callable: Label, locId: Label) = + tw.getFreshIdLabel().also { + tw.writeStmts_block(it, callable, 0, callable) + tw.writeHasLocation(it, locId) + } + +context(KaSession) +private fun KotlinFileExtractor.extractBlockBody(b: KtBlockExpression, callable: Label) { + with("block body", b) { + extractBlockBody(callable, tw.getLocation(b)).also { + for ((sIdx, stmt) in b.statements.withIndex()) { + extractExpression(stmt, callable, StmtParent(it, sIdx)) + } + } + } +} + +/* +OLD: KE1 + private fun extractSyntheticBody(b: IrSyntheticBody, callable: Label) { + with("synthetic body", b) { + val kind = b.kind + when { + kind == IrSyntheticBodyKind.ENUM_VALUES -> tw.writeKtSyntheticBody(callable, 1) + kind == IrSyntheticBodyKind.ENUM_VALUEOF -> tw.writeKtSyntheticBody(callable, 2) + kind == kind_ENUM_ENTRIES -> tw.writeKtSyntheticBody(callable, 3) + else -> { + logger.errorElement("Unhandled synthetic body kind " + kind, b) + } + } + } + } +*/ + +/*private*/ fun KotlinFileExtractor.extractExpressionStmt( + locId: Label, + parent: Label, + idx: Int, + callable: Label +) = + tw.getFreshIdLabel().also { + tw.writeStmts_exprstmt(it, parent, idx, callable) + tw.writeHasLocation(it, locId) + } + +/* +OLD: KE1 + private fun extractExpressionStmt( + e: IrExpression, + callable: Label, + parent: Label, + idx: Int + ) { + extractExpression(e, callable, StmtParent(parent, idx)) + } + + fun extractExpressionExpr( + e: IrExpression, + callable: Label, + parent: Label, + idx: Int, + enclosingStmt: Label + ) { + extractExpression(e, callable, ExprParent(parent, idx, enclosingStmt)) + } +*/ + +private fun KotlinFileExtractor.extractExprContext( + id: Label, + locId: Label, + callable: Label?, + enclosingStmt: Label? +) { + tw.writeHasLocation(id, locId) + callable?.let { tw.writeCallableEnclosingExpr(id, it) } + enclosingStmt?.let { tw.writeStatementEnclosingExpr(id, it) } +} + +/* +OLD: KE1 + 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 KotlinFileExtractor.extractConstantInteger( + text: String, + t: KaType, + v: Number, + locId: Label, + parent: Label, + idx: Int, + callable: Label?, + enclosingStmt: Label?, + /* + OLD: KE1 + overrideId: Label? = null + */ +) = + // OLD: KE1: Was: exprIdOrFresh(overrideId).also { + tw.getFreshIdLabel().also { + val type = useType(t) + tw.writeExprs_integerliteral(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + tw.writeNamestrings(text, v.toString(), it) + extractExprContext(it, locId, callable, enclosingStmt) + } + +/* +OLD: KE1 + private fun extractNull( + t: IrType, + locId: Label, + parent: Label, + idx: Int, + callable: Label?, + enclosingStmt: Label?, + overrideId: Label? = null + ) = + exprIdOrFresh(overrideId).also { + val type = useType(t) + tw.writeExprs_nullliteral(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + 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) + } +*/ + +context(KaSession) +private fun KotlinFileExtractor.extractExpression( + e: KtExpression, + callable: Label, + parent: StmtExprParent +) { + with("expression", e) { + when (e) { + /* + OLD: KE1 + is IrDelegatingConstructorCall -> { + val stmtParent = parent.stmt(e, callable) + + val irCallable = declarationStack.peek().first + + val delegatingClass = e.symbol.owner.parent + val currentClass = irCallable.parent + + if (delegatingClass !is IrClass) { + logger.warnElement( + "Delegating class isn't a class: " + delegatingClass.javaClass, + e + ) + } + if (currentClass !is IrClass) { + logger.warnElement( + "Current class isn't a class: " + currentClass.javaClass, + e + ) + } + + val id: Label + if (delegatingClass != currentClass) { + id = tw.getFreshIdLabel() + tw.writeStmts_superconstructorinvocationstmt( + id, + stmtParent.parent, + stmtParent.idx, + callable + ) + } else { + id = tw.getFreshIdLabel() + tw.writeStmts_constructorinvocationstmt( + id, + stmtParent.parent, + stmtParent.idx, + callable + ) + } + + val locId = tw.getLocation(e) + val methodId = useFunction(e.symbol.owner) + if (methodId == null) { + logger.errorElement("Cannot get ID for delegating constructor", e) + } else { + tw.writeCallableBinding(id.cast(), methodId) + } + + tw.writeHasLocation(id, locId) + extractCallValueArguments(id, e, id, callable, 0) + val dr = e.dispatchReceiver + if (dr != null) { + extractExpressionExpr(dr, callable, id, -1, id) + } + + // todo: type arguments at index -2, -3, ... + } + is IrThrow -> { + val stmtParent = parent.stmt(e, callable) + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + tw.writeStmts_throwstmt(id, stmtParent.parent, stmtParent.idx, callable) + tw.writeHasLocation(id, locId) + extractExpressionExpr(e.value, callable, id, 0, id) + } + is IrBreak -> { + val stmtParent = parent.stmt(e, callable) + val id = tw.getFreshIdLabel() + tw.writeStmts_breakstmt(id, stmtParent.parent, stmtParent.idx, callable) + extractBreakContinue(e, id) + } + is IrContinue -> { + val stmtParent = parent.stmt(e, callable) + val id = tw.getFreshIdLabel() + tw.writeStmts_continuestmt(id, stmtParent.parent, stmtParent.idx, callable) + extractBreakContinue(e, id) + } + */ + is KtReturnExpression -> { + val stmtParent = parent.stmt(e, callable) + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + tw.writeStmts_returnstmt(id, stmtParent.parent, stmtParent.idx, callable) + tw.writeHasLocation(id, locId) + val returned = e.getReturnedExpression() + if (returned != null) { + extractExpression(returned, callable, ExprParent(id, 0, id)) + } + // TODO: e.getLabeledExpression() + } + /* + OLD: KE1 + is IrTry -> { + val stmtParent = parent.stmt(e, callable) + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + tw.writeStmts_trystmt(id, stmtParent.parent, stmtParent.idx, callable) + tw.writeHasLocation(id, locId) + extractExpressionStmt(e.tryResult, callable, id, -1) + val finallyStmt = e.finallyExpression + if (finallyStmt != null) { + extractExpressionStmt(finallyStmt, callable, id, -2) + } + for ((catchIdx, catchClause) in e.catches.withIndex()) { + val catchId = tw.getFreshIdLabel() + tw.writeStmts_catchclause(catchId, id, catchIdx, callable) + val catchLocId = tw.getLocation(catchClause) + tw.writeHasLocation(catchId, catchLocId) + extractTypeAccessRecursive( + catchClause.catchParameter.type, + tw.getLocation(catchClause.catchParameter), + catchId, + -1, + callable, + catchId + ) + extractVariableExpr( + catchClause.catchParameter, + callable, + catchId, + 0, + catchId + ) + extractExpressionStmt(catchClause.result, callable, catchId, 1) + } + } + is IrContainerExpression -> { + if ( + !tryExtractArrayUpdate(e, callable, parent) && + !tryExtractForLoop(e, callable, parent) + ) { + + extractBlock(e, e.statements, parent, callable) + } + } + is IrWhileLoop -> { + extractLoopWithCondition(e, parent, callable) + } + is IrDoWhileLoop -> { + extractLoopWithCondition(e, parent, callable) + } + is IrInstanceInitializerCall -> { + val irConstructor = declarationStack.peek().first as? IrConstructor + if (irConstructor == null) { + logger.errorElement("IrInstanceInitializerCall outside constructor", e) + return + } + if (needsObinitFunction(irConstructor.parentAsClass)) { + val exprParent = parent.expr(e, callable) + val id = tw.getFreshIdLabel() + val type = useType(pluginContext.irBuiltIns.unitType) + val locId = tw.getLocation(e) + val parentClass = irConstructor.parentAsClass + val parentId = useDeclarationParentOf(irConstructor, false, null, true) + if (parentId == null) { + logger.errorElement("Cannot get parent ID for obinit", e) + return + } + val methodLabel = getObinitLabel(parentClass, parentId) + val methodId = tw.getLabelFor(methodLabel) + tw.writeExprs_methodaccess( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + tw.writeCallableBinding(id, methodId) + } else { + val stmtParent = parent.stmt(e, callable) + extractInstanceInitializerBlock(stmtParent, irConstructor) + } + } + is IrConstructorCall -> { + val exprParent = parent.expr(e, callable) + extractConstructorCall( + e, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + } + is IrEnumConstructorCall -> { + val exprParent = parent.expr(e, callable) + extractConstructorCall( + e, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + } + is IrCall -> { + extractCall(e, callable, parent) + } + is IrStringConcatenation -> { + val exprParent = parent.expr(e, callable) + val id = tw.getFreshIdLabel() + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_stringtemplateexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + e.arguments.forEachIndexed { i, a -> + extractExpressionExpr(a, callable, id, i, exprParent.enclosingStmt) + } + } + */ + is KtConstantExpression -> { + val exprParent = parent.expr(e, callable) + extractConstant( + e, + callable, + exprParent.parent, + exprParent.idx, + exprParent.enclosingStmt + ) + } + /* + OLD: KE1 + is IrGetValue -> { + val exprParent = parent.expr(e, callable) + val owner = e.symbol.owner + if ( + owner is IrValueParameter && + owner.index == -1 && + !owner.isExtensionReceiver() + ) { + 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 + extractVariableAccess( + useValueDeclaration(owner), + extractType, + tw.getLocation(e), + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + } + } + is IrGetField -> { + val exprParent = parent.expr(e, callable) + val owner = tryReplaceAndroidSyntheticField(e.symbol.owner) + val locId = tw.getLocation(e) + val fieldType = + if (isAnnotationClassField(owner)) kClassToJavaClass(e.type) else e.type + extractVariableAccess( + useField(owner), + fieldType, + locId, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + .also { id -> + val receiver = e.receiver + if (receiver != null) { + extractExpressionExpr( + receiver, + callable, + id, + -1, + exprParent.enclosingStmt + ) + } else if (owner.isStatic) { + extractStaticTypeAccessQualifier( + owner, + id, + locId, + callable, + exprParent.enclosingStmt + ) + } + } + } + is IrGetEnumValue -> { + val exprParent = parent.expr(e, callable) + extractEnumValue( + e, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + } + is IrSetValue, + is IrSetField -> { + val exprParent = parent.expr(e, callable) + val id = tw.getFreshIdLabel() + val type = useType(e.type) + val rhsValue = + when (e) { + is IrSetValue -> e.value + is IrSetField -> e.value + else -> { + logger.errorElement("Unhandled IrSet* element.", e) + return + } + } + // The set operation's location as actually that of its LHS. Hence, the + // assignment spans the + // set op plus its RHS, while the varAccess takes its location from `e`. + val locId = tw.getLocation(e.startOffset, rhsValue.endOffset) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + + val lhsId = tw.getFreshIdLabel() + val lhsLocId = tw.getLocation(e) + extractExprContext(lhsId, lhsLocId, callable, exprParent.enclosingStmt) + + when (e) { + is IrSetValue -> { + // Check for a desugared in-place update operator, such as "v += e": + val inPlaceUpdateRhs = + getUpdateInPlaceRHS( + e.origin, + { it is IrGetValue && it.symbol.owner == e.symbol.owner }, + rhsValue + ) + if (inPlaceUpdateRhs != null) { + val origin = e.origin + if (origin == null) { + logger.errorElement("No origin for set-value", e) + return + } else { + val writeUpdateInPlaceExprFun = writeUpdateInPlaceExpr(origin) + if (writeUpdateInPlaceExprFun == null) { + logger.errorElement("Unexpected origin for set-value", e) + return + } + writeUpdateInPlaceExprFun( + tw, + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + } + } else { + tw.writeExprs_assignexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + } + + val lhsType = useType(e.symbol.owner.type) + tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, id, 0) + tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id) + val vId = useValueDeclaration(e.symbol.owner) + if (vId != null) { + tw.writeVariableBinding(lhsId, vId) + } + extractExpressionExpr( + inPlaceUpdateRhs ?: rhsValue, + callable, + id, + 1, + exprParent.enclosingStmt + ) + } + is IrSetField -> { + tw.writeExprs_assignexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + val realField = tryReplaceAndroidSyntheticField(e.symbol.owner) + val lhsType = useType(realField.type) + tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, id, 0) + tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id) + val vId = useField(realField) + tw.writeVariableBinding(lhsId, vId) + extractExpressionExpr( + e.value, + callable, + id, + 1, + exprParent.enclosingStmt + ) + + val receiver = e.receiver + if (receiver != null) { + extractExpressionExpr( + receiver, + callable, + lhsId, + -1, + exprParent.enclosingStmt + ) + } else if (realField.isStatic) { + extractStaticTypeAccessQualifier( + realField, + lhsId, + lhsLocId, + callable, + exprParent.enclosingStmt + ) + } + } + else -> { + logger.errorElement("Unhandled IrSet* element.", e) + } + } + } + is IrWhen -> { + val isAndAnd = e.origin == IrStatementOrigin.ANDAND + val isOrOr = e.origin == IrStatementOrigin.OROR + + if ( + (isAndAnd || isOrOr) && + e.branches.size == 2 && + (e.branches[1].condition as? IrConst<*>)?.value == true && + (e.branches[if (e.origin == IrStatementOrigin.ANDAND) 1 else 0].result + as? IrConst<*>) + ?.value == isOrOr + ) { + + // resugar binary logical operators: + + val exprParent = parent.expr(e, callable) + val type = useType(e.type) + + val id = + if (e.origin == IrStatementOrigin.ANDAND) { + val id = tw.getFreshIdLabel() + tw.writeExprs_andlogicalexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + id + } else { + val id = tw.getFreshIdLabel() + tw.writeExprs_orlogicalexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + id + } + val locId = tw.getLocation(e) + + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + + extractExpressionExpr( + e.branches[0].condition, + callable, + id, + 0, + exprParent.enclosingStmt + ) + + var rhsIdx = if (e.origin == IrStatementOrigin.ANDAND) 0 else 1 + extractExpressionExpr( + e.branches[rhsIdx].result, + callable, + id, + 1, + exprParent.enclosingStmt + ) + + return + } + + val exprParent = parent.expr(e, callable) + val id = tw.getFreshIdLabel() + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_whenexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + if (e.origin == IrStatementOrigin.IF) { + tw.writeWhen_if(id) + } + e.branches.forEachIndexed { i, b -> + val bId = tw.getFreshIdLabel() + val bLocId = tw.getLocation(b) + tw.writeStmts_whenbranch(bId, id, i, callable) + tw.writeHasLocation(bId, bLocId) + extractExpressionExpr(b.condition, callable, bId, 0, bId) + extractExpressionStmt(b.result, callable, bId, 1) + if (b is IrElseBranch) { + tw.writeWhen_branch_else(bId) + } + } + } + is IrGetClass -> { + val exprParent = parent.expr(e, callable) + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_getclassexpr( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 0, exprParent.enclosingStmt) + } + is IrTypeOperatorCall -> { + val exprParent = parent.expr(e, callable) + extractTypeOperatorCall( + e, + callable, + exprParent.parent, + exprParent.idx, + exprParent.enclosingStmt + ) + } + is IrVararg -> { + // There are lowered IR cases when the vararg expression is not within a call, + // such as + // val temp0 = [*expr]. + // This AST element can also occur as a collection literal in an annotation + // class, such as + // annotation class Ann(val strings: Array = []) + val exprParent = parent.expr(e, callable) + extractArrayCreation( + e, + e.type, + e.varargElementType, + true, + e, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + } + is IrGetObjectValue -> { + // For `object MyObject { ... }`, the .class has an + // automatically-generated `public static final MyObject INSTANCE` + // field that we are accessing here. + val exprParent = parent.expr(e, callable) + val c = getBoundSymbolOwner(e.symbol, e) ?: return + + val instance = + if (c.isCompanion) useCompanionObjectClassInstance(c) + else useObjectClassInstance(c) + + if (instance != null) { + val id = tw.getFreshIdLabel() + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_varaccess( + id, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, exprParent.enclosingStmt) + + tw.writeVariableBinding(id, instance.id) + } + } + is IrFunctionReference -> { + extractFunctionReference(e, parent, callable) + } + is IrFunctionExpression -> { + /* + * Extract generated class: + * ``` + * class C : Any, kotlin.FunctionI { + * constructor() { super(); } + * fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... } + * } + * ``` + * or in case of big arity lambdas + * ``` + * class C : Any, kotlin.FunctionN { + * constructor() { super(); } + * fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... } + * fun invoke(vararg args: Any?): R { + * return invoke(args[0] as T0, args[1] as T1, ..., args[I] as TI) + * } + * } + * ``` + **/ + + val ids = getLocallyVisibleFunctionLabels(e.function) + val locId = tw.getLocation(e) + + val ext = e.function.extensionReceiverParameter + val parameters = + if (ext != null) { + listOf(ext) + e.function.valueParameters + } else { + e.function.valueParameters + } + + var types = parameters.map { it.type } + types += e.function.returnType + + val isBigArity = types.size > BuiltInFunctionArity.BIG_ARITY + if (isBigArity) { + implementFunctionNInvoke(e.function, ids, locId, parameters) + } else { + addModifiers(ids.function, "override") + } + + val exprParent = parent.expr(e, callable) + val idLambdaExpr = tw.getFreshIdLabel() + tw.writeExprs_lambdaexpr( + idLambdaExpr, + ids.type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + tw.writeExprsKotlinType(idLambdaExpr, ids.type.kotlinResult.id) + extractExprContext(idLambdaExpr, locId, callable, exprParent.enclosingStmt) + tw.writeCallableBinding(idLambdaExpr, ids.constructor) + + // todo: fix hard coded block body of lambda + tw.writeLambdaKind(idLambdaExpr, 1) + + val fnInterfaceType = getFunctionalInterfaceType(types) + if (fnInterfaceType == null) { + logger.warnElement( + "Cannot find functional interface type for function expression", + e + ) + } else { + val id = + extractGeneratedClass( + e + .function, // We're adding this function as a member, and + // changing its name to `invoke` to implement + // `kotlin.FunctionX<,,,>.invoke(,,)` + listOf(pluginContext.irBuiltIns.anyType, fnInterfaceType) + ) + + extractTypeAccessRecursive( + fnInterfaceType, + locId, + idLambdaExpr, + -3, + callable, + exprParent.enclosingStmt + ) + + tw.writeIsAnonymClass(id, idLambdaExpr) + } + } + is IrClassReference -> { + val exprParent = parent.expr(e, callable) + extractClassReference( + e, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + } + is IrPropertyReference -> { + extractPropertyReference( + "property reference", + e, + e.getter, + e.setter, + e.field, + parent, + callable + ) + } + is IrLocalDelegatedPropertyReference -> { + extractPropertyReference( + "local delegated property reference", + e, + e.getter, + e.setter, + null, + parent, + callable + ) + } + */ + else -> { + logger.errorElement("Unrecognised KtExpression: " + e.javaClass, e) + } + } + return + } +} + +/* +OLD: KE1 + private fun extractBlock( + e: IrContainerExpression, + statements: List, + parent: StmtExprParent, + callable: Label + ) { + val stmtParent = parent.stmt(e, callable) + extractBlock(e, statements, stmtParent.parent, stmtParent.idx, callable) + } + + private fun extractBlock( + e: IrElement, + statements: List, + parent: Label, + idx: Int, + callable: Label + ) { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + tw.writeStmts_block(id, parent, idx, callable) + tw.writeHasLocation(id, locId) + statements.forEachIndexed { i, s -> extractStatement(s, callable, id, i) } + } + + private inline fun getBoundSymbolOwner( + symbol: IrBindableSymbol, + e: IrExpression + ): B? { + if (symbol.isBound) { + return symbol.owner + } + + logger.errorElement("Unbound symbol found, skipping extraction of expression", e) + return null + } + + private fun extractSuperAccess( + irType: IrType, + callable: Label, + parent: Label, + idx: Int, + enclosingStmt: Label, + locId: Label + ) = + tw.getFreshIdLabel().also { + val type = useType(irType) + tw.writeExprs_superaccess(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + extractTypeAccessRecursive(irType, locId, it, 0) + } + + private fun extractThisAccess( + type: TypeResults, + callable: Label, + parent: Label, + idx: Int, + enclosingStmt: Label, + locId: Label + ) = + tw.getFreshIdLabel().also { + tw.writeExprs_thisaccess(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + } + + private fun extractThisAccess( + irType: IrType, + callable: Label, + parent: Label, + idx: Int, + enclosingStmt: Label, + locId: Label + ) = extractThisAccess(useType(irType), callable, parent, idx, enclosingStmt, locId) + + private fun extractThisAccess( + e: IrGetValue, + thisParamParent: IrDeclarationParent, + exprParent: ExprParent, + callable: Label + ) { + val containingDeclaration = declarationStack.peek().first + val locId = tw.getLocation(e) + + if ( + containingDeclaration.shouldExtractAsStatic && + containingDeclaration.parentClassOrNull?.isNonCompanionObject == true + ) { + // Use of `this` in a non-companion object member that will be lowered to a static + // function -- replace with a reference + // to the corresponding static object instance. + val instanceField = useObjectClassInstance(containingDeclaration.parentAsClass) + extractVariableAccess( + instanceField.id, + e.type, + locId, + exprParent.parent, + exprParent.idx, + callable, + exprParent.enclosingStmt + ) + .also { varAccessId -> + 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 replacementParamId = + tw.getLabelFor( + getValueParameterLabel(paramDeclarerId, replaceWithParamIdx) + ) + 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) { + extractTypeAccessRecursive( + parent.typeWith(listOf()), + locId, + id, + 0, + callable, + exprParent.enclosingStmt + ) + } + + val owner = e.symbol.owner + when (val ownerParent = owner.parent) { + is IrFunction -> { + if ( + ownerParent.dispatchReceiverParameter == owner && + ownerParent.extensionReceiverParameter != null + ) { + + val ownerParent2 = ownerParent.parent + if (ownerParent2 is IrClass) { + extractTypeAccess(ownerParent2) + } else { + logger.errorElement("Unhandled qualifier for this", e) + } + } + } + is IrClass -> { + if (ownerParent.thisReceiver == owner) { + extractTypeAccess(ownerParent) + } + } + else -> { + logger.errorElement( + "Unexpected owner parent for this access: " + ownerParent.javaClass, + e + ) + } + } + } + } + + private fun extractVariableAccess( + variable: Label?, + type: TypeResults, + locId: Label, + parent: Label, + idx: Int, + callable: Label, + enclosingStmt: Label + ) = + tw.getFreshIdLabel().also { + tw.writeExprs_varaccess(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + + if (variable != null) { + tw.writeVariableBinding(it, variable) + } + } + + private fun extractVariableAccess( + variable: Label?, + irType: IrType, + locId: Label, + parent: Label, + idx: Int, + callable: Label, + enclosingStmt: Label + ) = + extractVariableAccess( + variable, + useType(irType), + locId, + parent, + idx, + callable, + enclosingStmt + ) + + private fun extractLoop( + loop: IrLoop, + bodyIdx: Int?, + stmtExprParent: StmtExprParent, + callable: Label, + getId: (Label, Int) -> Label + ): Label { + val stmtParent = stmtExprParent.stmt(loop, callable) + val locId = tw.getLocation(loop) + + val idx: Int + val parent: Label + + val label = loop.label + if (label != null) { + val labeledStmt = tw.getFreshIdLabel() + tw.writeStmts_labeledstmt(labeledStmt, stmtParent.parent, stmtParent.idx, callable) + tw.writeHasLocation(labeledStmt, locId) + + tw.writeNamestrings(label, "", labeledStmt) + idx = 0 + parent = labeledStmt + } else { + idx = stmtParent.idx + parent = stmtParent.parent + } + + val id = getId(parent, idx) + tw.writeHasLocation(id, locId) + + val body = loop.body + if (body != null && bodyIdx != null) { + extractExpressionStmt(body, callable, id, bodyIdx) + } + + return id + } + + private fun extractLoopWithCondition( + loop: IrLoop, + stmtExprParent: StmtExprParent, + callable: Label + ) { + val id = + extractLoop(loop, 1, stmtExprParent, callable) { parent, idx -> + if (loop is IrWhileLoop) { + tw.getFreshIdLabel().also { + tw.writeStmts_whilestmt(it, parent, idx, callable) + } + } else { + tw.getFreshIdLabel().also { + tw.writeStmts_dostmt(it, parent, idx, callable) + } + } + } + extractExpressionExpr(loop.condition, callable, id, 0, id) + } + + private fun exprIdOrFresh(id: Label?) = + id?.cast() ?: tw.getFreshIdLabel() + + private fun extractClassReference( + e: IrClassReference, + parent: Label, + idx: Int, + enclosingCallable: Label?, + enclosingStmt: Label?, + overrideId: Label? = null, + typeAccessOverrideId: Label? = null, + useJavaLangClassType: Boolean = false + ) = + exprIdOrFresh(overrideId).also { id -> + val locId = tw.getLocation(e) + val jlcType = + if (useJavaLangClassType) this.javaLangClass?.let { it.typeWith() } else null + val type = useType(jlcType ?: e.type) + tw.writeExprs_typeliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + + extractTypeAccessRecursive( + e.classType, + locId, + id, + 0, + enclosingCallable, + enclosingStmt, + overrideId = typeAccessOverrideId + ) + } + + private fun extractEnumValue( + e: IrGetEnumValue, + parent: Label, + idx: Int, + enclosingCallable: Label?, + enclosingStmt: Label?, + extractTypeAccess: Boolean = true, + overrideId: Label? = null + ) = + exprIdOrFresh(overrideId).also { id -> + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_varaccess(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + + getBoundSymbolOwner(e.symbol, e)?.let { owner -> + val vId = useEnumEntry(owner) + tw.writeVariableBinding(id, vId) + + if (extractTypeAccess) + extractStaticTypeAccessQualifier( + owner, + id, + locId, + enclosingCallable, + enclosingStmt + ) + } + } + + private fun escapeCharForQuotedLiteral(c: Char) = + when (c) { + '\r' -> "\\r" + '\n' -> "\\n" + '\t' -> "\\t" + '\\' -> "\\\\" + '"' -> "\\\"" + else -> c.toString() + } + + // Render a string literal as it might occur in Kotlin source. Note this is a reasonable guess; + // the real source + // could use other escape sequences to describe the same String. Importantly, this is the same + // guess the Java + // extractor makes regarding string literals occurring within annotations, which we need to + // coincide with to ensure + // database consistency. + private fun toQuotedLiteral(s: String) = + s.toCharArray().joinToString(separator = "", prefix = "\"", postfix = "\"") { c -> + escapeCharForQuotedLiteral(c) + } +*/ + +context(KaSession) +private fun KotlinFileExtractor.extractConstant( + e: KtConstantExpression, + enclosingCallable: Label, // OLD: KE1: ?, + // TODO: Pass ExprParent rather than these 3? + parent: Label, + idx: Int, + enclosingStmt: Label, // OLD: KE1: ?, + /* + OLD: KE1 + overrideId: Label? = null + */ +): Label? { + val text = e.text + if (text == null) { + TODO() + } + + val elementType = e.node.elementType + when (elementType) { + KtNodeTypes.INTEGER_CONSTANT -> { + val t = e.expressionType + val i = parseNumericLiteral(text, elementType) + println("=== parsed") + println(text) + println(i) + println(i?.javaClass) + when { + i == null -> { + TODO() + } + + t == null -> { + TODO() + } + + t.isIntType || t.isShortType || t.isByteType -> { + extractConstantInteger( + text, + t, + i, + tw.getLocation(e), + parent, + idx, + enclosingCallable, + enclosingStmt, + /* + OLD: KE1 + overrideId = overrideId + */ + ) + } + + t.isLongType -> { + // OLD: KE1: Was: exprIdOrFresh(overrideId).also { id -> + tw.getFreshIdLabel().also { id -> + if (t == null) { + TODO() + } + val type = useType(t) + val locId = tw.getLocation(e) + tw.writeExprs_longliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + /* + OLD: KE1 + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + */ + tw.writeNamestrings(text, i.toString(), id) + } + } + + else -> { + TODO() + } + } + } + + else -> { + TODO() + } + } + + // TODO: Wrong + return null + /* + OLD: KE1 + val v = e.value + return when { + v is Float -> { + exprIdOrFresh(overrideId).also { id -> + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_floatingpointliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + tw.writeNamestrings(v.toString(), v.toString(), id) + } + } + v is Double -> { + exprIdOrFresh(overrideId).also { id -> + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_doubleliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + tw.writeNamestrings(v.toString(), v.toString(), id) + } + } + v is Boolean -> { + exprIdOrFresh(overrideId).also { id -> + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_booleanliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + tw.writeNamestrings(v.toString(), v.toString(), id) + } + } + v is Char -> { + exprIdOrFresh(overrideId).also { id -> + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_characterliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + tw.writeNamestrings(v.toString(), v.toString(), id) + } + } + v is String -> { + exprIdOrFresh(overrideId).also { id -> + val type = useType(e.type) + val locId = tw.getLocation(e) + tw.writeExprs_stringliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, enclosingCallable, enclosingStmt) + tw.writeNamestrings(toQuotedLiteral(v.toString()), v.toString(), id) + } + } + v == null -> { + extractNull( + e.type, + tw.getLocation(e), + parent, + idx, + enclosingCallable, + enclosingStmt, + overrideId = overrideId + ) + } + else -> { + null.also { logger.errorElement("Unrecognised IrConst: " + v.javaClass, e) } + } + } + */ +} + +/* + OLD: KE1 + private fun getStatementOriginOperator(origin: IrStatementOrigin?) = + when (origin) { + IrStatementOrigin.PLUSEQ -> "plus" + IrStatementOrigin.MINUSEQ -> "minus" + IrStatementOrigin.MULTEQ -> "times" + IrStatementOrigin.DIVEQ -> "div" + IrStatementOrigin.PERCEQ -> "rem" + else -> null + } + + private fun getUpdateInPlaceRHS( + origin: IrStatementOrigin?, + isExpectedLhs: (IrExpression?) -> Boolean, + updateRhs: IrExpression + ): IrExpression? { + // Check for a desugared in-place update operator, such as "v += e": + return getStatementOriginOperator(origin)?.let { + if (updateRhs is IrCall && isNumericFunction(updateRhs.symbol.owner, it)) { + // Check for an expression like x = get(x).op(e): + val opReceiver = updateRhs.dispatchReceiver + if (isExpectedLhs(opReceiver)) { + updateRhs.getValueArgument(0) + } else null + } else null + } + } + + private fun writeUpdateInPlaceExpr( + origin: IrStatementOrigin + ): (( + tw: TrapWriter, + id: Label, + type: Label, + exprParent: Label, + index: Int + ) -> Unit)? { + when (origin) { + IrStatementOrigin.PLUSEQ -> + return { + tw: TrapWriter, + id: Label, + type: Label, + exprParent: Label, + index: Int -> + tw.writeExprs_assignaddexpr(id.cast(), type, exprParent, index) + } + IrStatementOrigin.MINUSEQ -> + return { + tw: TrapWriter, + id: Label, + type: Label, + exprParent: Label, + index: Int -> + tw.writeExprs_assignsubexpr(id.cast(), type, exprParent, index) + } + IrStatementOrigin.MULTEQ -> + return { + tw: TrapWriter, + id: Label, + type: Label, + exprParent: Label, + index: Int -> + tw.writeExprs_assignmulexpr(id.cast(), type, exprParent, index) + } + IrStatementOrigin.DIVEQ -> + return { + tw: TrapWriter, + id: Label, + type: Label, + exprParent: Label, + index: Int -> + tw.writeExprs_assigndivexpr(id.cast(), type, exprParent, index) + } + IrStatementOrigin.PERCEQ -> + return { + tw: TrapWriter, + id: Label, + type: Label, + exprParent: Label, + index: Int -> + tw.writeExprs_assignremexpr(id.cast(), type, exprParent, index) + } + else -> return null + } + } + + /** + * This method tries to extract a block as an enhanced for loop. It returns true if it succeeds, + * and false otherwise. + */ + private fun tryExtractForLoop( + e: IrContainerExpression, + callable: Label, + parent: StmtExprParent + ): Boolean { + /* + * We're expecting the pattern + * { + * val iterator = [expr].iterator() + * while (iterator.hasNext()) { + * val [loopVar] = iterator.next() + * [block] + * } + * } + */ + + if (e.origin != IrStatementOrigin.FOR_LOOP || e.statements.size != 2) { + return false + } + + val iteratorVariable = e.statements[0] as? IrVariable + val innerWhile = e.statements[1] as? IrWhileLoop + + if ( + iteratorVariable == null || + iteratorVariable.origin != IrDeclarationOrigin.FOR_LOOP_ITERATOR || + innerWhile == null || + innerWhile.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE + ) { + return false + } + + val initializer = iteratorVariable.initializer as? IrCall + if ( + initializer == null || + initializer.origin != IrStatementOrigin.FOR_LOOP_ITERATOR || + initializer.symbol.owner.name.asString() != "iterator" + ) { + return false + } + + val expr = initializer.dispatchReceiver + val cond = innerWhile.condition as? IrCall + val body = innerWhile.body as? IrBlock + + if ( + expr == null || + cond == null || + cond.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT || + (cond.dispatchReceiver as? IrGetValue)?.symbol?.owner != iteratorVariable || + body == null || + body.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE || + body.statements.size < 2 + ) { + return false + } + + val loopVar = body.statements[0] as? IrVariable + val nextCall = loopVar?.initializer as? IrCall + + if ( + loopVar == null || + !(loopVar.origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE || + loopVar.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE) || + nextCall == null || + nextCall.origin != IrStatementOrigin.FOR_LOOP_NEXT || + (nextCall.dispatchReceiver as? IrGetValue)?.symbol?.owner != iteratorVariable + ) { + return false + } + + val id = + extractLoop(innerWhile, null, parent, callable) { p, idx -> + tw.getFreshIdLabel().also { + tw.writeStmts_enhancedforstmt(it, p, idx, callable) + } + } + + extractVariableExpr(loopVar, callable, id, 0, id, extractInitializer = false) + extractExpressionExpr(expr, callable, id, 1, id) + val block = body.statements[1] as? IrBlock + if (body.statements.size == 2 && block != null) { + // Extract the body that was given to us by the compiler + extractExpressionStmt(block, callable, id, 2) + } else { + // Extract a block with all but the first (loop variable declaration) statement + extractBlock(body, body.statements.takeLast(body.statements.size - 1), id, 2, callable) + } + + return true + } + + /** + * This tried to extract a block as an array update. It returns true if it succeeds, and false + * otherwise. + */ + private fun tryExtractArrayUpdate( + e: IrContainerExpression, + callable: Label, + parent: StmtExprParent + ): Boolean { + /* + * We're expecting the pattern + * { + * val array = e1 + * val idx = e2 + * array.set(idx, array.get(idx).op(e3)) + * } + * + * If we find it, we'll extract e1[e2] op= e3 (op is +, -, ...) + */ + if (e.statements.size != 3) return false + (e.statements[0] as? IrVariable)?.let { arrayVarDecl -> + arrayVarDecl.initializer?.let { arrayVarInitializer -> + (e.statements[1] as? IrVariable)?.let { indexVarDecl -> + indexVarDecl.initializer?.let { indexVarInitializer -> + (e.statements[2] as? IrCall)?.let { arraySetCall -> + if ( + isFunction( + arraySetCall.symbol.owner, + "kotlin", + "(some array type)", + { isArrayType(it) }, + "set" + ) + ) { + val updateRhs0 = arraySetCall.getValueArgument(1) + if (updateRhs0 == null) { + logger.errorElement("Update RHS not found", e) + return false + } + getUpdateInPlaceRHS( + e + .origin, // Using e.origin not arraySetCall.origin here + // distinguishes a compiler-generated block + // from a user manually code that looks the + // same. + { oldValue -> + oldValue is IrCall && + isFunction( + oldValue.symbol.owner, + "kotlin", + "(some array type)", + { typeName -> isArrayType(typeName) }, + "get" + ) && + (oldValue.dispatchReceiver as? IrGetValue)?.let { + receiverVal -> + receiverVal.symbol.owner == + arrayVarDecl.symbol.owner + } ?: false + }, + updateRhs0 + ) + ?.let { updateRhs -> + val origin = e.origin + if (origin == null) { + logger.errorElement("No origin found", e) + return false + } + val writeUpdateInPlaceExprFun = + writeUpdateInPlaceExpr(origin) + if (writeUpdateInPlaceExprFun == null) { + logger.errorElement("Unexpected origin", e) + return false + } + + // Create an assignment skeleton _ op= _ + val exprParent = parent.expr(e, callable) + val assignId = tw.getFreshIdLabel() + val type = useType(arrayVarInitializer.type) + val locId = tw.getLocation(e) + tw.writeExprsKotlinType(assignId, type.kotlinResult.id) + extractExprContext( + assignId, + locId, + callable, + exprParent.enclosingStmt + ) + + writeUpdateInPlaceExprFun( + tw, + assignId, + type.javaResult.id, + exprParent.parent, + exprParent.idx + ) + + // Extract e1[e2] + val lhsId = tw.getFreshIdLabel() + val elementType = useType(updateRhs.type) + tw.writeExprs_arrayaccess( + lhsId, + elementType.javaResult.id, + assignId, + 0 + ) + tw.writeExprsKotlinType(lhsId, elementType.kotlinResult.id) + extractExprContext( + lhsId, + locId, + callable, + exprParent.enclosingStmt + ) + extractExpressionExpr( + arrayVarInitializer, + callable, + lhsId, + 0, + exprParent.enclosingStmt + ) + extractExpressionExpr( + indexVarInitializer, + callable, + lhsId, + 1, + exprParent.enclosingStmt + ) + + // Extract e3 + extractExpressionExpr( + updateRhs, + callable, + assignId, + 1, + exprParent.enclosingStmt + ) + + return true + } + } + } + } + } + } + } + + return false + } + + + private fun extractArrayCreationWithInitializer( + parent: Label, + arraySize: Int, + locId: Label, + enclosingCallable: Label, + enclosingStmt: Label + ): Label { + + val arrayCreationId = tw.getFreshIdLabel() + val arrayType = + pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType) + val at = useType(arrayType) + tw.writeExprs_arraycreationexpr(arrayCreationId, at.javaResult.id, parent, 0) + tw.writeExprsKotlinType(arrayCreationId, at.kotlinResult.id) + extractExprContext(arrayCreationId, locId, enclosingCallable, enclosingStmt) + + extractTypeAccessRecursive( + pluginContext.irBuiltIns.anyNType, + locId, + arrayCreationId, + -1, + enclosingCallable, + enclosingStmt + ) + + val initId = tw.getFreshIdLabel() + tw.writeExprs_arrayinit(initId, at.javaResult.id, arrayCreationId, -2) + tw.writeExprsKotlinType(initId, at.kotlinResult.id) + extractExprContext(initId, locId, enclosingCallable, enclosingStmt) + + extractConstantInteger( + arraySize, + locId, + arrayCreationId, + 0, + enclosingCallable, + enclosingStmt + ) + + return initId + } + + private fun extractTypeOperatorCall( + e: IrTypeOperatorCall, + callable: Label, + parent: Label, + idx: Int, + enclosingStmt: Label + ) { + with("type operator call", e) { + when (e.operator) { + IrTypeOperator.CAST -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_castexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) + } + IrTypeOperator.IMPLICIT_CAST -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_implicitcastexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) + } + IrTypeOperator.IMPLICIT_NOTNULL -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_implicitnotnullexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) + } + IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_implicitcoerciontounitexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) + } + IrTypeOperator.SAFE_CAST -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_safecastexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 1, enclosingStmt) + } + IrTypeOperator.INSTANCEOF -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_instanceofexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 0, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 1, callable, enclosingStmt) + } + IrTypeOperator.NOT_INSTANCEOF -> { + val id = tw.getFreshIdLabel() + val locId = tw.getLocation(e) + val type = useType(e.type) + tw.writeExprs_notinstanceofexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractExpressionExpr(e.argument, callable, id, 0, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 1, callable, enclosingStmt) + } + IrTypeOperator.SAM_CONVERSION -> { + + /* + The following Kotlin code + + ``` + fun interface IntPredicate { + fun accept(i: Int): Boolean + } + + val x = IntPredicate { it % 2 == 0 } + ``` + + is extracted as + + ``` + interface IntPredicate { + Boolean accept(Integer i); + } + class extends Object implements IntPredicate { + Function1 ; + public (Function1 ) { this. = ; } + public override Boolean accept(Integer i) { return .invoke(i); } + } + + IntPredicate x = (IntPredicate)new (...); + ``` + */ + + val st = e.argument.type as? IrSimpleType + if (st == null) { + logger.errorElement("Expected to find a simple type in SAM conversion.", e) + return + } + + fun IrSimpleType.isKProperty() = + classFqName?.asString()?.startsWith("kotlin.reflect.KProperty") == true + + if ( + !st.isFunctionOrKFunction() && + !st.isSuspendFunctionOrKFunction() && + !st.isKProperty() + ) { + logger.errorElement( + "Expected to find expression with function type in SAM conversion.", + e + ) + return + } + + // Either Function1, ... Function22 or FunctionN type, but not Function23 or + // above. + val functionType = getFunctionalInterfaceTypeWithTypeArgs(st.arguments) + if (functionType == null) { + logger.errorElement("Cannot find functional interface.", e) + return + } + + val invokeMethod = + functionType.classOrNull?.owner?.declarations?.findSubType { + it.name.asString() == OperatorNameConventions.INVOKE.asString() + } + if (invokeMethod == null) { + logger.errorElement( + "Couldn't find `invoke` method on functional interface.", + e + ) + return + } + + val typeOwner = e.typeOperand.classifierOrFail.owner + if (typeOwner !is IrClass) { + logger.errorElement( + "Expected to find SAM conversion to IrClass. Found '${typeOwner.javaClass}' instead. Can't implement SAM interface.", + e + ) + return + } + val samMember = + typeOwner.declarations.findSubType { + it is IrOverridableMember && it.modality == Modality.ABSTRACT + } + if (samMember == null) { + logger.errorElement( + "Couldn't find SAM member in type '${typeOwner.kotlinFqName.asString()}'. Can't implement SAM interface.", + e + ) + return + } + + val javaResult = TypeResult(tw.getFreshIdLabel(), "", "") + val kotlinResult = TypeResult(tw.getFreshIdLabel(), "", "") + tw.writeKt_notnull_types(kotlinResult.id, javaResult.id) + val ids = + LocallyVisibleFunctionLabels( + TypeResults(javaResult, kotlinResult), + constructor = tw.getFreshIdLabel(), + constructorBlock = tw.getFreshIdLabel(), + function = tw.getFreshIdLabel() + ) + + val locId = tw.getLocation(e) + val helper = GeneratedClassHelper(locId, ids) + + val declarationParent = peekDeclStackAsDeclarationParent(e) ?: return + val classId = + extractGeneratedClass( + ids, + listOf(pluginContext.irBuiltIns.anyType, e.typeOperand), + locId, + e, + declarationParent + ) + + // add field + val fieldId = tw.getFreshIdLabel() + extractField( + fieldId, + "", + functionType, + classId, + locId, + DescriptorVisibilities.PRIVATE, + e, + isExternalDeclaration = false, + isFinal = true, + isStatic = false + ) + + // adjust constructor + helper.extractParameterToFieldAssignmentInConstructor( + "", + functionType, + fieldId, + 0, + 1 + ) + + // add implementation function + val classTypeArgs = (e.type as? IrSimpleType)?.arguments + val typeSub = + classTypeArgs?.let { makeGenericSubstitutionFunction(typeOwner, it) } + + fun trySub(t: IrType, context: TypeContext) = + if (typeSub == null) t else typeSub(t, context, pluginContext) + + // Force extraction of this function even if this is a fake override -- + // This happens in the case where a functional interface inherits its only + // abstract member, + // which usually we wouldn't extract, but in this case we're effectively using + // it as a template + // for the real function we're extracting that will implement this interface, + // and it serves fine + // for that purpose. By contrast if we looked through the fake to the underlying + // abstract method + // we would need to compose generic type substitutions -- for example, if we're + // implementing + // T UnaryOperator.apply(T t) here, we would need to compose substitutions so + // we can implement + // the real underlying R Function.apply(T t). + forceExtractFunction( + samMember, + classId, + extractBody = false, + extractMethodAndParameterTypeAccesses = true, + extractAnnotations = false, + typeSub, + classTypeArgs, + overriddenAttributes = + OverriddenFunctionAttributes( + id = ids.function, + sourceLoc = tw.getLocation(e), + modality = Modality.FINAL + ) + ) + + addModifiers(ids.function, "override") + if (st.isSuspendFunctionOrKFunction()) { + addModifiers(ids.function, "suspend") + } + + // body + val blockId = extractBlockBody(ids.function, locId) + + // return stmt + val returnId = tw.getFreshIdLabel() + tw.writeStmts_returnstmt(returnId, blockId, 0, ids.function) + tw.writeHasLocation(returnId, locId) + + // .invoke(vp0, cp1, vp2, vp3, ...) or + // .invoke(new Object[x]{vp0, vp1, vp2, ...}) + + // Call to original `invoke`: + val callId = tw.getFreshIdLabel() + val callType = useType(trySub(samMember.returnType, TypeContext.RETURN)) + tw.writeExprs_methodaccess(callId, callType.javaResult.id, returnId, 0) + tw.writeExprsKotlinType(callId, callType.kotlinResult.id) + extractExprContext(callId, locId, ids.function, returnId) + val calledMethodId = useFunction(invokeMethod, functionType.arguments) + if (calledMethodId == null) { + logger.errorElement("Cannot get ID for called method", invokeMethod) + } else { + tw.writeCallableBinding(callId, calledMethodId) + } + + // access + val lhsId = tw.getFreshIdLabel() + val lhsType = useType(functionType) + tw.writeExprs_varaccess(lhsId, lhsType.javaResult.id, callId, -1) + tw.writeExprsKotlinType(lhsId, lhsType.kotlinResult.id) + extractExprContext(lhsId, locId, ids.function, returnId) + tw.writeVariableBinding(lhsId, fieldId) + + val parameters = mutableListOf() + val extParam = samMember.extensionReceiverParameter + if (extParam != null) { + parameters.add(extParam) + } + parameters.addAll(samMember.valueParameters) + + fun extractArgument( + p: IrValueParameter, + idx: Int, + parent: Label + ) { + val argsAccessId = tw.getFreshIdLabel() + val paramType = useType(trySub(p.type, TypeContext.OTHER)) + tw.writeExprs_varaccess(argsAccessId, paramType.javaResult.id, parent, idx) + tw.writeExprsKotlinType(argsAccessId, paramType.kotlinResult.id) + extractExprContext(argsAccessId, locId, ids.function, returnId) + tw.writeVariableBinding(argsAccessId, useValueParameter(p, ids.function)) + } + + val isBigArity = st.arguments.size > BuiltInFunctionArity.BIG_ARITY + val argParent = + if (isBigArity) { + // .invoke(new Object[x]{vp0, vp1, vp2, ...}) + extractArrayCreationWithInitializer( + callId, + parameters.size, + locId, + ids.function, + returnId + ) + } else { + // .invoke(vp0, cp1, vp2, vp3, ...) or + callId + } + + for ((parameterIdx, vp) in parameters.withIndex()) { + extractArgument(vp, parameterIdx, argParent) + } + + val id = tw.getFreshIdLabel() + val type = useType(e.typeOperand) + tw.writeExprs_castexpr(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + extractTypeAccessRecursive(e.typeOperand, locId, id, 0, callable, enclosingStmt) + + val idNewexpr = + extractNewExpr( + ids.constructor, + ids.type, + locId, + id, + 1, + callable, + enclosingStmt + ) + + tw.writeIsAnonymClass( + ids.type.javaResult.id.cast(), + idNewexpr + ) + + extractTypeAccessRecursive( + e.typeOperand, + locId, + idNewexpr, + -3, + callable, + enclosingStmt + ) + + extractExpressionExpr(e.argument, callable, idNewexpr, 0, enclosingStmt) + } + else -> { + logger.errorElement( + "Unrecognised IrTypeOperatorCall for ${e.operator}: " + e.render(), + e + ) + } + } + } + } + + private fun extractBreakContinue(e: IrBreakContinue, id: Label) { + with("break/continue", e) { + val locId = tw.getLocation(e) + tw.writeHasLocation(id, locId) + val label = e.label + if (label != null) { + tw.writeNamestrings(label, "", id) + } + } + } + */ \ No newline at end of file diff --git a/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt b/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt index 90b80289df6..a919a8232fa 100644 --- a/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt +++ b/java/kotlin-extractor2/src/main/kotlin/entities/Function.kt @@ -1,6 +1,7 @@ package com.github.codeql import com.github.codeql.KotlinUsesExtractor.TypeContext +import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.name import org.jetbrains.kotlin.analysis.api.symbols.psiSafe @@ -301,6 +302,7 @@ private val IrDeclaration.isAnonymousFunction } */ +context(KaSession) fun KotlinFileExtractor.extractFunction( f: KaFunctionSymbol, parentId: Label, @@ -375,6 +377,7 @@ fun KotlinFileExtractor.extractFunction( } // TODO: Can this be inlined? +context(KaSession) private fun KotlinFileExtractor.forceExtractFunction( f: KaFunctionSymbol, parentId: Label,