@@ -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
@@ -669,7 +677,7 @@ class Objects(using Context @constructorOnly):
669
677
a match
670
678
case UnknownValue => UnknownValue
671
679
case Package (_) => a
672
- case SafeValue => SafeValue
680
+ case SafeValue (_) => a
673
681
case ref : Ref => if ref.klass.isSubClass(klass) then ref else Bottom
674
682
case ValueSet (values) => values.map(v => v.filterClass(klass)).join
675
683
case arr : OfArray => if defn.ArrayClass .isSubClass(klass) then arr else Bottom
@@ -698,7 +706,7 @@ class Objects(using Context @constructorOnly):
698
706
* @param superType The type of the super in a super call. NoType for non-super calls.
699
707
* @param needResolve Whether the target of the call needs resolution?
700
708
*/
701
- 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) {
709
+ 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) {
702
710
value.filterClass(meth.owner) match
703
711
case UnknownValue =>
704
712
if reportUnknown then
@@ -708,11 +716,33 @@ class Objects(using Context @constructorOnly):
708
716
UnknownValue
709
717
710
718
case Package (packageSym) =>
711
- report.warning(" [Internal error] Unexpected call on package = " + value.show + " , meth = " + meth.show + Trace .show, Trace .position)
712
- Bottom
713
-
714
- case SafeValue =>
715
- SafeValue // Check return type, if not safe, try to analyze body, 1.until(2).map(i => UninitializedObject)
719
+ // calls on packages are unexpected. However the typer might mistakenly
720
+ // set the receiver to be a package instead of package object.
721
+ // See packageObjectStringInterpolator.scala
722
+ if ! meth.owner.denot.isPackageObject then
723
+ report.warning(" [Internal error] Unexpected call on package = " + value.show + " , meth = " + meth.show + Trace .show, Trace .position)
724
+ Bottom
725
+ else
726
+ // Method call on package object instead
727
+ val packageObj = accessObject(meth.owner.moduleClass.asClass)
728
+ call(packageObj, meth, args, receiver, superType, needResolve)
729
+
730
+ case v @ SafeValue (tpe) =>
731
+ // Assume such method is pure. Check return type, only try to analyze body if return type is not safe
732
+ val target = resolve(v.typeref.symbol.asClass, meth)
733
+ if ! target.hasSource then
734
+ UnknownValue
735
+ else
736
+ val ddef = target.defTree.asInstanceOf [DefDef ]
737
+ val returnType = ddef.tpt.tpe
738
+ if SafeValue .safeTypes.contains(returnType) then
739
+ // since method is pure and return type is safe, no need to analyze method body
740
+ SafeValue (returnType)
741
+ else
742
+ val cls = target.owner.enclosingClass.asClass
743
+ // convert SafeType to an OfClass before analyzing method body
744
+ val ref = OfClass (cls, Bottom , NoSymbol , Nil , Env .NoEnv )
745
+ call(ref, meth, args, receiver, superType, needResolve)
716
746
717
747
case Bottom =>
718
748
Bottom
@@ -739,7 +769,7 @@ class Objects(using Context @constructorOnly):
739
769
Bottom
740
770
else
741
771
// Array.length is OK
742
- SafeValue
772
+ SafeValue (defn. IntType )
743
773
744
774
case ref : Ref =>
745
775
val isLocal = ! meth.owner.isClass
@@ -760,10 +790,10 @@ class Objects(using Context @constructorOnly):
760
790
arr
761
791
else if target.equals(defn.Predef_classOf ) then
762
792
// Predef.classOf is a stub method in tasty and is replaced in backend
763
- SafeValue
793
+ UnknownValue
764
794
else if target.equals(defn.ClassTagModule_apply ) then
765
- // ClassTag and other reflection related values are considered safe
766
- SafeValue
795
+ // ClassTag and other reflection related values are not analyzed
796
+ UnknownValue
767
797
else if target.hasSource then
768
798
val cls = target.owner.enclosingClass.asClass
769
799
val ddef = target.defTree.asInstanceOf [DefDef ]
@@ -851,6 +881,7 @@ class Objects(using Context @constructorOnly):
851
881
Returns .installHandler(ctor)
852
882
eval(ddef.rhs, ref, cls, cacheResult = true )
853
883
Returns .popHandler(ctor)
884
+ value
854
885
}
855
886
else
856
887
// no source code available
@@ -877,8 +908,9 @@ class Objects(using Context @constructorOnly):
877
908
else
878
909
UnknownValue
879
910
880
- case SafeValue =>
881
- SafeValue
911
+ case v @ SafeValue (_) =>
912
+ report.warning(" [Internal error] Unexpected selection on safe value " + v.show + " , field = " + field.show + Trace .show, Trace .position)
913
+ Bottom
882
914
883
915
case Package (packageSym) =>
884
916
if field.isStaticObject then
@@ -962,7 +994,7 @@ class Objects(using Context @constructorOnly):
962
994
case arr : OfArray =>
963
995
report.warning(" [Internal error] unexpected tree in assignment, array = " + arr.show + " field = " + field + Trace .show, Trace .position)
964
996
965
- case SafeValue | UnknownValue =>
997
+ case SafeValue (_) | UnknownValue =>
966
998
report.warning(" Assigning to base or unknown value is forbidden. " + Trace .show, Trace .position)
967
999
968
1000
case ValueSet (values) =>
@@ -994,7 +1026,7 @@ class Objects(using Context @constructorOnly):
994
1026
*/
995
1027
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) {
996
1028
outer.filterClass(klass.owner) match
997
- case _ : Fun | _ : OfArray | SafeValue =>
1029
+ case _ : Fun | _ : OfArray | SafeValue (_) =>
998
1030
report.warning(" [Internal error] unexpected outer in instantiating a class, outer = " + outer.show + " , class = " + klass.show + " , " + Trace .show, Trace .position)
999
1031
Bottom
1000
1032
@@ -1089,7 +1121,7 @@ class Objects(using Context @constructorOnly):
1089
1121
case UnknownValue =>
1090
1122
report.warning(" Calling on unknown value. " + Trace .show, Trace .position)
1091
1123
Bottom
1092
- case _ : ValueSet | _ : Ref | _ : OfArray | _ : Package | SafeValue =>
1124
+ case _ : ValueSet | _ : Ref | _ : OfArray | _ : Package | SafeValue (_) =>
1093
1125
report.warning(" [Internal error] Unexpected by-name value " + value.show + " . " + Trace .show, Trace .position)
1094
1126
Bottom
1095
1127
else
@@ -1276,8 +1308,8 @@ class Objects(using Context @constructorOnly):
1276
1308
case _ : This =>
1277
1309
evalType(expr.tpe, thisV, klass)
1278
1310
1279
- case Literal (_ ) =>
1280
- SafeValue
1311
+ case Literal (const ) =>
1312
+ SafeValue (const.tpe)
1281
1313
1282
1314
case Typed (expr, tpt) =>
1283
1315
if tpt.tpe.hasAnnotation(defn.UncheckedAnnot ) then
@@ -1352,7 +1384,12 @@ class Objects(using Context @constructorOnly):
1352
1384
res
1353
1385
1354
1386
case SeqLiteral (elems, elemtpt) =>
1355
- evalExprs(elems, thisV, klass).join
1387
+ // Obtain the output Seq from SeqLiteral tree by calling respective wrapArrayMethod
1388
+ val wrapArrayMethodName = ast.tpd.wrapArrayMethodName(elemtpt.tpe)
1389
+ val meth = defn.getWrapVarargsArrayModule.requiredMethod(wrapArrayMethodName)
1390
+ val module = defn.getWrapVarargsArrayModule.moduleClass.asClass
1391
+ val args = evalArgs(elems.map(Arg .apply), thisV, klass)
1392
+ call(ObjectRef (module), meth, args, module.typeRef, NoType )
1356
1393
1357
1394
case Inlined (call, bindings, expansion) =>
1358
1395
evalExprs(bindings, thisV, klass)
@@ -1563,7 +1600,7 @@ class Objects(using Context @constructorOnly):
1563
1600
1564
1601
// call .apply
1565
1602
val applyDenot = getMemberMethod(scrutineeType, nme.apply, applyType(elemType))
1566
- val applyRes = call(scrutinee, applyDenot.symbol, ArgInfo (SafeValue , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1603
+ val applyRes = call(scrutinee, applyDenot.symbol, ArgInfo (SafeValue (defn. IntType ) , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1567
1604
1568
1605
if isWildcardStarArgList(pats) then
1569
1606
if pats.size == 1 then
@@ -1574,7 +1611,7 @@ class Objects(using Context @constructorOnly):
1574
1611
else
1575
1612
// call .drop
1576
1613
val dropDenot = getMemberMethod(scrutineeType, nme.drop, dropType(elemType))
1577
- val dropRes = call(scrutinee, dropDenot.symbol, ArgInfo (SafeValue , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1614
+ val dropRes = call(scrutinee, dropDenot.symbol, ArgInfo (SafeValue (defn. IntType ) , summon[Trace ], EmptyTree ) :: Nil , scrutineeType, superType = NoType , needResolve = true )
1578
1615
for pat <- pats.init do evalPattern(applyRes, pat)
1579
1616
evalPattern(dropRes, pats.last)
1580
1617
end if
@@ -1585,8 +1622,7 @@ class Objects(using Context @constructorOnly):
1585
1622
end evalSeqPatterns
1586
1623
1587
1624
def canSkipCase (remainingScrutinee : Value , catchValue : Value ) =
1588
- (remainingScrutinee == Bottom && scrutinee != Bottom ) ||
1589
- (catchValue == Bottom && remainingScrutinee != Bottom )
1625
+ remainingScrutinee == Bottom || catchValue == Bottom
1590
1626
1591
1627
var remainingScrutinee = scrutinee
1592
1628
val caseResults : mutable.ArrayBuffer [Value ] = mutable.ArrayBuffer ()
@@ -1615,8 +1651,8 @@ class Objects(using Context @constructorOnly):
1615
1651
*/
1616
1652
def evalType (tp : Type , thisV : ThisValue , klass : ClassSymbol , elideObjectAccess : Boolean = false ): Contextual [Value ] = log(" evaluating " + tp.show, printer, (_ : Value ).show) {
1617
1653
tp match
1618
- case _ : ConstantType =>
1619
- SafeValue
1654
+ case consttpe : ConstantType =>
1655
+ SafeValue (consttpe.underlying)
1620
1656
1621
1657
case tmref : TermRef if tmref.prefix == NoPrefix =>
1622
1658
val sym = tmref.symbol
@@ -1866,7 +1902,7 @@ class Objects(using Context @constructorOnly):
1866
1902
resolveThis(target, ref.outerValue(klass), outerCls)
1867
1903
case ValueSet (values) =>
1868
1904
values.map(ref => resolveThis(target, ref, klass)).join
1869
- case _ : Fun | _ : OfArray | _ : Package | SafeValue =>
1905
+ case _ : Fun | _ : OfArray | _ : Package | SafeValue (_) =>
1870
1906
report.warning(" [Internal error] unexpected thisV = " + thisV + " , target = " + target.show + " , klass = " + klass.show + Trace .show, Trace .position)
1871
1907
Bottom
1872
1908
}
0 commit comments