소스 검색

Incorrect line and column number on error stack.

If the object or array literals are at the top level statement we didn't
enclosed them in Start/End Statement. Fixed that and fixed other places in
the Emit function wherever I can see problem.
Also for 'extends' we didn't enclose the expression in Start/End Statement so if that reproduce an error that error was reported on wrong place. Fixed that.

few more scenarios handled and added test for that.
Akrosh Gandhi 10 년 전
부모
커밋
cdfc1ee3e3
4개의 변경된 파일163개의 추가작업 그리고 13개의 파일을 삭제
  1. 47 13
      lib/Runtime/ByteCode/ByteCodeEmitter.cpp
  2. 14 0
      test/Error/rlexe.xml
  3. 40 0
      test/Error/validate_line_column.baseline
  4. 62 0
      test/Error/validate_line_column.js

+ 47 - 13
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -21,6 +21,18 @@ bool CallTargetIsArray(ParseNode *pnode)
     return pnode->nop == knopName && pnode->sxPid.PropertyIdFromNameNode() == Js::PropertyIds::Array;
 }
 
+#define STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode) \
+if ((isTopLevel)) \
+{ \
+    byteCodeGenerator->StartStatement(pnode); \
+}
+
+#define ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode) \
+if ((isTopLevel)) \
+{ \
+    byteCodeGenerator->EndStatement(pnode); \
+}
+
 BOOL MayHaveSideEffectOnNode(ParseNode *pnode, ParseNode *pnodeSE)
 {
     // Try to determine whether pnodeSE may kill the named var represented by pnode.
@@ -208,7 +220,7 @@ bool IsArguments(ParseNode *pnode)
 }
 
 bool ApplyEnclosesArgs(ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator);
-void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall = false, ParseNode *bindPnode = nullptr);
+void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall = false, ParseNode *bindPnode = nullptr, bool isTopLevel = false);
 void EmitComputedFunctionNameVar(ParseNode *nameNode, ParseNode *exprNode, ByteCodeGenerator *byteCodeGenerator);
 void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
 bool IsExpressionStatement(ParseNode* stmt, const Js::ScriptContext *const scriptContext);
@@ -952,7 +964,7 @@ void ByteCodeGenerator::EmitTopLevelStatement(ParseNode *stmt, FuncInfo *funcInf
         EndStatement(stmt);
     }
 
-    Emit(stmt, this, funcInfo, fReturnValue);
+    Emit(stmt, this, funcInfo, fReturnValue, false/*isConstructorCall*/, nullptr/*bindPnode*/, true/*isTopLevel*/);
     if (funcInfo->IsTmpReg(stmt->location))
     {
         if (!stmt->isUsed && !fReturnValue)
@@ -9355,7 +9367,7 @@ void TrackGlobalIntAssignments(ParseNodePtr pnode, ByteCodeGenerator * byteCodeG
     }
 }
 
-void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall, ParseNode * bindPnode)
+void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall, ParseNode * bindPnode, bool isTopLevel)
 {
     if (pnode == nullptr)
     {
@@ -9463,29 +9475,36 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         // Unary operators.
     // PTNODE(knopNot        , "~"            ,BitNot  ,Uni  ,fnopUni)
     case knopNot:
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         Emit(pnode->sxUni.pnode1, byteCodeGenerator, funcInfo, false);
         funcInfo->ReleaseLoc(pnode->sxUni.pnode1);
         byteCodeGenerator->Writer()->Reg2(
             Js::OpCode::Not_A, funcInfo->AcquireLoc(pnode), pnode->sxUni.pnode1->location);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     // PTNODE(knopNeg        , "unary -"    ,Neg     ,Uni  ,fnopUni)
     case knopNeg:
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         Emit(pnode->sxUni.pnode1, byteCodeGenerator, funcInfo, false);
         funcInfo->ReleaseLoc(pnode->sxUni.pnode1);
         funcInfo->AcquireLoc(pnode);
         byteCodeGenerator->Writer()->Reg2(
             Js::OpCode::Neg_A, pnode->location, pnode->sxUni.pnode1->location);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     // PTNODE(knopPos        , "unary +"    ,Pos     ,Uni  ,fnopUni)
     case knopPos:
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         Emit(pnode->sxUni.pnode1, byteCodeGenerator, funcInfo, false);
         funcInfo->ReleaseLoc(pnode->sxUni.pnode1);
         byteCodeGenerator->Writer()->Reg2(
             Js::OpCode::Conv_Num, funcInfo->AcquireLoc(pnode), pnode->sxUni.pnode1->location);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     // PTNODE(knopLogNot     , "!"            ,LogNot  ,Uni  ,fnopUni)
     case knopLogNot:
     {
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel();
         // For boolean expressions that compute a result, we have to burn a register for the result
         // so that the back end can identify it cheaply as a single temp lifetime. Revisit this if we do
@@ -9506,6 +9525,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
             byteCodeGenerator->Writer()->MarkLabel(doneLabel);
         }
         funcInfo->ReleaseLoc(pnode->sxUni.pnode1);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     }
     // PTNODE(knopEllipsis     , "..."       ,Spread  ,Uni         , fnopUni)
@@ -9621,6 +9641,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
     // PTNODE(knopTypeof     , "typeof"    ,None    ,Uni  ,fnopUni)
     case knopTypeof:
     {
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         ParseNode* pnodeOpnd = pnode->sxUni.pnode1;
         switch (pnodeOpnd->nop)
         {
@@ -9659,6 +9680,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
                 Js::OpCode::Typeof, funcInfo->AcquireLoc(pnode), pnodeOpnd->location);
             break;
         }
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     }
     // PTNODE(knopVoid       , "void"        ,Void    ,Uni  ,fnopUni)
