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 32c533852 null conditional operator with dynamic access
32c533852 is described below

commit 32c533852cc23f1d2019ed09de681a83440b3f05
Author: Josh Tynjala <[email protected]>
AuthorDate: Wed Aug 13 13:10:02 2025 -0700

    null conditional operator with dynamic access
    
    Example: a?.['b']
    
    This new syntax enables a combination of the null conditional operator 
(a?.b) and dynamic access (a['b']). In the example above, if a is nullish, the 
operator will return null instead of trying to access the field named 'b'.
    
    With a?.b, the name of b must be known at compile-time. With a?.['b'], the 
field name b can be generated at run-time.
---
 .../royale/compiler/internal/parsing/as/ASParser.g |  34 +-
 .../compiler/internal/parsing/as/BaseASParser.java | 177 +++++--
 ... => ASNullConditionalOperatorDynamicTests.java} | 542 +++++++++++----------
 .../java/as/ASNullConditionalOperatorTests.java    |  14 -
 4 files changed, 463 insertions(+), 304 deletions(-)

diff --git 
a/compiler/src/main/antlr/org/apache/royale/compiler/internal/parsing/as/ASParser.g
 
b/compiler/src/main/antlr/org/apache/royale/compiler/internal/parsing/as/ASParser.g
index 7996c3c46..c7dd0992d 100644
--- 
a/compiler/src/main/antlr/org/apache/royale/compiler/internal/parsing/as/ASParser.g
+++ 
b/compiler/src/main/antlr/org/apache/royale/compiler/internal/parsing/as/ASParser.g
@@ -3222,10 +3222,7 @@ propertyAccessExpression [ExpressionNodeBase l] returns 
[ExpressionNodeBase n]
         { n = new MemberAccessExpressionNode(l, op, r); }
     |   TOKEN_OPERATOR_DESCENDANT_ACCESS r=accessPart
         { n = new MemberAccessExpressionNode(l, op, r); }
-    |   TOKEN_OPERATOR_NULL_CONDITIONAL_ACCESS r=nullConditionalAccessPart
-        {
-                       n = transformNullConditional(l, op, r);
-               }
+    |   TOKEN_OPERATOR_NULL_CONDITIONAL_ACCESS n=nullConditionalExpression[l, 
op]
     |   TOKEN_OPERATOR_NS_QUALIFIER r=nsAccessPart
         { if (l instanceof NamespaceIdentifierNode)
           {
@@ -3278,6 +3275,35 @@ nsAccessPart returns [ExpressionNodeBase n]
        ;
        exception catch [RecognitionException ex] { n = 
handleMissingIdentifier(ex);  }
        
+/**
+ * Matches a ?. null conditional expression.
+ */
+nullConditionalExpression [ExpressionNodeBase l, ASToken op] returns 
[ExpressionNodeBase n]
+{
+       n = null;
+    ExpressionNodeBase r = null;
+       DynamicAccessNode d = null;
+}
+       :       r=nullConditionalAccessPart
+        {
+                       n = transformNullConditional(l, op, r);
+               }
+       |   (
+                       d=bracketExpression[l]
+                       {
+                               n = (ExpressionNodeBase) 
d.getRightOperandNode();
+                       }
+                       (
+                                       n=arguments[n]
+                               |       n=propertyAccessExpression[n]
+                       )*
+               )
+               {
+                       n = transformNullConditionalDynamicAccess(l, op, d, n);
+               }
+       ;
+       exception catch [RecognitionException ex] { n = 
handleMissingIdentifier(ex);  }
+
 /**
  * Matches parts after the ?. in a null conditional access expression.
  */
diff --git 
a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/BaseASParser.java
 
b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/BaseASParser.java
index f3785b8ce..0b93614a0 100644
--- 
a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/BaseASParser.java
+++ 
b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/BaseASParser.java
@@ -3371,6 +3371,12 @@ abstract class BaseASParser extends LLkParser implements 
IProblemReporter
         return ternaryNode;
     }
 
+    private enum NullConditionalType
+    {
+        MEMBER_ACCESS,
+        DYNAMIC_ACCESS;
+    }
+
     /**
      * The null conditional operator is rewritten as a ternary operator, in
      * order to support SWF without adding new bytecode.
@@ -3382,23 +3388,26 @@ abstract class BaseASParser extends LLkParser 
implements IProblemReporter
      * performed on null.
      */
     private static class NullConditionalTernaryOperatorNode extends 
TernaryOperatorNode
-    {
+    {   
+        private NullConditionalType type;
         private ExpressionNodeBase originalLeftOperandNode;
         private ExpressionNodeBase originalRightOperandNode;
         private ASToken originalOperator;
         private Collection<ICompilerProblem> problems;
 
-        public NullConditionalTernaryOperatorNode(ExpressionNodeBase 
leftOperandNode, ASToken operator, ExpressionNodeBase rightOperandNode, 
Collection<ICompilerProblem> problems)
+        private NullConditionalTernaryOperatorNode(NullConditionalType type,
+            ExpressionNodeBase leftOperandNode, ASToken operator, 
ExpressionNodeBase rightNode, Collection<ICompilerProblem> problems)
         {
             super(new ASToken(ASTokenTypes.TOKEN_OPERATOR_TERNARY, -1, -1, -1, 
-1, "?"),
                 generateConditionalNode(leftOperandNode),
                 generateLeftResultNode(),
-                generateRightResultNode(leftOperandNode, operator, 
rightOperandNode, problems));
+                generateRightResultNode(type, leftOperandNode, operator, 
rightNode, problems));
             setHasParenthesis(true);
+            this.type = type;
+            this.problems = problems;
             originalLeftOperandNode = leftOperandNode;
-            originalRightOperandNode = rightOperandNode;
             originalOperator = operator;
-            this.problems = problems;
+            originalRightOperandNode = rightNode;
         }
 
         private static ExpressionNodeBase 
generateConditionalNode(ExpressionNodeBase leftOperandNode)
@@ -3407,7 +3416,7 @@ abstract class BaseASParser extends LLkParser implements 
IProblemReporter
             ASToken conditionNullToken = new 
ASToken(ASTokenTypes.TOKEN_KEYWORD_NULL, -1, -1, -1, -1, "null");
             LiteralNode conditionNullNode = new 
LiteralNode(conditionNullToken, LiteralType.NULL);
             return new BinaryOperatorEqualNode(conditionEqualToken, 
leftOperandNode, conditionNullNode);
-        } 
+        }
 
         private static ExpressionNodeBase generateLeftResultNode()
         {
@@ -3415,57 +3424,113 @@ abstract class BaseASParser extends LLkParser 
implements IProblemReporter
             return new LiteralNode(resultNullToken, LiteralType.NULL);
         }
 
