Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1df9943
Add API for handling expression code generation.
viirya Apr 30, 2018
5fe425c
Add new abstraction for expression codegen.
viirya May 1, 2018
00bef6b
Add basic tests.
viirya May 3, 2018
5d9c454
Merge remote-tracking branch 'upstream/master' into SPARK-24121
viirya May 3, 2018
162deb2
Deal merging conflict.
viirya May 3, 2018
d138ee0
Address comments and add more tests.
viirya May 4, 2018
ee9a4c0
Address comments.
viirya May 5, 2018
e7cfa28
Remove JavaCode.block. We should always use code string interpolator …
viirya May 5, 2018
5945c15
We should not implicitly convert code block to string. Otherwise we m…
viirya May 5, 2018
2b30654
Address comment and trim expected code string.
viirya May 5, 2018
aff411b
Remove unused import.
viirya May 8, 2018
53b329a
Address comments.
viirya May 8, 2018
72faac3
Address some comments.
viirya May 9, 2018
ffbf4ab
Merge remote-tracking branch 'upstream/master' into SPARK-24121
viirya May 17, 2018
d040676
Use code block for newly merged codegen.
viirya May 17, 2018
c378ce2
Use Set as method exprValues method returning type.
viirya May 17, 2018
2ca9741
Address comments.
viirya May 19, 2018
d91f111
Merge remote-tracking branch 'upstream/master' into SPARK-24121
viirya May 19, 2018
5c40233
Merge remote-tracking branch 'upstream/master' into codeblock-api
viirya May 21, 2018
8d0b1b9
Add API to manipulate blocks and expressions.
viirya May 21, 2018
2d77332
Merge remote-tracking branch 'upstream/master' into codeblock-api
viirya May 23, 2018
e30be7a
Address comments.
viirya May 29, 2018
21411f5
Merge remote-tracking branch 'upstream/master' into codeblock-api
viirya Jul 5, 2018
ce8d9ed
Only Block extends TreeNode.
viirya Jul 5, 2018
707dd6f
Remove exprValues property from Block.
viirya Jul 5, 2018
c9dee44
Revert LiteralExpr.
viirya Jul 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import java.lang.{Boolean => JBool}
import scala.collection.mutable.ArrayBuffer
import scala.language.{existentials, implicitConversions}

import org.apache.spark.sql.catalyst.trees.TreeNode
import org.apache.spark.sql.types.{BooleanType, DataType}

