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 bad8386b0 FunctionNode: fix missing definitions in FunctionScope when 
emitting methods as JavaScript (references #249)
bad8386b0 is described below

commit bad8386b0555df1b6304cec79267d67ce56bc32f
Author: Josh Tynjala <[email protected]>
AuthorDate: Fri Jun 12 12:20:07 2026 -0700

    FunctionNode: fix missing definitions in FunctionScope when emitting 
methods as JavaScript (references #249)
    
    Uses the existing deferredBodyParsingLock that is already used by 
parseFunctionBody() and discardFunctionBody() to ensure thread safety when 
reconnecting the scope to the ScopedBlockNode, and also when removing stale 
definitions when the function body needs to be re-parsed and will create new 
definitions to replace them.
    
    I ended up moving the changes from 35eed62f13519c659e6346d26cca3f44afe3170f 
from FunctionScope to FunctionNode. FunctionNode is the only place where the 
scope is reconnected, and it's the only place where definitions are already 
added to and removed from the FunctionScope. So it's better to remove stale 
definitions in the same place.
    
    This issue is very difficult to reproduce (although building MXRoyaleBaseJS 
usually does it eventually... sometimes dozens or hundreds of compiles!). My 
best guess is that reconnecting the scope node while the body is currently 
being parsed, or while the definitions are currently being added to the scope, 
is where it leaves the scope without any definitions at all. I see that in 
analyze(), PostProcessStep.POPULATE_SCOPE creates a new FunctionScope, and 
PostProcessStep.RECONNECT_DEFINI [...]
---
 RELEASE_NOTES.md                                   |  1 +
 .../compiler/internal/scopes/FunctionScope.java    | 46 ----------------------
 .../compiler/internal/tree/as/FunctionNode.java    | 39 +++++++++++++++++-
 3 files changed, 39 insertions(+), 47 deletions(-)

diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 9b224eec2..3c26ff715 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -85,6 +85,7 @@ Apache Royale Compiler 1.0.0
 - compiler: Fixed parsing of namespace uri and manifest mappings to allow 
multiple manifest paths per URI.
 - compiler: Fixed missing require for package-level functions or variables in 
some situations.
 - compiler: Fixed missing binding data caused by thread conflicts.
+- compiler: Fixed lost typing information in method bodies when emitting 
JavaScript in some circumstances.
 - debugger: Added missing isolate ID to SWF load and unload events.
 - debugger: Fixed debugger targeting the current JDK version instead of the 
intended minimum JDK version.
 - debugger: Fixed localized messages appearing as unprocessed tokens.
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 b734781f3..c6ccbf297 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,14 +19,7 @@
 
 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
@@ -45,43 +38,4 @@ 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);
-    }
-
 }
diff --git 
a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
 
b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
index 51751d73c..f003f154b 100644
--- 
a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
+++ 
b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
@@ -278,7 +278,44 @@ public class FunctionNode extends BaseTypedDefinitionNode 
implements IFunctionNo
                 // getters and setters
                 if (contents != null && scope != null)
                 {
-                    contents.reconnectScope(scope);
+                    deferredBodyParsingLock.lock();
+                    try
+                    {
+                        // 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.
+                        if (!hasBeenParsed())
+                        {
+                            Collection<IDefinition> localDefs = 
scope.getAllLocalDefinitions();
+                            for (IDefinition def : localDefs)
+                            {
+                                if (! (def instanceof IParameterDefinition))
+                                {
+                                    scope.removeDefinition(def);
+                                }
+                            }
+                        }
+
+                        contents.reconnectScope(scope);
+                    }
+                    finally
+                    {
+                        deferredBodyParsingLock.unlock();
+                    }
                 }
             }
         }

Reply via email to