Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ext/ffi): Replace pointer integers with v8::External objects #16889

Merged
merged 18 commits into from
Feb 22, 2023

Conversation

aapoalas
Copy link
Collaborator

@aapoalas aapoalas commented Dec 1, 2022

Includes / Depends on: #17200

Replaces pointers integers with v8::External objects. This increases safety by a lot, as pointer spoofing is no longer as easy as writing a number out of thin air (though an API for doing just that is included since some C APIs do require that capability). The External objects are Fast API compliant with the new rusty_v8 release. Performance is even better than before.

Before:

benchmark                                time (avg)             (min … max)       p75       p99      p995
--------------------------------------------------------------------------- -----------------------------
nop()                                  3.59 ns/iter    (3.46 ns … 23.69 ns)   3.58 ns   4.32 ns   4.41 ns
hash()                                70.38 ns/iter   (69.23 ns … 95.56 ns)  70.42 ns  77.12 ns  78.97 ns
c string                              148.8 ns/iter (139.31 ns … 171.43 ns)    150 ns 167.08 ns 168.74 ns
add_u32()                              5.99 ns/iter    (5.49 ns … 10.63 ns)   5.96 ns   6.49 ns   6.88 ns
return_buffer()                        8.46 ns/iter    (7.87 ns … 19.04 ns)   8.45 ns   9.32 ns  11.59 ns
add_u64()                              8.17 ns/iter    (7.84 ns … 17.51 ns)   8.03 ns  13.94 ns  14.17 ns
return_u64()                          14.78 ns/iter   (13.71 ns … 21.38 ns)  14.35 ns  18.69 ns  18.87 ns
return_i64()                          43.28 ns/iter   (41.56 ns … 59.22 ns)  45.01 ns  46.38 ns  47.11 ns
nop_bool()                             5.74 ns/iter    (5.25 ns … 14.05 ns)   5.72 ns   6.17 ns   6.25 ns
nop_u8()                                5.8 ns/iter     (5.25 ns … 9.05 ns)   5.73 ns   7.06 ns   7.42 ns
nop_i8()                               5.88 ns/iter    (5.25 ns … 14.15 ns)   5.72 ns   9.09 ns   9.42 ns
nop_u16()                              6.09 ns/iter    (5.49 ns … 10.74 ns)   5.96 ns   9.45 ns   9.78 ns
nop_i16()                              6.01 ns/iter     (5.49 ns … 9.46 ns)   5.96 ns   7.16 ns   8.33 ns
nop_u32()                              5.99 ns/iter    (5.49 ns … 10.29 ns)   5.96 ns   6.49 ns   6.83 ns
nop_i32()                              5.98 ns/iter     (5.49 ns … 9.35 ns)   5.96 ns   6.49 ns   6.82 ns
nop_u64()                              5.77 ns/iter    (5.25 ns … 11.63 ns)   5.72 ns   7.19 ns   8.57 ns
nop_i64()                              5.76 ns/iter    (5.25 ns … 10.66 ns)   5.72 ns   6.25 ns   7.38 ns
nop_usize() number                     5.99 ns/iter     (5.49 ns … 9.19 ns)   5.96 ns    6.5 ns   6.88 ns
nop_usize() bigint                   149.54 ns/iter (143.42 ns … 184.87 ns) 150.86 ns 160.77 ns  165.1 ns
nop_isize() number                     6.03 ns/iter    (5.49 ns … 12.86 ns)   5.96 ns   8.19 ns   8.96 ns
nop_isize() bigint                   146.18 ns/iter (134.82 ns … 199.77 ns)  146.4 ns 184.75 ns 190.29 ns
nop_f32()                              5.99 ns/iter    (5.49 ns … 10.85 ns)   5.96 ns   6.67 ns   7.16 ns
nop_f64()                              6.17 ns/iter    (5.49 ns … 20.19 ns)   5.96 ns   9.86 ns  10.36 ns
nop_buffer()                           6.79 ns/iter     (6.2 ns … 15.47 ns)   6.26 ns  11.88 ns  12.16 ns
return_bool()                          6.22 ns/iter    (5.25 ns … 12.71 ns)   5.97 ns   9.34 ns   9.52 ns
return_u8()                            6.44 ns/iter    (5.73 ns … 11.23 ns)    6.2 ns   9.79 ns   9.93 ns
return_i8()                            6.48 ns/iter    (6.11 ns … 12.31 ns)    6.2 ns  10.78 ns  10.95 ns
return_u16()                           6.51 ns/iter    (5.73 ns … 12.95 ns)    6.2 ns   9.59 ns   9.79 ns
return_i16()                           6.71 ns/iter    (5.73 ns … 13.27 ns)    6.2 ns  10.94 ns  11.05 ns
return_u32()                           6.11 ns/iter    (5.22 ns … 11.36 ns)   5.53 ns     10 ns  10.21 ns
return_i32()                           6.34 ns/iter     (5.25 ns … 25.4 ns)   5.73 ns  10.67 ns  10.92 ns
return_usize()                        15.32 ns/iter    (14.5 ns … 31.05 ns)  15.22 ns  22.07 ns  23.99 ns
return_isize()                        44.26 ns/iter     (41.1 ns … 80.7 ns)   45.1 ns  70.61 ns   76.7 ns
return_f32()                            6.8 ns/iter     (5.73 ns … 13.3 ns)   6.73 ns  11.04 ns  11.11 ns
return_f64()                           6.51 ns/iter    (5.73 ns … 11.97 ns)    6.2 ns  11.07 ns   11.1 ns
nop_nonblocking()                       147 ns/iter (133.44 ns … 377.03 ns) 142.77 ns 354.19 ns 362.48 ns
nop_bool_nonblocking()               142.05 ns/iter (136.31 ns … 178.28 ns) 141.99 ns 167.73 ns 173.45 ns
nop_u8_nonblocking()                 148.33 ns/iter  (140.39 ns … 222.5 ns) 148.54 ns 209.27 ns 218.83 ns
nop_i8_nonblocking()                 165.55 ns/iter (154.46 ns … 230.54 ns)  165.4 ns 204.26 ns 217.29 ns
nop_u16_nonblocking()                146.12 ns/iter (140.06 ns … 207.22 ns) 145.95 ns 186.87 ns 206.93 ns
nop_i16_nonblocking()                146.03 ns/iter  (137.5 ns … 225.54 ns) 146.19 ns 218.16 ns 224.91 ns
nop_u32_nonblocking()                 151.6 ns/iter (139.41 ns … 239.47 ns) 149.73 ns 237.19 ns 238.34 ns
nop_i32_nonblocking()                144.14 ns/iter (136.83 ns … 221.74 ns) 146.18 ns 166.65 ns 172.39 ns
nop_u64_nonblocking()                151.26 ns/iter (143.93 ns … 212.32 ns) 152.64 ns 190.21 ns 208.15 ns
nop_i64_nonblocking()                145.76 ns/iter (136.37 ns … 240.48 ns) 144.64 ns 237.98 ns 240.21 ns
nop_usize_nonblocking()              148.43 ns/iter (139.36 ns … 192.15 ns) 148.57 ns 182.82 ns 186.06 ns
nop_isize_nonblocking()               148.5 ns/iter (141.08 ns … 213.84 ns) 148.59 ns 173.55 ns 178.38 ns
nop_f32_nonblocking()                144.86 ns/iter (137.03 ns … 187.65 ns) 145.59 ns 168.51 ns 173.95 ns
nop_f64_nonblocking()                147.75 ns/iter  (142.47 ns … 170.2 ns) 148.96 ns 159.15 ns 165.33 ns
nop_buffer_nonblocking()             146.32 ns/iter (134.94 ns … 232.89 ns) 144.51 ns 222.91 ns    227 ns
return_bool_nonblocking()            141.84 ns/iter (131.75 ns … 201.61 ns) 142.76 ns 184.78 ns 201.17 ns
return_u8_nonblocking()              157.36 ns/iter (144.56 ns … 189.18 ns) 159.41 ns 180.23 ns 186.81 ns
return_i8_nonblocking()              146.45 ns/iter (139.94 ns … 231.98 ns) 145.84 ns 191.32 ns  196.4 ns
return_u16_nonblocking()             142.16 ns/iter (137.35 ns … 219.66 ns) 142.23 ns 159.88 ns 208.34 ns
return_i16_nonblocking()              140.2 ns/iter (132.91 ns … 239.44 ns) 139.58 ns 222.27 ns 233.43 ns
return_u32_nonblocking()             144.53 ns/iter  (139.58 ns … 221.5 ns) 144.33 ns  171.6 ns 186.51 ns
return_i32_nonblocking()             139.21 ns/iter (133.43 ns … 197.44 ns) 139.88 ns 147.31 ns 169.55 ns
return_u64_nonblocking()             161.73 ns/iter (156.74 ns … 202.66 ns) 161.94 ns 180.49 ns 200.34 ns
return_i64_nonblocking()             194.84 ns/iter (187.76 ns … 231.79 ns) 196.02 ns 227.37 ns 231.31 ns
return_usize_nonblocking()           162.68 ns/iter (156.09 ns … 183.16 ns) 164.42 ns 174.76 ns 179.72 ns
return_isize_nonblocking()           198.64 ns/iter  (188.9 ns … 315.36 ns) 198.01 ns 306.44 ns 306.87 ns
return_f32_nonblocking()              144.9 ns/iter  (139.75 ns … 247.7 ns) 144.99 ns 157.28 ns 211.26 ns
return_f64_nonblocking()             148.97 ns/iter  (140.65 ns … 237.7 ns) 147.57 ns 209.94 ns 227.21 ns
return_buffer_nonblocking()           152.4 ns/iter (147.18 ns … 192.79 ns) 152.99 ns 167.72 ns 182.41 ns
nop_many_parameters()                896.74 ns/iter (865.04 ns … 984.54 ns) 908.38 ns 984.54 ns 984.54 ns
nop_many_parameters_nonblocking()      2.74 µs/iter     (2.39 µs … 3.33 µs)    2.9 µs   3.33 µs   3.33 µs
Deno.UnsafePointer.of                 97.76 ns/iter   (94.9 ns … 155.27 ns)  97.63 ns 127.36 ns 148.84 ns
Deno.UnsafePointerView#getCString    122.24 ns/iter (110.86 ns … 476.07 ns) 120.29 ns 195.12 ns 196.55 ns
Deno.UnsafePointerView#getBool        71.61 ns/iter   (68.58 ns … 86.79 ns)  72.63 ns  77.87 ns  80.27 ns
Deno.UnsafePointerView#getUint8        70.8 ns/iter   (68.34 ns … 86.66 ns)  70.84 ns  73.88 ns  76.59 ns
Deno.UnsafePointerView#getInt8         75.1 ns/iter  (71.92 ns … 121.44 ns)   74.2 ns 108.43 ns 117.33 ns
Deno.UnsafePointerView#getUint16      68.53 ns/iter  (65.25 ns … 112.59 ns)  67.99 ns 109.55 ns 110.61 ns
Deno.UnsafePointerView#getInt16       74.75 ns/iter  (71.44 ns … 115.81 ns)  75.22 ns  90.54 ns 102.42 ns
Deno.UnsafePointerView#getUint32      66.12 ns/iter  (65.25 ns … 109.65 ns)     66 ns  71.28 ns  99.93 ns
Deno.UnsafePointerView#getInt32       72.21 ns/iter   (70.25 ns … 103.6 ns)   72.8 ns  75.91 ns  86.16 ns
Deno.UnsafePointerView#getBigUint64   94.49 ns/iter  (91.59 ns … 128.74 ns)  95.21 ns 106.72 ns  119.4 ns
Deno.UnsafePointerView#getBigInt64    93.99 ns/iter   (88.92 ns … 152.3 ns)  95.44 ns  124.1 ns 135.64 ns
Deno.UnsafePointerView#getFloat32     80.92 ns/iter  (74.36 ns … 269.05 ns)  81.26 ns 120.23 ns 130.47 ns
Deno.UnsafePointerView#getFloat64     78.97 ns/iter  (75.82 ns … 107.03 ns)  79.34 ns  100.1 ns 103.32 ns

