Browse Source

[MSFT 12313182] Detect assignment to const in nested functions that were originally deferred. Add symbol-is-const flag to Symbol and to ScopeInfo to allow us to detect this case. Don't rely on having access to a parse node for the declaration.

Paul Leathers 8 years ago
parent
commit
8f40df4cb5

+ 1 - 1
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -5169,7 +5169,7 @@ void ByteCodeGenerator::EmitPropStore(Js::RegSlot rhsLocation, Symbol *sym, Iden
     }
     else if (sym->IsInSlot(funcInfo) || envIndex != -1)
     {
-        if (!isConstDecl && sym->GetDecl() && sym->GetDecl()->nop == knopConstDecl)
+        if (!isConstDecl && sym->GetIsConst())
         {
             // This is a case where const reassignment can't be proven statically (e.g., eval, with) so
             // we have to catch it at runtime.

+ 1 - 0
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -3552,6 +3552,7 @@ void PreVisitBlock(ParseNode *pnodeBlock, ByteCodeGenerator *byteCodeGenerator)
 #endif
             sym->SetIsGlobal(isGlobalScope);
             sym->SetIsBlockVar(true);
+            sym->SetIsConst(pnode->nop == knopConstDecl);
             sym->SetNeedDeclaration(true);
             pnode->sxVar.sym = sym;
         };

+ 2 - 0
lib/Runtime/ByteCode/ScopeInfo.cpp

@@ -33,6 +33,7 @@ namespace Js
             this->SetSymbolType(scopeSlot, sym->GetSymbolType());
             this->SetHasFuncAssignment(scopeSlot, sym->GetHasFuncAssignment());
             this->SetIsBlockVariable(scopeSlot, sym->GetIsBlockVar());
+            this->SetIsConst(scopeSlot, sym->GetIsConst());
             this->SetIsFuncExpr(scopeSlot, sym->GetIsFuncExpr());
             this->SetIsModuleExportStorage(scopeSlot, sym->GetIsModuleExportStorage());
             this->SetIsModuleImport(scopeSlot, sym->GetIsModuleImport());
@@ -235,6 +236,7 @@ namespace Js
 
                 sym->SetScopeSlot(static_cast<PropertyId>(i));
                 sym->SetIsBlockVar(GetIsBlockVariable(i));
+                sym->SetIsConst(GetIsConst(i));
                 sym->SetIsFuncExpr(GetIsFuncExpr(i));
                 sym->SetIsModuleExportStorage(GetIsModuleExportStorage(i));
                 sym->SetIsModuleImport(GetIsModuleImport(i));

+ 19 - 5
lib/Runtime/ByteCode/ScopeInfo.h

@@ -27,11 +27,12 @@ namespace Js {
                 Field(PropertyRecord const*) name;
             };
             Field(SymbolType) symbolType;
-            Field(bool) hasFuncAssignment;
-            Field(bool) isBlockVariable;
-            Field(bool) isFuncExpr;
-            Field(bool) isModuleExportStorage;
-            Field(bool) isModuleImport;
+            Field(bool) hasFuncAssignment : 1;
+            Field(bool) isBlockVariable : 1;
+            Field(bool) isConst : 1;
+            Field(bool) isFuncExpr : 1;
+            Field(bool) isModuleExportStorage : 1;
+            Field(bool) isModuleImport : 1;
         };
 
     private:
@@ -86,6 +87,13 @@ namespace Js {
             symbols[i].isBlockVariable = is;
         }
 
+        void SetIsConst(int i, bool is)
+        {
+            Assert(!areNamesCached);
+            Assert(i >= 0 && i < symbolCount);
+            symbols[i].isConst = is;
+        }
+
         void SetIsFuncExpr(int i, bool is)
         {
             Assert(!areNamesCached);
@@ -151,6 +159,12 @@ namespace Js {
             return symbols[i].isBlockVariable;
         }
 
+        bool GetIsConst(int i)
+        {
+            Assert(i >= 0 && i < symbolCount);
+            return symbols[i].isConst;
+        }
+
         bool GetIsFuncExpr(int i)
         {
             Assert(i >= 0 && i < symbolCount);

+ 12 - 0
lib/Runtime/ByteCode/Symbol.h

@@ -30,6 +30,7 @@ private:
     BYTE defCount;
     BYTE needDeclaration : 1;
     BYTE isBlockVar : 1;
+    BYTE isConst : 1;
     BYTE isGlobal : 1;
     BYTE isEval : 1;
     BYTE hasNonLocalReference : 1;  // if true, then this symbol needs to be heap-allocated
@@ -61,6 +62,7 @@ public:
         location(Js::Constants::NoRegister),
         needDeclaration(false),
         isBlockVar(false),
+        isConst(false),
         isGlobal(false),
         hasNonLocalReference(false),
         isFuncExpr(false),
@@ -150,6 +152,16 @@ public:
         return isBlockVar;
     }
 
+    void SetIsConst(bool is)
+    {
+        isConst = is;
+    }
+
+    bool GetIsConst() const
+    {
+        return isConst;
+    }
+
     void SetIsModuleExportStorage(bool is)
     {
         isModuleExportStorage = is;

+ 2 - 0
test/LetConst/AssignmentToConst.baseline

@@ -46,3 +46,5 @@ test 23
 ReferenceError: Use before declaration
 test 24
 ReferenceError: Use before declaration
+test 25
+TypeError: Assignment to const

+ 1 - 0
test/LetConst/AssignmentToConst.js

@@ -27,5 +27,6 @@ try { eval("WScript.Echo('test 21'); const x = 1; {const x = 2; x++;}"); WScript
 try { eval("WScript.Echo('test 22'); const x = 1; {const x = 2;} x++;"); WScript.Echo("passed"); } catch (e) { WScript.Echo(e); }
 try { eval("WScript.Echo('test 23'); x = 1; {let x = 2;} const x = 10;"); WScript.Echo("passed"); } catch (e) { WScript.Echo(e); }
 try { eval("WScript.Echo('test 24'); function f() {x = 1; {let x = 2;} const x = 10;} f();"); WScript.Echo("passed"); } catch (e) { WScript.Echo(e); }
+try { eval("WScript.Echo('test 25'); const x = 10; function f() {x = 1; {let x = 2;} } f();"); WScript.Echo("passed"); } catch (e) { WScript.Echo(e); }
 
 

+ 7 - 0
test/LetConst/rlexe.xml

@@ -141,6 +141,13 @@
       <baseline>AssignmentToConst.baseline</baseline>
     </default>
   </test>
+  <test>
+    <default>
+      <files>AssignmentToConst.js</files>
+      <baseline>AssignmentToConst.baseline</baseline>
+      <compile-flags>-force:deferparse</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>DeclOutofBlock.js</files>