-        private static ExpressionNodeBase 
generateRightResultNode(ExpressionNodeBase l, ASToken op, ExpressionNodeBase r, 
Collection<ICompilerProblem> problems)
+        private static ExpressionNodeBase 
generateRightResultNode(NullConditionalType type, ExpressionNodeBase l, ASToken 
op, ExpressionNodeBase r, Collection<ICompilerProblem> problems)
         {
             if (r instanceof NullConditionalTernaryOperatorNode)
             {
                 NullConditionalTernaryOperatorNode rightNullConditional = 
(NullConditionalTernaryOperatorNode) r;
 
                 ExpressionNodeBase oldNullConditionalLeft = 
rightNullConditional.getNullConditionalLeftOperandNode();
-                ExpressionNodeBase memberAccessRight = oldNullConditionalLeft;
-                if (oldNullConditionalLeft instanceof 
MemberAccessExpressionNode)
+
+                if (NullConditionalType.DYNAMIC_ACCESS.equals(type))
+                {
+                    ExpressionNodeBase newDynamicAccessRightOperand = 
oldNullConditionalLeft;
+                    if (oldNullConditionalLeft instanceof DynamicAccessNode)
+                    {
+                        // replace the entire left side, but keeping the right 
side
+                        DynamicAccessNode oldLeftDynamicAccess = 
(DynamicAccessNode) oldNullConditionalLeft;
+                        newDynamicAccessRightOperand = (ExpressionNodeBase) 
oldLeftDynamicAccess.getRightOperandNode();
+                    }
+                    DynamicAccessNode newDynamicAccessNode = new 
DynamicAccessNode(l);
+                    
newDynamicAccessNode.setRightOperandNode(newDynamicAccessRightOperand);
+                    ExpressionNodeBase newNullConditionalLeftOperand = 
rewriteDynamicAccess(newDynamicAccessNode);
+                    
rightNullConditional.setNullConditionalLeftOperandNode(newNullConditionalLeftOperand);
+                }
+                else
                 {
-                    // replace the entire left side, but keeping the right side
-                    MemberAccessExpressionNode oldLeftMemberAccess = 
(MemberAccessExpressionNode) oldNullConditionalLeft;
-                    memberAccessRight = (ExpressionNodeBase) 
oldLeftMemberAccess.getRightOperandNode();
+                    ExpressionNodeBase newMemberAccessRightOperand = 
oldNullConditionalLeft;
+                    if (oldNullConditionalLeft instanceof 
MemberAccessExpressionNode)
+                    {
+                        // replace the entire left side, but keeping the right 
side
+                        MemberAccessExpressionNode oldLeftMemberAccess = 
(MemberAccessExpressionNode) oldNullConditionalLeft;
+                        newMemberAccessRightOperand = (ExpressionNodeBase) 
oldLeftMemberAccess.getRightOperandNode();
+                    }
+                    ASToken memberAccessOperator = new 
ASToken(ASTokenTypes.TOKEN_OPERATOR_MEMBER_ACCESS, op.getStart(), op.getEnd(), 
op.getLine(), op.getColumn(), ".");
+                    MemberAccessExpressionNode newMemberAccessNode = new 
MemberAccessExpressionNode(l, memberAccessOperator, 
newMemberAccessRightOperand);
+                    ExpressionNodeBase newNullConditionalLeftOperand = 
rewriteMemberAccess(newMemberAccessNode);
+                    
rightNullConditional.setNullConditionalLeftOperandNode(newNullConditionalLeftOperand);
                 }
-                ASToken memberAccessOperator = new 
ASToken(ASTokenTypes.TOKEN_OPERATOR_MEMBER_ACCESS, op.getStart(), op.getEnd(), 
op.getLine(), op.getColumn(), ".");
-                MemberAccessExpressionNode memberAccessNode = new 
MemberAccessExpressionNode(l, memberAccessOperator, memberAccessRight);         
       
-                
rightNullConditional.setNullConditionalLeftOperandNode(rewriteMemberAccess(memberAccessNode));
-
                 return rightNullConditional;
             }
 
             FunctionCallNode originalFunctionCallNode = null;
             DynamicAccessNode originalDynamicAccessNode = null;
             MemberAccessExpressionNode originalMemberAccessNode = null;
-            ExpressionNodeBase memberAccessRight = r;
+            ExpressionNodeBase newRightOperand = r;
             if (r instanceof FunctionCallNode)
             {
                 originalFunctionCallNode = (FunctionCallNode) r;
-                memberAccessRight = (ExpressionNodeBase) 
originalFunctionCallNode.getNameNode();
+                newRightOperand = (ExpressionNodeBase) 
originalFunctionCallNode.getNameNode();
             }
             else if (r instanceof DynamicAccessNode)
             {
                 originalDynamicAccessNode = (DynamicAccessNode) r;
-                memberAccessRight = (ExpressionNodeBase) 
originalDynamicAccessNode.getLeftOperandNode();
+                newRightOperand = (ExpressionNodeBase) 
originalDynamicAccessNode.getLeftOperandNode();
             }
             else if (r instanceof MemberAccessExpressionNode)
             {
                 originalMemberAccessNode = (MemberAccessExpressionNode) r;
-                memberAccessRight = (ExpressionNodeBase) 
originalMemberAccessNode.getLeftOperandNode();
-            }
-            else if (!(r instanceof IIdentifierNode))
-            {
-                problems.add(new SyntaxProblem(r, r.getNodeKind()));
-                return null;
+                newRightOperand = (ExpressionNodeBase) 
originalMemberAccessNode.getLeftOperandNode();
             }
 
             ExpressionNodeBase expressionNode = null;
-            if (memberAccessRight != null)
+            if (NullConditionalType.DYNAMIC_ACCESS.equals(type))
+            {
+                DynamicAccessNode dynamicAccessNode = new DynamicAccessNode(l);
+                if (newRightOperand instanceof MemberAccessExpressionNode)
+                {
+                    MemberAccessExpressionNode rightMemberAccess = 
(MemberAccessExpressionNode) newRightOperand;
+                    
dynamicAccessNode.setRightOperandNode((ExpressionNodeBase)rightMemberAccess.getLeftOperandNode());
+
+                    ASToken memberAccessOperator = new 
ASToken(ASTokenTypes.TOKEN_OPERATOR_MEMBER_ACCESS, op.getStart(), op.getEnd(), 
op.getLine(), op.getColumn(), ".");
+                    MemberAccessExpressionNode newMemberAccessNode = new 
MemberAccessExpressionNode(dynamicAccessNode, memberAccessOperator, 
(ExpressionNodeBase)rightMemberAccess.getRightOperandNode());
+
+                    expressionNode = rewriteMemberAccess(newMemberAccessNode);
+                }
+                else if (newRightOperand instanceof DynamicAccessNode)
+                {
+                    DynamicAccessNode rightDynamicAccses = (DynamicAccessNode) 
newRightOperand;
+                    
dynamicAccessNode.setRightOperandNode((ExpressionNodeBase)rightDynamicAccses.getLeftOperandNode());
+
+                    DynamicAccessNode newDynamicAccessNode = new 
DynamicAccessNode(dynamicAccessNode);
+                    
newDynamicAccessNode.setRightOperandNode((ExpressionNodeBase)rightDynamicAccses.getRightOperandNode());
+
+                    expressionNode = 
rewriteDynamicAccess(newDynamicAccessNode);
+                }
+                else if (newRightOperand instanceof FunctionCallNode)
+                {
+                    FunctionCallNode rightFunctionCall = (FunctionCallNode) 
newRightOperand;
+                    
dynamicAccessNode.setRightOperandNode((ExpressionNodeBase)rightFunctionCall.getNameNode());
+
+                    FunctionCallNode newFunctionCallNode = new 
FunctionCallNode(dynamicAccessNode);
+                    ContainerNode argumentsNode = 
newFunctionCallNode.getArgumentsNode();
+                    for (IExpressionNode argNode : 
rightFunctionCall.getArgumentNodes())
+                    {
+                        argumentsNode.addItem((ExpressionNodeBase) argNode);
+                    }
+
+                    expressionNode = newFunctionCallNode;
+                }
+                else
+                {
+                    dynamicAccessNode.setRightOperandNode(newRightOperand);
+                    expressionNode = dynamicAccessNode;
+                }
+            }
+            else
             {
                 ASToken memberAccessOperator = new 
ASToken(ASTokenTypes.TOKEN_OPERATOR_MEMBER_ACCESS, op.getStart(), op.getEnd(), 
op.getLine(), op.getColumn(), ".");
-                MemberAccessExpressionNode memberAccessNode = new 
MemberAccessExpressionNode(l, memberAccessOperator, memberAccessRight);
+                MemberAccessExpressionNode memberAccessNode = new 
MemberAccessExpressionNode(l, memberAccessOperator, newRightOperand);
                 expressionNode = rewriteMemberAccess(memberAccessNode);
             }
 
@@ -3483,7 +3548,7 @@ abstract class BaseASParser extends LLkParser implements 
IProblemReporter
             {
                 DynamicAccessNode newDynamicAccessNode = new 
DynamicAccessNode(expressionNode);
                 newDynamicAccessNode.setRightOperandNode((ExpressionNodeBase) 
originalDynamicAccessNode.getRightOperandNode());
-                return newDynamicAccessNode;
+                return rewriteDynamicAccess(newDynamicAccessNode);
             }
             else if (originalMemberAccessNode != null)
             {
@@ -3507,7 +3572,7 @@ abstract class BaseASParser extends LLkParser implements 
IProblemReporter
         {
             originalLeftOperandNode = node;
             
setConditionalNode(generateConditionalNode(originalLeftOperandNode));
-            
setRightOperandNode(generateRightResultNode(originalLeftOperandNode, 
originalOperator, originalRightOperandNode, problems));
+            setRightOperandNode(generateRightResultNode(type, 
originalLeftOperandNode, originalOperator, originalRightOperandNode, problems));
         }
 
         private static ASToken 
copyMemberAccessOperator(MemberAccessExpressionNode m, ISourceLocation 
sourceLocation)
@@ -3538,6 +3603,11 @@ abstract class BaseASParser extends LLkParser implements 
IProblemReporter
         private static ExpressionNodeBase 
rewriteMemberAccess(MemberAccessExpressionNode m)
         {
             ExpressionNodeBase leftOperand = (ExpressionNodeBase) 
m.getLeftOperandNode();
+            if (leftOperand instanceof DynamicAccessNode)
+            {
+                leftOperand = rewriteDynamicAccess((DynamicAccessNode) 
leftOperand);
+                m.setLeftOperandNode(leftOperand);
+            }
             ExpressionNodeBase rightOperand = (ExpressionNodeBase) 
m.getRightOperandNode();
             if (rightOperand instanceof DynamicAccessNode)
             {
@@ -3546,7 +3616,7 @@ abstract class BaseASParser extends LLkParser implements 
IProblemReporter
                 MemberAccessExpressionNode innerMemberAccess = new 
MemberAccessExpressionNode(leftOperand, op, (ExpressionNodeBase) 
rightOperandDynamicAccess.getLeftOperandNode());
                 DynamicAccessNode newDynamicAccess = new 
DynamicAccessNode(rewriteMemberAccess(innerMemberAccess));
                 newDynamicAccess.setRightOperandNode((ExpressionNodeBase) 
rightOperandDynamicAccess.getRightOperandNode());
-                return newDynamicAccess;
+                return rewriteDynamicAccess(newDynamicAccess);
             }
             else if (rightOperand instanceof MemberAccessExpressionNode)
             {
@@ -3572,10 +3642,55 @@ abstract class BaseASParser extends LLkParser 
implements IProblemReporter
             }
             return m;
         }
+
+        private static ExpressionNodeBase 
rewriteDynamicAccess(DynamicAccessNode d)
+        {
+            ExpressionNodeBase leftOperand = (ExpressionNodeBase) 
d.getLeftOperandNode();
+            ExpressionNodeBase rightOperand = (ExpressionNodeBase) 
d.getRightOperandNode();
+            if (rightOperand instanceof DynamicAccessNode)
+            {
+                DynamicAccessNode rightOperandDynamicAccess = 
(DynamicAccessNode) rightOperand;
+                DynamicAccessNode innerDynamicAccess = new 
DynamicAccessNode(leftOperand);
+                innerDynamicAccess.setRightOperandNode((ExpressionNodeBase) 
rightOperandDynamicAccess.getLeftOperandNode());
+                DynamicAccessNode newOuterDynamicAccess = new 
DynamicAccessNode(rewriteDynamicAccess(innerDynamicAccess));
+                newOuterDynamicAccess.setRightOperandNode((ExpressionNodeBase) 
rightOperandDynamicAccess.getRightOperandNode());
+                return rewriteDynamicAccess(newOuterDynamicAccess);
+            }
+            else if (rightOperand instanceof MemberAccessExpressionNode)
+            {
+                MemberAccessExpressionNode rightOperandMemberAccess = 
(MemberAccessExpressionNode) rightOperand;
+                ASToken op = 
copyMemberAccessOperator(rightOperandMemberAccess, rightOperandMemberAccess);
+                DynamicAccessNode innerDynamicAccess = new 
DynamicAccessNode(leftOperand);
+                innerDynamicAccess.setRightOperandNode((ExpressionNodeBase) 
rightOperandMemberAccess.getLeftOperandNode());
+                ExpressionNodeBase innerExpression = 
rewriteDynamicAccess(innerDynamicAccess);
+                MemberAccessExpressionNode result = new 
MemberAccessExpressionNode(innerExpression, op, (ExpressionNodeBase) 
rightOperandMemberAccess.getRightOperandNode());
+                return rewriteMemberAccess(result);
+            }
+            else if (rightOperand instanceof FunctionCallNode)
+            {
+                FunctionCallNode rightOperandFunctionCall = (FunctionCallNode) 
rightOperand;
+                DynamicAccessNode innerDynamicAccess = new 
DynamicAccessNode(leftOperand);
+                
innerDynamicAccess.setRightOperandNode(rightOperandFunctionCall.getNameNode());
+                ExpressionNodeBase innerExpression = 
rewriteDynamicAccess(innerDynamicAccess);
+                FunctionCallNode newFunctionCall = new 
FunctionCallNode(innerExpression);
+                ContainerNode argumentsNode = 
newFunctionCall.getArgumentsNode();
+                for (IExpressionNode argNode : 
rightOperandFunctionCall.getArgumentNodes())
+                {
+                    argumentsNode.addItem((ExpressionNodeBase) argNode);
+                }
+                return newFunctionCall;
+            }
+            return d;
+        }
     }
 
     protected final ExpressionNodeBase 
transformNullConditional(ExpressionNodeBase l, ASToken op, ExpressionNodeBase r)
     {
-        return new NullConditionalTernaryOperatorNode(l, op, r, 
getSyntaxProblems());
+        return new 
NullConditionalTernaryOperatorNode(NullConditionalType.MEMBER_ACCESS, l, op, r, 
getSyntaxProblems());
+    }
+
+    protected final ExpressionNodeBase 
transformNullConditionalDynamicAccess(ExpressionNodeBase l, ASToken op, 
DynamicAccessNode d, ExpressionNodeBase r)
+    {
+        return new 
NullConditionalTernaryOperatorNode(NullConditionalType.DYNAMIC_ACCESS, l, op, 
r, getSyntaxProblems());
     }
 }
