diff --git a/lib/src/compiler/ir/ast2ir.rs b/lib/src/compiler/ir/ast2ir.rs index ffbe98085..e7f2a045b 100644 --- a/lib/src/compiler/ir/ast2ir.rs +++ b/lib/src/compiler/ir/ast2ir.rs @@ -1294,7 +1294,10 @@ fn is_potentially_large_range(ctx: &CompileContext, range: &Range) -> bool { // Don't traverse the arguments of `math.min`. |node| { if let Expr::FuncCall(func) = node { - func.signature.mangled_name.as_str().eq("math.min@ii@i") + func.signature + .mangled_name + .as_str() + .eq("math.min@a:i,b:i@i") } else { false } diff --git a/lib/src/compiler/ir/tests/testdata/10.cse.ir b/lib/src/compiler/ir/tests/testdata/10.cse.ir index f22e07772..f2a6014ec 100644 --- a/lib/src/compiler/ir/tests/testdata/10.cse.ir +++ b/lib/src/compiler/ir/tests/testdata/10.cse.ir @@ -1,14 +1,14 @@ RULE test - 13: FOR_IN -- hash: 0x96423d1fc9f13492 -- parent: None + 13: FOR_IN -- hash: 0x12dbfc09ed5ac100 -- parent: None 0: CONST integer(1) -- parent: 13 1: CONST integer(10) -- parent: 13 - 12: WITH -- hash: 0x2fa6e67ac30fd47a -- parent: 13 - 6: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0xb24700199f93dab8 -- parent: 12 + 12: WITH -- hash: 0xac40a564e67960e7 -- parent: 13 + 6: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0xdb7bdb9e18e2be98 -- parent: 12 5: ADD -- hash: 0x685e7a2ba5a36f1d -- parent: 6 3: PATTERN_OFFSET PatternIdx(0) INDEX -- hash: 0x7cb6e22690df7f1b -- parent: 5 2: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } -- parent: 3 4: CONST integer(16) -- parent: 5 - 8: FN_CALL uint32be@i@i:R0:4294967295u -- hash: 0x36dbf5f6d8719ced -- parent: 12 + 8: FN_CALL uint32be@offset:i@i:R0:4294967295u -- hash: 0x954bbb64201a5bf8 -- parent: 12 7: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 7 }, type_value: integer(unknown) } -- parent: 8 11: NE -- hash: 0xe0b33818592dcf55 -- parent: 12 9: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 8 }, type_value: integer(unknown) } -- parent: 11 diff --git a/lib/src/compiler/ir/tests/testdata/10.hoisting.ir b/lib/src/compiler/ir/tests/testdata/10.hoisting.ir index f22e07772..f2a6014ec 100644 --- a/lib/src/compiler/ir/tests/testdata/10.hoisting.ir +++ b/lib/src/compiler/ir/tests/testdata/10.hoisting.ir @@ -1,14 +1,14 @@ RULE test - 13: FOR_IN -- hash: 0x96423d1fc9f13492 -- parent: None + 13: FOR_IN -- hash: 0x12dbfc09ed5ac100 -- parent: None 0: CONST integer(1) -- parent: 13 1: CONST integer(10) -- parent: 13 - 12: WITH -- hash: 0x2fa6e67ac30fd47a -- parent: 13 - 6: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0xb24700199f93dab8 -- parent: 12 + 12: WITH -- hash: 0xac40a564e67960e7 -- parent: 13 + 6: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0xdb7bdb9e18e2be98 -- parent: 12 5: ADD -- hash: 0x685e7a2ba5a36f1d -- parent: 6 3: PATTERN_OFFSET PatternIdx(0) INDEX -- hash: 0x7cb6e22690df7f1b -- parent: 5 2: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } -- parent: 3 4: CONST integer(16) -- parent: 5 - 8: FN_CALL uint32be@i@i:R0:4294967295u -- hash: 0x36dbf5f6d8719ced -- parent: 12 + 8: FN_CALL uint32be@offset:i@i:R0:4294967295u -- hash: 0x954bbb64201a5bf8 -- parent: 12 7: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 7 }, type_value: integer(unknown) } -- parent: 8 11: NE -- hash: 0xe0b33818592dcf55 -- parent: 12 9: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 8 }, type_value: integer(unknown) } -- parent: 11 diff --git a/lib/src/compiler/ir/tests/testdata/10.ir b/lib/src/compiler/ir/tests/testdata/10.ir index f22e07772..f2a6014ec 100644 --- a/lib/src/compiler/ir/tests/testdata/10.ir +++ b/lib/src/compiler/ir/tests/testdata/10.ir @@ -1,14 +1,14 @@ RULE test - 13: FOR_IN -- hash: 0x96423d1fc9f13492 -- parent: None + 13: FOR_IN -- hash: 0x12dbfc09ed5ac100 -- parent: None 0: CONST integer(1) -- parent: 13 1: CONST integer(10) -- parent: 13 - 12: WITH -- hash: 0x2fa6e67ac30fd47a -- parent: 13 - 6: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0xb24700199f93dab8 -- parent: 12 + 12: WITH -- hash: 0xac40a564e67960e7 -- parent: 13 + 6: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0xdb7bdb9e18e2be98 -- parent: 12 5: ADD -- hash: 0x685e7a2ba5a36f1d -- parent: 6 3: PATTERN_OFFSET PatternIdx(0) INDEX -- hash: 0x7cb6e22690df7f1b -- parent: 5 2: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 5 }, type_value: integer(unknown) } -- parent: 3 4: CONST integer(16) -- parent: 5 - 8: FN_CALL uint32be@i@i:R0:4294967295u -- hash: 0x36dbf5f6d8719ced -- parent: 12 + 8: FN_CALL uint32be@offset:i@i:R0:4294967295u -- hash: 0x954bbb64201a5bf8 -- parent: 12 7: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 7 }, type_value: integer(unknown) } -- parent: 8 11: NE -- hash: 0xe0b33818592dcf55 -- parent: 12 9: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 8 }, type_value: integer(unknown) } -- parent: 11 diff --git a/lib/src/compiler/ir/tests/testdata/3.cse.ir b/lib/src/compiler/ir/tests/testdata/3.cse.ir index 8d7197b09..5741b1420 100644 --- a/lib/src/compiler/ir/tests/testdata/3.cse.ir +++ b/lib/src/compiler/ir/tests/testdata/3.cse.ir @@ -1,12 +1,12 @@ RULE test - 12: OR -- hash: 0x1b8fc9245609bbd8 -- parent: None - 5: EQ -- hash: 0xa7087e04c9789991 -- parent: 12 - 3: FN_CALL hash.md5@ii@s:N32:Lu -- hash: 0xb1521b9e4a43d71b -- parent: 5 + 12: OR -- hash: 0x3943b5785dde551a -- parent: None + 5: EQ -- hash: 0x7e5484873ebb2659 -- parent: 12 + 3: FN_CALL hash.md5@offset:i,size:i@s:N32:Lu -- hash: 0xeaf33e3e6d085dc7 -- parent: 5 1: CONST integer(0) -- parent: 3 2: FILESIZE -- parent: 3 4: CONST string("feba6c919e3797e7778e8f2e85fa033d") -- parent: 5 - 11: EQ -- hash: 0xf4e7b8bcfc86defd -- parent: 12 - 9: FN_CALL hash.md5@ii@s:N32:Lu -- hash: 0xb1521b9e4a43d71b -- parent: 11 + 11: EQ -- hash: 0xcc33bf3f71c96bc5 -- parent: 12 + 9: FN_CALL hash.md5@offset:i,size:i@s:N32:Lu -- hash: 0xeaf33e3e6d085dc7 -- parent: 11 7: CONST integer(0) -- parent: 9 8: FILESIZE -- parent: 9 10: CONST string("275876e34cf609db118f3d84b799a790") -- parent: 11 diff --git a/lib/src/compiler/ir/tests/testdata/3.hoisting.ir b/lib/src/compiler/ir/tests/testdata/3.hoisting.ir index 8d7197b09..5741b1420 100644 --- a/lib/src/compiler/ir/tests/testdata/3.hoisting.ir +++ b/lib/src/compiler/ir/tests/testdata/3.hoisting.ir @@ -1,12 +1,12 @@ RULE test - 12: OR -- hash: 0x1b8fc9245609bbd8 -- parent: None - 5: EQ -- hash: 0xa7087e04c9789991 -- parent: 12 - 3: FN_CALL hash.md5@ii@s:N32:Lu -- hash: 0xb1521b9e4a43d71b -- parent: 5 + 12: OR -- hash: 0x3943b5785dde551a -- parent: None + 5: EQ -- hash: 0x7e5484873ebb2659 -- parent: 12 + 3: FN_CALL hash.md5@offset:i,size:i@s:N32:Lu -- hash: 0xeaf33e3e6d085dc7 -- parent: 5 1: CONST integer(0) -- parent: 3 2: FILESIZE -- parent: 3 4: CONST string("feba6c919e3797e7778e8f2e85fa033d") -- parent: 5 - 11: EQ -- hash: 0xf4e7b8bcfc86defd -- parent: 12 - 9: FN_CALL hash.md5@ii@s:N32:Lu -- hash: 0xb1521b9e4a43d71b -- parent: 11 + 11: EQ -- hash: 0xcc33bf3f71c96bc5 -- parent: 12 + 9: FN_CALL hash.md5@offset:i,size:i@s:N32:Lu -- hash: 0xeaf33e3e6d085dc7 -- parent: 11 7: CONST integer(0) -- parent: 9 8: FILESIZE -- parent: 9 10: CONST string("275876e34cf609db118f3d84b799a790") -- parent: 11 diff --git a/lib/src/compiler/ir/tests/testdata/3.ir b/lib/src/compiler/ir/tests/testdata/3.ir index 8d7197b09..5741b1420 100644 --- a/lib/src/compiler/ir/tests/testdata/3.ir +++ b/lib/src/compiler/ir/tests/testdata/3.ir @@ -1,12 +1,12 @@ RULE test - 12: OR -- hash: 0x1b8fc9245609bbd8 -- parent: None - 5: EQ -- hash: 0xa7087e04c9789991 -- parent: 12 - 3: FN_CALL hash.md5@ii@s:N32:Lu -- hash: 0xb1521b9e4a43d71b -- parent: 5 + 12: OR -- hash: 0x3943b5785dde551a -- parent: None + 5: EQ -- hash: 0x7e5484873ebb2659 -- parent: 12 + 3: FN_CALL hash.md5@offset:i,size:i@s:N32:Lu -- hash: 0xeaf33e3e6d085dc7 -- parent: 5 1: CONST integer(0) -- parent: 3 2: FILESIZE -- parent: 3 4: CONST string("feba6c919e3797e7778e8f2e85fa033d") -- parent: 5 - 11: EQ -- hash: 0xf4e7b8bcfc86defd -- parent: 12 - 9: FN_CALL hash.md5@ii@s:N32:Lu -- hash: 0xb1521b9e4a43d71b -- parent: 11 + 11: EQ -- hash: 0xcc33bf3f71c96bc5 -- parent: 12 + 9: FN_CALL hash.md5@offset:i,size:i@s:N32:Lu -- hash: 0xeaf33e3e6d085dc7 -- parent: 11 7: CONST integer(0) -- parent: 9 8: FILESIZE -- parent: 9 10: CONST string("275876e34cf609db118f3d84b799a790") -- parent: 11 diff --git a/lib/src/compiler/ir/tests/testdata/4.cse.ir b/lib/src/compiler/ir/tests/testdata/4.cse.ir index a7f439f2f..acf3db4d7 100644 --- a/lib/src/compiler/ir/tests/testdata/4.cse.ir +++ b/lib/src/compiler/ir/tests/testdata/4.cse.ir @@ -1,18 +1,18 @@ RULE test - 52: WITH -- hash: 0x5e28f45a3380ebce -- parent: None + 52: WITH -- hash: 0x3d25b3d4794433e2 -- parent: None 2: FIELD_ACCESS -- hash: 0x30adb8d0b7ea7b20 -- parent: 52 0: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } -- parent: 2 1: SYMBOL Field { index: 12, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: None } -- parent: 2 - 51: AND -- hash: 0xd48c9e4c1bbd6e98 -- parent: 52 - 22: FOR_IN -- hash: 0x7854178bc588aac4 -- parent: 51 + 51: AND -- hash: 0xb3895dc66180b6ac -- parent: 52 + 22: FOR_IN -- hash: 0xb5766871f0d8434f -- parent: 51 3: CONST integer(0) -- parent: 22 4: CONST integer(1) -- parent: 22 - 21: AND -- hash: 0x9cf8fe424d5f369 -- parent: 22 + 21: AND -- hash: 0x46f1e0ca50258bf4 -- parent: 22 7: EQ -- hash: 0xa866a1c3637edc78 -- parent: 21 5: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 6 }, type_value: integer(unknown) } -- parent: 7 6: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 0 }, type_value: integer(unknown) } -- parent: 7 - 13: EQ -- hash: 0x15f5b2aba7d47f8 -- parent: 21 - 11: FN_CALL test_proto2.add@ii@i -- hash: 0x4b8def611ce41371 -- parent: 13 + 13: EQ -- hash: 0x452e9b6d563a2e0e -- parent: 21 + 11: FN_CALL test_proto2.add@a:i,b:i@i -- hash: 0x261e822f79c74aad -- parent: 13 9: CONST integer(1) -- parent: 11 10: CONST integer(2) -- parent: 11 12: CONST integer(3) -- parent: 13 @@ -23,12 +23,12 @@ RULE test 15: SYMBOL Field { index: 10, is_root: false, type_value: float(unknown), acl: None, deprecation_notice: None } -- parent: 16 17: CONST integer(1) -- parent: 18 19: CONST float(1.0) -- parent: 20 - 39: FOR_IN -- hash: 0x3aeb7cd882a3770d -- parent: 51 + 39: FOR_IN -- hash: 0x780dcdbeadf30f98 -- parent: 51 23: CONST integer(0) -- parent: 39 24: CONST integer(1) -- parent: 39 - 38: OR -- hash: 0xc7fb5e4615f1997 -- parent: 39 - 30: NE -- hash: 0xb189c2a481168c22 -- parent: 38 - 28: FN_CALL test_proto2.add@ii@i -- hash: 0x4b8def611ce41371 -- parent: 30 + 38: OR -- hash: 0x49a206ca8caeb222 -- parent: 39 + 30: NE -- hash: 0xf55902e71cd37238 -- parent: 38 + 28: FN_CALL test_proto2.add@a:i,b:i@i -- hash: 0x261e822f79c74aad -- parent: 30 26: CONST integer(1) -- parent: 28 27: CONST integer(2) -- parent: 28 29: CONST integer(0) -- parent: 30 diff --git a/lib/src/compiler/ir/tests/testdata/4.hoisting.ir b/lib/src/compiler/ir/tests/testdata/4.hoisting.ir index 5c0cbb655..e3cd7aa4d 100644 --- a/lib/src/compiler/ir/tests/testdata/4.hoisting.ir +++ b/lib/src/compiler/ir/tests/testdata/4.hoisting.ir @@ -1,11 +1,11 @@ RULE test - 52: WITH -- hash: 0xe52165a3e4f9c004 -- parent: None + 52: WITH -- hash: 0x9bec9656dce82b6d -- parent: None 2: FIELD_ACCESS -- hash: 0x30adb8d0b7ea7b20 -- parent: 52 0: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } -- parent: 2 1: SYMBOL Field { index: 12, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: None } -- parent: 2 - 51: AND -- hash: 0x77e386988c8be1cf -- parent: 52 - 54: WITH -- hash: 0x21fb65f365d018b2 -- parent: 51 - 53: FN_CALL test_proto2.add@ii@i -- hash: 0x4b8def611ce41371 -- parent: 54 + 51: AND -- hash: 0x2eaeb74b847a4d38 -- parent: 52 + 54: WITH -- hash: 0x1146bb568254efab -- parent: 51 + 53: FN_CALL test_proto2.add@a:i,b:i@i -- hash: 0x261e822f79c74aad -- parent: 54 9: CONST integer(1) -- parent: 53 10: CONST integer(2) -- parent: 53 56: WITH -- hash: 0x58086382660011ee -- parent: 54 @@ -33,8 +33,8 @@ RULE test 6: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 0 }, type_value: integer(unknown) } -- parent: 7 13: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 2 }, type_value: boolean(unknown) } -- parent: 21 20: SYMBOL Var { var: Var { frame_id: 0, ty: boolean, index: 5 }, type_value: boolean(unknown) } -- parent: 21 - 64: WITH -- hash: 0xca40c776e4c60a3a -- parent: 51 - 63: FN_CALL test_proto2.add@ii@i -- hash: 0x4b8def611ce41371 -- parent: 64 + 64: WITH -- hash: 0x2e00596c13d056e8 -- parent: 51 + 63: FN_CALL test_proto2.add@a:i,b:i@i -- hash: 0x261e822f79c74aad -- parent: 64 26: CONST integer(1) -- parent: 63 27: CONST integer(2) -- parent: 63 66: WITH -- hash: 0xa0cb3969e5369c8a -- parent: 64 diff --git a/lib/src/compiler/ir/tests/testdata/4.ir b/lib/src/compiler/ir/tests/testdata/4.ir index a7f439f2f..acf3db4d7 100644 --- a/lib/src/compiler/ir/tests/testdata/4.ir +++ b/lib/src/compiler/ir/tests/testdata/4.ir @@ -1,18 +1,18 @@ RULE test - 52: WITH -- hash: 0x5e28f45a3380ebce -- parent: None + 52: WITH -- hash: 0x3d25b3d4794433e2 -- parent: None 2: FIELD_ACCESS -- hash: 0x30adb8d0b7ea7b20 -- parent: 52 0: SYMBOL Field { index: 0, is_root: true, type_value: struct, acl: None, deprecation_notice: None } -- parent: 2 1: SYMBOL Field { index: 12, is_root: false, type_value: integer(unknown), acl: None, deprecation_notice: None } -- parent: 2 - 51: AND -- hash: 0xd48c9e4c1bbd6e98 -- parent: 52 - 22: FOR_IN -- hash: 0x7854178bc588aac4 -- parent: 51 + 51: AND -- hash: 0xb3895dc66180b6ac -- parent: 52 + 22: FOR_IN -- hash: 0xb5766871f0d8434f -- parent: 51 3: CONST integer(0) -- parent: 22 4: CONST integer(1) -- parent: 22 - 21: AND -- hash: 0x9cf8fe424d5f369 -- parent: 22 + 21: AND -- hash: 0x46f1e0ca50258bf4 -- parent: 22 7: EQ -- hash: 0xa866a1c3637edc78 -- parent: 21 5: SYMBOL Var { var: Var { frame_id: 2, ty: integer, index: 6 }, type_value: integer(unknown) } -- parent: 7 6: SYMBOL Var { var: Var { frame_id: 1, ty: integer, index: 0 }, type_value: integer(unknown) } -- parent: 7 - 13: EQ -- hash: 0x15f5b2aba7d47f8 -- parent: 21 - 11: FN_CALL test_proto2.add@ii@i -- hash: 0x4b8def611ce41371 -- parent: 13 + 13: EQ -- hash: 0x452e9b6d563a2e0e -- parent: 21 + 11: FN_CALL test_proto2.add@a:i,b:i@i -- hash: 0x261e822f79c74aad -- parent: 13 9: CONST integer(1) -- parent: 11 10: CONST integer(2) -- parent: 11 12: CONST integer(3) -- parent: 13 @@ -23,12 +23,12 @@ RULE test 15: SYMBOL Field { index: 10, is_root: false, type_value: float(unknown), acl: None, deprecation_notice: None } -- parent: 16 17: CONST integer(1) -- parent: 18 19: CONST float(1.0) -- parent: 20 - 39: FOR_IN -- hash: 0x3aeb7cd882a3770d -- parent: 51 + 39: FOR_IN -- hash: 0x780dcdbeadf30f98 -- parent: 51 23: CONST integer(0) -- parent: 39 24: CONST integer(1) -- parent: 39 - 38: OR -- hash: 0xc7fb5e4615f1997 -- parent: 39 - 30: NE -- hash: 0xb189c2a481168c22 -- parent: 38 - 28: FN_CALL test_proto2.add@ii@i -- hash: 0x4b8def611ce41371 -- parent: 30 + 38: OR -- hash: 0x49a206ca8caeb222 -- parent: 39 + 30: NE -- hash: 0xf55902e71cd37238 -- parent: 38 + 28: FN_CALL test_proto2.add@a:i,b:i@i -- hash: 0x261e822f79c74aad -- parent: 30 26: CONST integer(1) -- parent: 28 27: CONST integer(2) -- parent: 28 29: CONST integer(0) -- parent: 30 diff --git a/lib/src/compiler/ir/tests/testdata/6.cse.ir b/lib/src/compiler/ir/tests/testdata/6.cse.ir index 0a53aa0cd..8e416a4a6 100644 --- a/lib/src/compiler/ir/tests/testdata/6.cse.ir +++ b/lib/src/compiler/ir/tests/testdata/6.cse.ir @@ -1,22 +1,22 @@ RULE test - 19: OR -- hash: 0x38ac661bc7edb303 -- parent: None - 9: AND -- hash: 0x55644aa7ab376dc4 -- parent: 19 - 3: EQ -- hash: 0xb74a347972d27f07 -- parent: 9 - 1: FN_CALL uint16@i@i:R0:65535u -- hash: 0x5c99c42ccd549597 -- parent: 3 + 19: OR -- hash: 0x15c1b9fd8bb07934 -- parent: None + 9: AND -- hash: 0x4f03f88669200ec7 -- parent: 19 + 3: EQ -- hash: 0x6636f0caaaf0ce1 -- parent: 9 + 1: FN_CALL uint16@offset:i@i:R0:65535u -- hash: 0xd9c315b7872786ad -- parent: 3 0: CONST integer(0) -- parent: 1 2: CONST integer(23117) -- parent: 3 - 8: EQ -- hash: 0xefd8aad42cfb8415 -- parent: 9 - 6: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x7b9b59ee5ef72c64 -- parent: 8 - 5: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x9112b2d627005c55 -- parent: 6 + 8: EQ -- hash: 0x23d5cb37154e6e05 -- parent: 9 + 6: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0x114d562bc44dc92b -- parent: 8 + 5: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0x70d97345f01748c4 -- parent: 6 4: CONST integer(60) -- parent: 5 7: CONST integer(17744) -- parent: 8 - 18: AND -- hash: 0x42c53eddef4faaf7 -- parent: 19 - 13: EQ -- hash: 0xd49847c981d59e8b -- parent: 18 - 11: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x59633ad74de391a -- parent: 13 + 18: AND -- hash: 0xeb59fd3df0f925fc -- parent: 19 + 13: EQ -- hash: 0x8bd5c0fe8f781437 -- parent: 18 + 11: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0xe55cf41d41f52588 -- parent: 13 10: CONST integer(0) -- parent: 11 12: CONST integer(1179403647) -- parent: 13 - 17: NE -- hash: 0xe122eeddb12026ad -- parent: 18 - 15: FN_CALL uint16@i@i:R0:65535u -- hash: 0x5c99c42ccd549597 -- parent: 17 + 17: NE -- hash: 0x303c2970e8fcb487 -- parent: 18 + 15: FN_CALL uint16@offset:i@i:R0:65535u -- hash: 0xd9c315b7872786ad -- parent: 17 14: CONST integer(0) -- parent: 15 16: CONST integer(0) -- parent: 17 diff --git a/lib/src/compiler/ir/tests/testdata/6.hoisting.ir b/lib/src/compiler/ir/tests/testdata/6.hoisting.ir index 0a53aa0cd..8e416a4a6 100644 --- a/lib/src/compiler/ir/tests/testdata/6.hoisting.ir +++ b/lib/src/compiler/ir/tests/testdata/6.hoisting.ir @@ -1,22 +1,22 @@ RULE test - 19: OR -- hash: 0x38ac661bc7edb303 -- parent: None - 9: AND -- hash: 0x55644aa7ab376dc4 -- parent: 19 - 3: EQ -- hash: 0xb74a347972d27f07 -- parent: 9 - 1: FN_CALL uint16@i@i:R0:65535u -- hash: 0x5c99c42ccd549597 -- parent: 3 + 19: OR -- hash: 0x15c1b9fd8bb07934 -- parent: None + 9: AND -- hash: 0x4f03f88669200ec7 -- parent: 19 + 3: EQ -- hash: 0x6636f0caaaf0ce1 -- parent: 9 + 1: FN_CALL uint16@offset:i@i:R0:65535u -- hash: 0xd9c315b7872786ad -- parent: 3 0: CONST integer(0) -- parent: 1 2: CONST integer(23117) -- parent: 3 - 8: EQ -- hash: 0xefd8aad42cfb8415 -- parent: 9 - 6: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x7b9b59ee5ef72c64 -- parent: 8 - 5: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x9112b2d627005c55 -- parent: 6 + 8: EQ -- hash: 0x23d5cb37154e6e05 -- parent: 9 + 6: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0x114d562bc44dc92b -- parent: 8 + 5: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0x70d97345f01748c4 -- parent: 6 4: CONST integer(60) -- parent: 5 7: CONST integer(17744) -- parent: 8 - 18: AND -- hash: 0x42c53eddef4faaf7 -- parent: 19 - 13: EQ -- hash: 0xd49847c981d59e8b -- parent: 18 - 11: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x59633ad74de391a -- parent: 13 + 18: AND -- hash: 0xeb59fd3df0f925fc -- parent: 19 + 13: EQ -- hash: 0x8bd5c0fe8f781437 -- parent: 18 + 11: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0xe55cf41d41f52588 -- parent: 13 10: CONST integer(0) -- parent: 11 12: CONST integer(1179403647) -- parent: 13 - 17: NE -- hash: 0xe122eeddb12026ad -- parent: 18 - 15: FN_CALL uint16@i@i:R0:65535u -- hash: 0x5c99c42ccd549597 -- parent: 17 + 17: NE -- hash: 0x303c2970e8fcb487 -- parent: 18 + 15: FN_CALL uint16@offset:i@i:R0:65535u -- hash: 0xd9c315b7872786ad -- parent: 17 14: CONST integer(0) -- parent: 15 16: CONST integer(0) -- parent: 17 diff --git a/lib/src/compiler/ir/tests/testdata/6.ir b/lib/src/compiler/ir/tests/testdata/6.ir index 0a53aa0cd..8e416a4a6 100644 --- a/lib/src/compiler/ir/tests/testdata/6.ir +++ b/lib/src/compiler/ir/tests/testdata/6.ir @@ -1,22 +1,22 @@ RULE test - 19: OR -- hash: 0x38ac661bc7edb303 -- parent: None - 9: AND -- hash: 0x55644aa7ab376dc4 -- parent: 19 - 3: EQ -- hash: 0xb74a347972d27f07 -- parent: 9 - 1: FN_CALL uint16@i@i:R0:65535u -- hash: 0x5c99c42ccd549597 -- parent: 3 + 19: OR -- hash: 0x15c1b9fd8bb07934 -- parent: None + 9: AND -- hash: 0x4f03f88669200ec7 -- parent: 19 + 3: EQ -- hash: 0x6636f0caaaf0ce1 -- parent: 9 + 1: FN_CALL uint16@offset:i@i:R0:65535u -- hash: 0xd9c315b7872786ad -- parent: 3 0: CONST integer(0) -- parent: 1 2: CONST integer(23117) -- parent: 3 - 8: EQ -- hash: 0xefd8aad42cfb8415 -- parent: 9 - 6: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x7b9b59ee5ef72c64 -- parent: 8 - 5: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x9112b2d627005c55 -- parent: 6 + 8: EQ -- hash: 0x23d5cb37154e6e05 -- parent: 9 + 6: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0x114d562bc44dc92b -- parent: 8 + 5: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0x70d97345f01748c4 -- parent: 6 4: CONST integer(60) -- parent: 5 7: CONST integer(17744) -- parent: 8 - 18: AND -- hash: 0x42c53eddef4faaf7 -- parent: 19 - 13: EQ -- hash: 0xd49847c981d59e8b -- parent: 18 - 11: FN_CALL uint32@i@i:R0:4294967295u -- hash: 0x59633ad74de391a -- parent: 13 + 18: AND -- hash: 0xeb59fd3df0f925fc -- parent: 19 + 13: EQ -- hash: 0x8bd5c0fe8f781437 -- parent: 18 + 11: FN_CALL uint32@offset:i@i:R0:4294967295u -- hash: 0xe55cf41d41f52588 -- parent: 13 10: CONST integer(0) -- parent: 11 12: CONST integer(1179403647) -- parent: 13 - 17: NE -- hash: 0xe122eeddb12026ad -- parent: 18 - 15: FN_CALL uint16@i@i:R0:65535u -- hash: 0x5c99c42ccd549597 -- parent: 17 + 17: NE -- hash: 0x303c2970e8fcb487 -- parent: 18 + 15: FN_CALL uint16@offset:i@i:R0:65535u -- hash: 0xd9c315b7872786ad -- parent: 17 14: CONST integer(0) -- parent: 15 16: CONST integer(0) -- parent: 17 diff --git a/lib/src/modules/console.rs b/lib/src/modules/console.rs index f94ccbbb0..73958152e 100644 --- a/lib/src/modules/console.rs +++ b/lib/src/modules/console.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::modules::prelude::*; use crate::modules::protos::console::*; @@ -7,12 +9,14 @@ fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { Ok(Console::new()) } +/// Logs a string to the console. #[module_export(name = "log")] fn log_str(ctx: &mut ScanContext, string: RuntimeString) -> bool { ctx.console_log(format!("{}", string.as_bstr(ctx))); true } +/// Logs a string with a message to the console. #[module_export(name = "log")] fn log_msg_str( ctx: &mut ScanContext, @@ -27,12 +31,83 @@ fn log_msg_str( true } +pub fn escape(bytes: &[u8]) -> Cow<'_, str> { + // First, try to interpret as UTF-8 + let s = match std::str::from_utf8(bytes) { + Ok(s) => s, + Err(_) => { + // If invalid UTF-8, you must allocate anyway + return Cow::Owned( + bytes + .iter() + .flat_map(|&b| std::ascii::escape_default(b)) + .map(|b| b as char) + .collect(), + ); + } + }; + + // Check if escaping is needed + let needs_escape = s.chars().any(|c| { + matches!(c, '\n' | '\r' | '\t' | '\\' | '"') || c.is_control() + }); + + if !needs_escape { + // Zero-copy: borrow directly + return Cow::Borrowed(s); + } + + Cow::Owned(bytes.escape_ascii().to_string()) +} + +/// Given an offset and length return Option where the string is the +/// escaped ascii slice of scanned data. If either offset is negative or (offset +/// + length) wraps, the result will be None. +fn get_data<'a>( + ctx: &'a mut ScanContext, + offset: i64, + length: i64, +) -> Option> { + ctx.scanned_data()? + .get(offset as usize..(offset + length) as usize) + .map(escape) +} + +#[module_export(name = "log")] +fn log_bytes(ctx: &mut ScanContext, offset: i64, length: i64) -> bool { + let message = match get_data(ctx, offset, length) { + Some(data) => format!("{}", data), + None => return true, + }; + + ctx.console_log(message); + true +} + +#[module_export(name = "log")] +fn log_msg_bytes( + ctx: &mut ScanContext, + message: RuntimeString, + offset: i64, + length: i64, +) -> bool { + let message = message.as_bstr(ctx).to_string(); + let message = match get_data(ctx, offset, length) { + Some(data) => format!("{}{}", message, data), + None => return true, + }; + + ctx.console_log(message); + true +} + #[module_export(name = "log")] fn log_bool(ctx: &mut ScanContext, b: bool) -> bool { ctx.console_log(format!("{b}")); true } +/// Logs a boolean value with a message to the console. #[module_export(name = "log")] fn log_msg_bool( ctx: &mut ScanContext, @@ -43,24 +118,28 @@ fn log_msg_bool( true } +/// Logs an integer value to the console. #[module_export(name = "log")] fn log_int(ctx: &mut ScanContext, i: i64) -> bool { ctx.console_log(format!("{i}")); true } +/// Logs an integer value with a message to the console. #[module_export(name = "log")] fn log_msg_int(ctx: &mut ScanContext, message: RuntimeString, i: i64) -> bool { ctx.console_log(format!("{}{}", message.as_bstr(ctx), i)); true } +/// Logs a float value to the console. #[module_export(name = "log")] fn log_float(ctx: &mut ScanContext, f: f64) -> bool { ctx.console_log(format!("{f}")); true } +/// Logs a float value with a message to the console. #[module_export(name = "log")] fn log_msg_float( ctx: &mut ScanContext, @@ -71,12 +150,14 @@ fn log_msg_float( true } +/// Logs an integer value as a hexadecimal string to the console. #[module_export(name = "hex")] fn log_hex(ctx: &mut ScanContext, i: i64) -> bool { ctx.console_log(format!("0x{i:x}")); true } +/// Logs an integer value as a hexadecimal string with a message to the console. #[module_export(name = "hex")] fn log_msg_hex(ctx: &mut ScanContext, message: RuntimeString, i: i64) -> bool { ctx.console_log(format!("{}0x{:x}", message.as_bstr(ctx), i)); @@ -102,7 +183,8 @@ mod tests { console.log("bool: ", true) and console.hex(10) and console.hex("qux: ", 255) and - console.log("hello ", "world!") + console.log("hello ", "world!") and + console.log(0, 4) } "#, ) @@ -112,7 +194,7 @@ mod tests { crate::scanner::Scanner::new(&rules) .console_log(|message| messages.push(message)) - .scan(b"") + .scan(b"\x00\x11ABC") .expect("scan should not fail"); assert_eq!( @@ -128,6 +210,7 @@ mod tests { "0xa", "qux: 0xff", "hello world!", + r"\x00\x11AB", ] ); } diff --git a/lib/src/modules/crx/mod.rs b/lib/src/modules/crx/mod.rs index 1fc7176ce..be631bf49 100644 --- a/lib/src/modules/crx/mod.rs +++ b/lib/src/modules/crx/mod.rs @@ -33,6 +33,7 @@ fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { } } +/// Returns the SHA-256 hash of the permissions in the manifest. #[module_export] fn permhash(ctx: &ScanContext) -> Option>> { let cached = PERMHASH_CACHE.with( diff --git a/lib/src/modules/cuckoo/mod.rs b/lib/src/modules/cuckoo/mod.rs index 9df7f673b..dbae1bbee 100644 --- a/lib/src/modules/cuckoo/mod.rs +++ b/lib/src/modules/cuckoo/mod.rs @@ -45,6 +45,8 @@ fn main(_data: &[u8], meta: Option<&[u8]>) -> Result { Ok(Cuckoo::new()) } +/// Returns true if the Cuckoo report contains a DNS lookup where the domain +/// matches the given regular expression. #[module_export(name = "network.dns_lookup")] fn network_dns_lookup_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() @@ -62,6 +64,8 @@ fn network_dns_lookup_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains an HTTP request (either, GET, +/// or any other method) to some URI that matches the given regular expression. #[module_export(name = "network.http_request")] fn network_http_request_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() @@ -79,6 +83,8 @@ fn network_http_request_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains an HTTP GET request to some URI +/// that matches the given regular expression. #[module_export(name = "network.http_get")] fn network_http_get_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() @@ -99,6 +105,8 @@ fn network_http_get_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains an HTTP POST request to some URI +/// that matches the given regular expression. #[module_export(name = "network.http_post")] fn network_http_post_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() @@ -119,6 +127,8 @@ fn network_http_post_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains an HTTP where the User-Agent +/// header matches the given regular expression. #[module_export(name = "network.http_user_agent")] fn network_http_user_agent_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() @@ -136,6 +146,9 @@ fn network_http_user_agent_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains some TCP connection to the +/// destination `port` where the destination domain matches the given regular +/// expression #[module_export(name = "network.tcp")] fn network_tcp_ri(ctx: &ScanContext, dst_re: RegexpId, port: i64) -> i64 { get_local() @@ -159,6 +172,9 @@ fn network_tcp_ri(ctx: &ScanContext, dst_re: RegexpId, port: i64) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains some UDP connection to the +/// destination `port` where the destination domain matches the given regular +/// expression #[module_export(name = "network.udp")] fn network_udp_ri(ctx: &ScanContext, dst_re: RegexpId, port: i64) -> i64 { get_local() @@ -180,6 +196,8 @@ fn network_udp_ri(ctx: &ScanContext, dst_re: RegexpId, port: i64) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo report contains an HTTP request where the Host +/// header matches the given regular expression. #[module_export(name = "network.host")] fn network_host_r(ctx: &ScanContext, re: RegexpId) -> i64 { get_local() @@ -195,6 +213,8 @@ fn network_host_r(ctx: &ScanContext, re: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo contains some mutex operation where the name +/// of the mutex matches the given regular expression. #[module_export(name = "sync.mutex")] fn sync_mutex_r(ctx: &ScanContext, mutex_re: RegexpId) -> i64 { get_local() @@ -212,6 +232,8 @@ fn sync_mutex_r(ctx: &ScanContext, mutex_re: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo contains some file access operation where the +/// file path matches the given regular expression. #[module_export(name = "filesystem.file_access")] fn filesystem_file_access_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() @@ -229,6 +251,8 @@ fn filesystem_file_access_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { .unwrap_or(0) } +/// Returns true if the Cuckoo contains some registry access operation where +/// the registry key matches the given regular expression. #[module_export(name = "registry.key_access")] fn registry_key_access_r(ctx: &ScanContext, regexp_id: RegexpId) -> i64 { get_local() diff --git a/lib/src/modules/elf/mod.rs b/lib/src/modules/elf/mod.rs index f6e3e71f7..865fd43df 100644 --- a/lib/src/modules/elf/mod.rs +++ b/lib/src/modules/elf/mod.rs @@ -37,6 +37,7 @@ fn main(data: &[u8], _meta: Option<&[u8]>) -> Result { } } +/// Returns an MD5 hash of the ELF's imported symbols. #[module_export] fn import_md5(ctx: &mut ScanContext) -> Option>> { let cached = IMPORT_MD5_CACHE.with( diff --git a/lib/src/modules/hash/mod.rs b/lib/src/modules/hash/mod.rs index 8074b2887..371a13f39 100644 --- a/lib/src/modules/hash/mod.rs +++ b/lib/src/modules/hash/mod.rs @@ -40,6 +40,7 @@ fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { Ok(Hash::new()) } +/// Calculates the MD5 hash of a portion of the scanned data. #[module_export(name = "md5")] fn md5_data( ctx: &mut ScanContext, @@ -72,6 +73,7 @@ fn md5_data( Some(Lowercase::>::new(digest)) } +/// Calculates the MD5 hash of a string. #[module_export(name = "md5")] fn md5_str( ctx: &mut ScanContext, @@ -86,6 +88,7 @@ fn md5_str( ))) } +/// Calculates the SHA-1 hash of a portion of the scanned data. #[module_export(name = "sha1")] fn sha1_data( ctx: &mut ScanContext, @@ -118,6 +121,7 @@ fn sha1_data( Some(Lowercase::>::new(digest)) } +/// Calculates the SHA-1 hash of a string. #[module_export(name = "sha1")] fn sha1_str( ctx: &mut ScanContext, @@ -132,6 +136,7 @@ fn sha1_str( ))) } +/// Calculates the SHA-256 hash of a portion of the scanned data. #[module_export(name = "sha256")] fn sha256_data( ctx: &mut ScanContext, @@ -164,6 +169,7 @@ fn sha256_data( Some(Lowercase::>::new(digest)) } +/// Calculates the SHA-256 hash of a string. #[module_export(name = "sha256")] fn sha256_str( ctx: &mut ScanContext, @@ -178,6 +184,7 @@ fn sha256_str( ))) } +/// Calculates the CRC32 checksum of a portion of the scanned data. #[module_export(name = "crc32")] fn crc_data(ctx: &ScanContext, offset: i64, size: i64) -> Option { let cached = CRC32_CACHE.with(|cache| -> Option { @@ -199,12 +206,14 @@ fn crc_data(ctx: &ScanContext, offset: i64, size: i64) -> Option { Some(crc.into()) } +/// Calculates the CRC32 checksum of a string. #[module_export(name = "crc32")] fn crc_str(ctx: &ScanContext, s: RuntimeString) -> Option { let crc = crc32fast::hash(s.as_bstr(ctx)); Some(crc.into()) } +/// Calculates the 32-bit checksum of a portion of the scanned data. #[module_export(name = "checksum32")] fn checksum_data(ctx: &ScanContext, offset: i64, size: i64) -> Option { let cached = CHECKSUM32_CACHE.with(|cache| -> Option { @@ -230,6 +239,7 @@ fn checksum_data(ctx: &ScanContext, offset: i64, size: i64) -> Option { Some(checksum.into()) } +/// Calculates the 32-bit checksum of a string. #[module_export(name = "checksum32")] fn checksum_str(ctx: &ScanContext, s: RuntimeString) -> Option { let mut checksum = 0_u32; diff --git a/lib/src/modules/magic/mod.rs b/lib/src/modules/magic/mod.rs index d82273212..358404eaf 100644 --- a/lib/src/modules/magic/mod.rs +++ b/lib/src/modules/magic/mod.rs @@ -36,6 +36,7 @@ fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { Ok(Magic::new()) } +/// Returns the file type provided by libmagic. #[module_export(name = "type")] fn file_type(ctx: &mut ScanContext) -> Option { let cached = TYPE_CACHE.with(|cache| cache.borrow().clone()); @@ -58,6 +59,7 @@ fn file_type(ctx: &mut ScanContext) -> Option { } } +/// Returns the MIME type provided by libmagic. #[module_export(name = "mime_type")] fn mime_type(ctx: &mut ScanContext) -> Option { let cached = MIME_TYPE_CACHE.with(|cache| cache.borrow().clone()); diff --git a/lib/src/modules/math.rs b/lib/src/modules/math.rs index fdf2eafc4..60cd7ab1c 100644 --- a/lib/src/modules/math.rs +++ b/lib/src/modules/math.rs @@ -13,36 +13,43 @@ fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { Ok(Math::new()) } +/// Returns the minimum of two integers. #[module_export] fn min(_ctx: &ScanContext, a: i64, b: i64) -> i64 { i64::min(a, b) } +/// Returns the maximum of two integers. #[module_export] fn max(_ctx: &ScanContext, a: i64, b: i64) -> i64 { i64::max(a, b) } +/// Returns the absolute value of an integer. #[module_export] fn abs(_ctx: &ScanContext, x: i64) -> i64 { x.abs() } +/// Returns true if the given float is mostly within the [min, max] range. #[module_export(name = "in_range")] fn in_range_float(_ctx: &ScanContext, x: f64, min: f64, max: f64) -> bool { min <= x && x <= max } +/// Returns true if the given int is within the [min, max] range. #[module_export(name = "in_range")] fn in_range_int(_ctx: &ScanContext, x: i64, min: i64, max: i64) -> bool { min <= x && x <= max } +/// Converts an integer to a string. #[module_export(name = "to_string")] fn to_string(_ctx: &ScanContext, x: i64) -> RuntimeString { RuntimeString::new(x.to_string()) } +/// Converts an integer to a string in the given base. #[module_export(name = "to_string")] fn to_string_base( _ctx: &ScanContext, @@ -57,6 +64,7 @@ fn to_string_base( } } +/// Converts a boolean to an integer (0 or 1). #[module_export] fn to_number(_ctx: &ScanContext, b: bool) -> i64 { if b { @@ -66,6 +74,7 @@ fn to_number(_ctx: &ScanContext, b: bool) -> i64 { } } +/// Counts the occurrences of a byte in a range of the scanned data. #[module_export(name = "count")] fn count_range( ctx: &ScanContext, @@ -82,6 +91,7 @@ fn count_range( Some(data.iter().filter(|b| **b == byte).count() as i64) } +/// Returns the percentage of occurrences of a byte in the scanned data. #[module_export(name = "percentage")] fn percentage_global(ctx: &ScanContext, byte: i64) -> Option { let byte: u8 = byte.try_into().ok()?; @@ -93,6 +103,7 @@ fn percentage_global(ctx: &ScanContext, byte: i64) -> Option { Some(count as f64 / data.len() as f64) } +/// Returns the percentage of occurrences of a byte in a range of the scanned data. #[module_export(name = "percentage")] fn percentage_range( ctx: &ScanContext, @@ -113,11 +124,13 @@ fn percentage_range( Some(count as f64 / data.len() as f64) } +/// Returns the most frequent byte in the scanned data. #[module_export(name = "mode")] fn mode_global(ctx: &ScanContext) -> Option { mode(ctx.scanned_data()?) } +/// Returns the most frequent byte in a range of the scanned data. #[module_export(name = "mode")] fn mode_range(ctx: &ScanContext, offset: i64, length: i64) -> Option { let length: usize = length.try_into().ok()?; @@ -127,12 +140,14 @@ fn mode_range(ctx: &ScanContext, offset: i64, length: i64) -> Option { mode(data.get(start..end)?) } +/// Counts the occurrences of a byte in the scanned data. #[module_export(name = "count")] fn count_global(ctx: &ScanContext, byte: i64) -> Option { let byte: u8 = byte.try_into().ok()?; Some(ctx.scanned_data()?.iter().filter(|b| **b == byte).count() as i64) } +/// Calculates the entropy of a range of the scanned data. #[module_export(name = "entropy")] fn entropy_data(ctx: &ScanContext, offset: i64, length: i64) -> Option { let length: usize = length.try_into().ok()?; @@ -142,11 +157,13 @@ fn entropy_data(ctx: &ScanContext, offset: i64, length: i64) -> Option { Some(entropy(data.get(start..end)?)) } +/// Calculates the entropy of a string. #[module_export(name = "entropy")] fn entropy_string(ctx: &ScanContext, s: RuntimeString) -> Option { Some(entropy(s.as_bstr(ctx).as_bytes())) } +/// Calculates the deviation of a range of the scanned data. #[module_export(name = "deviation")] fn deviation_data( ctx: &ScanContext, @@ -161,6 +178,7 @@ fn deviation_data( deviation(data.get(start..end)?, mean) } +/// Calculates the deviation of a string. #[module_export(name = "deviation")] fn deviation_string( ctx: &ScanContext, @@ -170,6 +188,7 @@ fn deviation_string( deviation(s.as_bstr(ctx).as_bytes(), mean) } +/// Calculates the mean of a range of the scanned data. #[module_export(name = "mean")] fn mean_data(ctx: &ScanContext, offset: i64, length: i64) -> Option { let length: usize = length.try_into().ok()?; @@ -179,11 +198,13 @@ fn mean_data(ctx: &ScanContext, offset: i64, length: i64) -> Option { mean(data.get(start..end)?) } +/// Calculates the mean of a string. #[module_export(name = "mean")] fn mean_string(ctx: &ScanContext, s: RuntimeString) -> Option { mean(s.as_bstr(ctx).as_bytes()) } +/// Calculates the serial correlation of a range of the scanned data. #[module_export(name = "serial_correlation")] fn serial_correlation_data( ctx: &ScanContext, @@ -197,6 +218,7 @@ fn serial_correlation_data( serial_correlation(data.get(start..end)?) } +/// Calculates the serial correlation of a string. #[module_export(name = "serial_correlation")] fn serial_correlation_string( ctx: &ScanContext, @@ -205,6 +227,7 @@ fn serial_correlation_string( serial_correlation(s.as_bstr(ctx).as_bytes()) } +/// Calculates the Monte Carlo Pi approximation of a range of the scanned data. #[module_export(name = "monte_carlo_pi")] fn monte_carlo_pi_data( ctx: &ScanContext, @@ -218,6 +241,7 @@ fn monte_carlo_pi_data( monte_carlo_pi(data.get(start..end)?) } +/// Calculates the Monte Carlo Pi approximation of a string. #[module_export(name = "monte_carlo_pi")] fn monte_carlo_pi_string(ctx: &ScanContext, s: RuntimeString) -> Option { monte_carlo_pi(s.as_bstr(ctx).as_bytes()) diff --git a/lib/src/modules/pe/mod.rs b/lib/src/modules/pe/mod.rs index ab88ef034..4205dc374 100644 --- a/lib/src/modules/pe/mod.rs +++ b/lib/src/modules/pe/mod.rs @@ -280,16 +280,20 @@ fn imphash(ctx: &mut ScanContext) -> Option>> { Some(Lowercase::>::new(digest)) } +/// Returns the number of toolid records with the given toolid. #[module_export(name = "rich_signature.toolid")] fn rich_toolid(ctx: &mut ScanContext, toolid: i64) -> Option { rich_version_impl(ctx.module_output::()?, Some(toolid), None) } +/// Returns the number of toolid records matching the given version. #[module_export(name = "rich_signature.version")] fn rich_version(ctx: &mut ScanContext, version: i64) -> Option { rich_version_impl(ctx.module_output::()?, None, Some(version)) } + +/// Returns the number of toolid records matching the given toolid and version. #[module_export(name = "rich_signature.version")] fn rich_version_toolid( ctx: &mut ScanContext, @@ -299,6 +303,7 @@ fn rich_version_toolid( rich_version_impl(ctx.module_output::()?, Some(toolid), Some(version)) } +/// Returns the number of toolid records matching the given toolid and version. #[module_export(name = "rich_signature.toolid")] fn rich_toolid_version( ctx: &mut ScanContext, diff --git a/lib/src/modules/string.rs b/lib/src/modules/string.rs index 638c61e30..3a76a0a8c 100644 --- a/lib/src/modules/string.rs +++ b/lib/src/modules/string.rs @@ -8,12 +8,14 @@ fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { Ok(String::new()) } +/// Converts a string to an integer. #[module_export] fn to_int(ctx: &ScanContext, string: RuntimeString) -> Option { let string = string.to_str(ctx).ok()?; string.parse::().ok() } +/// Converts a string to an integer in the given base. #[module_export(name = "to_int")] fn to_int_base( ctx: &ScanContext, @@ -28,6 +30,7 @@ fn to_int_base( i64::from_str_radix(string, base).ok() } +/// Returns the length of the string. #[module_export] fn length(ctx: &ScanContext, string: RuntimeString) -> Option { Some(string.as_bstr(ctx).len().try_into().unwrap()) diff --git a/lib/src/modules/test_proto2/mod.rs b/lib/src/modules/test_proto2/mod.rs index c5ee41e2a..7be984fba 100644 --- a/lib/src/modules/test_proto2/mod.rs +++ b/lib/src/modules/test_proto2/mod.rs @@ -8,16 +8,19 @@ use crate::types::Struct; #[cfg(test)] mod tests; +/// Adds two integers. #[module_export(name = "add")] pub(crate) fn add_i64(_ctx: &mut ScanContext, a: i64, b: i64) -> i64 { a + b } +/// Adds two floats. #[module_export(name = "add")] pub(crate) fn add_f64(_ctx: &mut ScanContext, a: f64, b: f64) -> f64 { a + b } +/// Converts a string to uppercase. #[module_export(name = "uppercase")] pub(crate) fn uppercase( ctx: &mut ScanContext, @@ -26,11 +29,13 @@ pub(crate) fn uppercase( Uppercase::new(s.as_bstr(ctx).to_uppercase()) } +/// A nested function. #[module_export(name = "nested.nested_func")] pub(crate) fn nested_func(_ctx: &mut ScanContext) -> bool { true } +/// A nested method. #[module_export( name = "nested_method", method_of = "test_proto2.NestedProto2" @@ -42,6 +47,7 @@ pub(crate) fn nested_method( structure.field_by_name("nested_bool").unwrap().type_value.as_bool() } +/// A nested method with an argument. #[module_export( name = "nested_method_with_arg", method_of = "test_proto2.NestedProto2" @@ -61,17 +67,20 @@ pub(crate) fn nested_method_with_arg( arg.eq(field.as_bstr()) } +/// Returns an undefined integer. #[module_export] pub(crate) fn undef_i64(_ctx: &mut ScanContext) -> Option { None } +/// Extracts the first n bytes of the scanned data. #[module_export] fn head(ctx: &mut ScanContext, n: i64) -> Option { let head = ctx.scanned_data()?.get(0..n as usize)?; Some(RuntimeString::from_slice(ctx, head)) } +/// Gets foo from the protobuf. #[module_export] fn get_foo(ctx: &mut ScanContext) -> Option { let proto = ctx.module_output::()?; @@ -79,6 +88,7 @@ fn get_foo(ctx: &mut ScanContext) -> Option { Some(RuntimeString::new(string_foo)) } +/// Converts a string to an integer. #[module_export] fn to_int(ctx: &ScanContext, string: RuntimeString) -> Option { let string = string.to_str(ctx).ok()?; diff --git a/lib/src/modules/time.rs b/lib/src/modules/time.rs index 2e71f172d..68e32f8be 100644 --- a/lib/src/modules/time.rs +++ b/lib/src/modules/time.rs @@ -7,6 +7,7 @@ fn main(_data: &[u8], _meta: Option<&[u8]>) -> Result { Ok(Time::new()) } +/// Returns the current time as the number of seconds since January 1, 1970 (UTC). #[module_export] fn now(_ctx: &ScanContext) -> Option { current_unix_timestamp() diff --git a/lib/src/modules/vt/mod.rs b/lib/src/modules/vt/mod.rs index a630464ad..298c69af3 100644 --- a/lib/src/modules/vt/mod.rs +++ b/lib/src/modules/vt/mod.rs @@ -57,6 +57,7 @@ fn main( Ok(LiveHuntData::new()) } +/// Returns true if the IP address is within the given CIDR range. #[module_export(method_of = "vt.net.EnrichedIP")] fn in_range( ctx: &mut ScanContext, @@ -79,6 +80,7 @@ fn in_range( cidr.contains(&ip) } +/// Returns true if the domain is a permutation of the given `target` domain. #[module_export(name = "permutation_of", method_of = "vt.net.EnrichedDomain")] fn all_permutations( ctx: &mut ScanContext, @@ -88,6 +90,8 @@ fn all_permutations( permutations(ctx, domain, target, 0x1F) } +/// Returns true if the domain is a permutation of the given `target` domain, +/// but the permutation must be any of the kinds specified in `permutation_kinds`. #[module_export(name = "permutation_of", method_of = "vt.net.EnrichedDomain")] fn permutations( ctx: &mut ScanContext, diff --git a/lib/src/types/func.rs b/lib/src/types/func.rs index d9dbd01b7..0811e63f3 100644 --- a/lib/src/types/func.rs +++ b/lib/src/types/func.rs @@ -21,7 +21,9 @@ use std::str::Chars; /// /// The prefix `::` is optional, it is present only if the function /// is a method of the type identified by ``. `` is a -/// sequence of characters that specify the argument's type. Allowed types are: +/// comma-separated list of `:` pairs, where `` is the name +/// of the argument, and `` is a sequence of characters that specify the +/// argument's type. Allowed type characters are: /// /// ```text /// i: integer @@ -31,12 +33,12 @@ use std::str::Chars; /// r: regexp /// ``` /// -/// `` is also a sequence of one or more of the characters -/// above, specifying the type returned by the function (except `r`, -/// because functions can't return regular expressions). For example, a -/// function `add` with two integer arguments that return another integer -/// would have the mangled name `add@ii@i`. A function `foo` that returns -/// a tuple of two integers has the mangled name `foo@@ii`. +/// `` is a sequence of one or more of the characters above, +/// specifying the type returned by the function (except `r`, because +/// functions can't return regular expressions). For example, a function `add` +/// with two integer arguments `a` and `b` that returns another integer +/// would have the mangled name `add@a:i,b:i@i`. A function `foo` that takes no +/// arguments and returns a tuple of two integers has the mangled name `foo@@ii`. /// /// Additionally, the return type may be followed by a `u` character if /// the returned value may be undefined. For example, a function `foo` that @@ -44,12 +46,12 @@ use std::str::Chars; /// a mangled name: `foo@@su`. /// /// Both `` and `` can be empty if the function -/// doesn't receive arguments or doesn't return a value. Let's see some e +/// doesn't receive arguments or doesn't return a value. Let's see some /// examples: /// /// ```text /// foo() -> foo@@ -/// foo(i: i64) -> foo@i@ +/// foo(a: i64) -> foo@a:i@ /// foo() -> i32 -> foo@@i /// foo() -> Option<()> -> foo@@u /// foo() -> Option -> foo@@fu @@ -88,16 +90,36 @@ impl MangledFnName { impl MangledFnName { /// Returns the types of arguments and return value for the function. - pub fn unmangle(&self) -> (Vec, TypeValue) { - let (_name, arg_types, ret_type) = + pub fn unmangle(&self) -> (Vec<(&str, TypeValue)>, TypeValue) { + let (_fn_name, arg_names_and_types, ret_type) = self.0.split('@').collect_tuple().unwrap_or_else(|| { panic!("invalid mangled name: `{}`", self.0) }); let mut args = Vec::new(); - let mut chars = arg_types.chars().peekable(); - while let Some(type_value) = self.next_type(&mut chars) { - args.push(type_value); + + if !arg_names_and_types.is_empty() { + for arg_str in arg_names_and_types.split(',') { + let (arg_name, arg_type) = + if let Some((n, t)) = arg_str.split_once(':') { + (n, t) + } else { + panic!( + "argument name missing in mangled name: `{}`", + self.0 + ) + }; + + let mut chars = arg_type.chars().peekable(); + let type_value = + self.next_type(&mut chars).unwrap_or_else(|| { + panic!( + "invalid argument type in mangled name: `{}`", + self.0 + ) + }); + args.push((arg_name, type_value)); + } } let mut chars = ret_type.chars().peekable(); @@ -217,6 +239,7 @@ where pub(crate) struct FuncSignature { pub mangled_name: MangledFnName, pub args: Vec, + pub arg_names: Vec, pub result: TypeValue, pub description: Option>, } @@ -265,8 +288,16 @@ impl> From for FuncSignature { /// Creates a [`FuncSignature`] from a string containing a mangled function name. fn from(value: T) -> Self { let mangled_name = MangledFnName::from(value.into()); - let (args, result) = mangled_name.unmangle(); - Self { mangled_name, args, result, description: None } + let (args_with_names, result) = mangled_name.unmangle(); + + let mut args = Vec::with_capacity(args_with_names.len()); + let mut arg_names = Vec::with_capacity(args_with_names.len()); + for (name, ty) in args_with_names { + args.push(ty); + arg_names.push(name.to_string()); + } + + Self { mangled_name, args, arg_names, result, description: None } } } @@ -357,29 +388,61 @@ mod test { ); assert_eq!( - MangledFnName::from("foo@i@i").unmangle(), - (vec![TypeValue::unknown_integer()], TypeValue::unknown_integer()) + MangledFnName::from("foo@a:i,b:i@i").unmangle(), + ( + vec![ + ("a", TypeValue::unknown_integer()), + ("b", TypeValue::unknown_integer()) + ], + TypeValue::unknown_integer() + ) ); assert_eq!( - MangledFnName::from("foo@f@f").unmangle(), - (vec![TypeValue::unknown_float()], TypeValue::unknown_float()) + MangledFnName::from("foo@a:f,b:f@f").unmangle(), + ( + vec![ + ("a", TypeValue::unknown_float()), + ("b", TypeValue::unknown_float()) + ], + TypeValue::unknown_float() + ) ); assert_eq!( - MangledFnName::from("foo@b@b").unmangle(), - (vec![TypeValue::unknown_bool()], TypeValue::unknown_bool()) + MangledFnName::from("foo@a:b,b:b@b").unmangle(), + ( + vec![ + ("a", TypeValue::unknown_bool()), + ("b", TypeValue::unknown_bool()) + ], + TypeValue::unknown_bool() + ) ); assert_eq!( - MangledFnName::from("foo@s@s").unmangle(), - (vec![TypeValue::unknown_string()], TypeValue::unknown_string()) + MangledFnName::from("foo@a:s,b:s@s").unmangle(), + ( + vec![ + ("a", TypeValue::unknown_string()), + ("b", TypeValue::unknown_string()) + ], + TypeValue::unknown_string() + ) ); assert_eq!( - MangledFnName::from("foo@s@s:L").unmangle(), + MangledFnName::from("foo@a:s,b:s:L@s:L").unmangle(), ( - vec![TypeValue::unknown_string()], + vec![ + ("a", TypeValue::unknown_string()), + ( + "b", + TypeValue::unknown_string_with_constraints(vec![ + StringConstraint::Lowercase + ]) + ) + ], TypeValue::unknown_string_with_constraints(vec![ StringConstraint::Lowercase ]) @@ -387,9 +450,17 @@ mod test { ); assert_eq!( - MangledFnName::from("foo@s@s:U").unmangle(), + MangledFnName::from("foo@a:s,b:s:U@s:U").unmangle(), ( - vec![TypeValue::unknown_string()], + vec![ + ("a", TypeValue::unknown_string()), + ( + "b", + TypeValue::unknown_string_with_constraints(vec![ + StringConstraint::Uppercase + ]) + ) + ], TypeValue::unknown_string_with_constraints(vec![ StringConstraint::Uppercase ]) @@ -397,9 +468,17 @@ mod test { ); assert_eq!( - MangledFnName::from("foo@s@s:N16").unmangle(), + MangledFnName::from("foo@a:s,b:s:N16@s:N16").unmangle(), ( - vec![TypeValue::unknown_string()], + vec![ + ("a", TypeValue::unknown_string()), + ( + "b", + TypeValue::unknown_string_with_constraints(vec![ + StringConstraint::ExactLength(16), + ]) + ) + ], TypeValue::unknown_string_with_constraints(vec![ StringConstraint::ExactLength(16), ]) @@ -407,9 +486,18 @@ mod test { ); assert_eq!( - MangledFnName::from("foo@s@s:N16:L").unmangle(), + MangledFnName::from("foo@a:s,b:s:N16:L@s:N16:L").unmangle(), ( - vec![TypeValue::unknown_string()], + vec![ + ("a", TypeValue::unknown_string()), + ( + "b", + TypeValue::unknown_string_with_constraints(vec![ + StringConstraint::ExactLength(16), + StringConstraint::Lowercase + ]) + ) + ], TypeValue::unknown_string_with_constraints(vec![ StringConstraint::ExactLength(16), StringConstraint::Lowercase @@ -438,25 +526,25 @@ mod test { ); assert_eq!( - MangledFnName::from("Bar::foo@i@iu").method_of(), + MangledFnName::from("Bar::foo@a:i,b:i@iu").method_of(), Some("Bar") ); assert_eq!( - MangledFnName::from("bar.Bar::foo@i@iu").method_of(), + MangledFnName::from("bar.Bar::foo@a:i,b:i@iu").method_of(), Some("bar.Bar") ); - assert_eq!(MangledFnName::from("foo@i@iu").method_of(), None); + assert_eq!(MangledFnName::from("foo@a:i,b:i@iu").method_of(), None); - assert!(!MangledFnName::from("foo@i@i").result_may_be_undef()); - assert!(MangledFnName::from("foo@i@iu").result_may_be_undef()); + assert!(!MangledFnName::from("foo@a:i,b:i@i").result_may_be_undef()); + assert!(MangledFnName::from("foo@a:i,b:i@iu").result_may_be_undef()); } #[test] #[should_panic] fn invalid_mangled_name_1() { - MangledFnName::from("foo@i").unmangle(); + MangledFnName::from("foo@a:i").unmangle(); } #[test] @@ -468,6 +556,12 @@ mod test { #[test] #[should_panic] fn invalid_mangled_name_3() { - MangledFnName::from("foo@x@i").unmangle(); + MangledFnName::from("foo@a:x@i").unmangle(); + } + + #[test] + #[should_panic] + fn missing_argument_name() { + MangledFnName::from("foo@i@i").unmangle(); } } diff --git a/ls/src/tests/testdata/completion8.response.json b/ls/src/tests/testdata/completion8.response.json index 018682ed3..bc06569f2 100644 --- a/ls/src/tests/testdata/completion8.response.json +++ b/ls/src/tests/testdata/completion8.response.json @@ -1803,19 +1803,6 @@ "description": "func()" } }, - { - "documentation": { - "kind": "markdown", - "value": "## `delayed_import_rva(string, integer) -> integer`\n\n Returns the RVA of an import where the DLL name matches\n `dll_name` and the ordinal number is `ordinal`.\n\n `dll_name` is case-insensitive." - }, - "insertText": "delayed_import_rva(${1:string},${2:integer})", - "insertTextFormat": 2, - "kind": 2, - "label": "delayed_import_rva(string, integer)", - "labelDetails": { - "description": "func()" - } - }, { "documentation": { "kind": "markdown", @@ -1832,12 +1819,12 @@ { "documentation": { "kind": "markdown", - "value": "## `exports(integer) -> bool`\n\n Returns true if the PE file exports a function with the given ordinal." + "value": "## `delayed_import_rva(string, integer) -> integer`\n\n Returns the RVA of an import where the DLL name matches\n `dll_name` and the ordinal number is `ordinal`.\n\n `dll_name` is case-insensitive." }, - "insertText": "exports(${1:integer})", + "insertText": "delayed_import_rva(${1:string},${2:integer})", "insertTextFormat": 2, "kind": 2, - "label": "exports(integer)", + "label": "delayed_import_rva(string, integer)", "labelDetails": { "description": "func()" } @@ -1871,12 +1858,12 @@ { "documentation": { "kind": "markdown", - "value": "## `exports_index(integer) -> integer`\n\n Returns true if the PE file exports a function with the given ordinal." + "value": "## `exports(integer) -> bool`\n\n Returns true if the PE file exports a function with the given ordinal." }, - "insertText": "exports_index(${1:integer})", + "insertText": "exports(${1:integer})", "insertTextFormat": 2, "kind": 2, - "label": "exports_index(integer)", + "label": "exports(integer)", "labelDetails": { "description": "func()" } @@ -1910,12 +1897,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imphash() -> string`\n\n Returns the PE import hash.\n\n The import hash represents the MD5 checksum of the PE's import table\n following a normalization process. PE files sharing the same import hash\n import precisely identical functions from the same DLLs. This characteristic\n often signifies file similarity, despite not being byte-for-byte identical.\n For additional details, refer to:\n https://www.mandiant.com/resources/blog/tracking-malware-import-hashing\n\n The resulting hash string is consistently in lowercase." + "value": "## `exports_index(integer) -> integer`\n\n Returns true if the PE file exports a function with the given ordinal." }, - "insertText": "imphash()", + "insertText": "exports_index(${1:integer})", "insertTextFormat": 2, "kind": 2, - "label": "imphash()", + "label": "exports_index(integer)", "labelDetails": { "description": "func()" } @@ -1923,12 +1910,12 @@ { "documentation": { "kind": "markdown", - "value": "## `import_rva(string, integer) -> integer`\n\n Returns the RVA of an import where the DLL name matches\n `dll_name` and the ordinal number is `ordinal`.\n\n `dll_name` is case-insensitive." + "value": "## `imphash() -> string`\n\n Returns the PE import hash.\n\n The import hash represents the MD5 checksum of the PE's import table\n following a normalization process. PE files sharing the same import hash\n import precisely identical functions from the same DLLs. This characteristic\n often signifies file similarity, despite not being byte-for-byte identical.\n For additional details, refer to:\n https://www.mandiant.com/resources/blog/tracking-malware-import-hashing\n\n The resulting hash string is consistently in lowercase." }, - "insertText": "import_rva(${1:string},${2:integer})", + "insertText": "imphash()", "insertTextFormat": 2, "kind": 2, - "label": "import_rva(string, integer)", + "label": "imphash()", "labelDetails": { "description": "func()" } @@ -1949,12 +1936,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(integer, regexp, regexp) -> integer`\n\n Returns the number of imported functions where the function's name matches\n `func_name` and the DLL name matches `dll_name`.\n\n Both `dll_name` and `func_name` are case-sensitive unless you use the \"/i\"\n modifier in the regexp, as shown in the example below. See [`imports_dll`]\n for details about the `import_flags` argument." + "value": "## `import_rva(string, integer) -> integer`\n\n Returns the RVA of an import where the DLL name matches\n `dll_name` and the ordinal number is `ordinal`.\n\n `dll_name` is case-insensitive." }, - "insertText": "imports(${1:integer},${2:regexp},${3:regexp})", + "insertText": "import_rva(${1:string},${2:integer})", "insertTextFormat": 2, "kind": 2, - "label": "imports(integer, regexp, regexp)", + "label": "import_rva(string, integer)", "labelDetails": { "description": "func()" } @@ -1962,12 +1949,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(integer, string) -> integer`\n\n Returns the number of functions imported by the PE from `dll_name`.\n\n `dll_name` is case-insensitive. `import_flags` specify the types of\n import which should be taken into account. This value can be composed\n by a bitwise OR of the following values:\n\n * `pe.IMPORT_STANDARD` : standard import only\n * `pe.IMPORT_DELAYED` : delayed imports only\n * `pe.IMPORT_ANY` : both standard and delayed imports" + "value": "## `imports(regexp, regexp) -> integer`\n\n Returns the number of imported functions where the function's name matches\n `func_name` and the DLL name matches `dll_name`.\n\n Both `dll_name` and `func_name` are case-sensitive unless you use the \"/i\"\n modifier in the regexp, as shown in the example below." }, - "insertText": "imports(${1:integer},${2:string})", + "insertText": "imports(${1:regexp},${2:regexp})", "insertTextFormat": 2, "kind": 2, - "label": "imports(integer, string)", + "label": "imports(regexp, regexp)", "labelDetails": { "description": "func()" } @@ -1975,12 +1962,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(integer, string, integer) -> bool`\n\n Returns true if the PE imports `ordinal` from `dll_name`.\n\n `dll_name` is case-insensitive. See [`imports_dll`] for details about\n the `import_flags` argument." + "value": "## `imports(string, string) -> bool`\n\n Returns true if the PE imports `func_name` from `dll_name`.\n\n Both `func_name` and `dll_name` are case-insensitive." }, - "insertText": "imports(${1:integer},${2:string},${3:integer})", + "insertText": "imports(${1:string},${2:string})", "insertTextFormat": 2, "kind": 2, - "label": "imports(integer, string, integer)", + "label": "imports(string, string)", "labelDetails": { "description": "func()" } @@ -1988,12 +1975,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(integer, string, string) -> bool`\n\n Returns true if the PE imports `func_name` from `dll_name`.\n\n Both `func_name` and `dll_name` are case-insensitive. See [`imports_dll`]\n for details about the `import_flags` argument." + "value": "## `imports(string, integer) -> integer`\n\n Returns true if the PE imports `ordinal` from `dll_name`.\n\n `dll_name` is case-insensitive." }, - "insertText": "imports(${1:integer},${2:string},${3:string})", + "insertText": "imports(${1:string},${2:integer})", "insertTextFormat": 2, "kind": 2, - "label": "imports(integer, string, string)", + "label": "imports(string, integer)", "labelDetails": { "description": "func()" } @@ -2001,12 +1988,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(regexp, regexp) -> integer`\n\n Returns the number of imported functions where the function's name matches\n `func_name` and the DLL name matches `dll_name`.\n\n Both `dll_name` and `func_name` are case-sensitive unless you use the \"/i\"\n modifier in the regexp, as shown in the example below." + "value": "## `imports(string) -> integer`\n\n Returns the number of functions imported by the PE from `dll_name`.\n\n `dll_name` is case-insensitive." }, - "insertText": "imports(${1:regexp},${2:regexp})", + "insertText": "imports(${1:string})", "insertTextFormat": 2, "kind": 2, - "label": "imports(regexp, regexp)", + "label": "imports(string)", "labelDetails": { "description": "func()" } @@ -2014,12 +2001,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(string) -> integer`\n\n Returns the number of functions imported by the PE from `dll_name`.\n\n `dll_name` is case-insensitive." + "value": "## `imports(integer, regexp, regexp) -> integer`\n\n Returns the number of imported functions where the function's name matches\n `func_name` and the DLL name matches `dll_name`.\n\n Both `dll_name` and `func_name` are case-sensitive unless you use the \"/i\"\n modifier in the regexp, as shown in the example below. See [`imports_dll`]\n for details about the `import_flags` argument." }, - "insertText": "imports(${1:string})", + "insertText": "imports(${1:integer},${2:regexp},${3:regexp})", "insertTextFormat": 2, "kind": 2, - "label": "imports(string)", + "label": "imports(integer, regexp, regexp)", "labelDetails": { "description": "func()" } @@ -2027,12 +2014,12 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(string, integer) -> integer`\n\n Returns true if the PE imports `ordinal` from `dll_name`.\n\n `dll_name` is case-insensitive." + "value": "## `imports(integer, string, string) -> bool`\n\n Returns true if the PE imports `func_name` from `dll_name`.\n\n Both `func_name` and `dll_name` are case-insensitive. See [`imports_dll`]\n for details about the `import_flags` argument." }, - "insertText": "imports(${1:string},${2:integer})", + "insertText": "imports(${1:integer},${2:string},${3:string})", "insertTextFormat": 2, "kind": 2, - "label": "imports(string, integer)", + "label": "imports(integer, string, string)", "labelDetails": { "description": "func()" } @@ -2040,12 +2027,25 @@ { "documentation": { "kind": "markdown", - "value": "## `imports(string, string) -> bool`\n\n Returns true if the PE imports `func_name` from `dll_name`.\n\n Both `func_name` and `dll_name` are case-insensitive." + "value": "## `imports(integer, string, integer) -> bool`\n\n Returns true if the PE imports `ordinal` from `dll_name`.\n\n `dll_name` is case-insensitive. See [`imports_dll`] for details about\n the `import_flags` argument." }, - "insertText": "imports(${1:string},${2:string})", + "insertText": "imports(${1:integer},${2:string},${3:integer})", "insertTextFormat": 2, "kind": 2, - "label": "imports(string, string)", + "label": "imports(integer, string, integer)", + "labelDetails": { + "description": "func()" + } + }, + { + "documentation": { + "kind": "markdown", + "value": "## `imports(integer, string) -> integer`\n\n Returns the number of functions imported by the PE from `dll_name`.\n\n `dll_name` is case-insensitive. `import_flags` specify the types of\n import which should be taken into account. This value can be composed\n by a bitwise OR of the following values:\n\n * `pe.IMPORT_STANDARD` : standard import only\n * `pe.IMPORT_DELAYED` : delayed imports only\n * `pe.IMPORT_ANY` : both standard and delayed imports" + }, + "insertText": "imports(${1:integer},${2:string})", + "insertTextFormat": 2, + "kind": 2, + "label": "imports(integer, string)", "labelDetails": { "description": "func()" } @@ -2131,12 +2131,12 @@ { "documentation": { "kind": "markdown", - "value": "## `section_index(integer) -> integer`\n\n Returns the index in the section table of the first section that contains\n the given file offset." + "value": "## `section_index(string) -> integer`\n\n Returns the index in the section table of the first section with the given\n name." }, - "insertText": "section_index(${1:integer})", + "insertText": "section_index(${1:string})", "insertTextFormat": 2, "kind": 2, - "label": "section_index(integer)", + "label": "section_index(string)", "labelDetails": { "description": "func()" } @@ -2144,12 +2144,12 @@ { "documentation": { "kind": "markdown", - "value": "## `section_index(string) -> integer`\n\n Returns the index in the section table of the first section with the given\n name." + "value": "## `section_index(integer) -> integer`\n\n Returns the index in the section table of the first section that contains\n the given file offset." }, - "insertText": "section_index(${1:string})", + "insertText": "section_index(${1:integer})", "insertTextFormat": 2, "kind": 2, - "label": "section_index(string)", + "label": "section_index(integer)", "labelDetails": { "description": "func()" } diff --git a/ls/src/tests/testdata/signature_help2.response.json b/ls/src/tests/testdata/signature_help2.response.json index 32286fcfd..c96665230 100644 --- a/ls/src/tests/testdata/signature_help2.response.json +++ b/ls/src/tests/testdata/signature_help2.response.json @@ -2,7 +2,7 @@ "activeParameter": 1, "signatures": [ { - "label": "delayed_import_rva(string, integer) -> integer", + "label": "delayed_import_rva(string, string) -> integer", "parameters": [ { "label": [ @@ -13,13 +13,13 @@ { "label": [ 27, - 34 + 33 ] } ] }, { - "label": "delayed_import_rva(string, string) -> integer", + "label": "delayed_import_rva(string, integer) -> integer", "parameters": [ { "label": [ @@ -30,7 +30,7 @@ { "label": [ 27, - 33 + 34 ] } ] diff --git a/macros/src/wasm_export.rs b/macros/src/wasm_export.rs index 421829b63..9c200dc5a 100644 --- a/macros/src/wasm_export.rs +++ b/macros/src/wasm_export.rs @@ -16,12 +16,12 @@ use syn::{ /// Parses the signature of a Rust function and returns its mangled named. struct FuncSignatureParser<'ast> { - arg_types: Option>, + args: Option>, } impl<'ast> FuncSignatureParser<'ast> { fn new() -> Self { - Self { arg_types: None } + Self { args: None } } #[inline(always)] @@ -180,20 +180,20 @@ impl<'ast> FuncSignatureParser<'ast> { } fn parse(&mut self, func: &'ast ItemFn) -> Result { - self.arg_types = Some(VecDeque::new()); + self.args = Some(VecDeque::new()); // This loop traverses the function arguments' AST, populating - // `self.arg_types`. + // `self.args`. for fn_arg in func.sig.inputs.iter() { self.visit_fn_arg(fn_arg); } - let mut arg_types = self.arg_types.take().unwrap(); + let mut args = self.args.take().unwrap(); let mut first_argument_is_ok = false; // Make sure that the first argument is `&mut Caller`. - if let Some(Type::Reference(ref_type)) = arg_types.pop_front() + if let Some((_, Type::Reference(ref_type))) = args.pop_front() && let Type::Path(type_) = ref_type.elem.as_ref() { first_argument_is_ok = Self::type_ident(type_) == "Caller"; @@ -211,8 +211,17 @@ impl<'ast> FuncSignatureParser<'ast> { let mut mangled_name = String::from("@"); - for arg_type in arg_types { + let mut first = true; + for (arg_name, arg_type) in args { + if !first { + mangled_name.push(','); + } + if !arg_name.is_empty() { + mangled_name.push_str(&arg_name); + mangled_name.push(':'); + } mangled_name.push_str(Self::mangled_type(arg_type)?.as_ref()); + first = false; } mangled_name.push('@'); @@ -224,7 +233,12 @@ impl<'ast> FuncSignatureParser<'ast> { impl<'ast> Visit<'ast> for FuncSignatureParser<'ast> { fn visit_pat_type(&mut self, pat_type: &'ast PatType) { - self.arg_types.as_mut().unwrap().push_back(pat_type.ty.as_ref()); + let name = if let syn::Pat::Ident(ident) = &*pat_type.pat { + ident.ident.to_string() + } else { + "".to_string() + }; + self.args.as_mut().unwrap().push_back((name, pat_type.ty.as_ref())); } } @@ -454,7 +468,7 @@ mod tests { fn foo(caller: &mut Caller<'_, ScanContext>, a: i32, b: i32) -> i32 { a + b } }; - assert_eq!(parser.parse(&func).unwrap(), "@ii@i"); + assert_eq!(parser.parse(&func).unwrap(), "@a:i,b:i@i"); let func = parse_quote! { fn foo(caller: &mut Caller<'_, ScanContext>) -> Option<()> { None } diff --git a/site/content/docs/modules/console.md b/site/content/docs/modules/console.md index a71981751..9f1419859 100644 --- a/site/content/docs/modules/console.md +++ b/site/content/docs/modules/console.md @@ -50,6 +50,20 @@ Logs the given message and string. Example: `console.log("The imphash is: ", pe.imphash())` +### log(offset, length) (New in version v.1.15.0) + +Logs the bytes starting at offset and continuing for length. The result is an +ASCII escaped string. + +Example: `console.log(10, 5)` + +### log(message, offset, length) (New in version v.1.15.0) + +Logs the bytes starting at offset and continuing for length. The result is an +ASCII escaped string. + +Example: `console.log("5 BYTES AT 10: ", 10, 5)` + ### log(integer) Logs the given integer. @@ -96,4 +110,4 @@ Example: `console.hex(uint32(0))` Logs the given message and number, with the number as hex. -Example: `console.hex("Hex at 0: ", uint32(0))` \ No newline at end of file +Example: `console.hex("Hex at 0: ", uint32(0))`