Skip to content

Commit

Permalink
Handle slice operator. (#4255)
Browse files Browse the repository at this point in the history
Slices are no longer emitted as UNKNOWN nodes.
Instead we emit a static call `<operator>.slice`.
  • Loading branch information
ml86 authored Mar 1, 2024
1 parent fe195d1 commit 310f209
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.joern.pysrc2cpg

import io.joern.pysrc2cpg.PythonAstVisitor.{builtinPrefix, metaClassSuffix}
import PythonAstVisitor.{builtinPrefix, logger, metaClassSuffix, noLineAndColumn}
import io.joern.pysrc2cpg.memop.*
import io.joern.pythonparser.ast
import io.joern.x2cpg.{AstCreatorBase, ValidationMode}
Expand All @@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory
import overflowdb.BatchedUpdate.DiffGraphBuilder

import scala.collection.mutable
import PythonAstVisitor.logger

object MethodParameters {
def empty(): MethodParameters = {
Expand Down Expand Up @@ -1321,8 +1320,12 @@ class PythonAstVisitor(
case node: ast.Name => convert(node)
case node: ast.List => convert(node)
case node: ast.Tuple => convert(node)
case node: ast.Slice => unhandled(node)
case node: ast.StringExpList => convert(node)
case node: ast.Slice =>
// Our expectation is that ast.Slice only appears as part of ast.Subscript
// and thus we should never get here becauase convert(ast.Subscript)
// directly handles the case of a nested ast.Slice.
unhandled(node)
case node: ast.StringExpList => convert(node)
}
}

Expand Down Expand Up @@ -1887,7 +1890,24 @@ class PythonAstVisitor(
}

def convert(subscript: ast.Subscript): NewNode = {
createIndexAccess(convert(subscript.value), convert(subscript.slice), lineAndColOf(subscript))
subscript.slice match {
case slice: ast.Slice =>
val value = convert(subscript.value)
val lower = slice.lower.map(convert).getOrElse(nodeBuilder.literalNode("None", None, noLineAndColumn))
val upper = slice.upper.map(convert).getOrElse(nodeBuilder.literalNode("None", None, noLineAndColumn))
val step = slice.step.map(convert).getOrElse(nodeBuilder.literalNode("None", None, noLineAndColumn))

val code = nodeToCode.getCode(subscript)
val callNode =
nodeBuilder.callNode(code, "<operator>.slice", DispatchTypes.STATIC_DISPATCH, lineAndColOf(slice))

val args = value :: lower :: upper :: step :: Nil
addAstChildrenAsArguments(callNode, 1, args)

callNode
case _ =>
createIndexAccess(convert(subscript.value), convert(subscript.slice), lineAndColOf(subscript))
}
}

def convert(starred: ast.Starred): NewNode = {
Expand Down Expand Up @@ -1952,7 +1972,19 @@ class PythonAstVisitor(
callNode
}

def convert(slice: ast.Slice): NewNode = ???
def convert(slice: ast.Slice): NewNode = {
val args = mutable.ArrayBuffer.empty[NewNode]
slice.lower.foreach(expr => args.append(convert(expr)))
slice.upper.foreach(expr => args.append(convert(expr)))
slice.step.foreach(expr => args.append(convert(expr)))

val code = nodeToCode.getCode(slice)
val callNode = nodeBuilder.callNode(code, "<operator>.slice", DispatchTypes.STATIC_DISPATCH, lineAndColOf(slice))

addAstChildrenAsArguments(callNode, 1, args)

callNode
}

def convert(stringExpList: ast.StringExpList): NewNode = {
val stringNodes = stringExpList.elts.map(convert)
Expand Down Expand Up @@ -2059,6 +2091,8 @@ object PythonAstVisitor {
val typingPrefix = "typing."
val metaClassSuffix = "<meta>"

val noLineAndColumn = LineAndColumn(-1, -1, -1, -1, -1, -1)

// This list contains all functions from https://docs.python.org/3/library/functions.html#built-in-funcs
// for python version 3.9.5.
// There is a corresponding list in policies which needs to be updated if this one is updated and vice versa.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.joern.pysrc2cpg.cpg

import io.joern.pysrc2cpg.PySrc2CpgFixture
import io.shiftleft.semanticcpg.language.*

class SliceCpgTests extends PySrc2CpgFixture() {
"slice" should {
"have correct AST case 1" in {
val cpg = code("""
|x[1:2:1]
|""".stripMargin)

val sliceOperator = cpg.call.name("<operator>.slice").head
sliceOperator.code shouldBe "x[1:2:1]"
sliceOperator.argument(1).code shouldBe "x"
sliceOperator.argument(2).code shouldBe "1"
sliceOperator.argument(3).code shouldBe "2"
sliceOperator.argument(4).code shouldBe "1"
}

"have correct AST case 2" in {
val cpg = code("""
|x[::]
|""".stripMargin)

val sliceOperator = cpg.call.name("<operator>.slice").head
sliceOperator.code shouldBe "x[::]"
sliceOperator.argument(1).code shouldBe "x"
sliceOperator.argument(2).code shouldBe "None"
sliceOperator.argument(3).code shouldBe "None"
sliceOperator.argument(4).code shouldBe "None"
}

"have correct AST case 3" in {
val cpg = code("""
|x[::][1:2]
|""".stripMargin)

val outerOperator = cpg.call.name("<operator>.slice").codeExact("x[::][1:2]").head
val innerOperator = outerOperator.argument.argumentIndex(1).isCall.head

innerOperator.code shouldBe "x[::]"
innerOperator.argument(1).code shouldBe "x"
innerOperator.argument(2).code shouldBe "None"
innerOperator.argument(3).code shouldBe "None"
innerOperator.argument(4).code shouldBe "None"

outerOperator.argument(2).code shouldBe "1"
outerOperator.argument(3).code shouldBe "2"
outerOperator.argument(4).code shouldBe "None"
}
}

}

0 comments on commit 310f209

Please sign in to comment.