diff --git a/compiler/src/test/java/as/ASNullConditionalOperatorTests.java 
b/compiler/src/test/java/as/ASNullConditionalOperatorDynamicTests.java
similarity index 58%
copy from compiler/src/test/java/as/ASNullConditionalOperatorTests.java
copy to compiler/src/test/java/as/ASNullConditionalOperatorDynamicTests.java
index 160b02755..a3802052d 100644
--- a/compiler/src/test/java/as/ASNullConditionalOperatorTests.java
+++ b/compiler/src/test/java/as/ASNullConditionalOperatorDynamicTests.java
@@ -24,35 +24,8 @@ import java.io.File;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class ASNullConditionalOperatorTests extends ASFeatureTestsBase
+public class ASNullConditionalOperatorDynamicTests extends ASFeatureTestsBase
 {
-    @Test
-    public void testInvalidSyntaxBeforeDynamicAccess()
-    {
-        String[] testCode = new String[]
-        {
-            "var o:Object = {};",
-            // the ?. operator before [] square brackets is not valid syntax
-            "var result:* = o?.a?.[0];",
-        };
-        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
-
-        compileAndExpectErrors(source, false, false, false, new String[0], 
"'[' is not allowed here\n");
-    }
-
-    @Test
-    public void testInvalidSyntaxBeforeFunctionCall()
-    {
-        String[] testCode = new String[]
-        {
-            "var o:Object = {};",
-            // the ?. operator before () parentheses is not valid syntax
-            "var result:* = o?.a?.toString?.();",
-        };
-        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
-
-        compileAndExpectErrors(source, false, false, false, new String[0], 
"'(' is not allowed here\n");
-    }
 
     // null is considered nullish
     @Test
@@ -61,8 +34,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -76,8 +49,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -91,8 +64,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var b:Boolean = false;",
-            "var result:* = b?.toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = b?.['toString']();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -106,8 +79,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var n:Number = NaN;",
-            "var result:* = n?.toString();",
-            "assertEqual('null conditional', result, 'NaN');",
+            "var result:* = n?.['toString']();",
+            "assertEqual('dynamic null conditional', result, 'NaN');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -121,8 +94,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var n:Number = 0;",
-            "var result:* = n?.toString();",
-            "assertEqual('null conditional', result, '0');",
+            "var result:* = n?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '0');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -136,8 +109,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var s:String = '';",
-            "var result:* = s?.toString();",
-            "assertEqual('null conditional', result, '');",
+            "var result:* = s?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -150,8 +123,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {};",
-            "var result:* = o?.toString();",
-            "assertEqual('null conditional', result, '[object Object]');",
+            "var result:* = o?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '[object 
Object]');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -164,8 +137,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: null};",
-            "var result:* = o?.a?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -178,8 +151,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {};",
-            "var result:* = o?.a?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -192,8 +165,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: undefined};",
-            "var result:* = o?.a?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -206,8 +179,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: false};",
-            "var result:* = o?.a?.toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = o?.['a']?.['toString']();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -220,8 +193,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: 0};",
-            "var result:* = o?.a?.toString();",
-            "assertEqual('null conditional', result, '0');",
+            "var result:* = o?.['a']?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '0');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -234,8 +207,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {}};",
-            "var result:* = o?.a?.toString();",
-            "assertEqual('null conditional', result, '[object Object]');",
+            "var result:* = o?.['a']?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '[object 
Object]');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -248,8 +221,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: null};",
-            "var result:* = o?.a;",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -262,8 +235,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {};",
-            "var result:* = o?.a;",
-            "assertEqual('null conditional', result, undefined);",
+            "var result:* = o?.['a'];",
+            "assertEqual('dynamic null conditional', result, undefined);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -276,8 +249,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: undefined};",
-            "var result:* = o?.a;",
-            "assertEqual('null conditional', result, undefined);",
+            "var result:* = o?.['a'];",
+            "assertEqual('dynamic null conditional', result, undefined);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -290,8 +263,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: false};",
-            "var result:* = o?.a;",
-            "assertEqual('null conditional', result, false);",
+            "var result:* = o?.['a'];",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -304,8 +277,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: 0};",
-            "var result:* = o?.a;",
-            "assertEqual('null conditional', result, 0);",
+            "var result:* = o?.['a'];",
+            "assertEqual('dynamic null conditional', result, 0);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -319,8 +292,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         {
             "var expected:Object = {};",
             "var o:Object = {a: expected};",