/**
Expand Down Expand Up @@ -118,12 +119,9 @@ object JavaCode {
/**
* A trait representing a block of java code.
*/
trait Block extends JavaCode {
trait Block extends TreeNode[Block] with JavaCode {
import Block._

// The expressions to be evaluated inside this block.
def exprValues: Set[ExprValue]

// Returns java code string for this code block.
override def toString: String = _marginChar match {
case Some(c) => code.stripMargin(c).trim
Expand All @@ -148,11 +146,41 @@ trait Block extends JavaCode {
this
}

/**
* Apply a map function to each java expression codes present in this java code, and return a new
* java code based on the mapped java expression codes.
*/
def transformExprValues(f: PartialFunction[ExprValue, ExprValue]): this.type = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

am I wrong or we are not updating the exprValues here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transformExprValues will create a new instance Block because a block is immutable. So once there are change, new block should take new exprValues.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, can we add some tests for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Will add them in next commit. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test for it.

var changed = false

@inline def transform(e: ExprValue): ExprValue = {
val newE = f lift e
if (!newE.isDefined || newE.get.equals(e)) {
e
} else {
changed = true
newE.get
}
}

def doTransform(arg: Any): AnyRef = arg match {
case e: ExprValue => transform(e)
case Some(value) => Some(doTransform(value))
case seq: Traversable[_] => seq.map(doTransform)
case other: AnyRef => other
}

val newArgs = mapProductIterator(doTransform)
if (changed) makeCopy(newArgs).asInstanceOf[this.type] else this
}

// Concatenates this block with other block.
def + (other: Block): Block = other match {
case EmptyBlock => this
case _ => code"$this\n$other"
}

override def verboseString: String = toString
}

object Block {
Expand Down Expand Up @@ -219,12 +247,8 @@ object Block {
* method splitting.
*/
case class CodeBlock(codeParts: Seq[String], blockInputs: Seq[JavaCode]) extends Block {
override lazy val exprValues: Set[ExprValue] = {
blockInputs.flatMap {
case b: Block => b.exprValues
case e: ExprValue => Set(e)
}.toSet
}
override def children: Seq[Block] =
blockInputs.filter(_.isInstanceOf[Block]).asInstanceOf[Seq[Block]]

override lazy val code: String = {
val strings = codeParts.iterator
Expand All @@ -239,9 +263,9 @@ case class CodeBlock(codeParts: Seq[String], blockInputs: Seq[JavaCode]) extends
}
}

object EmptyBlock extends Block with Serializable {
case object EmptyBlock extends Block with Serializable {
override val code: String = ""
override val exprValues: Set[ExprValue] = Set.empty
override def children: Seq[Block] = Seq.empty
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ class CodeBlockSuite extends SparkFunSuite {
|boolean $isNull = false;
|int $value = -1;
""".stripMargin
val exprValues = code.exprValues
val exprValues = code.asInstanceOf[CodeBlock].blockInputs.collect {
case e: ExprValue => e
}.toSet
assert(exprValues.size == 2)
assert(exprValues === Set(value, isNull))
}
Expand Down Expand Up @@ -94,7 +96,9 @@ class CodeBlockSuite extends SparkFunSuite {

assert(code.toString == expected)

val exprValues = code.exprValues
val exprValues = code.children.flatMap(_.asInstanceOf[CodeBlock].blockInputs.collect {
case e: ExprValue => e
}).toSet
assert(exprValues.size == 5)
assert(exprValues === Set(isNull1, value1, isNull2, value2, literal))
}
Expand All @@ -107,7 +111,7 @@ class CodeBlockSuite extends SparkFunSuite {
assert(e.getMessage().contains(s"Can not interpolate ${obj.getClass.getName}"))
}

test("replace expr values in code block") {
test("transform expr in code block") {
val expr = JavaCode.expression("1 + 1", IntegerType)
val isNull = JavaCode.isNullVariable("expr1_isNull")
val exprInFunc = JavaCode.variable("expr1", IntegerType)
Expand All @@ -120,11 +124,11 @@ class CodeBlockSuite extends SparkFunSuite {
|}""".stripMargin

val aliasedParam = JavaCode.variable("aliased", expr.javaType)
val aliasedInputs = code.asInstanceOf[CodeBlock].blockInputs.map {
case _: SimpleExprValue => aliasedParam
case other => other

// We want to replace all occurrences of `expr` with the variable `aliasedParam`.
val aliasedCode = code.transformExprValues {
case SimpleExprValue("1 + 1", java.lang.Integer.TYPE) => aliasedParam
}
val aliasedCode = CodeBlock(code.asInstanceOf[CodeBlock].codeParts, aliasedInputs).stripMargin
val expected =
code"""
|callFunc(int $aliasedParam) {
Expand All @@ -133,4 +137,61 @@ class CodeBlockSuite extends SparkFunSuite {
|}""".stripMargin
assert(aliasedCode.toString == expected.toString)
}

test ("transform expr in nested blocks") {
val expr = JavaCode.expression("1 + 1", IntegerType)
val isNull = JavaCode.isNullVariable("expr1_isNull")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not related to this PR, shall we remove isNullVariable and always use expression(..., BooleanType)? Or we can add a bunch of intExpression(...), booleanExpression(...) etc.

Copy link
Member Author

@viirya viirya Jul 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is fine and maybe more clear. I was thinking the name of isNullVariable is a bit confusing at the first look actually.

val exprInFunc = JavaCode.variable("expr1", IntegerType)

val funcs = Seq("callFunc1", "callFunc2", "callFunc3")
val subBlocks = funcs.map { funcName =>
code"""
|$funcName(int $expr) {
| boolean $isNull = false;
| int $exprInFunc = $expr + 1;
|}""".stripMargin
}

val aliasedParam = JavaCode.variable("aliased", expr.javaType)

val block = code"${subBlocks(0)}\n${subBlocks(1)}\n${subBlocks(2)}"
val transformedBlock = block.transform {
case b: Block => b.transformExprValues {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, I'd image transformExprValues will also transform child blocks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so the this should be

block.transformExprValues {
  case SimpleExprValue ...
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I may call it transformAllExprValues.

case SimpleExprValue("1 + 1", java.lang.Integer.TYPE) => aliasedParam
}
}.asInstanceOf[CodeBlock]

val expected1 =
code"""
|callFunc1(int aliased) {
| boolean expr1_isNull = false;
| int expr1 = aliased + 1;
|}""".stripMargin

val expected2 =
code"""
|callFunc2(int aliased) {
| boolean expr1_isNull = false;
| int expr1 = aliased + 1;
|}""".stripMargin

val expected3 =
code"""
|callFunc3(int aliased) {
| boolean expr1_isNull = false;
| int expr1 = aliased + 1;
|}""".stripMargin

val exprValues = transformedBlock.children.flatMap { block =>
block.asInstanceOf[CodeBlock].blockInputs.collect {
case e: ExprValue => e
}
}.toSet

assert(transformedBlock.children(0).toString == expected1.toString)
assert(transformedBlock.children(1).toString == expected2.toString)
assert(transformedBlock.children(2).toString == expected3.toString)
assert(transformedBlock.toString == (expected1 + expected2 + expected3).toString)
assert(exprValues === Set(isNull, exprInFunc, aliasedParam))
}
}