@@ -653,8 +653,8 @@ class Objects(using Context @constructorOnly):
653
653
* GC may only be performed from method call contexts --- otherwise, we need
654
654
* to consider values of the current local environment as well.
655
655
*/
656
- def gc (returnValue : Value , heapBefore : Data , heapAfter : Data , changeSet : Set [Addr ], currentObj : ObjectRef ): Data =
657
- val roots : Iterable [Addr | Value ] = changeSet.toSeq :+ returnValue
656
+ def gc (returnValues : List [ Value ] , heapBefore : Data , heapAfter : Data , changeSet : Set [Addr ], currentObj : ObjectRef ): Data =
657
+ val roots : Iterable [Addr | Value ] = changeSet.toSeq ++ returnValues
658
658
// reachable locations from the return value and change set
659
659
val reachableKeys = reachableAddresses(roots, heapAfter, currentObj)
660
660
@@ -674,24 +674,25 @@ class Objects(using Context @constructorOnly):
674
674
val config = Config (thisV, summon[Env .Data ], Heap .getHeapData())
675
675
super .get(config, expr).map(_.value)
676
676
677
- def cachedEval (thisV : ThisValue , expr : Tree , cacheResult : Boolean )(fun : Tree => Value )(using Heap .MutableData , Env .Data , State .Data ): Value =
677
+ def cachedEval (thisV : ThisValue , expr : Tree , ctx : EvalContext )(fun : Tree => Value )(using Heap .MutableData , Env .Data , State . Data , Returns .Data ): Value =
678
678
val env = summon[Env .Data ]
679
679
val heapBefore = Heap .getHeapData()
680
680
val changeSetBefore = Heap .getChangeSet()
681
- // Only perform footprint optimization when cacheResult is true
681
+ // Only perform footprint optimization for method context
682
682
val footprint =
683
- if cacheResult then Heap .footprint(Heap .getHeapData(), thisV, env, State .currentObjectRef)
683
+ if ctx == EvalContext . Method then Heap .footprint(Heap .getHeapData(), thisV, env, State .currentObjectRef)
684
684
else heapBefore
685
685
val config = Config (thisV, env, footprint)
686
686
687
687
Heap .update(footprint, changeSet = Set .empty)
688
+ val cacheResult = ctx == EvalContext .Method || ctx == EvalContext .Function || ctx == EvalContext .LazyVal
688
689
val result = super .cachedEval(config, expr, cacheResult, default = Res (Bottom , footprint, Set .empty)) { expr =>
689
690
val value = fun(expr)
690
691
val heapAfter = Heap .getHeapData()
691
692
val changeSetNew = Heap .getChangeSet()
692
- // Only perform garbage collection when cacheResult is true
693
+ // Only perform garbage collection for method context
693
694
val heapGC =
694
- if cacheResult then Heap .gc(value, footprint, heapAfter, changeSetNew, State .currentObjectRef)
695
+ if ctx == EvalContext then Heap .gc(value :: Returns .currentReturns , footprint, heapAfter, changeSetNew, State .currentObjectRef)
695
696
else heapAfter
696
697
Res (value, heapGC, changeSetNew)
697
698
}
@@ -740,6 +741,17 @@ class Objects(using Context @constructorOnly):
740
741
case None =>
741
742
report.warning(" [Internal error] Unhandled return for method " + meth + " in " + meth.owner.show + " . Trace:\n " + Trace .show, Trace .position)
742
743
744
+ /**
745
+ * Return the current return values in a method context.
746
+ *
747
+ * Warning: only use this method if it is certain that a method body has just
748
+ * finished before any other code is executed.
749
+ *
750
+ * Calling this method from lazy val or function context is problematic.
751
+ */
752
+ def currentReturns (using data : Data ): List [Value ] =
753
+ data.last.values.toList
754
+
743
755
type Contextual [T ] = (Context , State .Data , Env .Data , Cache .Data , Heap .MutableData , Regions .Data , Returns .Data , Trace ) ?=> T
744
756
745
757
// --------------------------- domain operations -----------------------------
@@ -875,7 +887,7 @@ class Objects(using Context @constructorOnly):
875
887
val env2 = Env .of(ddef, args.map(_.value), outerEnv)
876
888
extendTrace(ddef) {
877
889
given Env .Data = env2
878
- cache.cachedEval(ref, ddef.rhs, cacheResult = true ) { expr =>
890
+ cache.cachedEval(ref, ddef.rhs, EvalContext . Method ) { expr =>
879
891
Returns .installHandler(meth)
880
892
val res = cases(expr, thisV, cls)
881
893
val returns = Returns .popHandler(meth)
@@ -904,7 +916,7 @@ class Objects(using Context @constructorOnly):
904
916
case ddef : DefDef =>
905
917
if meth.name == nme.apply then
906
918
given Env .Data = Env .of(ddef, args.map(_.value), env)
907
- extendTrace(code) { eval(ddef.rhs, thisV, klass, cacheResult = true ) }
919
+ extendTrace(code) { eval(ddef.rhs, thisV, klass, EvalContext . Function ) }
908
920
else
909
921
// The methods defined in `Any` and `AnyRef` are trivial and don't affect initialization.
910
922
if meth.owner == defn.AnyClass || meth.owner == defn.ObjectClass then
@@ -919,7 +931,7 @@ class Objects(using Context @constructorOnly):
919
931
case _ =>
920
932
// by-name closure
921
933
given Env .Data = env
922
- extendTrace(code) { eval(code, thisV, klass, cacheResult = true ) }
934
+ extendTrace(code) { eval(code, thisV, klass, EvalContext . Function ) }
923
935
924
936
case ValueSet (vs) =>
925
937
vs.map(v => call(v, meth, args, receiver, superType)).join
@@ -942,11 +954,11 @@ class Objects(using Context @constructorOnly):
942
954
given Env .Data = Env .of(ddef, argValues, Env .NoEnv )
943
955
if ctor.isPrimaryConstructor then
944
956
val tpl = cls.defTree.asInstanceOf [TypeDef ].rhs.asInstanceOf [Template ]
945
- extendTrace(cls.defTree) { eval(tpl, ref, cls, cacheResult = true ) }
957
+ extendTrace(cls.defTree) { eval(tpl, ref, cls, EvalContext . Method ) }
946
958
else
947
959
extendTrace(ddef) { // The return values for secondary constructors can be ignored
948
960
Returns .installHandler(ctor)
949
- eval(ddef.rhs, ref, cls, cacheResult = true )
961
+ eval(ddef.rhs, ref, cls, EvalContext . Method )
950
962
Returns .popHandler(ctor)
951
963
}
952
964
else
@@ -977,7 +989,7 @@ class Objects(using Context @constructorOnly):
977
989
given Env .Data = Env .emptyEnv(target.owner.asInstanceOf [ClassSymbol ].primaryConstructor)
978
990
if target.hasSource then
979
991
val rhs = target.defTree.asInstanceOf [ValDef ].rhs
980
- eval(rhs, ref, target.owner.asClass, cacheResult = true )
992
+ eval(rhs, ref, target.owner.asClass, EvalContext . LazyVal )
981
993
else
982
994
Bottom
983
995
else if target.exists then
@@ -1159,7 +1171,7 @@ class Objects(using Context @constructorOnly):
1159
1171
given Env .Data = env
1160
1172
if sym.is(Flags .Lazy ) then
1161
1173
val rhs = sym.defTree.asInstanceOf [ValDef ].rhs
1162
- eval(rhs, thisV, sym.enclosingClass.asClass, cacheResult = true )
1174
+ eval(rhs, thisV, sym.enclosingClass.asClass, EvalContext . LazyVal )
1163
1175
else
1164
1176
// Assume forward reference check is doing a good job
1165
1177
val value = Env .valValue(sym)
@@ -1232,6 +1244,9 @@ class Objects(using Context @constructorOnly):
1232
1244
do
1233
1245
accessObject(classSym)
1234
1246
1247
+ enum EvalContext :
1248
+ case Method , Function , LazyVal , Other
1249
+
1235
1250
/** Evaluate an expression with the given value for `this` in a given class `klass`
1236
1251
*
1237
1252
* Note that `klass` might be a super class of the object referred by `thisV`.
@@ -1250,10 +1265,10 @@ class Objects(using Context @constructorOnly):
1250
1265
* @param expr The expression to be evaluated.
1251
1266
* @param thisV The value for `C.this` where `C` is represented by the parameter `klass`.
1252
1267
* @param klass The enclosing class where the expression is located.
1253
- * @param cacheResult It is used to reduce the size of the cache .
1268
+ * @param ctx The context where `eval` is called .
1254
1269
*/
1255
- def eval (expr : Tree , thisV : ThisValue , klass : ClassSymbol , cacheResult : Boolean = false ): Contextual [Value ] = log(" evaluating " + expr.show + " , this = " + thisV.show + " , regions = " + Regions .show + " in " + klass.show, printer, (_ : Value ).show) {
1256
- cache.cachedEval(thisV, expr, cacheResult ) { expr => cases(expr, thisV, klass) }
1270
+ def eval (expr : Tree , thisV : ThisValue , klass : ClassSymbol , ctx : EvalContext = EvalContext . Other ): Contextual [Value ] = log(" evaluating " + expr.show + " , this = " + thisV.show + " , regions = " + Regions .show + " in " + klass.show, printer, (_ : Value ).show) {
1271
+ cache.cachedEval(thisV, expr, ctx ) { expr => cases(expr, thisV, klass) }
1257
1272
}
1258
1273
1259
1274
@@ -1393,9 +1408,11 @@ class Objects(using Context @constructorOnly):
1393
1408
withTrace(trace2) { assign(receiver, lhs.symbol, widened, rhs.tpe) }
1394
1409
1395
1410
case closureDef(ddef) =>
1411
+ // TODO: trim the environment and `thisV` to only captured locals
1396
1412
Fun (ddef, thisV, klass, summon[Env .Data ])
1397
1413
1398
1414
case PolyFun (ddef) =>
1415
+ // TODO: trim the environment and `thisV` to only captured locals
1399
1416
Fun (ddef, thisV, klass, summon[Env .Data ])
1400
1417
1401
1418
case Block (stats, expr) =>
0 commit comments