-            "var result:* = o?.a;",
-            "assertEqual('null conditional', result, expected);",
+            "var result:* = o?.['a'];",
+            "assertEqual('dynamic null conditional', result, expected);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -333,8 +306,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: null}};",
-            "var result:* = o?.a?.b;",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']?.['b'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -347,8 +320,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {}};",
-            "var result:* = o?.a?.b;",
-            "assertEqual('null conditional', result, undefined);",
+            "var result:* = o?.['a']?.['b'];",
+            "assertEqual('dynamic null conditional', result, undefined);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -361,8 +334,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: undefined}};",
-            "var result:* = o?.a?.b;",
-            "assertEqual('null conditional', result, undefined);",
+            "var result:* = o?.['a']?.['b'];",
+            "assertEqual('dynamic null conditional', result, undefined);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -375,8 +348,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: false}};",
-            "var result:* = o?.a?.b;",
-            "assertEqual('null conditional', result, false);",
+            "var result:* = o?.['a']?.['b'];",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -389,8 +362,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: 0}};",
-            "var result:* = o?.a?.b;",
-            "assertEqual('null conditional', result, 0);",
+            "var result:* = o?.['a']?.['b'];",
+            "assertEqual('dynamic null conditional', result, 0);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -404,8 +377,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         {
             "var expected:Object = {};",
             "var o:Object = {a: {b: expected}};",
