@@ -1015,6 +1015,7 @@ v8_static_strings::v8_static_strings! {
1015
1015
TO_STRING = "toString" ,
1016
1016
PREPARE_STACK_TRACE = "prepareStackTrace" ,
1017
1017
ORIGINAL = "deno_core::original_call_site" ,
1018
+ SOURCE_MAPPED_INFO = "deno_core::source_mapped_call_site_info" ,
1018
1019
ERROR_RECEIVER_IS_NOT_VALID_CALLSITE_OBJECT = "The receiver is not a valid callsite object." ,
1019
1020
}
1020
1021
@@ -1026,6 +1027,13 @@ pub(crate) fn original_call_site_key<'a>(
1026
1027
v8:: Private :: for_api ( scope, Some ( name) )
1027
1028
}
1028
1029
1030
+ pub ( crate ) fn source_mapped_info_key < ' a > (
1031
+ scope : & mut v8:: HandleScope < ' a > ,
1032
+ ) -> v8:: Local < ' a , v8:: Private > {
1033
+ let name = SOURCE_MAPPED_INFO . v8_string ( scope) . unwrap ( ) ;
1034
+ v8:: Private :: for_api ( scope, Some ( name) )
1035
+ }
1036
+
1029
1037
fn make_patched_callsite < ' s > (
1030
1038
scope : & mut v8:: HandleScope < ' s > ,
1031
1039
callsite : v8:: Local < ' s , v8:: Object > ,
@@ -1095,44 +1103,186 @@ fn maybe_to_path_str(string: &str) -> Option<String> {
1095
1103
}
1096
1104
1097
1105
pub mod callsite_fns {
1106
+ use capacity_builder:: StringBuilder ;
1107
+
1108
+ use crate :: convert;
1109
+ use crate :: FromV8 ;
1110
+ use crate :: ToV8 ;
1111
+
1098
1112
use super :: * ;
1099
1113
1114
+ enum SourceMappedCallsiteInfo < ' a > {
1115
+ Ref ( v8:: Local < ' a , v8:: Array > ) ,
1116
+ Value {
1117
+ file_name : v8:: Local < ' a , v8:: Value > ,
1118
+ line_number : v8:: Local < ' a , v8:: Value > ,
1119
+ column_number : v8:: Local < ' a , v8:: Value > ,
1120
+ } ,
1121
+ }
1122
+ impl < ' a > SourceMappedCallsiteInfo < ' a > {
1123
+ #[ inline]
1124
+ fn file_name (
1125
+ & self ,
1126
+ scope : & mut v8:: HandleScope < ' a > ,
1127
+ ) -> v8:: Local < ' a , v8:: Value > {
1128
+ match self {
1129
+ Self :: Ref ( array) => array. get_index ( scope, 0 ) . unwrap ( ) ,
1130
+ Self :: Value { file_name, .. } => * file_name,
1131
+ }
1132
+ }
1133
+ #[ inline]
1134
+ fn line_number (
1135
+ & self ,
1136
+ scope : & mut v8:: HandleScope < ' a > ,
1137
+ ) -> v8:: Local < ' a , v8:: Value > {
1138
+ match self {
1139
+ Self :: Ref ( array) => array. get_index ( scope, 1 ) . unwrap ( ) ,
1140
+ Self :: Value { line_number, .. } => * line_number,
1141
+ }
1142
+ }
1143
+ #[ inline]
1144
+ fn column_number (
1145
+ & self ,
1146
+ scope : & mut v8:: HandleScope < ' a > ,
1147
+ ) -> v8:: Local < ' a , v8:: Value > {
1148
+ match self {
1149
+ Self :: Ref ( array) => array. get_index ( scope, 2 ) . unwrap ( ) ,
1150
+ Self :: Value { column_number, .. } => * column_number,
1151
+ }
1152
+ }
1153
+ }
1154
+
1155
+ type MaybeValue < ' a > = Option < v8:: Local < ' a , v8:: Value > > ;
1156
+
1157
+ fn maybe_apply_source_map < ' a > (
1158
+ scope : & mut v8:: HandleScope < ' a > ,
1159
+ file_name : MaybeValue < ' a > ,
1160
+ line_number : MaybeValue < ' a > ,
1161
+ column_number : MaybeValue < ' a > ,
1162
+ ) -> Option < ( String , i64 , i64 ) > {
1163
+ let file_name = serde_v8:: to_utf8 ( file_name?. try_cast ( ) . ok ( ) ?, scope) ;
1164
+ let convert:: Number ( line_number) =
1165
+ FromV8 :: from_v8 ( scope, line_number?) . ok ( ) ?;
1166
+ let convert:: Number ( column_number) =
1167
+ FromV8 :: from_v8 ( scope, column_number?) . ok ( ) ?;
1168
+
1169
+ let state = JsRuntime :: state_from ( scope) ;
1170
+ let mut source_mapper = state. source_mapper . borrow_mut ( ) ;
1171
+ let ( mapped_file_name, mapped_line_number, mapped_column_number) =
1172
+ apply_source_map (
1173
+ & mut source_mapper,
1174
+ Cow :: Owned ( file_name) ,
1175
+ line_number,
1176
+ column_number,
1177
+ ) ;
1178
+ Some ( (
1179
+ mapped_file_name. into_owned ( ) ,
1180
+ mapped_line_number,
1181
+ mapped_column_number,
1182
+ ) )
1183
+ }
1184
+ fn source_mapped_call_site_info < ' a > (
1185
+ scope : & mut v8:: HandleScope < ' a > ,
1186
+ callsite : v8:: Local < ' a , v8:: Object > ,
1187
+ ) -> Option < SourceMappedCallsiteInfo < ' a > > {
1188
+ let key = source_mapped_info_key ( scope) ;
1189
+ // return the cached value if it exists
1190
+ if let Some ( info) = callsite. get_private ( scope, key) {
1191
+ if let Ok ( array) = info. try_cast :: < v8:: Array > ( ) {
1192
+ return Some ( SourceMappedCallsiteInfo :: Ref ( array) ) ;
1193
+ }
1194
+ }
1195
+ let orig_callsite = original_call_site ( scope, callsite) ?;
1196
+
1197
+ let file_name =
1198
+ call_method :: < v8:: Value > ( scope, orig_callsite, super :: GET_FILE_NAME , & [ ] ) ;
1199
+ let line_number = call_method :: < v8:: Value > (
1200
+ scope,
1201
+ orig_callsite,
1202
+ super :: GET_LINE_NUMBER ,
1203
+ & [ ] ,
1204
+ ) ;
1205
+ let column_number = call_method :: < v8:: Value > (
1206
+ scope,
1207
+ orig_callsite,
1208
+ super :: GET_COLUMN_NUMBER ,
1209
+ & [ ] ,
1210
+ ) ;
1211
+
1212
+ let info = v8:: Array :: new ( scope, 3 ) ;
1213
+
1214
+ // if the types are right, apply the source map, otherwise just take them as is
1215
+ if let Some ( ( mapped_file_name, mapped_line_number, mapped_column_number) ) =
1216
+ maybe_apply_source_map ( scope, file_name, line_number, column_number)
1217
+ {
1218
+ let mapped_file_name_trimmed =
1219
+ maybe_to_path_str ( & mapped_file_name) . unwrap_or ( mapped_file_name) ;
1220
+ let mapped_file_name = crate :: FastString :: from ( mapped_file_name_trimmed)
1221
+ . v8_string ( scope)
1222
+ . unwrap ( ) ;
1223
+ let Ok ( mapped_line_number) =
1224
+ convert:: Number ( mapped_line_number) . to_v8 ( scope) ;
1225
+ let Ok ( mapped_column_number) =
1226
+ convert:: Number ( mapped_column_number) . to_v8 ( scope) ;
1227
+ info. set_index ( scope, 0 , mapped_file_name. into ( ) ) ;
1228
+ info. set_index ( scope, 1 , mapped_line_number) ;
1229
+ info. set_index ( scope, 2 , mapped_column_number) ;
1230
+ callsite. set_private ( scope, key, info. into ( ) ) ;
1231
+ Some ( SourceMappedCallsiteInfo :: Value {
1232
+ file_name : mapped_file_name. into ( ) ,
1233
+ line_number : mapped_line_number,
1234
+ column_number : mapped_column_number,
1235
+ } )
1236
+ } else {
1237
+ let file_name = file_name. unwrap_or_else ( || v8:: undefined ( scope) . into ( ) ) ;
1238
+ let line_number =
1239
+ line_number. unwrap_or_else ( || v8:: undefined ( scope) . into ( ) ) ;
1240
+ let column_number =
1241
+ column_number. unwrap_or_else ( || v8:: undefined ( scope) . into ( ) ) ;
1242
+ info. set_index ( scope, 0 , file_name) ;
1243
+ info. set_index ( scope, 1 , line_number) ;
1244
+ info. set_index ( scope, 2 , column_number) ;
1245
+ callsite. set_private ( scope, key, info. into ( ) ) ;
1246
+ Some ( SourceMappedCallsiteInfo :: Ref ( info) )
1247
+ }
1248
+ }
1249
+
1100
1250
make_callsite_fn ! ( get_this, GET_THIS ) ;
1101
1251
make_callsite_fn ! ( get_type_name, GET_TYPE_NAME ) ;
1102
1252
make_callsite_fn ! ( get_function, GET_FUNCTION ) ;
1103
1253
make_callsite_fn ! ( get_function_name, GET_FUNCTION_NAME ) ;
1104
1254
make_callsite_fn ! ( get_method_name, GET_METHOD_NAME ) ;
1105
1255
1106
- pub fn get_file_name (
1107
- scope : & mut v8:: HandleScope < ' _ > ,
1108
- args : v8:: FunctionCallbackArguments < ' _ > ,
1256
+ pub fn get_file_name < ' a > (
1257
+ scope : & mut v8:: HandleScope < ' a > ,
1258
+ args : v8:: FunctionCallbackArguments < ' a > ,
1109
1259
mut rv : v8:: ReturnValue < ' _ > ,
1110
1260
) {
1111
- let Some ( orig) = original_call_site ( scope, args. this ( ) ) else {
1112
- return ;
1113
- } ;
1114
- // call getFileName
1115
- let orig_ret =
1116
- call_method :: < v8:: Value > ( scope, orig, super :: GET_FILE_NAME , & [ ] ) ;
1117
- if let Some ( ret_val) =
1118
- orig_ret. and_then ( |v| v. try_cast :: < v8:: String > ( ) . ok ( ) )
1119
- {
1120
- // strip off `file://`
1121
- let string = ret_val. to_rust_string_lossy ( scope) ;
1122
- if let Some ( file_name) = maybe_to_path_str ( & string) {
1123
- let v8_str = crate :: FastString :: from ( file_name)
1124
- . v8_string ( scope)
1125
- . unwrap ( )
1126
- . into ( ) ;
1127
- rv. set ( v8_str) ;
1128
- } else {
1129
- rv. set ( ret_val. into ( ) ) ;
1130
- }
1261
+ if let Some ( info) = source_mapped_call_site_info ( scope, args. this ( ) ) {
1262
+ rv. set ( info. file_name ( scope) ) ;
1263
+ }
1264
+ }
1265
+
1266
+ pub fn get_line_number < ' a > (
1267
+ scope : & mut v8:: HandleScope < ' a > ,
1268
+ args : v8:: FunctionCallbackArguments < ' a > ,
1269
+ mut rv : v8:: ReturnValue < ' _ > ,
1270
+ ) {
1271
+ if let Some ( info) = source_mapped_call_site_info ( scope, args. this ( ) ) {
1272
+ rv. set ( info. line_number ( scope) ) ;
1273
+ }
1274
+ }
1275
+
1276
+ pub fn get_column_number < ' a > (
1277
+ scope : & mut v8:: HandleScope < ' a > ,
1278
+ args : v8:: FunctionCallbackArguments < ' a > ,
1279
+ mut rv : v8:: ReturnValue < ' _ > ,
1280
+ ) {
1281
+ if let Some ( info) = source_mapped_call_site_info ( scope, args. this ( ) ) {
1282
+ rv. set ( info. column_number ( scope) ) ;
1131
1283
}
1132
1284
}
1133
1285
1134
- make_callsite_fn ! ( get_line_number, GET_LINE_NUMBER ) ;
1135
- make_callsite_fn ! ( get_column_number, GET_COLUMN_NUMBER ) ;
1136
1286
make_callsite_fn ! ( get_eval_origin, GET_EVAL_ORIGIN ) ;
1137
1287
make_callsite_fn ! ( is_toplevel, IS_TOPLEVEL ) ;
1138
1288
make_callsite_fn ! ( is_eval, IS_EVAL ) ;
@@ -1146,12 +1296,65 @@ pub mod callsite_fns {
1146
1296
GET_SCRIPT_NAME_OR_SOURCE_URL
1147
1297
) ;
1148
1298
1149
- pub fn to_string (
1150
- scope : & mut v8:: HandleScope < ' _ > ,
1151
- args : v8:: FunctionCallbackArguments < ' _ > ,
1299
+ // the bulk of the to_string logic
1300
+ fn to_string_inner < ' e > (
1301
+ scope : & mut v8:: HandleScope < ' e > ,
1302
+ this : v8:: Local < ' e , v8:: Object > ,
1303
+ orig : v8:: Local < ' e , Object > ,
1304
+ orig_to_string_v8 : v8:: Local < ' e , v8:: String > ,
1305
+ ) -> Option < v8:: Local < ' e , v8:: String > > {
1306
+ let orig_to_string = serde_v8:: to_utf8 ( orig_to_string_v8, scope) ;
1307
+ // `this[kOriginalCallsite].getFileName()`
1308
+ let orig_file_name =
1309
+ call_method :: < v8:: Value > ( scope, orig, GET_FILE_NAME , & [ ] )
1310
+ . and_then ( |v| v. try_cast :: < v8:: String > ( ) . ok ( ) ) ?;
1311
+ let orig_line_number =
1312
+ call_method :: < v8:: Value > ( scope, orig, GET_LINE_NUMBER , & [ ] )
1313
+ . and_then ( |v| v. try_cast :: < v8:: Number > ( ) . ok ( ) ) ?;
1314
+ let orig_column_number =
1315
+ call_method :: < v8:: Value > ( scope, orig, GET_COLUMN_NUMBER , & [ ] )
1316
+ . and_then ( |v| v. try_cast :: < v8:: Number > ( ) . ok ( ) ) ?;
1317
+ let orig_file_name = serde_v8:: to_utf8 ( orig_file_name, scope) ;
1318
+ let orig_line_number = orig_line_number. value ( ) as i64 ;
1319
+ let orig_column_number = orig_column_number. value ( ) as i64 ;
1320
+ let orig_file_name_line_col =
1321
+ fmt_file_line_col ( & orig_file_name, orig_line_number, orig_column_number) ;
1322
+ let mapped = source_mapped_call_site_info ( scope, this) ?;
1323
+ let mapped_file_name = mapped. file_name ( scope) . to_rust_string_lossy ( scope) ;
1324
+ let mapped_line_num = mapped
1325
+ . line_number ( scope)
1326
+ . try_cast :: < v8:: Number > ( )
1327
+ . ok ( )
1328
+ . map ( |n| n. value ( ) as i64 ) ?;
1329
+ let mapped_col_num =
1330
+ mapped. column_number ( scope) . cast :: < v8:: Number > ( ) . value ( ) as i64 ;
1331
+ let file_name_line_col =
1332
+ fmt_file_line_col ( & mapped_file_name, mapped_line_num, mapped_col_num) ;
1333
+ // replace file URL with file path, and source map in original `toString`
1334
+ let to_string = orig_to_string
1335
+ . replace ( & orig_file_name_line_col, & file_name_line_col)
1336
+ . replace ( & orig_file_name, & mapped_file_name) ; // maybe unnecessary?
1337
+ Some ( crate :: FastString :: from ( to_string) . v8_string ( scope) . unwrap ( ) )
1338
+ }
1339
+
1340
+ fn fmt_file_line_col ( file : & str , line : i64 , col : i64 ) -> String {
1341
+ StringBuilder :: build ( |builder| {
1342
+ builder. append ( file) ;
1343
+ builder. append ( ':' ) ;
1344
+ builder. append ( line) ;
1345
+ builder. append ( ':' ) ;
1346
+ builder. append ( col) ;
1347
+ } )
1348
+ . unwrap ( )
1349
+ }
1350
+
1351
+ pub fn to_string < ' a > (
1352
+ scope : & mut v8:: HandleScope < ' a > ,
1353
+ args : v8:: FunctionCallbackArguments < ' a > ,
1152
1354
mut rv : v8:: ReturnValue < ' _ > ,
1153
1355
) {
1154
- let Some ( orig) = original_call_site ( scope, args. this ( ) ) else {
1356
+ let this = args. this ( ) ;
1357
+ let Some ( orig) = original_call_site ( scope, this) else {
1155
1358
return ;
1156
1359
} ;
1157
1360
// `this[kOriginalCallsite].toString()`
@@ -1160,24 +1363,10 @@ pub mod callsite_fns {
1160
1363
else {
1161
1364
return ;
1162
1365
} ;
1163
- let orig_to_string = serde_v8:: to_utf8 ( orig_to_string_v8, scope) ;
1164
- // `this[kOriginalCallsite].getFileName()`
1165
- let orig_ret_file_name =
1166
- call_method :: < v8:: Value > ( scope, orig, GET_FILE_NAME , & [ ] ) ;
1167
- let Some ( orig_file_name) =
1168
- orig_ret_file_name. and_then ( |v| v. try_cast :: < v8:: String > ( ) . ok ( ) )
1169
- else {
1170
- return ;
1171
- } ;
1172
- // replace file URL with file path in original `toString`
1173
- let orig_file_name = serde_v8:: to_utf8 ( orig_file_name, scope) ;
1174
- if let Some ( file_name) = maybe_to_path_str ( & orig_file_name) {
1175
- let to_string = orig_to_string. replace ( & orig_file_name, & file_name) ;
1176
- let v8_str = crate :: FastString :: from ( to_string)
1177
- . v8_string ( scope)
1178
- . unwrap ( )
1179
- . into ( ) ;
1180
- rv. set ( v8_str) ;
1366
+
1367
+ if let Some ( v8_str) = to_string_inner ( scope, this, orig, orig_to_string_v8)
1368
+ {
1369
+ rv. set ( v8_str. into ( ) ) ;
1181
1370
} else {
1182
1371
rv. set ( orig_to_string_v8. into ( ) ) ;
1183
1372
}
0 commit comments