This is an automated email from the ASF dual-hosted git repository.

joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git


The following commit(s) were added to refs/heads/develop by this push:
     new 35eed62f1 FunctionScope: fix exception where a FunctionNode has been 
garbage collected, and a new FunctionNode gets re-attached to the existing 
FunctionScope scope
35eed62f1 is described below

commit 35eed62f13519c659e6346d26cca3f44afe3170f
Author: Josh Tynjala <[email protected]>
AuthorDate: Thu Apr 3 15:29:28 2025 -0700

    FunctionScope: fix exception where a FunctionNode has been garbage 
collected, and a new FunctionNode gets re-attached to the existing 
FunctionScope scope
    
    Similar to FunctionNode's discardFunctionBody() method, we need to clear 
out any old definitions that will no longer be valid because the new function 
body (even if it has the same contents) will parse new VariableNodes and things 
for local variables and create new definitions to replace the old ones.
    
    This issue is definitely an edge case, but I've hit it more than once over 
the years. It seems to be uncommon for FunctionNodes to get garbage collected, 
but when they do, an assertion failure in parseFunctionBody() gets triggered. 
In particular, this was causing the ASDoc JSON generation to fail 
intermittently (but relatively often, since it parses so many classes). It was 
originally doing that silently (so files weren't generated, but the ASDoc tool 
reported success), but I fixed th [...]
    
    In the past, I think that I encountered this same issue when implementing 
the -watch compiler option. When watching, the compiler continues to run after 
the initial compilation and then recompiles on source path file changes. 
Running indefinitely made it more likely for the GC to eventually run and 
FunctionNodes to be replaced. I found a workaround for that at the time (I 
think using workspace.startBuilding() and  workspace.doneBuilding()), but that 
didn't seem to work here. That make [...]
    
    Removing definitions like discardFunctionBody() when the FunctionScope 
detects that function body hasn't been parsed yet seems like it will be much 
more reliable.
    
    The reason that FunctionNodes can be GCed is that a weak reference to the 
AST is kept by ASCompilationUnit's syntax tree request. At first, it holds a 
hard reference, but at some point later in the compilation process, it is 
deemed safe to be GCed. From that point, it is demoted to a weak reference. 
This appears to be by design in the code that was donated to us by Adobe. This 
process of demoting from strong to weak reference technically allows the 
FunctionNode (technically, I think i [...]
    
    I'm not sure if FunctionScope is the best place to put this code. There may 
be a way to put it into FunctionNode instead, which would keep the quirks of 
FunctionNodes being able to defer parsing bodies or to discard bodies a bit 
more encapsulated. However, even in FunctionScope, that's still pretty specific 
to functions.
---
 .../compiler/internal/scopes/FunctionScope.java    | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git 
a/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/FunctionScope.java
 
b/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/FunctionScope.java
index c6ccbf297..b734781f3 100644
--- 
a/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/FunctionScope.java
+++ 
b/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/FunctionScope.java
@@ -19,7 +19,14 @@
 
 package org.apache.royale.compiler.internal.scopes;
 
+import java.util.Collection;
+
+import org.apache.royale.compiler.definitions.IDefinition;
+import org.apache.royale.compiler.definitions.IParameterDefinition;
+import org.apache.royale.compiler.internal.tree.as.FunctionNode;
 import org.apache.royale.compiler.internal.tree.as.ScopedBlockNode;
+import org.apache.royale.compiler.tree.as.IASNode;
+import org.apache.royale.compiler.tree.as.IScopedNode;
 
 /**
  * Sub-class of ASScope used for function scopes. all definition in scope of
@@ -38,4 +45,43 @@ public class FunctionScope extends ASScope
         super(containingScope);
     }
 
+    @Override
+    public void reconnectScopeNode(IScopedNode node)
+    {
+        IASNode parentNode = node.getParent();
+        if (parentNode instanceof FunctionNode)
+        {
+            FunctionNode functionNode = (FunctionNode) parentNode;
+            if (!functionNode.hasBeenParsed())
+            {
+                // the compiler holds weak references to nodes in certain 
places
+                // which allows them to be garbage collected. however, from 
time
+                // to time, the compiler may need the node again later, after 
it
+                // has been garbage collected, so that node needs to be
+                // recreated.
+                // when a function node is recreated, its scope may still be
+                // populated with definitions from the old function node's 
body,
+                // such as local variables. upon recreation, those local
+                // definitions should be considered invalid because they will
+                // be replaced with new definitions after parsing the new
+                // function node's body.
+                // to reiterate, removing the definitions below is not the same
+                // case where a function node's discardFunctionBody() method is
+                // called and the local definitions need to be removed. 
instead,
+                // this is a separate case where a function node is garbage
+                // collected and the body was never discarded, so the scope
+                // still contains definitions from the old function node.
+                Collection<IDefinition> localDefs = getAllLocalDefinitions();
+                for (IDefinition def : localDefs)
+                {
+                    if (! (def instanceof IParameterDefinition))
+                    {
+                        removeDefinition(def);
+                    }
+                }
+            }
+        }
+        super.reconnectScopeNode(node);
+    }
+
 }

Reply via email to