-            "var result:* = o?.a?.b;",
-            "assertEqual('null conditional', result, expected);",
+            "var result:* = o?.['a']?.['b'];",
+            "assertEqual('dynamic null conditional', result, expected);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -418,8 +391,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.hasOwnProperty('a');",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['hasOwnProperty']('a');",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -432,8 +405,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.hasOwnProperty('a');",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['hasOwnProperty']('a');",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -446,8 +419,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var b:Boolean = false;",
-            "var result:* = b?.hasOwnProperty('xyz');",
-            "assertEqual('null conditional', result, false);",
+            "var result:* = b?.['hasOwnProperty']('xyz');",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -460,8 +433,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var n:Number = 0;",
-            "var result:* = n?.hasOwnProperty('xyz');",
-            "assertEqual('null conditional', result, false);",
+            "var result:* = n?.['hasOwnProperty']('xyz');",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -474,8 +447,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: 123};",
-            "var result:* = o?.hasOwnProperty('a');",
-            "assertEqual('null conditional', result, true);",
+            "var result:* = o?.['hasOwnProperty']('a');",
+            "assertEqual('dynamic null conditional', result, true);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -488,8 +461,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a['b'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']['b'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -502,8 +475,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a['b'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']['b'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -516,8 +489,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {}};",
-            "var result:* = o?.a['b'];",
-            "assertEqual('null conditional', result, undefined);",
+            "var result:* = o?.['a']['b'];",
+            "assertEqual('dynamic null conditional', result, undefined);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -530,8 +503,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: 2}};",
-            "var result:* = o?.a['b'];",
-            "assertEqual('null conditional', result, 2);",
+            "var result:* = o?.['a']['b'];",
+            "assertEqual('dynamic null conditional', result, 2);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -544,8 +517,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a.b;",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].b;",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -558,8 +531,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a.b;",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].b;",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -572,8 +545,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {}};",
-            "var result:* = o?.a.b;",
-            "assertEqual('null conditional', result, undefined);",
+            "var result:* = o?.['a'].b;",
+            "assertEqual('dynamic null conditional', result, undefined);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -586,8 +559,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: 2}};",
-            "var result:* = o?.a.b;",
-            "assertEqual('null conditional', result, 2);",
+            "var result:* = o?.['a'].b;",
+            "assertEqual('dynamic null conditional', result, 2);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -600,82 +573,82 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = {a: {b: {c: {d1: undefined, d2: null, d3: 0, d4: 
false}}}};",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, undefined);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, 0);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, false);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
'0');",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
'false');",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], undefined);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], 0);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], false);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), '0');",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), 'false');",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
             "o = {a: {b: {c: {}}}};",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, undefined);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, undefined);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, undefined);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, undefined);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], undefined);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], undefined);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], undefined);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], undefined);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
             "o = {a: {b: {}}};",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
             "o = {a: {}};",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
             "o = {};",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
             "o = null;",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
             "o = undefined;",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d3?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d4?.toString(), 
