@@ -23,7 +23,16 @@ import { EvaluateFunc, EvaluateMap, Kind, ScopeType } from "./type";
23
23
// tslint:disable-next-line
24
24
import { Signal } from "./signal" ;
25
25
import { 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" ;
27
36
28
37
import {
29
38
isArrayExpression ,
@@ -42,11 +51,28 @@ import {
42
51
isObjectProperty ,
43
52
isRestElement ,
44
53
isSpreadElement ,
45
- isVariableDeclaration
54
+ isVariableDeclaration ,
55
+ isStringLiteral
46
56
} from "./packages/babel-types" ;
47
57
48
58
import { defineFunctionLength , defineFunctionName } from "./utils" ;
49
59
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
+
50
76
const visitors : EvaluateMap = {
51
77
File ( path ) {
52
78
evaluate ( path . createChild ( path . node . program ) ) ;
@@ -74,15 +100,15 @@ const visitors: EvaluateMap = {
74
100
} ,
75
101
76
102
Identifier ( path ) {
77
- const { node, scope } = path ;
103
+ const { node, scope, stack } = path ;
78
104
if ( node . name === UNDEFINED ) {
79
105
return undefined ;
80
106
}
81
107
const $var = scope . hasBinding ( node . name ) ;
82
108
if ( $var ) {
83
109
return $var . value ;
84
110
} else {
85
- throw ErrNotDefined ( node . name ) ;
111
+ throw overriteStack ( ErrNotDefined ( node . name ) , stack , node ) ;
86
112
}
87
113
} ,
88
114
RegExpLiteral ( path ) {
@@ -468,7 +494,7 @@ const visitors: EvaluateMap = {
468
494
} ,
469
495
// @es 2015 for of
470
496
ForOfStatement ( path ) {
471
- const { node, scope, ctx } = path ;
497
+ const { node, scope, ctx, stack } = path ;
472
498
const labelName : string | void = ctx . labelName ;
473
499
const entity = evaluate ( path . createChild ( node . right ) ) ;
474
500
const SymbolConst : any = ( ( ) => {
@@ -480,7 +506,11 @@ const visitors: EvaluateMap = {
480
506
if ( ! entity || ! entity [ SymbolConst . iterator ] ) {
481
507
// FIXME: how to get function name
482
508
// 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
+ ) ;
484
514
}
485
515
}
486
516
@@ -657,7 +687,12 @@ const visitors: EvaluateMap = {
657
687
}
658
688
} ,
659
689
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
+ }
661
696
} ,
662
697
CatchClause ( path ) {
663
698
return evaluate ( path . createChild ( path . node . body ) ) ;
@@ -729,14 +764,14 @@ const visitors: EvaluateMap = {
729
764
}
730
765
} ,
731
766
UpdateExpression ( path ) {
732
- const { node, scope } = path ;
767
+ const { node, scope, stack } = path ;
733
768
const { prefix } = node ;
734
769
let $var : IVar ;
735
770
if ( isIdentifier ( node . argument ) ) {
736
771
const { name } = node . argument ;
737
772
const $$var = scope . hasBinding ( name ) ;
738
773
if ( ! $$var ) {
739
- throw ErrNotDefined ( name ) ;
774
+ throw overriteStack ( ErrNotDefined ( name ) , stack , node . argument ) ;
740
775
}
741
776
$var = $$var ;
742
777
} else if ( isMemberExpression ( node . argument ) ) {
@@ -772,7 +807,7 @@ const visitors: EvaluateMap = {
772
807
// use this in class constructor it it never call super();
773
808
if ( scope . type === ScopeType . Constructor ) {
774
809
if ( ! scope . hasOwnBinding ( THIS ) ) {
775
- throw ErrNoSuper ( ) ;
810
+ throw overriteStack ( ErrNoSuper ( ) , path . stack , path . node ) ;
776
811
}
777
812
}
778
813
const thisVar = scope . hasBinding ( THIS ) ;
@@ -831,13 +866,14 @@ const visitors: EvaluateMap = {
831
866
}
832
867
} ,
833
868
ObjectMethod ( path ) {
834
- const { node, scope } = path ;
869
+ const { node, scope, stack } = path ;
835
870
const methodName : string = ! node . computed
836
871
? isIdentifier ( node . key )
837
872
? node . key . name
838
873
: evaluate ( path . createChild ( node . key ) )
839
874
: evaluate ( path . createChild ( node . key ) ) ;
840
875
const method = function ( ) {
876
+ stack . enter ( "Object." + methodName ) ;
841
877
const args = [ ] . slice . call ( arguments ) ;
842
878
const newScope = scope . createChild ( ScopeType . Function ) ;
843
879
newScope . const ( THIS , this ) ;
@@ -846,6 +882,7 @@ const visitors: EvaluateMap = {
846
882
newScope . const ( ( param as types . Identifier ) . name , args [ i ] ) ;
847
883
} ) ;
848
884
const result = evaluate ( path . createChild ( node . body , newScope ) ) ;
885
+ stack . leave ( ) ;
849
886
if ( Signal . isReturn ( result ) ) {
850
887
return result . value ;
851
888
}
@@ -873,8 +910,11 @@ const visitors: EvaluateMap = {
873
910
}
874
911
} ,
875
912
FunctionExpression ( path ) {
876
- const { node, scope } = path ;
913
+ const { node, scope, stack } = path ;
914
+
915
+ const functionName = node . id ? node . id . name : "" ;
877
916
const func = function functionDeclaration ( ...args ) {
917
+ stack . enter ( functionName ) ; // enter the stack
878
918
const funcScope = scope . createChild ( ScopeType . Function ) ;
879
919
for ( let i = 0 ; i < node . params . length ; i ++ ) {
880
920
const param = node . params [ i ] ;
@@ -902,6 +942,7 @@ const visitors: EvaluateMap = {
902
942
funcScope . isolated = false ;
903
943
904
944
const result = evaluate ( path . createChild ( node . body , funcScope ) ) ;
945
+ stack . leave ( ) ; // leave stack
905
946
if ( result instanceof Signal ) {
906
947
return result . value ;
907
948
} else {
@@ -992,17 +1033,55 @@ const visitors: EvaluateMap = {
992
1033
} ,
993
1034
994
1035
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
+
996
1056
const func = evaluate ( path . createChild ( node . callee ) ) ;
997
1057
const args = node . arguments . map ( arg => evaluate ( path . createChild ( arg ) ) ) ;
998
1058
const isValidFunction = isFunction ( func ) as boolean ;
999
1059
1000
1060
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
+ }
1001
1074
const object = evaluate ( path . createChild ( node . callee . object ) ) ;
1002
1075
return func . apply ( object , args ) ;
1003
1076
} else {
1004
1077
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
+ } ) ;
1006
1085
}
1007
1086
const thisVar = scope . hasBinding ( THIS ) ;
1008
1087
return func . apply ( thisVar ? thisVar . value : null , args ) ;
@@ -1037,7 +1116,7 @@ const visitors: EvaluateMap = {
1037
1116
if ( globalVar ) {
1038
1117
$var = globalVar ;
1039
1118
} else {
1040
- throw ErrNotDefined ( name ) ;
1119
+ throw overriteStack ( ErrNotDefined ( name ) , path . stack , node . right ) ;
1041
1120
}
1042
1121
} else {
1043
1122
$var = varOrNot as Var < any > ;
@@ -1046,7 +1125,11 @@ const visitors: EvaluateMap = {
1046
1125
* test = 321 // it should throw an error
1047
1126
*/
1048
1127
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
+ ) ;
1050
1133
}
1051
1134
}
1052
1135
} else if ( isMemberExpression ( node . left ) ) {
@@ -1141,12 +1224,24 @@ const visitors: EvaluateMap = {
1141
1224
: evaluate ( path . createChild ( path . node . alternate ) ) ;
1142
1225
} ,
1143
1226
NewExpression ( path ) {
1144
- const { node } = path ;
1227
+ const { node, stack } = path ;
1145
1228
const func = evaluate ( path . createChild ( node . callee ) ) ;
1146
1229
const args : any [ ] = node . arguments . map ( arg =>
1147
1230
evaluate ( path . createChild ( arg ) )
1148
1231
) ;
1149
1232
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
+ }
1150
1245
entity . prototype = entity . prototype || { } ;
1151
1246
entity . prototype . constructor = func ;
1152
1247
return entity ;
@@ -1215,7 +1310,7 @@ const visitors: EvaluateMap = {
1215
1310
path . scope . const ( path . node . id . name , ClassConstructor ) ;
1216
1311
} ,
1217
1312
ClassBody ( path ) {
1218
- const { node, scope } = path ;
1313
+ const { node, scope, stack } = path ;
1219
1314
const constructor : types . ClassMethod | void = node . body . find (
1220
1315
n => isClassMethod ( n ) && n . kind === "constructor"
1221
1316
) as types . ClassMethod | void ;
@@ -1234,6 +1329,7 @@ const visitors: EvaluateMap = {
1234
1329
}
1235
1330
1236
1331
function ClassConstructor ( ...args ) {
1332
+ stack . enter ( parentNode . id . name + ".constructor" ) ;
1237
1333
_classCallCheck ( this , ClassConstructor ) ;
1238
1334
const classScope = scope . createChild ( ScopeType . Constructor ) ;
1239
1335
@@ -1280,9 +1376,11 @@ const visitors: EvaluateMap = {
1280
1376
}
1281
1377
1282
1378
if ( ! classScope . hasOwnBinding ( THIS ) ) {
1283
- throw ErrNoSuper ( ) ;
1379
+ throw overriteStack ( ErrNoSuper ( ) , path . stack , node ) ;
1284
1380
}
1285
1381
1382
+ stack . leave ( ) ;
1383
+
1286
1384
return this ;
1287
1385
}
1288
1386
@@ -1295,8 +1393,14 @@ const visitors: EvaluateMap = {
1295
1393
1296
1394
const classMethods = methods
1297
1395
. 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 ;
1298
1401
const methodScope = scope . createChild ( ScopeType . Function ) ;
1299
1402
const func = function ( ...args ) {
1403
+ stack . enter ( parentNode . id . name + "." + methodName ) ;
1300
1404
methodScope . const ( THIS , this ) ;
1301
1405
methodScope . const ( NEW , { target : undefined } ) ;
1302
1406
@@ -1316,21 +1420,22 @@ const visitors: EvaluateMap = {
1316
1420
} )
1317
1421
) ;
1318
1422
1423
+ stack . leave ( ) ;
1424
+
1319
1425
if ( Signal . isReturn ( result ) ) {
1320
1426
return result . value ;
1321
1427
}
1322
1428
} ;
1323
1429
1324
1430
defineFunctionLength ( func , method . params . length ) ;
1325
- defineFunctionName ( func , method . id ? method . id . name : "" ) ;
1431
+ defineFunctionName ( func , methodName ) ;
1326
1432
1327
1433
return {
1328
1434
key : ( method . key as any ) . name ,
1329
1435
[ method . kind === "method" ? "value" : method . kind ] : func
1330
1436
} ;
1331
1437
} )
1332
1438
. concat ( [ { key : "constructor" , value : ClassConstructor } ] ) ;
1333
-
1334
1439
// define class methods
1335
1440
_createClass ( ClassConstructor , classMethods ) ;
1336
1441
@@ -1397,7 +1502,7 @@ const visitors: EvaluateMap = {
1397
1502
Object . assign ( object , evaluate ( path . createChild ( node . argument ) ) ) ;
1398
1503
} ,
1399
1504
ImportDeclaration ( path ) {
1400
- const { node, scope } = path ;
1505
+ const { node, scope, stack } = path ;
1401
1506
let defaultImport : string = "" ; // default import object
1402
1507
const otherImport : string [ ] = [ ] ; // import property
1403
1508
const moduleName : string = evaluate ( path . createChild ( node . source ) ) ;
@@ -1414,13 +1519,13 @@ const visitors: EvaluateMap = {
1414
1519
const requireVar = scope . hasBinding ( REQUIRE ) ;
1415
1520
1416
1521
if ( requireVar === undefined ) {
1417
- throw ErrNotDefined ( REQUIRE ) ;
1522
+ throw overriteStack ( ErrNotDefined ( REQUIRE ) , stack , node ) ;
1418
1523
}
1419
1524
1420
1525
const requireFunc = requireVar . value ;
1421
1526
1422
1527
if ( ! isFunction ( requireFunc ) ) {
1423
- throw ErrIsNotFunction ( REQUIRE ) ;
1528
+ throw overriteStack ( ErrIsNotFunction ( REQUIRE ) , stack , node ) ;
1424
1529
}
1425
1530
1426
1531
const targetModule : any = requireFunc ( moduleName ) || { } ;
0 commit comments