@@ -9669,12 +9691,16 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         break;
     // PTNODE(knopArray      , "arr cnst"    ,None    ,Uni  ,fnopUni)
     case knopArray:
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         EmitArrayLiteral(pnode, byteCodeGenerator, funcInfo);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     // PTNODE(knopObject     , "obj cnst"    ,None    ,Uni  ,fnopUni)
     case knopObject:
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         funcInfo->AcquireLoc(pnode);
         EmitObjectInitializers(pnode->sxUni.pnode1, pnode->location, byteCodeGenerator, funcInfo);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     // PTNODE(knopComputedName, "[name]"      ,None    ,Uni  ,fnopUni)
     case knopComputedName:
@@ -9710,7 +9736,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         break;
     case knopInstOf:
     {
-        byteCodeGenerator->StartStatement(pnode);
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         EmitBinaryOpnds(pnode->sxBin.pnode1, pnode->sxBin.pnode2, byteCodeGenerator, funcInfo);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode2);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode1);
@@ -9718,7 +9744,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         uint cacheId = funcInfo->NewIsInstInlineCache();
         byteCodeGenerator->Writer()->Reg3C(nopToOp[pnode->nop], pnode->location, pnode->sxBin.pnode1->location,
             pnode->sxBin.pnode2->location, cacheId);
-        byteCodeGenerator->EndStatement(pnode);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
     }
     break;
     case knopEq:
@@ -9729,14 +9755,14 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
     case knopLe:
     case knopGe:
     case knopGt:
-        byteCodeGenerator->StartStatement(pnode);
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         EmitBinaryOpnds(pnode->sxBin.pnode1, pnode->sxBin.pnode2, byteCodeGenerator, funcInfo);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode2);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode1);
         funcInfo->AcquireLoc(pnode);
         byteCodeGenerator->Writer()->Reg3(nopToCMOp[pnode->nop], pnode->location, pnode->sxBin.pnode1->location,
             pnode->sxBin.pnode2->location);
-        byteCodeGenerator->EndStatement(pnode);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     case knopNew:
     {
@@ -9915,7 +9941,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
     }
     case knopIndex:
     {
-        byteCodeGenerator->StartStatement(pnode);
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         EmitBinaryOpnds(pnode->sxBin.pnode1, pnode->sxBin.pnode2, byteCodeGenerator, funcInfo);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode2);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode1);
@@ -9926,7 +9952,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         EmitSuperMethodBegin(pnode, byteCodeGenerator, funcInfo);
         byteCodeGenerator->Writer()->Element(
             Js::OpCode::LdElemI_A, pnode->location, protoLocation, pnode->sxBin.pnode2->location);
-        byteCodeGenerator->EndStatement(pnode);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     }
     // this is MemberExpression as rvalue
@@ -10003,7 +10029,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
 
     case knopComma:
     {
-        byteCodeGenerator->StartStatement(pnode);
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         // The parser marks binary opnd pnodes as used, but value of the first opnd of a comma is not used.
         // Easier to correct this here than to check every binary op in the parser.
         ParseNode *pnode1 = pnode->sxBin.pnode1;
@@ -10059,7 +10085,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         {
             byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->sxBin.pnode2->location);
         }
-        byteCodeGenerator->EndStatement(pnode);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
     }
     break;
 
@@ -10069,6 +10095,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
     // PTNODE(knopLogOr      , "||"        ,None    ,Bin  ,fnopBin)
     case knopLogOr:
     {
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel();
         // For boolean expressions that compute a result, we have to burn a register for the result
         // so that the back end can identify it cheaply as a single temp lifetime. Revisit this if we do
@@ -10084,11 +10111,13 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->sxBin.pnode2->location);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode2);
         byteCodeGenerator->Writer()->MarkLabel(doneLabel);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     }
     // PTNODE(knopLogAnd     , "&&"        ,None    ,Bin  ,fnopBin)
     case knopLogAnd:
     {
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel();
         // For boolean expressions that compute a result, we have to burn a register for the result
         // so that the back end can identify it cheaply as a single temp lifetime. Revisit this if we do
@@ -10104,6 +10133,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->sxBin.pnode2->location);
         funcInfo->ReleaseLoc(pnode->sxBin.pnode2);
         byteCodeGenerator->Writer()->MarkLabel(doneLabel);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     }
     // PTNODE(knopQmark      , "?"            ,None    ,Tri  ,fnopBin)
