@@ -266,9 +266,10 @@ public void VisitNode (JSFieldAccess fa) {
266266 parentNodeIndices , StatementIndex , NodeIndex , field . Field . DeclaringType
267267 ) ) ;
268268 } else if ( v != null ) {
269- State . SideEffects . Add ( new FunctionAnalysis1stPass . SideEffect (
270- parentNodeIndices , StatementIndex , NodeIndex , v . Identifier , "field modified"
271- ) ) ;
269+ if ( fa . IsWrite )
270+ State . SideEffects . Add ( new FunctionAnalysis1stPass . SideEffect (
271+ parentNodeIndices , StatementIndex , NodeIndex , v . Identifier , "field modified"
272+ ) ) ;
272273 }
273274
274275 bool isRead = true ;
@@ -403,7 +404,7 @@ public void VisitNode (JSInvocationExpression ie) {
403404 ) ) ;
404405
405406 // HACK: Synthesize an assignment record for direct invocations of constructors on struct locals
406- if ( ( method ) != null && ( method . Method . Name == ".ctor" ) ) {
407+ if ( ( method != null ) && ( method . Method . Name == ".ctor" ) ) {
407408 var t = thisVar . GetActualType ( TypeSystem ) ;
408409 var synthesizedAssignment = new FunctionAnalysis1stPass . Assignment (
409410 pni , StatementIndex , NodeIndex , thisVar . Name ,
@@ -749,6 +750,7 @@ public class FunctionAnalysis2ndPass {
749750 public readonly HashSet < HashSet < FieldInfo > > RecursivelyMutatedFields ;
750751 private readonly HashSet < string > ModifiedVariables ;
751752 private readonly HashSet < string > EscapingVariables ;
753+ public readonly HashSet < string > IndirectSideEffectVariables ;
752754 public readonly string ResultVariable ;
753755 public readonly bool ResultIsNew ;
754756 public readonly bool ViolatesThisReferenceImmutability ;
@@ -768,6 +770,15 @@ bool isSealed
768770 Data = data ;
769771 IsSealed = isSealed ;
770772
773+ if ( data == null )
774+ throw new ArgumentNullException ( "data" ) ;
775+ else if ( data . Function == null )
776+ throw new ArgumentNullException ( "data.Function" ) ;
777+ else if ( data . Function . Method == null )
778+ throw new ArgumentNullException ( "data.Function.Method" ) ;
779+ else if ( data . Function . Method . Method == null )
780+ throw new ArgumentNullException ( "data.Function.Method.Method" ) ;
781+
771782 if ( data . Function . Method . Method . Metadata . HasAttribute ( "JSIsPure" ) )
772783 _IsPure = true ;
773784 else
@@ -940,14 +951,60 @@ from p in data.Function.Parameters select p.Name
940951 }
941952
942953 if (
943- ! data . Function . Method . Method . IsStatic &&
944- data . Function . Method . Method . DeclaringType . IsImmutable &&
945- data . ReassignsThisReference
954+ (
955+ ! data . Function . Method . Method . IsStatic &&
956+ data . Function . Method . Method . DeclaringType . IsImmutable &&
957+ data . ReassignsThisReference
958+ ) ||
959+ data . Function . Method . Method . Name == ".ctor"
946960 ) {
947961 ViolatesThisReferenceImmutability = true ;
948962 ModifiedVariables . Add ( "this" ) ;
949963 }
950964
965+ IndirectSideEffectVariables = new HashSet < string > ( ) ;
966+
967+ // Invocations that reassign or mutate this need to have a synthesized side effect
968+ foreach ( var invocation in data . Invocations ) {
969+ if ( invocation . ThisVariable == null )
970+ continue ;
971+ else if ( IndirectSideEffectVariables . Contains ( invocation . ThisVariable ) )
972+ continue ;
973+
974+ bool shouldSynthesizeSideEffect = false ;
975+ do {
976+ var jsm = invocation . Method ;
977+ if ( jsm == null ) {
978+ shouldSynthesizeSideEffect = true ;
979+ break ;
980+ }
981+
982+ var targetSecondPass = functionCache . GetSecondPass ( invocation . Method , data . Identifier ) ;
983+ if ( targetSecondPass == null ) {
984+ shouldSynthesizeSideEffect = true ;
985+ break ;
986+ }
987+
988+ if ( targetSecondPass . ViolatesThisReferenceImmutability ) {
989+ shouldSynthesizeSideEffect = true ;
990+ break ;
991+ } else if (
992+ ( targetSecondPass . Data != null ) &&
993+ targetSecondPass . Data . SideEffects . Any ( se => se . Variable == "this" )
994+ ) {
995+ // FIXME: Is this necessary or is it overly conservative?
996+ shouldSynthesizeSideEffect = true ;
997+ break ;
998+ }
999+
1000+ if ( ! shouldSynthesizeSideEffect )
1001+ ;
1002+ } while ( false ) ;
1003+
1004+ if ( shouldSynthesizeSideEffect )
1005+ IndirectSideEffectVariables . Add ( invocation . ThisVariable ) ;
1006+ }
1007+
9511008 MutatedFields = new HashSet < FieldInfo > (
9521009 from fa in data . FieldAccesses where ! fa . IsRead select fa . Field . Field
9531010 ) ;
@@ -1006,12 +1063,15 @@ public FunctionAnalysis2ndPass (FunctionCache functionCache, MethodInfo method)
10061063 }
10071064
10081065 VariableAliases = new Dictionary < string , HashSet < string > > ( ) ;
1066+ IndirectSideEffectVariables = new HashSet < string > ( ) ;
10091067
10101068 ResultVariable = null ;
10111069 ResultIsNew = method . Metadata . HasAttribute ( "JSIL.Meta.JSResultIsNew" ) ;
10121070
10131071 MutatedFields = null ;
10141072
1073+ ViolatesThisReferenceImmutability = ( method . Name == ".ctor" ) ;
1074+
10151075 Trace ( method . Member . FullName ) ;
10161076 }
10171077
0 commit comments