@@ -23,7 +23,16 @@ import { EvaluateFunc, EvaluateMap, Kind, ScopeType } from "./type";
2323// tslint:disable-next-line
2424import { Signal } from "./signal" ;
2525import { Scope } from "./scope" ;
26- import { MODULE , THIS , REQUIRE , UNDEFINED , ARGUMENTS , NEW } from "./constant" ;
26+ import { Stack } from "./stack" ;
27+ import {
28+ MODULE ,
29+ THIS ,
30+ REQUIRE ,
31+ UNDEFINED ,
32+ ARGUMENTS ,
33+ NEW ,
34+ ANONYMOUS
35+ } from "./constant" ;
2736
2837import {
2938 isArrayExpression ,
@@ -42,11 +51,28 @@ import {
4251 isObjectProperty ,
4352 isRestElement ,
4453 isSpreadElement ,
45- isVariableDeclaration
54+ isVariableDeclaration ,
55+ isStringLiteral
4656} from "./packages/babel-types" ;
4757
4858import { defineFunctionLength , defineFunctionName } from "./utils" ;
4959
60+ function overriteStack ( err : Error , stack : Stack , node : types . Node ) : Error {
61+ stack . push ( {
62+ filename : ANONYMOUS ,
63+ stack : stack . currentStackName ,
64+ location : node . loc
65+ } ) ;
66+ const originStack = ( err . stack || "" ) . split ( "\n" ) ;
67+ originStack . shift ( ) ; // drop the error message
68+ err . stack =
69+ err . toString ( ) +
70+ "\n" +
71+ stack . raw +
72+ ( originStack ? "\n" + originStack . join ( "\n" ) : "" ) ;
73+ return err ;
74+ }
75+
5076const visitors : EvaluateMap = {
5177 File ( path ) {
5278 evaluate ( path . createChild ( path . node . program ) ) ;
@@ -74,15 +100,15 @@ const visitors: EvaluateMap = {
74100 } ,
75101
76102 Identifier ( path ) {
77- const { node, scope } = path ;
103+ const { node, scope, stack } = path ;
78104 if ( node . name === UNDEFINED ) {
79105 return undefined ;
80106 }
81107 const $var = scope . hasBinding ( node . name ) ;
82108 if ( $var ) {
83109 return $var . value ;
84110 } else {
85- throw ErrNotDefined ( node . name ) ;
111+ throw overriteStack ( ErrNotDefined ( node . name ) , stack , node ) ;
86112 }
87113 } ,
88114 RegExpLiteral ( path ) {
@@ -468,7 +494,7 @@ const visitors: EvaluateMap = {
468494 } ,
469495 // @es 2015 for of
470496 ForOfStatement ( path ) {
471- const { node, scope, ctx } = path ;
497+ const { node, scope, ctx, stack } = path ;
472498 const labelName : string | void = ctx . labelName ;
473499 const entity = evaluate ( path . createChild ( node . right ) ) ;
474500 const SymbolConst : any = ( ( ) => {
@@ -480,7 +506,11 @@ const visitors: EvaluateMap = {
480506 if ( ! entity || ! entity [ SymbolConst . iterator ] ) {
481507 // FIXME: how to get function name
482508 // for (let value of get()){}
483- throw ErrInvalidIterable ( ( node . right as types . Identifier ) . name ) ;
509+ throw overriteStack (
510+ ErrInvalidIterable ( ( node . right as types . Identifier ) . name ) ,
511+ stack ,
512+ node . right
513+ ) ;
484514 }
485515 }
486516
@@ -657,7 +687,12 @@ const visitors: EvaluateMap = {
657687 }
658688 } ,
659689 ThrowStatement ( path ) {
660- throw evaluate ( path . createChild ( path . node . argument ) ) ;
690+ const r = evaluate ( path . createChild ( path . node . argument ) ) ;
691+ if ( r instanceof Error ) {
692+ throw r ;
693+ } else {
694+ throw overriteStack ( r , path . stack , path . node . argument ) ;
695+ }
661696 } ,
662697 CatchClause ( path ) {
663698 return evaluate ( path . createChild ( path . node . body ) ) ;
@@ -729,14 +764,14 @@ const visitors: EvaluateMap = {
729764 }
730765 } ,
731766 UpdateExpression ( path ) {
732- const { node, scope } = path ;
767+ const { node, scope, stack } = path ;
733768 const { prefix } = node ;
734769 let $var : IVar ;
735770 if ( isIdentifier ( node . argument ) ) {
736771 const { name } = node . argument ;
737772 const $$var = scope . hasBinding ( name ) ;
738773 if ( ! $$var ) {
739- throw ErrNotDefined ( name ) ;
774+ throw overriteStack ( ErrNotDefined ( name ) , stack , node . argument ) ;
740775 }
741776 $var = $$var ;
742777 } else if ( isMemberExpression ( node . argument ) ) {
@@ -772,7 +807,7 @@ const visitors: EvaluateMap = {
772807 // use this in class constructor it it never call super();
773808 if ( scope . type === ScopeType . Constructor ) {
774809 if ( ! scope . hasOwnBinding ( THIS ) ) {
775- throw ErrNoSuper ( ) ;
810+ throw overriteStack ( ErrNoSuper ( ) , path . stack , path . node ) ;
776811 }
777812 }
778813 const thisVar = scope . hasBinding ( THIS ) ;
@@ -831,13 +866,14 @@ const visitors: EvaluateMap = {
831866 }
832867 } ,
833868 ObjectMethod ( path ) {
834- const { node, scope } = path ;
869+ const { node, scope, stack } = path ;
835870 const methodName : string = ! node . computed
836871 ? isIdentifier ( node . key )
837872 ? node . key . name
838873 : evaluate ( path . createChild ( node . key ) )
839874 : evaluate ( path . createChild ( node . key ) ) ;
840875 const method = function ( ) {
876+ stack . enter ( "Object." + methodName ) ;
841877 const args = [ ] . slice . call ( arguments ) ;
842878 const newScope = scope . createChild ( ScopeType . Function ) ;
843879 newScope . const ( THIS , this ) ;
@@ -846,6 +882,7 @@ const visitors: EvaluateMap = {
846882 newScope . const ( ( param as types . Identifier ) . name , args [ i ] ) ;
847883 } ) ;
848884 const result = evaluate ( path . createChild ( node . body , newScope ) ) ;
885+ stack . leave ( ) ;
849886 if ( Signal . isReturn ( result ) ) {
850887 return result . value ;
851888 }
@@ -873,8 +910,11 @@ const visitors: EvaluateMap = {
873910 }
874911 } ,
875912 FunctionExpression ( path ) {
876- const { node, scope } = path ;
913+ const { node, scope, stack } = path ;
914+
915+ const functionName = node . id ? node . id . name : "" ;
877916 const func = function functionDeclaration ( ...args ) {
917+ stack . enter ( functionName ) ; // enter the stack
878918 const funcScope = scope . createChild ( ScopeType . Function ) ;
879919 for ( let i = 0 ; i < node . params . length ; i ++ ) {
880920 const param = node . params [ i ] ;
@@ -902,6 +942,7 @@ const visitors: EvaluateMap = {
902942 funcScope . isolated = false ;
903943
904944 const result = evaluate ( path . createChild ( node . body , funcScope ) ) ;
945+ stack . leave ( ) ; // leave stack
905946 if ( result instanceof Signal ) {
906947 return result . value ;
907948 } else {
@@ -992,17 +1033,55 @@ const visitors: EvaluateMap = {
9921033 } ,
9931034
9941035 CallExpression ( path ) {
995- const { node, scope } = path ;
1036+ const { node, scope, stack } = path ;
1037+
1038+ const functionName : string = isMemberExpression ( node . callee )
1039+ ? ( ( ) => {
1040+ if ( isIdentifier ( node . callee . property ) ) {
1041+ return (
1042+ ( node . callee . object as any ) . name + "." + node . callee . property . name
1043+ ) ;
1044+ } else if ( isStringLiteral ( node . callee . property ) ) {
1045+ return (
1046+ ( node . callee . object as any ) . name +
1047+ "." +
1048+ node . callee . property . value
1049+ ) ;
1050+ } else {
1051+ return "undefined" ;
1052+ }
1053+ } ) ( )
1054+ : ( node . callee as types . Identifier ) . name ;
1055+
9961056 const func = evaluate ( path . createChild ( node . callee ) ) ;
9971057 const args = node . arguments . map ( arg => evaluate ( path . createChild ( arg ) ) ) ;
9981058 const isValidFunction = isFunction ( func ) as boolean ;
9991059
10001060 if ( isMemberExpression ( node . callee ) ) {
1061+ if ( ! isValidFunction ) {
1062+ throw overriteStack (
1063+ ErrIsNotFunction ( functionName ) ,
1064+ stack ,
1065+ node . callee . property
1066+ ) ;
1067+ } else {
1068+ stack . push ( {
1069+ filename : ANONYMOUS ,
1070+ stack : stack . currentStackName ,
1071+ location : node . callee . property . loc
1072+ } ) ;
1073+ }
10011074 const object = evaluate ( path . createChild ( node . callee . object ) ) ;
10021075 return func . apply ( object , args ) ;
10031076 } else {
10041077 if ( ! isValidFunction ) {
1005- throw ErrIsNotFunction ( ( node . callee as types . Identifier ) . name ) ;
1078+ throw overriteStack ( ErrIsNotFunction ( functionName ) , stack , node ) ;
1079+ } else {
1080+ stack . push ( {
1081+ filename : ANONYMOUS ,
1082+ stack : stack . currentStackName ,
1083+ location : node . loc
1084+ } ) ;
10061085 }
10071086 const thisVar = scope . hasBinding ( THIS ) ;
10081087 return func . apply ( thisVar ? thisVar . value : null , args ) ;
@@ -1037,7 +1116,7 @@ const visitors: EvaluateMap = {
10371116 if ( globalVar ) {
10381117 $var = globalVar ;
10391118 } else {
1040- throw ErrNotDefined ( name ) ;
1119+ throw overriteStack ( ErrNotDefined ( name ) , path . stack , node . right ) ;
10411120 }
10421121 } else {
10431122 $var = varOrNot as Var < any > ;
@@ -1046,7 +1125,11 @@ const visitors: EvaluateMap = {
10461125 * test = 321 // it should throw an error
10471126 */
10481127 if ( $var . kind === Kind . Const ) {
1049- throw new TypeError ( "Assignment to constant variable." ) ;
1128+ throw overriteStack (
1129+ new TypeError ( "Assignment to constant variable." ) ,
1130+ path . stack ,
1131+ node . left
1132+ ) ;
10501133 }
10511134 }
10521135 } else if ( isMemberExpression ( node . left ) ) {
@@ -1141,12 +1224,24 @@ const visitors: EvaluateMap = {
11411224 : evaluate ( path . createChild ( path . node . alternate ) ) ;
11421225 } ,
11431226 NewExpression ( path ) {
1144- const { node } = path ;
1227+ const { node, stack } = path ;
11451228 const func = evaluate ( path . createChild ( node . callee ) ) ;
11461229 const args : any [ ] = node . arguments . map ( arg =>
11471230 evaluate ( path . createChild ( arg ) )
11481231 ) ;
11491232 const entity = new func ( ...args ) ;
1233+
1234+ // stack track for Error constructor
1235+ if ( func === Error || entity instanceof Error ) {
1236+ path . stack . push ( {
1237+ filename : ANONYMOUS ,
1238+ stack : stack . currentStackName ,
1239+ location : node . loc
1240+ } ) ;
1241+ entity . stack = `Error: ${ entity . message }
1242+ ${ path . stack . raw }
1243+ ` ;
1244+ }
11501245 entity . prototype = entity . prototype || { } ;
11511246 entity . prototype . constructor = func ;
11521247 return entity ;
@@ -1215,7 +1310,7 @@ const visitors: EvaluateMap = {
12151310 path . scope . const ( path . node . id . name , ClassConstructor ) ;
12161311 } ,
12171312 ClassBody ( path ) {
1218- const { node, scope } = path ;
1313+ const { node, scope, stack } = path ;
12191314 const constructor : types . ClassMethod | void = node . body . find (
12201315 n => isClassMethod ( n ) && n . kind === "constructor"
12211316 ) as types . ClassMethod | void ;
@@ -1234,6 +1329,7 @@ const visitors: EvaluateMap = {
12341329 }
12351330
12361331 function ClassConstructor ( ...args ) {
1332+ stack . enter ( parentNode . id . name + ".constructor" ) ;
12371333 _classCallCheck ( this , ClassConstructor ) ;
12381334 const classScope = scope . createChild ( ScopeType . Constructor ) ;
12391335
@@ -1280,9 +1376,11 @@ const visitors: EvaluateMap = {
12801376 }
12811377
12821378 if ( ! classScope . hasOwnBinding ( THIS ) ) {
1283- throw ErrNoSuper ( ) ;
1379+ throw overriteStack ( ErrNoSuper ( ) , path . stack , node ) ;
12841380 }
12851381
1382+ stack . leave ( ) ;
1383+
12861384 return this ;
12871385 }
12881386
@@ -1295,8 +1393,14 @@ const visitors: EvaluateMap = {
12951393
12961394 const classMethods = methods
12971395 . map ( ( method : types . ClassMethod ) => {
1396+ const methodName : string = method . id
1397+ ? method . id . name
1398+ : method . computed
1399+ ? evaluate ( path . createChild ( method . key ) )
1400+ : ( method . key as types . Identifier ) . name ;
12981401 const methodScope = scope . createChild ( ScopeType . Function ) ;
12991402 const func = function ( ...args ) {
1403+ stack . enter ( parentNode . id . name + "." + methodName ) ;
13001404 methodScope . const ( THIS , this ) ;
13011405 methodScope . const ( NEW , { target : undefined } ) ;
13021406
@@ -1316,21 +1420,22 @@ const visitors: EvaluateMap = {
13161420 } )
13171421 ) ;
13181422
1423+ stack . leave ( ) ;
1424+
13191425 if ( Signal . isReturn ( result ) ) {
13201426 return result . value ;
13211427 }
13221428 } ;
13231429
13241430 defineFunctionLength ( func , method . params . length ) ;
1325- defineFunctionName ( func , method . id ? method . id . name : "" ) ;
1431+ defineFunctionName ( func , methodName ) ;
13261432
13271433 return {
13281434 key : ( method . key as any ) . name ,
13291435 [ method . kind === "method" ? "value" : method . kind ] : func
13301436 } ;
13311437 } )
13321438 . concat ( [ { key : "constructor" , value : ClassConstructor } ] ) ;
1333-
13341439 // define class methods
13351440 _createClass ( ClassConstructor , classMethods ) ;
13361441
@@ -1397,7 +1502,7 @@ const visitors: EvaluateMap = {
13971502 Object . assign ( object , evaluate ( path . createChild ( node . argument ) ) ) ;
13981503 } ,
13991504 ImportDeclaration ( path ) {
1400- const { node, scope } = path ;
1505+ const { node, scope, stack } = path ;
14011506 let defaultImport : string = "" ; // default import object
14021507 const otherImport : string [ ] = [ ] ; // import property
14031508 const moduleName : string = evaluate ( path . createChild ( node . source ) ) ;
@@ -1414,13 +1519,13 @@ const visitors: EvaluateMap = {
14141519 const requireVar = scope . hasBinding ( REQUIRE ) ;
14151520
14161521 if ( requireVar === undefined ) {
1417- throw ErrNotDefined ( REQUIRE ) ;
1522+ throw overriteStack ( ErrNotDefined ( REQUIRE ) , stack , node ) ;
14181523 }
14191524
14201525 const requireFunc = requireVar . value ;
14211526
14221527 if ( ! isFunction ( requireFunc ) ) {
1423- throw ErrIsNotFunction ( REQUIRE ) ;
1528+ throw overriteStack ( ErrIsNotFunction ( REQUIRE ) , stack , node ) ;
14241529 }
14251530
14261531 const targetModule : any = requireFunc ( moduleName ) || { } ;
0 commit comments