null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d1?.e, null);",
-            "assertEqual('null conditional', o?.a?.b?.c?.d2?.e, null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d3']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d4']?.toString(), null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d1']?.['e'], null);",
+            "assertEqual('dynamic null conditional', 
o?.['a']?.['b']?.['c']?.['d2']?.['e'], null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -692,7 +665,7 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
             "if (o?.toString() === null) {",
             "  result = true;",
             "}",
-            "assertEqual('null conditional', result, true);",
+            "assertEqual('dynamic null conditional', result, true);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -706,10 +679,10 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         {
             "var o:* = undefined;",
             "var result:Boolean = false;",
-            "if (o?.toString() === null) {",
+            "if (o?.['toString']() === null) {",
             "  result = true;",
             "}",
-            "assertEqual('null conditional', result, true);",
+            "assertEqual('dynamic null conditional', result, true);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -723,10 +696,10 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         {
             "var b:Boolean = false;",
             "var result:Boolean = false;",
-            "if (b?.toString() === null) {",
+            "if (b?.['toString']() === null) {",
             "  result = true;",
             "}",
-            "assertEqual('null conditional', result, false);",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -740,10 +713,10 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         {
             "var n:Number = 0;",
             "var result:Boolean = false;",
-            "if (n?.toString() === null) {",
+            "if (n?.['toString']() === null) {",
             "  result = true;",
             "}",
-            "assertEqual('null conditional', result, false);",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -757,10 +730,10 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         {
             "var o:Object = {};",
             "var result:Boolean = false;",
-            "if (o?.toString() === null) {",
+            "if (o?.['toString']() === null) {",
             "  result = true;",
             "}",
-            "assertEqual('null conditional', result, false);",
+            "assertEqual('dynamic null conditional', result, false);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -773,8 +746,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.hasOwnProperty('a')?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['hasOwnProperty']('a')?.toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -787,8 +760,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.hasOwnProperty('a')?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['hasOwnProperty']('a')?.toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -801,8 +774,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var b:Boolean = false;",
-            "var result:* = b?.hasOwnProperty('a')?.toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = b?.['hasOwnProperty']('a')?.toString();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -815,8 +788,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var n:Number = 0;",
-            "var result:* = n?.hasOwnProperty('a')?.toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = n?.['hasOwnProperty']('a')?.toString();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -829,8 +802,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: 123};",
-            "var result:* = o?.hasOwnProperty('a')?.toString();",
-            "assertEqual('null conditional', result, 'true');",
+            "var result:* = o?.['hasOwnProperty']('a')?.toString();",
+            "assertEqual('dynamic null conditional', result, 'true');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -843,8 +816,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a.b.c;",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].b.c;",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -857,8 +830,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a.b.c;",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].b.c;",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -871,8 +844,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: {c: 123}}};",
-            "var result:* = o?.a.b.c;",
-            "assertEqual('null conditional', result, 123);",
+            "var result:* = o?.['a'].b.c;",
+            "assertEqual('dynamic null conditional', result, 123);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -885,8 +858,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -899,8 +872,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -913,8 +886,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {}};",
-            "var result:* = o?.a.toString();",
-            "assertEqual('null conditional', result, '[object Object]');",
+            "var result:* = o?.['a'].toString();",
+            "assertEqual('dynamic null conditional', result, '[object 
Object]');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -927,8 +900,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a.b['c'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].b['c'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -941,8 +914,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a.b['c'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a'].b['c'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -955,8 +928,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: {c: 123}}};",
-            "var result:* = o?.a.b['c'];",
-            "assertEqual('null conditional', result, 123);",
+            "var result:* = o?.['a'].b['c'];",
+            "assertEqual('dynamic null conditional', result, 123);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -969,8 +942,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: null};",
-            "var result:* = o.a?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o.a?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -983,8 +956,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {};",
-            "var result:* = o.a?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o.a?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -997,8 +970,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: undefined};",
-            "var result:* = o.a?.toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o.a?.['toString']();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1011,8 +984,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: false};",
-            "var result:* = o.a?.toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = o.a?.['toString']();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1025,8 +998,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: 0};",
-            "var result:* = o.a?.toString();",
-            "assertEqual('null conditional', result, '0');",
+            "var result:* = o.a?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '0');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1039,8 +1012,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {}};",
-            "var result:* = o.a?.toString();",
-            "assertEqual('null conditional', result, '[object Object]');",
+            "var result:* = o.a?.['toString']();",
+            "assertEqual('dynamic null conditional', result, '[object 
Object]');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1053,8 +1026,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a['b']['c'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']['b']['c'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1067,8 +1040,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a['b']['c'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']['b']['c'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1081,8 +1054,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: {c: 123}}};",
-            "var result:* = o?.a['b']['c'];",
-            "assertEqual('null conditional', result, 123);",
+            "var result:* = o?.['a']['b']['c'];",
+            "assertEqual('dynamic null conditional', result, 123);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1095,8 +1068,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.hasOwnProperty('a').toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['hasOwnProperty']('a').toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1109,8 +1082,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.hasOwnProperty('a').toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['hasOwnProperty']('a').toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1123,8 +1096,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var b:Boolean = false;",
-            "var result:* = b?.hasOwnProperty('a').toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = b?.['hasOwnProperty']('a').toString();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1137,8 +1110,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var n:Number = 0;",
-            "var result:* = n?.hasOwnProperty('a').toString();",
-            "assertEqual('null conditional', result, 'false');",
+            "var result:* = n?.['hasOwnProperty']('a').toString();",
+            "assertEqual('dynamic null conditional', result, 'false');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1151,8 +1124,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: 123};",
-            "var result:* = o?.hasOwnProperty('a').toString();",
-            "assertEqual('null conditional', result, 'true');",
+            "var result:* = o?.['hasOwnProperty']('a').toString();",
+            "assertEqual('dynamic null conditional', result, 'true');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1165,8 +1138,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.a['b'].toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']['b'].toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1179,8 +1152,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.a['b'].toString();",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['a']['b'].toString();",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1193,8 +1166,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {a: {b: 123}};",
-            "var result:* = o?.a['b'].toString();",
-            "assertEqual('null conditional', result, '123');",
+            "var result:* = o?.['a']['b'].toString();",
+            "assertEqual('dynamic null conditional', result, '123');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1207,8 +1180,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = null;",
-            "var result:* = o?.toString()['length'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['toString']()['length'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1221,8 +1194,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:* = undefined;",
-            "var result:* = o?.toString()['length'];",
-            "assertEqual('null conditional', result, null);",
+            "var result:* = o?.['toString']()['length'];",
+            "assertEqual('dynamic null conditional', result, null);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1235,8 +1208,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var b:Boolean = false;",
-            "var result:* = b?.toString()['length'];",
-            "assertEqual('null conditional', result, 5);",
+            "var result:* = b?.['toString']()['length'];",
+            "assertEqual('dynamic null conditional', result, 5);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1249,8 +1222,8 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var n:Number = 0;",
-            "var result:* = n?.toString()['length'];",
-            "assertEqual('null conditional', result, 1);",
+            "var result:* = n?.['toString']()['length'];",
+            "assertEqual('dynamic null conditional', result, 1);",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
@@ -1263,8 +1236,67 @@ public class ASNullConditionalOperatorTests extends 
ASFeatureTestsBase
         String[] testCode = new String[]
         {
             "var o:Object = {};",
-            "var result:* = o?.toString()['length'];",
-            "assertEqual('null conditional', result, '[object 
Object]'.length);",
+            "var result:* = o?.['toString']()['length'];",
+            "assertEqual('dynamic null conditional', result, '[object 
Object]'.length);",
+        };
+        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
+
+        compileAndRun(source);
+    }
+
+    @Test
+    public void testStringFieldNameWithConcatenation()
+    {
+        String[] testCode = new String[]
+        {
+            "var n:Number = 123.4;",
+            "var result:* = n?.['to' + 'String']();",
+            "assertEqual('dynamic null conditional', result, '123.4');",
+        };
+        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
+
+        compileAndRun(source);
+    }
+
+    @Test
+    public void testNestedStringFieldNameWithConcatenation()
+    {
+        String[] testCode = new String[]
+        {
+            "var o:Object = {a: {}}",
+            "var result:* = o?.['a']?.['to' + 'String']();",
+            "assertEqual('dynamic null conditional', result, '[object 
Object]');",
+        };
+        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
+
+        compileAndRun(source);
+    }
+
+    @Test
+    public void testVariableFieldName()
+    {
+        String[] testCode = new String[]
+        {
+            "var n:Number = 123.4;",
+            "var fieldName:String = 'toString';",
+            "var result:* = n?.[fieldName]();",
+            "assertEqual('dynamic null conditional', result, '123.4');",
+        };
+        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
+
+        compileAndRun(source);
+    }
+
+    @Test
+    public void testVariableFieldNameWithConcatenation()
+    {
+        String[] testCode = new String[]
+        {
+            "var n:Number = 123.4;",
+            "var f1:String = 'to';",
+            "var f2:String = 'String';",
+            "var result:* = n?.[f1 + f2]();",
+            "assertEqual('dynamic null conditional', result, '123.4');",
         };
         String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
 
diff --git a/compiler/src/test/java/as/ASNullConditionalOperatorTests.java 
b/compiler/src/test/java/as/ASNullConditionalOperatorTests.java
index 160b02755..9cd4dd2ad 100644
--- a/compiler/src/test/java/as/ASNullConditionalOperatorTests.java
+++ b/compiler/src/test/java/as/ASNullConditionalOperatorTests.java
@@ -26,20 +26,6 @@ import org.junit.Test;
 
 public class ASNullConditionalOperatorTests extends ASFeatureTestsBase
 {
-    @Test
-    public void testInvalidSyntaxBeforeDynamicAccess()
-    {
-        String[] testCode = new String[]
-        {
-            "var o:Object = {};",
-            // the ?. operator before [] square brackets is not valid syntax
-            "var result:* = o?.a?.[0];",
-        };
-        String source = getAS(new String[0], new String[0], testCode, new 
String[0]);
-
-        compileAndExpectErrors(source, false, false, false, new String[0], 
"'[' is not allowed here\n");
-    }
-
     @Test
     public void testInvalidSyntaxBeforeFunctionCall()
     {

Reply via email to