@@ -10152,7 +10182,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
     case knopAsgLsh:
     case knopAsgRsh:
     case knopAsgRs2:
-        byteCodeGenerator->StartStatement(pnode);
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         // Assign a register for the result only if the result is used or the LHS can't be assigned to
         // (i.e., is a constant).
         if (pnode->isUsed || fReturnValue || funcInfo->RegIsConst(pnode->sxBin.pnode1->location))
@@ -10181,7 +10211,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         }
         funcInfo->ReleaseLoad(pnode->sxBin.pnode1);
 
-        byteCodeGenerator->EndStatement(pnode);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
 
     // General nodes.
@@ -10308,7 +10338,9 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         // Extends
         if (pnode->sxClass.pnodeExtends)
         {
+            byteCodeGenerator->StartStatement(pnode->sxClass.pnodeExtends);
             Emit(pnode->sxClass.pnodeExtends, byteCodeGenerator, funcInfo, false);
+            byteCodeGenerator->EndStatement(pnode->sxClass.pnodeExtends);
         }
 
         Assert(pnode->sxClass.pnodeConstructor);
@@ -10370,7 +10402,9 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         break;
     }
     case knopStrTemplate:
+        STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         EmitStringTemplate(pnode, byteCodeGenerator, funcInfo);
+        ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
         break;
     case knopEndCode:
         byteCodeGenerator->Writer()->RecordStatementAdjustment(Js::FunctionBody::SAT_All);

+ 14 - 0
test/Error/rlexe.xml

@@ -155,4 +155,18 @@
       <compile-flags>-ExtendedErrorStackForTestHost -nonative -InjectPartiallyInitializedInterpreterFrameError:3 -InjectPartiallyInitializedInterpreterFrameErrorType:1</compile-flags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>validate_line_column.js</files>
+      <baseline>validate_line_column.baseline</baseline>
+      <compile-flags>-ExtendedErrorStackForTestHost</compile-flags>
+    </default>
+  </test>
+  <test>
+    <default>
+      <files>validate_line_column.js</files>
+      <baseline>validate_line_column.baseline</baseline>
+      <compile-flags>-ExtendedErrorStackForTestHost -force:DeferParse</compile-flags>
+    </default>
+  </test>
 </regress-exe>

+ 40 - 0
test/Error/validate_line_column.baseline

@@ -0,0 +1,40 @@
+ReferenceError: 'z1' is undefined
+	at Anonymous function (validate_line_column.js:17:6)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:16:1)
+ReferenceError: 'z1' is undefined
+	at Anonymous function (validate_line_column.js:21:6)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:20:1)
+TypeError: Function is not a constructor
+	at Anonymous function (validate_line_column.js:26:24)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:24:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:30:6)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:29:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:35:5)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:33:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:40:5)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:38:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:45:6)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:43:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:50:6)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:48:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:55:6)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:53:1)
+ReferenceError: 'a' is undefined
+	at Anonymous function (validate_line_column.js:60:5)
+	at foo (validate_line_column.js:10:9)
+	at Global code (validate_line_column.js:58:1)

+ 62 - 0
test/Error/validate_line_column.js

@@ -0,0 +1,62 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// Validating that error thrown has right line and column number
+
+function foo(validate) {
+    try {
+        validate();
+    } catch (e) {
+        print(e.stack);
+    }
+}
+
+foo(function() {
+    ([z1]);         // Error thrown here.
+});
+
+foo(function() {
+    ({a:z1});         // Error thrown here.
+});
+
+foo(function() {
+    var a;
+    a;class b extends ([]){};    // Error thrown here.
+});
+
+foo(function() {
+    (typeof a.b);         // Error thrown here.
+});
+
+foo(function() {
+    var k = 1;
+    !a.b;         // Error thrown here.
+});
+
+foo(function() {
+    var k = 1;
+    ~a.b;         // Error thrown here.
+});
+
+foo(function() {
+    var k = 1;
+    (a.b && a.b);          // Error thrown here.
+});
+
+foo(function() {
+    var k = 1;
+    (a.b || a.b);         // Error thrown here.
+});
+
+foo(function() {
+    var k = 1;
+    (a.b * a.b);         // Error thrown here.
+});
+
+foo(function() {
+    var k = 1;
+    `${a.b}`;         // Error thrown here.
+});
+