@@ -113,7 +113,7 @@ class CodegenContext {
113113    val  idx  =  references.length
114114    references +=  obj
115115    val  clsName  =  Option (className).getOrElse(obj.getClass.getName)
116-     addMutableState(clsName, term, s " this. $term = ( $clsName) references[ $idx]; " )
116+     addMutableState(clsName, term, s " $term = ( $clsName) references[ $idx]; " )
117117    term
118118  }
119119
@@ -202,16 +202,6 @@ class CodegenContext {
202202    partitionInitializationStatements.mkString(" \n "  )
203203  }
204204
205-   /**  
206-    * Holding all the functions those will be added into generated class. 
207-    */  
208-   val  addedFunctions :  mutable.Map [String , String ] = 
209-     mutable.Map .empty[String , String ]
210- 
211-   def  addNewFunction (funcName : String , funcCode : String ):  Unit  =  {
212-     addedFunctions +=  ((funcName, funcCode))
213-   }
214- 
215205  /**  
216206   * Holds expressions that are equivalent. Used to perform subexpression elimination 
217207   * during codegen. 
@@ -233,10 +223,118 @@ class CodegenContext {
233223  //  The collection of sub-expression result resetting methods that need to be called on each row.
234224  val  subexprFunctions  =  mutable.ArrayBuffer .empty[String ]
235225
236-   def  declareAddedFunctions ():  String  =  {
237-     addedFunctions.map { case  (funcName, funcCode) =>  funcCode }.mkString(" \n "  )
226+   private  val  outerClassName  =  " OuterClass" 
227+ 
228+   /**  
229+    * Holds the class and instance names to be generated, where `OuterClass` is a placeholder 
230+    * standing for whichever class is generated as the outermost class and which will contain any 
231+    * nested sub-classes. All other classes and instance names in this list will represent private, 
232+    * nested sub-classes. 
233+    */  
234+   private  val  classes :  mutable.ListBuffer [(String , String )] = 
235+     mutable.ListBuffer [(String , String )](outerClassName ->  null )
236+ 
237+   //  A map holding the current size in bytes of each class to be generated.
238+   private  val  classSize :  mutable.Map [String , Int ] = 
239+     mutable.Map [String , Int ](outerClassName ->  0 )
240+ 
241+   //  Nested maps holding function names and their code belonging to each class.
242+   private  val  classFunctions :  mutable.Map [String , mutable.Map [String , String ]] = 
243+     mutable.Map (outerClassName ->  mutable.Map .empty[String , String ])
244+ 
245+   //  Returns the size of the most recently added class.
246+   private  def  currClassSize ():  Int  =  classSize(classes.head._1)
247+ 
248+   //  Returns the class name and instance name for the most recently added class.
249+   private  def  currClass ():  (String , String ) =  classes.head
250+ 
251+   //  Adds a new class. Requires the class' name, and its instance name.
252+   private  def  addClass (className : String , classInstance : String ):  Unit  =  {
253+     classes.prepend(className ->  classInstance)
254+     classSize +=  className ->  0 
255+     classFunctions +=  className ->  mutable.Map .empty[String , String ]
238256  }
239257
258+   /**  
259+    * Adds a function to the generated class. If the code for the `OuterClass` grows too large, the 
260+    * function will be inlined into a new private, nested class, and a instance-qualified name for 
261+    * the function will be returned. Otherwise, the function will be inined to the `OuterClass` the 
262+    * simple `funcName` will be returned. 
263+    * 
264+    * @param  funcName  the class-unqualified name of the function 
265+    * @param  funcCode  the body of the function 
266+    * @param  inlineToOuterClass  whether the given code must be inlined to the `OuterClass`. This 
267+    *                           can be necessary when a function is declared outside of the context 
268+    *                           it is eventually referenced and a returned qualified function name 
269+    *                           cannot otherwise be accessed. 
270+    * @return  the name of the function, qualified by class if it will be inlined to a private, 
271+    *         nested sub-class 
272+    */  
273+   def  addNewFunction (
274+       funcName : String ,
275+       funcCode : String ,
276+       inlineToOuterClass : Boolean  =  false ):  String  =  {
277+     //  The number of named constants that can exist in the class is limited by the Constant Pool
278+     //  limit, 65,536. We cannot know how many constants will be inserted for a class, so we use a
279+     //  threshold of 1600k bytes to determine when a function should be inlined to a private, nested
280+     //  sub-class.
281+     val  (className, classInstance) =  if  (inlineToOuterClass) {
282+       outerClassName ->  " " 
283+     } else  if  (currClassSize >  1600000 ) {
284+       val  className  =  freshName(" NestedClass"  )
285+       val  classInstance  =  freshName(" nestedClassInstance"  )
286+ 
287+       addClass(className, classInstance)
288+ 
289+       className ->  classInstance
290+     } else  {
291+       currClass()
292+     }
293+ 
294+     classSize(className) +=  funcCode.length
295+     classFunctions(className) +=  funcName ->  funcCode
296+ 
297+     if  (className ==  outerClassName) {
298+       funcName
299+     } else  {
300+ 
301+       s " $classInstance. $funcName" 
302+     }
303+   }
304+ 
305+   /**  
306+    * Instantiates all nested, private sub-classes as objects to the `OuterClass` 
307+    */  
308+   private [sql] def  initNestedClasses ():  String  =  {
309+     //  Nested, private sub-classes have no mutable state (though they do reference the outer class'
310+     //  mutable state), so we declare and initialize them inline to the OuterClass.
311+     classes.filter(_._1 !=  outerClassName).map {
312+       case  (className, classInstance) => 
313+         s " private  $className  $classInstance = new  $className(); " 
314+     }.mkString(" \n "  )
315+   }
316+ 
317+   /**  
318+    * Declares all function code that should be inlined to the `OuterClass`. 
319+    */  
320+   private [sql] def  declareAddedFunctions ():  String  =  {
321+     classFunctions(outerClassName).values.mkString(" \n "  )
322+   }
323+ 
324+   /**  
325+    * Declares all nested, private sub-classes and the function code that should be inlined to them. 
326+    */  
327+   private [sql] def  declareNestedClasses ():  String  =  {
328+     classFunctions.filterKeys(_ !=  outerClassName).map {
329+       case  (className, functions) => 
330+         s """ 
331+            |private class  $className { 
332+            |   ${functions.values.mkString(" \n "  )}
333+            |} 
334+             """ .stripMargin
335+     }
336+   }.mkString(" \n "  )
337+ 
240338  final  val  JAVA_BOOLEAN  =  " boolean" 
241339  final  val  JAVA_BYTE  =  " byte" 
242340  final  val  JAVA_SHORT  =  " short" 
@@ -556,8 +654,7 @@ class CodegenContext {
556654            return 0; 
557655          } 
558656         """ 
559-       addNewFunction(compareFunc, funcCode)
560-       s " this. $compareFunc( $c1,  $c2) " 
657+       s " ${addNewFunction(compareFunc, funcCode)}( $c1,  $c2) " 
561658    case  schema : StructType  => 
562659      val  comparisons  =  GenerateOrdering .genComparisons(this , schema)
563660      val  compareFunc  =  freshName(" compareStruct"  )
@@ -573,8 +670,7 @@ class CodegenContext {
573670            return 0; 
574671          } 
575672         """ 
576-       addNewFunction(compareFunc, funcCode)
577-       s " this. $compareFunc( $c1,  $c2) " 
673+       s " ${addNewFunction(compareFunc, funcCode)}( $c1,  $c2) " 
578674    case  other if  other.isInstanceOf [AtomicType ] =>  s " $c1.compare( $c2) " 
579675    case  udt : UserDefinedType [_] =>  genComp(udt.sqlType, c1, c2)
580676    case  _ => 
@@ -689,7 +785,6 @@ class CodegenContext {
689785           |} 
690786          """ .stripMargin
691787        addNewFunction(name, code)
692-         name
693788      }
694789
695790      foldFunctions(functions.map(name =>  s " $name( ${arguments.map(_._2).mkString(" , "  )}) " ))
@@ -773,8 +868,6 @@ class CodegenContext {
773868           |} 
774869            """ .stripMargin
775870
776-       addNewFunction(fnName, fn)
777- 
778871      //  Add a state and a mapping of the common subexpressions that are associate with this
779872      //  state. Adding this expression to subExprEliminationExprMap means it will call `fn`
780873      //  when it is code generated. This decision should be a cost based one.
@@ -792,7 +885,7 @@ class CodegenContext {
792885      addMutableState(javaType(expr.dataType), value,
793886        s " $value =  ${defaultValue(expr.dataType)}; " )
794887
795-       subexprFunctions +=  s " $fnName( $INPUT_ROW); " 
888+       subexprFunctions +=  s " ${addNewFunction( fnName, fn)} ( $INPUT_ROW); " 
796889      val  state  =  SubExprEliminationState (isNull, value)
797890      e.foreach(subExprEliminationExprs.put(_, state))
798891    }
0 commit comments