@@ -227,11 +227,19 @@ class Objects(using Context @constructorOnly):
227
227
case class Fun (code : Tree , thisV : ThisValue , klass : ClassSymbol , env : Env .Data ) extends ValueElement :
228
228
def show (using Context ) = " Fun(" + code.show + " , " + thisV.show + " , " + klass.show + " )"
229
229
230
- /** Represents common base values like Int, String, etc.
230
+ /**
231
+ * Represents common base values like Int, String, etc.
232
+ * Assumption: all methods calls on such values should be pure (no side effects)
231
233
*/
232
- case object SafeValue extends ValueElement :
233
- val safeTypes = defn.ScalaNumericValueTypeList ++ List (defn.UnitType , defn.BooleanType , defn.StringType )
234
- def show (using Context ): String = " SafeValue"
234
+ case class SafeValue (tpe : Type ) extends ValueElement :
235
+ // tpe could be a AppliedType(java.lang.Class, T)
236
+ val baseType = if tpe.isInstanceOf [AppliedType ] then tpe.asInstanceOf [AppliedType ].underlying else tpe
237
+ assert(baseType.isInstanceOf [TypeRef ] && SafeValue .safeTypes.contains(baseType), " Invalid creation of SafeValue! Type = " + tpe)
238
+ val typeref = baseType.asInstanceOf [TypeRef ]
239
+ def show (using Context ): String = " SafeValue of type " + tpe
240
+
241
+ object SafeValue :
242
+ val safeTypes = defn.ScalaNumericValueTypeList ++ List (defn.UnitType , defn.BooleanType , defn.StringType , defn.NullType , defn.ClassClass .typeRef)
235
243
236
244
/**
237
245
* Represents a set of values
@@ -704,7 +712,7 @@ class Objects(using Context @constructorOnly):
704
712
a match
705
713
case UnknownValue => UnknownValue
706
714
case Package (_) => a
707
- case SafeValue => SafeValue
715
+ case SafeValue (_) => a
708
716
case ref : Ref => if ref.klass.isSubClass(klass) then ref else Bottom
709
717
case ValueSet (values) => values.map(v => v.filterClass(klass)).join
710
718
case arr : OfArray => if defn.ArrayClass .isSubClass(klass) then arr else Bottom
@@ -733,7 +741,7 @@ class Objects(using Context @constructorOnly):
733
741
* @param superType The type of the super in a super call. NoType for non-super calls.
734
742
* @param needResolve Whether the target of the call needs resolution?
735
743
*/
736
- def call (value : Value , meth : Symbol , args : List [ArgInfo ], receiver : Type , superType : Type , needResolve : Boolean = true ): Contextual [Value ] = log(" call " + meth.show + " , this = " + value.show + " , args = " + args.map(_.tree .show), printer, (_ : Value ).show) {
744
+ def call (value : Value , meth : Symbol , args : List [ArgInfo ], receiver : Type , superType : Type , needResolve : Boolean = true ): Contextual [Value ] = log(" call " + meth.show + " , this = " + value.show + " , args = " + args.map(_.value .show), printer, (_ : Value ).show) {
737
745
value.filterClass(meth.owner) match
738
746
case UnknownValue =>
739
747
if reportUnknown then
@@ -743,11 +751,33 @@ class Objects(using Context @constructorOnly):
743
751
UnknownValue
744
752
745
753
case Package (packageSym) =>
746
- report.warning(" [Internal error] Unexpected call on package = " + value.show + " , meth = " + meth.show + Trace .show, Trace .position)
747
- Bottom
748
-
749
- case SafeValue =>
750
- SafeValue // Check return type, if not safe, try to analyze body, 1.until(2).map(i => UninitializedObject)
754
+ // calls on packages are unexpected. However the typer might mistakenly
755
+ // set the receiver to be a package instead of package object.
756
+ // See packageObjectStringInterpolator.scala
757
+ if ! meth.owner.denot.isPackageObject then
758
+ report.warning(" [Internal error] Unexpected call on package = " + value.show + " , meth = " + meth.show + Trace .show, Trace .position)
759
+ Bottom
760
+ else
761
+ // Method call on package object instead
762
+ val packageObj = accessObject(meth.owner.moduleClass.asClass)
763
+ call(packageObj, meth, args, receiver, superType, needResolve)
764
+
765
+ case v @ SafeValue (tpe) =>
766
+ // Assume such method is pure. Check return type, only try to analyze body if return type is not safe
767
+ val target = resolve(v.typeref.symbol.asClass, meth)
768
+ if ! target.hasSource then
769
+ UnknownValue
770
+ else
771
+ val ddef = target.defTree.asInstanceOf [DefDef ]
772
+ val returnType = ddef.tpt.tpe
773
+ if SafeValue .safeTypes.contains(returnType) then
774
+ // since method is pure and return type is safe, no need to analyze method body
775
+ SafeValue (returnType)
776
+ else
777
+ val cls = target.owner.enclosingClass.asClass
778
+ // convert SafeType to an OfClass before analyzing method body
779
+ val ref = OfClass (cls, Bottom , NoSymbol , Nil , Env .NoEnv )
780
+ call(ref, meth, args, receiver, superType, needResolve)
751
781
752
782
case Bottom =>
753
783
Bottom
@@ -774,7 +804,7 @@ class Objects(using Context @constructorOnly):
774
804
Bottom
775
805
else
776
806
// Array.length is OK
777
- SafeValue
807
+ SafeValue (defn. IntType )
778
808
779
809
case ref : Ref =>
780
810
val isLocal = ! meth.owner.isClass
@@ -795,10 +825,10 @@ class Objects(using Context @constructorOnly):
795
825
arr
796
826
else if target.equals(defn.Predef_classOf ) then
797
827
// Predef.classOf is a stub method in tasty and is replaced in backend
798
- SafeValue
828
+ UnknownValue
799
829
else if target.equals(defn.ClassTagModule_apply ) then
800
- // ClassTag and other reflection related values are considered safe
801
- SafeValue
830
+ // ClassTag and other reflection related values are not analyzed
831
+ UnknownValue
802
832
else if target.hasSource then
803
833
val cls = target.owner.enclosingClass.asClass
804
834
val ddef = target.defTree.asInstanceOf [DefDef ]
@@ -886,6 +916,7 @@ class Objects(using Context @constructorOnly):
886
916
Returns .installHandler(ctor)
887
917
eval(ddef.rhs, ref, cls, cacheResult = true )
888
918
Returns .popHandler(ctor)
919
+ value
889
920
}
890
921
else
891
922
// no source code available
@@ -912,8 +943,9 @@ class Objects(using Context @constructorOnly):
912
943
else
913
944
UnknownValue
914
945
915
- case SafeValue =>
916
- SafeValue
946
+ case v @ SafeValue (_) =>
947
+ report.warning(" [Internal error] Unexpected selection on safe value " + v.show + " , field = " + field.show + Trace .show, Trace .position)
948
+ Bottom
917
949
918
950
case Package (packageSym) =>
919
951
if field.isStaticObject then
@@ -997,7 +1029,7 @@ class Objects(using Context @constructorOnly):
997
1029
case arr : OfArray =>
998
1030
report.warning(" [Internal error] unexpected tree in assignment, array = " + arr.show + " field = " + field + Trace .show, Trace .position)
999
1031
1000
- case SafeValue | UnknownValue =>
1032
+ case SafeValue (_) | UnknownValue =>
1001
1033
report.warning(" Assigning to base or unknown value is forbidden. " + Trace .show, Trace .position)
1002
1034
1003
1035
case ValueSet (values) =>
@@ -1029,7 +1061,7 @@ class Objects(using Context @constructorOnly):
1029
1061
*/
1030
1062
def instantiate (outer : Value , klass : ClassSymbol , ctor : Symbol , args : List [ArgInfo ]): Contextual [Value ] = log(" instantiating " + klass.show + " , outer = " + outer + " , args = " + args.map(_.value.show), printer, (_ : Value ).show) {
1031
1063
outer.filterClass(klass.owner) match
1032
- case _ : Fun | _ : OfArray | SafeValue =>
1064
+ case _ : Fun | _ : OfArray | SafeValue (_) =>
1033
1065
report.warning(" [Internal error] unexpected outer in instantiating a class, outer = " + outer.show + " , class = " + klass.show + " , " + Trace .show, Trace .position)
1034
1066
Bottom
1035
1067
@@ -1126,7 +1158,7 @@ class Objects(using Context @constructorOnly):
1126
1158
case UnknownValue =>
1127
1159
report.warning(" Calling on unknown value. " + Trace .show, Trace .position)
1128
1160
Bottom
1129
- case _ : ValueSet | _ : Ref | _ : OfArray | _ : Package | SafeValue =>
1161
+ case _ : ValueSet | _ : Ref | _ : OfArray | _ : Package | SafeValue (_) =>
1130
1162
report.warning(" [Internal error] Unexpected by-name value " + value.show + " . " + Trace .show, Trace .position)
1131
1163
Bottom
1132
1164
else
@@ -1314,8 +1346,8 @@ class Objects(using Context @constructorOnly):
1314
1346
case _ : This =>
1315
1347
evalType(expr.tpe, thisV, klass)
1316
1348
1317
- case Literal (_ ) =>
1318
- SafeValue
1349
+ case Literal (const ) =>
1350
+ SafeValue (const.tpe)
1319
1351
1320
1352
case Typed (expr, tpt) =>
1321
1353
if tpt.tpe.hasAnnotation(defn.UncheckedAnnot ) then
@@ -1390,7 +1422,12 @@ class Objects(using Context @constructorOnly):
1390
1422
res
1391
1423
1392
1424
case SeqLiteral (elems, elemtpt) =>
1393
- evalExprs(elems, thisV, klass).join
1425
+ // Obtain the output Seq from SeqLiteral tree by calling respective wrapArrayMethod
1426
+ val wrapArrayMethodName = ast.tpd.wrapArrayMethodName(elemtpt.tpe)
1427
+ val meth = defn.getWrapVarargsArrayModule.requiredMethod(wrapArrayMethodName)
1428
+ val module = defn.getWrapVarargsArrayModule.moduleClass.asClass
1429
+ val args = evalArgs(elems.map(Arg .apply), thisV, klass)
1430
+ call(ObjectRef (module), meth, args, module.typeRef, NoType )
1394
1431
1395
1432
case Inlined (call, bindings, expansion) =>
1396
1433
evalExprs(bindings, thisV, klass)
@@ -1601,7 +1638,7 @@ class Objects(using Context @constructorOnly):
1601
1638
1602
1639
// call .apply
1603
1640
val applyDenot = getMemberMethod(scrutineeType, nme.apply, applyType(elemType))
1604
- val applyRes = call(scrutinee, applyDenot.symbol, ArgInfo (SafeValue , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1641
+ val applyRes = call(scrutinee, applyDenot.symbol, ArgInfo (SafeValue (defn. IntType ) , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1605
1642
1606
1643
if isWildcardStarArgList(pats) then
1607
1644
if pats.size == 1 then
@@ -1612,7 +1649,7 @@ class Objects(using Context @constructorOnly):
1612
1649
else
1613
1650
// call .drop
1614
1651
val dropDenot = getMemberMethod(scrutineeType, nme.drop, dropType(elemType))
1615
- val dropRes = call(scrutinee, dropDenot.symbol, ArgInfo (SafeValue , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1652
+ val dropRes = call(scrutinee, dropDenot.symbol, ArgInfo (SafeValue (defn. IntType ) , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1616
1653
for pat <- pats.init do evalPattern(applyRes, pat)
1617
1654
evalPattern(dropRes, pats.last)
1618
1655
end if
@@ -1623,8 +1660,7 @@ class Objects(using Context @constructorOnly):
1623
1660
end evalSeqPatterns
1624
1661
1625
1662
def canSkipCase (remainingScrutinee : Value , catchValue : Value ) =
1626
- (remainingScrutinee == Bottom && scrutinee != Bottom ) ||
1627
- (catchValue == Bottom && remainingScrutinee != Bottom )
1663
+ remainingScrutinee == Bottom || catchValue == Bottom
1628
1664
1629
1665
var remainingScrutinee = scrutinee
1630
1666
val caseResults : mutable.ArrayBuffer [Value ] = mutable.ArrayBuffer ()
@@ -1653,8 +1689,8 @@ class Objects(using Context @constructorOnly):
1653
1689
*/
1654
1690
def evalType (tp : Type , thisV : ThisValue , klass : ClassSymbol , elideObjectAccess : Boolean = false ): Contextual [Value ] = log(" evaluating " + tp.show, printer, (_ : Value ).show) {
1655
1691
tp match
1656
- case _ : ConstantType =>
1657
- SafeValue
1692
+ case consttpe : ConstantType =>
1693
+ SafeValue (consttpe.underlying)
1658
1694
1659
1695
case tmref : TermRef if tmref.prefix == NoPrefix =>
1660
1696
val sym = tmref.symbol
@@ -1904,7 +1940,7 @@ class Objects(using Context @constructorOnly):
1904
1940
resolveThis(target, ref.outerValue(klass), outerCls)
1905
1941
case ValueSet (values) =>
1906
1942
values.map(ref => resolveThis(target, ref, klass)).join
1907
- case _ : Fun | _ : OfArray | _ : Package | SafeValue =>
1943
+ case _ : Fun | _ : OfArray | _ : Package | SafeValue (_) =>
1908
1944
report.warning(" [Internal error] unexpected thisV = " + thisV + " , target = " + target.show + " , klass = " + klass.show + Trace .show, Trace .position)
1909
1945
Bottom
1910
1946
}
0 commit comments