Affter:

benchmark                                time (avg)             (min … max)       p75       p99      p995
--------------------------------------------------------------------------- -----------------------------
nop()                                   3.5 ns/iter    (3.12 ns … 19.42 ns)   3.46 ns   4.12 ns   4.14 ns
hash()                                70.44 ns/iter  (69.06 ns … 106.69 ns)  70.28 ns  77.77 ns  84.11 ns
c string                             144.42 ns/iter (140.97 ns … 159.14 ns) 145.72 ns 158.69 ns 159.06 ns
add_u32()                              5.98 ns/iter     (5.96 ns … 8.32 ns)   5.96 ns   6.46 ns   6.49 ns
return_buffer()                        5.51 ns/iter    (5.02 ns … 10.01 ns)   5.49 ns   5.98 ns   6.17 ns
add_u64()                              7.97 ns/iter    (7.84 ns … 11.45 ns)   7.96 ns   8.48 ns    8.6 ns
return_u64()                          14.64 ns/iter   (13.75 ns … 30.77 ns)  13.95 ns  24.17 ns  24.42 ns
return_i64()                          43.24 ns/iter    (39.8 ns … 67.17 ns)     44 ns  61.14 ns  62.78 ns
nop_bool()                             6.09 ns/iter    (5.52 ns … 11.25 ns)   5.96 ns   9.79 ns   9.93 ns
nop_u8()                               6.05 ns/iter    (5.49 ns … 10.27 ns)   5.96 ns   9.17 ns   9.39 ns
nop_i8()                               5.74 ns/iter     (5.54 ns … 7.76 ns)   5.72 ns   6.25 ns   6.32 ns
nop_u16()                              5.77 ns/iter     (5.25 ns … 9.79 ns)   5.72 ns   7.26 ns   8.74 ns
nop_i16()                              5.78 ns/iter     (5.25 ns … 9.74 ns)   5.72 ns   7.66 ns   9.08 ns
nop_u32()                              6.03 ns/iter    (5.49 ns … 10.29 ns)   5.96 ns   8.57 ns   9.62 ns
nop_i32()                              5.74 ns/iter     (5.25 ns … 8.88 ns)   5.72 ns   6.25 ns   6.55 ns
nop_u64()                              6.02 ns/iter     (5.49 ns … 10.8 ns)   5.96 ns   7.97 ns   9.69 ns
nop_i64()                              5.98 ns/iter    (5.49 ns … 10.25 ns)   5.96 ns   6.49 ns   6.78 ns
nop_usize() number                     6.76 ns/iter     (6.2 ns … 10.62 ns)   6.68 ns   8.91 ns   9.43 ns
nop_usize() bigint                   148.88 ns/iter (138.86 ns … 179.19 ns) 151.29 ns 170.91 ns 177.02 ns
nop_isize() number                      5.8 ns/iter    (5.25 ns … 10.91 ns)   5.72 ns   8.42 ns   9.21 ns
nop_isize() bigint                   141.76 ns/iter (132.67 ns … 169.14 ns) 145.58 ns 161.71 ns 166.95 ns
nop_f32()                              6.07 ns/iter     (5.49 ns … 9.71 ns)   5.96 ns   8.34 ns   8.34 ns
nop_f64()                               5.8 ns/iter     (5.25 ns … 9.71 ns)   5.72 ns   8.49 ns   9.07 ns
nop_buffer()                           6.37 ns/iter    (6.04 ns … 12.23 ns)   6.23 ns  10.31 ns  10.55 ns
return_bool()                          5.86 ns/iter    (5.72 ns … 10.66 ns)   5.72 ns   8.75 ns   8.98 ns
return_u8()                            6.22 ns/iter     (5.73 ns … 8.45 ns)    6.2 ns   6.74 ns   6.83 ns
return_i8()                            6.31 ns/iter     (5.73 ns … 9.86 ns)    6.2 ns   8.42 ns    8.8 ns
return_u16()                           6.28 ns/iter     (5.73 ns … 9.59 ns)    6.2 ns   8.58 ns   8.68 ns
return_i16()                           6.27 ns/iter     (5.73 ns … 9.88 ns)    6.2 ns   8.34 ns   8.82 ns
return_u32()                           5.76 ns/iter     (5.44 ns … 9.99 ns)   5.72 ns   6.72 ns   7.89 ns
return_i32()                           5.75 ns/iter    (5.25 ns … 10.06 ns)   5.72 ns    6.4 ns   6.62 ns
return_usize()                        13.97 ns/iter   (13.64 ns … 28.92 ns)  13.79 ns  18.18 ns  19.02 ns
return_isize()                        42.96 ns/iter   (41.86 ns … 63.72 ns)   42.8 ns   49.5 ns  52.06 ns
return_f32()                           6.23 ns/iter     (5.74 ns … 9.35 ns)    6.2 ns   6.81 ns    7.1 ns
return_f64()                           6.27 ns/iter     (6.2 ns … 10.21 ns)    6.2 ns   8.82 ns   9.42 ns
nop_nonblocking()                    155.08 ns/iter (145.09 ns … 247.43 ns) 152.55 ns 229.89 ns 234.87 ns
nop_bool_nonblocking()               150.58 ns/iter (143.38 ns … 175.15 ns) 150.83 ns 171.66 ns  173.3 ns
nop_u8_nonblocking()                 148.96 ns/iter (143.37 ns … 170.82 ns) 149.73 ns 164.23 ns 169.19 ns
nop_i8_nonblocking()                 148.47 ns/iter (141.87 ns … 174.04 ns) 149.71 ns 166.73 ns 170.28 ns
nop_u16_nonblocking()                151.08 ns/iter (138.89 ns … 180.18 ns)  151.5 ns 170.45 ns 172.78 ns
nop_i16_nonblocking()                153.38 ns/iter (145.04 ns … 240.61 ns) 151.12 ns 234.62 ns    237 ns
nop_u32_nonblocking()                148.13 ns/iter (141.89 ns … 197.17 ns) 148.53 ns 178.46 ns 179.35 ns
nop_i32_nonblocking()                 151.3 ns/iter (139.41 ns … 169.54 ns)    152 ns 164.55 ns 166.71 ns
nop_u64_nonblocking()                 151.8 ns/iter (143.89 ns … 214.54 ns) 151.71 ns 180.77 ns 211.79 ns
nop_i64_nonblocking()                148.02 ns/iter (141.99 ns … 169.52 ns) 149.18 ns 162.95 ns 164.72 ns
nop_usize_nonblocking()              152.03 ns/iter (140.05 ns … 183.87 ns) 151.96 ns 172.92 ns 176.76 ns
nop_isize_nonblocking()              149.71 ns/iter (140.75 ns … 173.78 ns) 150.01 ns 172.88 ns 173.38 ns
nop_f32_nonblocking()                147.09 ns/iter (139.11 ns … 171.08 ns) 147.61 ns 165.78 ns 169.64 ns
nop_f64_nonblocking()                148.26 ns/iter (142.59 ns … 164.39 ns)  149.2 ns 156.44 ns 160.59 ns
nop_buffer_nonblocking()             151.87 ns/iter (147.09 ns … 169.13 ns) 152.37 ns 164.45 ns  168.8 ns
return_bool_nonblocking()            148.56 ns/iter (143.09 ns … 172.34 ns) 148.92 ns 164.62 ns 171.51 ns
return_u8_nonblocking()              148.91 ns/iter (143.83 ns … 195.85 ns) 149.37 ns 178.98 ns 180.39 ns
return_i8_nonblocking()              152.71 ns/iter (142.74 ns … 233.93 ns) 151.77 ns 220.02 ns 222.33 ns
return_u16_nonblocking()             148.48 ns/iter (140.87 ns … 207.18 ns) 149.09 ns  185.4 ns 204.87 ns
return_i16_nonblocking()             148.76 ns/iter (141.73 ns … 166.57 ns) 149.44 ns 164.99 ns 165.68 ns
return_u32_nonblocking()             151.08 ns/iter (141.93 ns … 179.53 ns) 151.42 ns 166.66 ns 167.16 ns
return_i32_nonblocking()             149.96 ns/iter  (142.3 ns … 175.71 ns) 150.44 ns 172.08 ns 174.09 ns
return_u64_nonblocking()              165.7 ns/iter (156.34 ns … 231.36 ns) 165.47 ns 201.83 ns 228.01 ns
return_i64_nonblocking()             198.91 ns/iter (187.97 ns … 242.91 ns) 198.31 ns 235.98 ns 235.99 ns
return_usize_nonblocking()           164.53 ns/iter (159.36 ns … 176.01 ns) 165.53 ns 173.52 ns 175.03 ns
return_isize_nonblocking()           191.84 ns/iter (185.16 ns … 234.38 ns) 192.36 ns 211.82 ns 224.34 ns
return_f32_nonblocking()             155.09 ns/iter (145.09 ns … 251.46 ns) 153.01 ns 217.99 ns 222.03 ns
return_f64_nonblocking()             153.43 ns/iter (143.39 ns … 204.17 ns) 151.61 ns 196.65 ns 202.35 ns
return_buffer_nonblocking()          162.29 ns/iter (153.65 ns … 183.41 ns) 162.93 ns 180.35 ns 182.14 ns
nop_many_parameters()                957.21 ns/iter (926.96 ns … 993.29 ns) 972.06 ns 993.29 ns 993.29 ns
nop_many_parameters_nonblocking()      2.68 µs/iter     (2.35 µs … 3.41 µs)   2.84 µs   3.41 µs   3.41 µs
Deno.UnsafePointer.of                107.65 ns/iter  (104.6 ns … 458.24 ns) 106.63 ns 130.77 ns 137.58 ns
Deno.UnsafePointerView#getCString    123.57 ns/iter (119.44 ns … 155.68 ns) 124.08 ns 148.64 ns 152.91 ns
Deno.UnsafePointerView#getBool        78.81 ns/iter   (75.25 ns … 87.47 ns)   79.8 ns  83.46 ns  84.06 ns
Deno.UnsafePointerView#getUint8       70.69 ns/iter      (67.87 ns … 74 ns)  71.32 ns  73.34 ns  73.62 ns
Deno.UnsafePointerView#getInt8        84.92 ns/iter  (80.96 ns … 100.14 ns)  86.01 ns  98.15 ns  98.64 ns
Deno.UnsafePointerView#getUint16      79.96 ns/iter   (77.87 ns … 99.97 ns)  79.79 ns  91.86 ns  92.27 ns
Deno.UnsafePointerView#getInt16       86.16 ns/iter   (78.34 ns … 99.38 ns)  87.63 ns  93.31 ns  93.72 ns
Deno.UnsafePointerView#getUint32      72.84 ns/iter   (69.06 ns … 85.37 ns)  73.82 ns  79.35 ns  79.99 ns
Deno.UnsafePointerView#getInt32        81.7 ns/iter   (77.63 ns … 97.73 ns)  82.59 ns  88.16 ns  91.05 ns
Deno.UnsafePointerView#getBigUint64  106.68 ns/iter  (99.26 ns … 138.39 ns) 107.03 ns 133.18 ns 135.99 ns
Deno.UnsafePointerView#getBigInt64   106.69 ns/iter (101.27 ns … 150.97 ns) 105.97 ns 143.01 ns 146.18 ns
Deno.UnsafePointerView#getFloat32     88.52 ns/iter  (81.48 ns … 116.61 ns)   89.1 ns 114.23 ns 114.87 ns
Deno.UnsafePointerView#getFloat64     86.72 ns/iter  (83.55 ns … 114.05 ns)  87.23 ns    112 ns 113.11 ns

Note that nonblocking calls seem like they are a bit less performant now. This is probably caused by my the extra indirection going from plain Value return to FfiValue. Hard to say for sure though.

@aapoalas aapoalas force-pushed the exp-ffi-external-pointers branch 3 times, most recently from 97633a4 to eb0bacd Compare December 27, 2022 09:36
@aapoalas aapoalas marked this pull request as ready for review December 28, 2022 15:20
@aapoalas aapoalas force-pushed the exp-ffi-external-pointers branch 5 times, most recently from d469f60 to ea7839e Compare January 25, 2023 18:16
@aapoalas aapoalas force-pushed the exp-ffi-external-pointers branch from fb2011f to 7832160 Compare February 8, 2023 19:23
@aapoalas aapoalas force-pushed the exp-ffi-external-pointers branch from 999aca3 to 27c0c92 Compare February 9, 2023 11:33
@bartlomieju bartlomieju added this to the 1.31 milestone Feb 9, 2023
cli/tests/unit/ffi_test.ts Show resolved Hide resolved
ext/ffi/00_ffi.js Outdated Show resolved Hide resolved
@bartlomieju
Copy link
Member

@littledivy please review this one.

Copy link
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants