@nomicfoundation/edr 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -14,8 +14,8 @@ k256 = { version = "0.13.1", default-features = false, features = ["arithmetic",
14
14
  log = { version = "0.4.20", default-features = false }
15
15
  # when napi is pinned, be sure to pin napi-derive to the same version
16
16
  # The `async` feature ensures that a tokio runtime is available
17
- napi = { version = "2.12.4", default-features = false, features = ["async", "error_anyhow", "napi8", "serde-json"] }
18
- napi-derive = "2.12.3"
17
+ napi = { version = "2.16.0", default-features = false, features = ["async", "error_anyhow", "napi8", "serde-json"] }
18
+ napi-derive = "2.16.0"
19
19
  edr_defaults = { version = "0.2.0-dev", path = "../edr_defaults" }
20
20
  edr_evm = { version = "0.2.0-dev", path = "../edr_evm", features = ["tracing"]}
21
21
  edr_eth = { version = "0.2.0-dev", path = "../edr_eth" }
package/index.d.ts CHANGED
@@ -313,7 +313,7 @@ export const enum ExceptionalHalt {
313
313
  /** Error on created contract that begins with EF */
314
314
  CreateContractStartingWithEF = 12,
315
315
  /** EIP-3860: Limit and meter initcode. Initcode size limit exceeded. */
316
- CreateInitcodeSizeLimit = 13
316
+ CreateInitCodeSizeLimit = 13
317
317
  }
318
318
  /** The result when the EVM terminates due to an exceptional halt. */
319
319
  export interface HaltResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nomicfoundation/edr",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "files": [
@@ -43,15 +43,15 @@
43
43
  "node": ">= 18"
44
44
  },
45
45
  "optionalDependencies": {
46
- "@nomicfoundation/edr-win32-x64-msvc": "0.3.2",
47
- "@nomicfoundation/edr-darwin-x64": "0.3.2",
48
- "@nomicfoundation/edr-linux-x64-gnu": "0.3.2",
49
- "@nomicfoundation/edr-darwin-arm64": "0.3.2",
50
- "@nomicfoundation/edr-win32-arm64-msvc": "0.3.2",
51
- "@nomicfoundation/edr-linux-arm64-gnu": "0.3.2",
52
- "@nomicfoundation/edr-linux-arm64-musl": "0.3.2",
53
- "@nomicfoundation/edr-linux-x64-musl": "0.3.2",
54
- "@nomicfoundation/edr-win32-ia32-msvc": "0.3.2"
46
+ "@nomicfoundation/edr-win32-x64-msvc": "0.3.3",
47
+ "@nomicfoundation/edr-darwin-x64": "0.3.3",
48
+ "@nomicfoundation/edr-linux-x64-gnu": "0.3.3",
49
+ "@nomicfoundation/edr-darwin-arm64": "0.3.3",
50
+ "@nomicfoundation/edr-win32-arm64-msvc": "0.3.3",
51
+ "@nomicfoundation/edr-linux-arm64-gnu": "0.3.3",
52
+ "@nomicfoundation/edr-linux-arm64-musl": "0.3.3",
53
+ "@nomicfoundation/edr-linux-x64-musl": "0.3.3",
54
+ "@nomicfoundation/edr-win32-ia32-msvc": "0.3.3"
55
55
  },
56
56
  "scripts": {
57
57
  "artifacts": "napi artifacts",
@@ -62,8 +62,8 @@
62
62
  "universal": "napi universal",
63
63
  "version": "napi version",
64
64
  "pretest": "pnpm build",
65
- "test": "pnpm tsc && mocha --recursive \"test/**/*.ts\" --exit",
66
- "testNoBuild": "pnpm tsc && mocha --recursive \"test/**/*.ts\" --exit",
65
+ "test": "pnpm tsc && mocha --recursive \"test/**/*.ts\"",
66
+ "testNoBuild": "pnpm tsc && mocha --recursive \"test/**/*.ts\"",
67
67
  "clean": "rm -rf @nomicfoundation/edr.node"
68
68
  }
69
69
  }
@@ -1,14 +1,16 @@
1
- use std::sync::mpsc::{channel, Sender};
1
+ use std::sync::mpsc::channel;
2
2
 
3
3
  use edr_eth::{Address, Bytes};
4
- use napi::{bindgen_prelude::Buffer, Env, JsFunction, NapiRaw, Status};
4
+ use napi::{
5
+ bindgen_prelude::Buffer,
6
+ threadsafe_function::{
7
+ ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
8
+ },
9
+ Env, JsFunction, Status,
10
+ };
5
11
  use napi_derive::napi;
6
12
 
7
- use crate::{
8
- cast::TryCast,
9
- sync::{await_promise, handle_error},
10
- threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
11
- };
13
+ use crate::cast::TryCast;
12
14
 
13
15
  /// The result of executing a call override.
14
16
  #[napi(object)]
@@ -24,7 +26,7 @@ impl TryCast<Option<edr_provider::CallOverrideResult>> for Option<CallOverrideRe
24
26
  match self {
25
27
  None => Ok(None),
26
28
  Some(result) => Ok(Some(edr_provider::CallOverrideResult {
27
- result: result.result.try_cast()?,
29
+ output: result.result.try_cast()?,
28
30
  should_revert: result.should_revert,
29
31
  })),
30
32
  }
@@ -34,20 +36,16 @@ impl TryCast<Option<edr_provider::CallOverrideResult>> for Option<CallOverrideRe
34
36
  struct CallOverrideCall {
35
37
  contract_address: Address,
36
38
  data: Bytes,
37
- sender: Sender<napi::Result<Option<edr_provider::CallOverrideResult>>>,
38
39
  }
39
40
 
40
41
  #[derive(Clone)]
41
42
  pub struct CallOverrideCallback {
42
- call_override_callback_fn: ThreadsafeFunction<CallOverrideCall>,
43
+ call_override_callback_fn: ThreadsafeFunction<CallOverrideCall, ErrorStrategy::Fatal>,
43
44
  }
44
45
 
45
46
  impl CallOverrideCallback {
46
47
  pub fn new(env: &Env, call_override_callback: JsFunction) -> napi::Result<Self> {
47
- let call_override_callback_fn = ThreadsafeFunction::create(
48
- env.raw(),
49
- // SAFETY: The callback is guaranteed to be valid for the lifetime of the inspector.
50
- unsafe { call_override_callback.raw() },
48
+ let mut call_override_callback_fn = call_override_callback.create_threadsafe_function(
51
49
  0,
52
50
  |ctx: ThreadSafeCallContext<CallOverrideCall>| {
53
51
  let address = ctx
@@ -60,17 +58,14 @@ impl CallOverrideCallback {
60
58
  .create_buffer_with_data(ctx.value.data.to_vec())?
61
59
  .into_raw();
62
60
 
63
- let sender = ctx.value.sender.clone();
64
- let promise = ctx.callback.call(None, &[address, data])?;
65
- let result = await_promise::<
66
- Option<CallOverrideResult>,
67
- Option<edr_provider::CallOverrideResult>,
68
- >(ctx.env, promise, ctx.value.sender);
69
-
70
- handle_error(sender, result)
61
+ Ok(vec![address, data])
71
62
  },
72
63
  )?;
73
64
 
65
+ // Maintain a weak reference to the function to avoid the event loop from
66
+ // exiting.
67
+ call_override_callback_fn.unref(env)?;
68
+
74
69
  Ok(Self {
75
70
  call_override_callback_fn,
76
71
  })
@@ -83,13 +78,22 @@ impl CallOverrideCallback {
83
78
  ) -> Option<edr_provider::CallOverrideResult> {
84
79
  let (sender, receiver) = channel();
85
80
 
86
- let status = self.call_override_callback_fn.call(
81
+ let status = self.call_override_callback_fn.call_with_return_value(
87
82
  CallOverrideCall {
88
83
  contract_address,
89
84
  data,
90
- sender,
91
85
  },
92
86
  ThreadsafeFunctionCallMode::Blocking,
87
+ move |result: Option<CallOverrideResult>| {
88
+ let result = result.try_cast();
89
+
90
+ sender.send(result).map_err(|_error| {
91
+ napi::Error::new(
92
+ Status::GenericFailure,
93
+ "Failed to send result from call_override_callback",
94
+ )
95
+ })
96
+ },
93
97
  );
94
98
 
95
99
  assert_eq!(status, Status::Ok, "Call override callback failed");
package/src/lib.rs CHANGED
@@ -16,7 +16,5 @@ mod result;
16
16
  #[cfg(feature = "scenarios")]
17
17
  mod scenarios;
18
18
  mod subscribe;
19
- mod sync;
20
- mod threadsafe_function;
21
19
  mod trace;
22
20
  mod withdrawal;
package/src/log.rs CHANGED
@@ -1,5 +1,3 @@
1
- use std::mem;
2
-
3
1
  use napi::{bindgen_prelude::Buffer, Env, JsBuffer, JsBufferValue};
4
2
  use napi_derive::napi;
5
3
 
@@ -14,23 +12,14 @@ pub struct ExecutionLog {
14
12
  impl ExecutionLog {
15
13
  pub fn new(env: &Env, log: &edr_evm::Log) -> napi::Result<Self> {
16
14
  let topics = log
17
- .topics
15
+ .topics()
18
16
  .iter()
19
17
  .map(|topic| Buffer::from(topic.as_slice()))
20
18
  .collect();
21
19
 
22
- let data = log.data.clone();
23
- let data = unsafe {
24
- env.create_buffer_with_borrowed_data(
25
- data.as_ptr(),
26
- data.len(),
27
- data,
28
- |data: edr_eth::Bytes, _env| {
29
- mem::drop(data);
30
- },
31
- )
32
- }
33
- .map(JsBufferValue::into_raw)?;
20
+ let data = env
21
+ .create_buffer_with_data(log.data.data.to_vec())
22
+ .map(JsBufferValue::into_raw)?;
34
23
 
35
24
  Ok(Self {
36
25
  address: Buffer::from(log.address.as_slice()),
package/src/logger.rs CHANGED
@@ -1,7 +1,4 @@
1
- use std::{
2
- fmt::Display,
3
- sync::mpsc::{channel, Sender},
4
- };
1
+ use std::{fmt::Display, sync::mpsc::channel};
5
2
 
6
3
  use ansi_term::{Color, Style};
7
4
  use edr_eth::{Bytes, B256, U256};
@@ -13,14 +10,15 @@ use edr_evm::{
13
10
  };
14
11
  use edr_provider::{ProviderError, TransactionFailure};
15
12
  use itertools::izip;
16
- use napi::{Env, JsFunction, NapiRaw, Status};
13
+ use napi::{
14
+ threadsafe_function::{
15
+ ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
16
+ },
17
+ Env, JsFunction, Status,
18
+ };
17
19
  use napi_derive::napi;
18
20
 
19
- use crate::{
20
- cast::TryCast,
21
- sync::{await_promise, handle_error},
22
- threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
23
- };
21
+ use crate::cast::TryCast;
24
22
 
25
23
  #[napi(object)]
26
24
  pub struct ContractAndFunctionName {
@@ -38,16 +36,10 @@ impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
38
36
  }
39
37
  }
40
38
 
41
- struct DecodeConsoleLogInputsCall {
42
- inputs: Vec<Bytes>,
43
- sender: Sender<napi::Result<Vec<String>>>,
44
- }
45
-
46
39
  struct ContractAndFunctionNameCall {
47
40
  code: Bytes,
48
41
  /// Only present for calls.
49
42
  calldata: Option<Bytes>,
50
- sender: Sender<napi::Result<(String, Option<String>)>>,
51
43
  }
52
44
 
53
45
  #[napi(object)]
@@ -247,116 +239,71 @@ pub struct CollapsedMethod {
247
239
 
248
240
  #[derive(Clone)]
249
241
  struct LogCollector {
250
- decode_console_log_inputs_fn: ThreadsafeFunction<DecodeConsoleLogInputsCall>,
251
- get_contract_and_function_name_fn: ThreadsafeFunction<ContractAndFunctionNameCall>,
242
+ decode_console_log_inputs_fn: ThreadsafeFunction<Vec<Bytes>, ErrorStrategy::Fatal>,
243
+ get_contract_and_function_name_fn:
244
+ ThreadsafeFunction<ContractAndFunctionNameCall, ErrorStrategy::Fatal>,
252
245
  indentation: usize,
253
246
  is_enabled: bool,
254
247
  logs: Vec<LogLine>,
255
- print_line_fn: ThreadsafeFunction<(String, bool)>,
248
+ print_line_fn: ThreadsafeFunction<(String, bool), ErrorStrategy::Fatal>,
256
249
  state: LoggingState,
257
250
  title_length: usize,
258
251
  }
259
252
 
260
253
  impl LogCollector {
261
254
  pub fn new(env: &Env, config: LoggerConfig) -> napi::Result<Self> {
262
- let decode_console_log_inputs_fn = ThreadsafeFunction::create(
263
- env.raw(),
264
- // SAFETY: The callback is guaranteed to be valid for the lifetime of the tracer.
265
- unsafe { config.decode_console_log_inputs_callback.raw() },
266
- 0,
267
- |ctx: ThreadSafeCallContext<DecodeConsoleLogInputsCall>| {
268
- // Bytes[]
269
- let inputs = ctx
270
- .env
271
- .create_array_with_length(ctx.value.inputs.len())
272
- .and_then(|mut inputs| {
273
- for (idx, input) in ctx.value.inputs.into_iter().enumerate() {
274
- // SAFETY: The input is guaranteed to be valid for the lifetime of the
275
- // JS buffer.
276
- unsafe {
277
- ctx.env.create_buffer_with_borrowed_data(
278
- input.as_ptr(),
279
- input.len(),
280
- input,
281
- |input: Bytes, _env| {
282
- std::mem::drop(input);
283
- },
284
- )
255
+ let mut decode_console_log_inputs_fn = config
256
+ .decode_console_log_inputs_callback
257
+ .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
258
+ let inputs =
259
+ ctx.env
260
+ .create_array_with_length(ctx.value.len())
261
+ .and_then(|mut inputs| {
262
+ for (idx, input) in ctx.value.into_iter().enumerate() {
263
+ ctx.env.create_buffer_with_data(input.to_vec()).and_then(
264
+ |input| inputs.set_element(idx as u32, input.into_raw()),
265
+ )?;
285
266
  }
286
- .and_then(|input| inputs.set_element(idx as u32, input.into_raw()))?;
287
- }
288
-
289
- Ok(inputs)
290
- })?;
291
-
292
- let sender = ctx.value.sender.clone();
293
267
 
294
- let promise = ctx.callback.call(None, &[inputs])?;
295
- let result =
296
- await_promise::<Vec<String>, Vec<String>>(ctx.env, promise, ctx.value.sender);
297
-
298
- handle_error(sender, result)
299
- },
300
- )?;
301
-
302
- let get_contract_and_function_name_fn = ThreadsafeFunction::create(
303
- env.raw(),
304
- // SAFETY: The callback is guaranteed to be valid for the lifetime of the tracer.
305
- unsafe { config.get_contract_and_function_name_callback.raw() },
306
- 0,
307
- |ctx: ThreadSafeCallContext<ContractAndFunctionNameCall>| {
308
- // Buffer
309
- let code = ctx.value.code;
310
- // SAFETY: The code is guaranteed to be valid for the lifetime of the
311
- // JS buffer.
312
- let code = unsafe {
313
- ctx.env.create_buffer_with_borrowed_data(
314
- code.as_ptr(),
315
- code.len(),
316
- code,
317
- |code: Bytes, _env| {
318
- std::mem::drop(code);
319
- },
320
- )
321
- }?
322
- .into_unknown();
323
-
324
- // Option<Buffer>
325
- let calldata = if let Some(calldata) = ctx.value.calldata {
326
- // SAFETY: The calldata is guaranteed to be valid for the lifetime of the
327
- // JS buffer.
328
- unsafe {
329
- ctx.env.create_buffer_with_borrowed_data(
330
- calldata.as_ptr(),
331
- calldata.len(),
332
- calldata,
333
- |calldata: Bytes, _env| {
334
- std::mem::drop(calldata);
335
- },
336
- )
337
- }?
338
- .into_unknown()
339
- } else {
340
- ctx.env.get_undefined()?.into_unknown()
341
- };
268
+ Ok(inputs)
269
+ })?;
342
270
 
343
- let sender = ctx.value.sender.clone();
271
+ Ok(vec![inputs])
272
+ })?;
273
+
274
+ // Maintain a weak reference to the function to avoid the event loop from
275
+ // exiting.
276
+ decode_console_log_inputs_fn.unref(env)?;
277
+
278
+ let mut get_contract_and_function_name_fn = config
279
+ .get_contract_and_function_name_callback
280
+ .create_threadsafe_function(
281
+ 0,
282
+ |ctx: ThreadSafeCallContext<ContractAndFunctionNameCall>| {
283
+ // Buffer
284
+ let code = ctx
285
+ .env
286
+ .create_buffer_with_data(ctx.value.code.to_vec())?
287
+ .into_unknown();
288
+
289
+ // Option<Buffer>
290
+ let calldata = if let Some(calldata) = ctx.value.calldata {
291
+ ctx.env
292
+ .create_buffer_with_data(calldata.to_vec())?
293
+ .into_unknown()
294
+ } else {
295
+ ctx.env.get_undefined()?.into_unknown()
296
+ };
344
297
 
345
- let promise = ctx.callback.call(None, &[code, calldata])?;
346
- let result = await_promise::<ContractAndFunctionName, (String, Option<String>)>(
347
- ctx.env,
348
- promise,
349
- ctx.value.sender,
350
- );
298
+ Ok(vec![code, calldata])
299
+ },
300
+ )?;
351
301
 
352
- handle_error(sender, result)
353
- },
354
- )?;
302
+ // Maintain a weak reference to the function to avoid the event loop from
303
+ // exiting.
304
+ get_contract_and_function_name_fn.unref(env)?;
355
305
 
356
- let print_line_fn = ThreadsafeFunction::create(
357
- env.raw(),
358
- // SAFETY: The callback is guaranteed to be valid for the lifetime of the tracer.
359
- unsafe { config.print_line_callback.raw() },
306
+ let mut print_line_fn = config.print_line_callback.create_threadsafe_function(
360
307
  0,
361
308
  |ctx: ThreadSafeCallContext<(String, bool)>| {
362
309
  // String
@@ -365,12 +312,14 @@ impl LogCollector {
365
312
  // bool
366
313
  let replace = ctx.env.get_boolean(ctx.value.1)?;
367
314
 
368
- ctx.callback
369
- .call(None, &[message.into_unknown(), replace.into_unknown()])?;
370
- Ok(())
315
+ Ok(vec![message.into_unknown(), replace.into_unknown()])
371
316
  },
372
317
  )?;
373
318
 
319
+ // Maintain a weak reference to the function to avoid the event loop from
320
+ // exiting.
321
+ print_line_fn.unref(env)?;
322
+
374
323
  Ok(Self {
375
324
  decode_console_log_inputs_fn,
376
325
  get_contract_and_function_name_fn,
@@ -599,14 +548,21 @@ impl LogCollector {
599
548
  ) -> (String, Option<String>) {
600
549
  let (sender, receiver) = channel();
601
550
 
602
- let status = self.get_contract_and_function_name_fn.call(
603
- ContractAndFunctionNameCall {
604
- code,
605
- calldata,
606
- sender,
607
- },
608
- ThreadsafeFunctionCallMode::Blocking,
609
- );
551
+ let status = self
552
+ .get_contract_and_function_name_fn
553
+ .call_with_return_value(
554
+ ContractAndFunctionNameCall { code, calldata },
555
+ ThreadsafeFunctionCallMode::Blocking,
556
+ move |result: ContractAndFunctionName| {
557
+ let contract_and_function_name = result.try_cast();
558
+ sender.send(contract_and_function_name).map_err(|_error| {
559
+ napi::Error::new(
560
+ Status::GenericFailure,
561
+ "Failed to send result from get_contract_and_function_name",
562
+ )
563
+ })
564
+ },
565
+ );
610
566
  assert_eq!(status, Status::Ok);
611
567
 
612
568
  receiver
@@ -793,19 +749,21 @@ impl LogCollector {
793
749
  fn log_console_log_messages(&mut self, console_log_inputs: &[Bytes]) {
794
750
  let (sender, receiver) = channel();
795
751
 
796
- let status = self.decode_console_log_inputs_fn.call(
797
- DecodeConsoleLogInputsCall {
798
- inputs: console_log_inputs.to_vec(),
799
- sender,
800
- },
752
+ let status = self.decode_console_log_inputs_fn.call_with_return_value(
753
+ console_log_inputs.to_vec(),
801
754
  ThreadsafeFunctionCallMode::Blocking,
755
+ move |decoded_inputs: Vec<String>| {
756
+ sender.send(decoded_inputs).map_err(|_error| {
757
+ napi::Error::new(
758
+ Status::GenericFailure,
759
+ "Failed to send result from decode_console_log_inputs",
760
+ )
761
+ })
762
+ },
802
763
  );
803
764
  assert_eq!(status, Status::Ok);
804
765
 
805
- let console_log_inputs = receiver
806
- .recv()
807
- .unwrap()
808
- .expect("Failed call to decode_console_log_inputs");
766
+ let console_log_inputs = receiver.recv().unwrap();
809
767
  // This is a special case, as we always want to print the console.log messages.
810
768
  // The difference is how. If we have a logger, we should use that, so that logs
811
769
  // are printed in order. If we don't, we just print the messages here.
@@ -840,9 +798,9 @@ impl LogCollector {
840
798
  if let Some(to) = before_message.to {
841
799
  // Call
842
800
  let is_precompile = {
843
- let num_precompiles =
844
- Precompiles::new(precompile::SpecId::from_spec_id(spec_id)).len();
845
- precompile::is_precompile(to, num_precompiles)
801
+ let precompiles =
802
+ Precompiles::new(precompile::PrecompileSpecId::from_spec_id(spec_id));
803
+ precompiles.contains(&to)
846
804
  };
847
805
 
848
806
  if is_precompile {
package/src/result.rs CHANGED
@@ -1,5 +1,3 @@
1
- use std::mem;
2
-
3
1
  use napi::{
4
2
  bindgen_prelude::{BigInt, Buffer, Either3},
5
3
  Either, Env, JsBuffer, JsBufferValue,
@@ -19,17 +17,17 @@ pub enum SuccessReason {
19
17
  SelfDestruct,
20
18
  }
21
19
 
22
- impl From<edr_evm::Eval> for SuccessReason {
23
- fn from(eval: edr_evm::Eval) -> Self {
20
+ impl From<edr_evm::SuccessReason> for SuccessReason {
21
+ fn from(eval: edr_evm::SuccessReason) -> Self {
24
22
  match eval {
25
- edr_evm::Eval::Stop => Self::Stop,
26
- edr_evm::Eval::Return => Self::Return,
27
- edr_evm::Eval::SelfDestruct => Self::SelfDestruct,
23
+ edr_evm::SuccessReason::Stop => Self::Stop,
24
+ edr_evm::SuccessReason::Return => Self::Return,
25
+ edr_evm::SuccessReason::SelfDestruct => Self::SelfDestruct,
28
26
  }
29
27
  }
30
28
  }
31
29
 
32
- impl From<SuccessReason> for edr_evm::Eval {
30
+ impl From<SuccessReason> for edr_evm::SuccessReason {
33
31
  fn from(value: SuccessReason) -> Self {
34
32
  match value {
35
33
  SuccessReason::Stop => Self::Stop,
@@ -97,43 +95,47 @@ pub enum ExceptionalHalt {
97
95
  /// Error on created contract that begins with EF
98
96
  CreateContractStartingWithEF,
99
97
  /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded.
100
- CreateInitcodeSizeLimit,
98
+ CreateInitCodeSizeLimit,
101
99
  }
102
100
 
103
- impl From<edr_evm::Halt> for ExceptionalHalt {
104
- fn from(halt: edr_evm::Halt) -> Self {
101
+ impl From<edr_evm::HaltReason> for ExceptionalHalt {
102
+ fn from(halt: edr_evm::HaltReason) -> Self {
105
103
  match halt {
106
- edr_evm::Halt::OutOfGas(..) => ExceptionalHalt::OutOfGas,
107
- edr_evm::Halt::OpcodeNotFound => ExceptionalHalt::OpcodeNotFound,
108
- edr_evm::Halt::InvalidFEOpcode => ExceptionalHalt::InvalidFEOpcode,
109
- edr_evm::Halt::InvalidJump => ExceptionalHalt::InvalidJump,
110
- edr_evm::Halt::NotActivated => ExceptionalHalt::NotActivated,
111
- edr_evm::Halt::StackUnderflow => ExceptionalHalt::StackUnderflow,
112
- edr_evm::Halt::StackOverflow => ExceptionalHalt::StackOverflow,
113
- edr_evm::Halt::OutOfOffset => ExceptionalHalt::OutOfOffset,
114
- edr_evm::Halt::CreateCollision => ExceptionalHalt::CreateCollision,
115
- edr_evm::Halt::PrecompileError => ExceptionalHalt::PrecompileError,
116
- edr_evm::Halt::NonceOverflow => ExceptionalHalt::NonceOverflow,
117
- edr_evm::Halt::CreateContractSizeLimit => ExceptionalHalt::CreateContractSizeLimit,
118
- edr_evm::Halt::CreateContractStartingWithEF => {
104
+ edr_evm::HaltReason::OutOfGas(..) => ExceptionalHalt::OutOfGas,
105
+ edr_evm::HaltReason::OpcodeNotFound => ExceptionalHalt::OpcodeNotFound,
106
+ edr_evm::HaltReason::InvalidFEOpcode => ExceptionalHalt::InvalidFEOpcode,
107
+ edr_evm::HaltReason::InvalidJump => ExceptionalHalt::InvalidJump,
108
+ edr_evm::HaltReason::NotActivated => ExceptionalHalt::NotActivated,
109
+ edr_evm::HaltReason::StackUnderflow => ExceptionalHalt::StackUnderflow,
110
+ edr_evm::HaltReason::StackOverflow => ExceptionalHalt::StackOverflow,
111
+ edr_evm::HaltReason::OutOfOffset => ExceptionalHalt::OutOfOffset,
112
+ edr_evm::HaltReason::CreateCollision => ExceptionalHalt::CreateCollision,
113
+ edr_evm::HaltReason::PrecompileError => ExceptionalHalt::PrecompileError,
114
+ edr_evm::HaltReason::NonceOverflow => ExceptionalHalt::NonceOverflow,
115
+ edr_evm::HaltReason::CreateContractSizeLimit => {
116
+ ExceptionalHalt::CreateContractSizeLimit
117
+ }
118
+ edr_evm::HaltReason::CreateContractStartingWithEF => {
119
119
  ExceptionalHalt::CreateContractStartingWithEF
120
120
  }
121
- edr_evm::Halt::CreateInitcodeSizeLimit => ExceptionalHalt::CreateInitcodeSizeLimit,
122
- edr_evm::Halt::OverflowPayment
123
- | edr_evm::Halt::StateChangeDuringStaticCall
124
- | edr_evm::Halt::CallNotAllowedInsideStatic
125
- | edr_evm::Halt::OutOfFund
126
- | edr_evm::Halt::CallTooDeep => {
121
+ edr_evm::HaltReason::CreateInitCodeSizeLimit => {
122
+ ExceptionalHalt::CreateInitCodeSizeLimit
123
+ }
124
+ edr_evm::HaltReason::OverflowPayment
125
+ | edr_evm::HaltReason::StateChangeDuringStaticCall
126
+ | edr_evm::HaltReason::CallNotAllowedInsideStatic
127
+ | edr_evm::HaltReason::OutOfFunds
128
+ | edr_evm::HaltReason::CallTooDeep => {
127
129
  unreachable!("Internal halts that can be only found inside Inspector: {halt:?}")
128
130
  }
129
131
  }
130
132
  }
131
133
  }
132
134
 
133
- impl From<ExceptionalHalt> for edr_evm::Halt {
135
+ impl From<ExceptionalHalt> for edr_evm::HaltReason {
134
136
  fn from(value: ExceptionalHalt) -> Self {
135
137
  match value {
136
- ExceptionalHalt::OutOfGas => Self::OutOfGas(edr_evm::OutOfGasError::BasicOutOfGas),
138
+ ExceptionalHalt::OutOfGas => Self::OutOfGas(edr_evm::OutOfGasError::Basic),
137
139
  ExceptionalHalt::OpcodeNotFound => Self::OpcodeNotFound,
138
140
  ExceptionalHalt::InvalidFEOpcode => Self::InvalidFEOpcode,
139
141
  ExceptionalHalt::InvalidJump => Self::InvalidJump,
@@ -146,7 +148,7 @@ impl From<ExceptionalHalt> for edr_evm::Halt {
146
148
  ExceptionalHalt::NonceOverflow => Self::NonceOverflow,
147
149
  ExceptionalHalt::CreateContractSizeLimit => Self::CreateContractSizeLimit,
148
150
  ExceptionalHalt::CreateContractStartingWithEF => Self::CreateContractStartingWithEF,
149
- ExceptionalHalt::CreateInitcodeSizeLimit => Self::CreateInitcodeSizeLimit,
151
+ ExceptionalHalt::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit,
150
152
  }
151
153
  }
152
154
  }
@@ -190,36 +192,19 @@ impl ExecutionResult {
190
192
  logs,
191
193
  output: match output {
192
194
  edr_evm::Output::Call(return_value) => {
193
- let return_value = return_value.clone();
194
- Either::A(CallOutput {
195
- return_value: unsafe {
196
- env.create_buffer_with_borrowed_data(
197
- return_value.as_ptr(),
198
- return_value.len(),
199
- return_value,
200
- |return_value: edr_eth::Bytes, _env| {
201
- mem::drop(return_value);
202
- },
203
- )
204
- }
205
- .map(JsBufferValue::into_raw)?,
206
- })
195
+ let return_value = env
196
+ .create_buffer_with_data(return_value.to_vec())
197
+ .map(JsBufferValue::into_raw)?;
198
+
199
+ Either::A(CallOutput { return_value })
207
200
  }
208
201
  edr_evm::Output::Create(return_value, address) => {
209
- let return_value = return_value.clone();
202
+ let return_value = env
203
+ .create_buffer_with_data(return_value.to_vec())
204
+ .map(JsBufferValue::into_raw)?;
210
205
 
211
206
  Either::B(CreateOutput {
212
- return_value: unsafe {
213
- env.create_buffer_with_borrowed_data(
214
- return_value.as_ptr(),
215
- return_value.len(),
216
- return_value,
217
- |return_value: edr_eth::Bytes, _env| {
218
- mem::drop(return_value);
219
- },
220
- )
221
- }
222
- .map(JsBufferValue::into_raw)?,
207
+ return_value,
223
208
  address: address.map(|address| Buffer::from(address.as_slice())),
224
209
  })
225
210
  }
@@ -227,20 +212,13 @@ impl ExecutionResult {
227
212
  })
228
213
  }
229
214
  edr_evm::ExecutionResult::Revert { gas_used, output } => {
230
- let output = output.clone();
215
+ let output = env
216
+ .create_buffer_with_data(output.to_vec())
217
+ .map(JsBufferValue::into_raw)?;
218
+
231
219
  Either3::B(RevertResult {
232
220
  gas_used: BigInt::from(*gas_used),
233
- output: unsafe {
234
- env.create_buffer_with_borrowed_data(
235
- output.as_ptr(),
236
- output.len(),
237
- output,
238
- |output: edr_eth::Bytes, _env| {
239
- mem::drop(output);
240
- },
241
- )
242
- }
243
- .map(JsBufferValue::into_raw)?,
221
+ output,
244
222
  })
245
223
  }
246
224
  edr_evm::ExecutionResult::Halt { reason, gas_used } => Either3::C(HaltResult {
package/src/subscribe.rs CHANGED
@@ -1,22 +1,21 @@
1
1
  use edr_eth::{remote::eth, B256};
2
- use napi::{bindgen_prelude::BigInt, Env, JsFunction, NapiRaw};
3
- use napi_derive::napi;
4
-
5
- use crate::threadsafe_function::{
6
- ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
2
+ use napi::{
3
+ bindgen_prelude::BigInt,
4
+ threadsafe_function::{
5
+ ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
6
+ },
7
+ Env, JsFunction,
7
8
  };
9
+ use napi_derive::napi;
8
10
 
9
11
  #[derive(Clone)]
10
12
  pub struct SubscriberCallback {
11
- inner: ThreadsafeFunction<edr_provider::SubscriptionEvent>,
13
+ inner: ThreadsafeFunction<edr_provider::SubscriptionEvent, ErrorStrategy::Fatal>,
12
14
  }
13
15
 
14
16
  impl SubscriberCallback {
15
17
  pub fn new(env: &Env, subscription_event_callback: JsFunction) -> napi::Result<Self> {
16
- let callback = ThreadsafeFunction::create(
17
- env.raw(),
18
- // SAFETY: The callback is guaranteed to be valid for the lifetime of the inspector.
19
- unsafe { subscription_event_callback.raw() },
18
+ let mut callback = subscription_event_callback.create_threadsafe_function(
20
19
  0,
21
20
  |ctx: ThreadSafeCallContext<edr_provider::SubscriptionEvent>| {
22
21
  // SubscriptionEvent
@@ -39,10 +38,14 @@ impl SubscriberCallback {
39
38
 
40
39
  event.set_named_property("result", result)?;
41
40
 
42
- ctx.callback.call(None, &[event])?;
43
- Ok(())
41
+ Ok(vec![event])
44
42
  },
45
43
  )?;
44
+
45
+ // Maintain a weak reference to the function to avoid the event loop from
46
+ // exiting.
47
+ callback.unref(env)?;
48
+
46
49
  Ok(Self { inner: callback })
47
50
  }
48
51
 
package/src/trace.rs CHANGED
@@ -1,6 +1,6 @@
1
- use std::{mem, sync::Arc};
1
+ use std::sync::Arc;
2
2
 
3
- use edr_evm::{trace::BeforeMessage, OPCODE_JUMPMAP};
3
+ use edr_evm::{interpreter::OPCODE_JUMPMAP, trace::BeforeMessage};
4
4
  use napi::{
5
5
  bindgen_prelude::{BigInt, Buffer, Either3},
6
6
  Env, JsBuffer, JsBufferValue,
@@ -47,34 +47,14 @@ pub struct TracingMessage {
47
47
 
48
48
  impl TracingMessage {
49
49
  pub fn new(env: &Env, message: &BeforeMessage) -> napi::Result<Self> {
50
- let data = message.data.clone();
51
- let data = unsafe {
52
- env.create_buffer_with_borrowed_data(
53
- data.as_ptr(),
54
- data.len(),
55
- data,
56
- |data: edr_eth::Bytes, _env| {
57
- mem::drop(data);
58
- },
59
- )
60
- }
61
- .map(JsBufferValue::into_raw)?;
50
+ let data = env
51
+ .create_buffer_with_data(message.data.to_vec())
52
+ .map(JsBufferValue::into_raw)?;
62
53
 
63
54
  let code = message.code.as_ref().map_or(Ok(None), |code| {
64
- let code = code.original_bytes();
65
-
66
- unsafe {
67
- env.create_buffer_with_borrowed_data(
68
- code.as_ptr(),
69
- code.len(),
70
- code,
71
- |code: edr_eth::Bytes, _env| {
72
- mem::drop(code);
73
- },
74
- )
75
- }
76
- .map(JsBufferValue::into_raw)
77
- .map(Some)
55
+ env.create_buffer_with_data(code.original_bytes().to_vec())
56
+ .map(JsBufferValue::into_raw)
57
+ .map(Some)
78
58
  })?;
79
59
 
80
60
  Ok(TracingMessage {
package/src/sync.rs DELETED
@@ -1,85 +0,0 @@
1
- use std::{fmt::Debug, sync::mpsc::Sender};
2
-
3
- use napi::{bindgen_prelude::FromNapiValue, Env, JsFunction, JsObject, JsUnknown, NapiRaw, Status};
4
-
5
- use crate::cast::TryCast;
6
-
7
- pub fn await_promise<I, O>(
8
- env: Env,
9
- result: JsUnknown,
10
- tx: Sender<napi::Result<O>>,
11
- ) -> napi::Result<()>
12
- where
13
- I: FromNapiValue + TryCast<O, Error = napi::Error>,
14
- O: 'static,
15
- {
16
- // If the result is a promise, wait for it to resolve, and send the result to
17
- // the channel. Otherwise, send the result immediately.
18
- if result.is_promise()? {
19
- let result: JsObject = result.try_into()?;
20
- let then: JsFunction = result.get_named_property("then")?;
21
- let tx2 = tx.clone();
22
- let cb = env.create_function_from_closure("callback", move |ctx| {
23
- let result = ctx.get::<I>(0)?;
24
- tx.send(Ok(result.try_cast()?)).unwrap();
25
- ctx.env.get_undefined()
26
- })?;
27
- let eb = env.create_function_from_closure("error_callback", move |ctx| {
28
- // TODO: need a way to convert a JsUnknown to an Error
29
- tx2.send(Err(napi::Error::from_reason("Promise rejected")))
30
- .unwrap();
31
- ctx.env.get_undefined()
32
- })?;
33
- then.call(Some(&result), &[cb, eb])?;
34
- } else {
35
- let result = unsafe { I::from_napi_value(env.raw(), result.raw())? };
36
- tx.send(Ok(result.try_cast()?)).unwrap();
37
- }
38
-
39
- Ok(())
40
- }
41
-
42
- #[allow(dead_code)]
43
- pub fn await_void_promise(
44
- env: Env,
45
- result: JsUnknown,
46
- tx: Sender<napi::Result<()>>,
47
- ) -> napi::Result<()> {
48
- // If the result is a promise, wait for it to resolve, and send the result to
49
- // the channel. Otherwise, send the result immediately.
50
- if result.is_promise()? {
51
- let result: JsObject = result.try_into()?;
52
- let then: JsFunction = result.get_named_property("then")?;
53
- let tx2 = tx.clone();
54
- let cb = env.create_function_from_closure("callback", move |ctx| {
55
- tx.send(Ok(())).unwrap();
56
- ctx.env.get_undefined()
57
- })?;
58
- let eb = env.create_function_from_closure("error_callback", move |ctx| {
59
- // TODO: need a way to convert a JsUnknown to an Error
60
- tx2.send(Err(napi::Error::from_reason("Promise rejected")))
61
- .unwrap();
62
- ctx.env.get_undefined()
63
- })?;
64
- then.call(Some(&result), &[cb, eb])?;
65
- Ok(())
66
- } else {
67
- Err(napi::Error::new(
68
- Status::ObjectExpected,
69
- "Expected promise".to_owned(),
70
- ))
71
- }
72
- }
73
-
74
- pub fn handle_error<T: Debug>(
75
- tx: Sender<napi::Result<T>>,
76
- res: napi::Result<()>,
77
- ) -> napi::Result<()> {
78
- match res {
79
- Ok(_) => Ok(()),
80
- Err(e) => {
81
- tx.send(Err(e)).expect("send error");
82
- Ok(())
83
- }
84
- }
85
- }
@@ -1,305 +0,0 @@
1
- // Fork of threadsafe_function from napi-rs that allows calling JS function
2
- // manually rather than only returning args. This enables us to use the return
3
- // value of the function.
4
-
5
- #![allow(clippy::single_component_path_imports)]
6
-
7
- use std::{
8
- convert::Into,
9
- ffi::CString,
10
- marker::PhantomData,
11
- os::raw::c_void,
12
- ptr,
13
- sync::{
14
- atomic::{AtomicBool, AtomicUsize, Ordering},
15
- Arc,
16
- },
17
- };
18
-
19
- use napi::{check_status, sys, Env, JsError, JsFunction, NapiValue, Result, Status};
20
-
21
- /// `ThreadSafeFunction` context object
22
- /// the `value` is the value passed to `call` method
23
- pub struct ThreadSafeCallContext<T: 'static> {
24
- pub env: Env,
25
- pub value: T,
26
- pub callback: JsFunction,
27
- }
28
-
29
- #[repr(u8)]
30
- pub enum ThreadsafeFunctionCallMode {
31
- NonBlocking,
32
- Blocking,
33
- }
34
-
35
- impl From<ThreadsafeFunctionCallMode> for sys::napi_threadsafe_function_call_mode {
36
- fn from(value: ThreadsafeFunctionCallMode) -> Self {
37
- match value {
38
- ThreadsafeFunctionCallMode::Blocking => sys::ThreadsafeFunctionCallMode::blocking,
39
- ThreadsafeFunctionCallMode::NonBlocking => sys::ThreadsafeFunctionCallMode::nonblocking,
40
- }
41
- }
42
- }
43
-
44
- /// Communicate with the addon's main thread by invoking a JavaScript function
45
- /// from other threads.
46
- ///
47
- /// ## Example
48
- /// An example of using `ThreadsafeFunction`:
49
- ///
50
- /// ```rust
51
- /// #[macro_use]
52
- /// extern crate napi_derive;
53
- ///
54
- /// use std::thread;
55
- ///
56
- /// use napi::{
57
- /// threadsafe_function::{
58
- /// ThreadSafeCallContext, ThreadsafeFunctionCallMode, ThreadsafeFunctionReleaseMode,
59
- /// },
60
- /// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status,
61
- /// };
62
- ///
63
- /// #[js_function(1)]
64
- /// pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
65
- /// let func = ctx.get::<JsFunction>(0)?;
66
- ///
67
- /// let tsfn =
68
- /// ctx
69
- /// .env
70
- /// .create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
71
- /// ctx.value
72
- /// .iter()
73
- /// .map(|v| ctx.env.create_uint32(*v))
74
- /// .collect::<Result<Vec<JsNumber>>>()
75
- /// })?;
76
- ///
77
- /// let tsfn_cloned = tsfn.clone();
78
- ///
79
- /// thread::spawn(move || {
80
- /// let output: Vec<u32> = vec![0, 1, 2, 3];
81
- /// // It's okay to call a threadsafe function multiple times.
82
- /// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking);
83
- /// });
84
- ///
85
- /// thread::spawn(move || {
86
- /// let output: Vec<u32> = vec![3, 2, 1, 0];
87
- /// // It's okay to call a threadsafe function multiple times.
88
- /// tsfn_cloned.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking);
89
- /// });
90
- ///
91
- /// ctx.env.get_undefined()
92
- /// }
93
- /// ```
94
- #[derive(Debug)]
95
- pub struct ThreadsafeFunction<T: 'static> {
96
- raw_tsfn: sys::napi_threadsafe_function,
97
- aborted: Arc<AtomicBool>,
98
- ref_count: Arc<AtomicUsize>,
99
- _phantom: PhantomData<T>,
100
- }
101
-
102
- impl<T: 'static> Clone for ThreadsafeFunction<T> {
103
- fn clone(&self) -> Self {
104
- if !self.aborted.load(Ordering::Acquire) {
105
- let acquire_status = unsafe { sys::napi_acquire_threadsafe_function(self.raw_tsfn) };
106
- debug_assert!(
107
- acquire_status == sys::Status::napi_ok,
108
- "Acquire threadsafe function failed in clone"
109
- );
110
- }
111
-
112
- Self {
113
- raw_tsfn: self.raw_tsfn,
114
- aborted: Arc::clone(&self.aborted),
115
- ref_count: Arc::clone(&self.ref_count),
116
- _phantom: PhantomData,
117
- }
118
- }
119
- }
120
-
121
- unsafe impl<T> Send for ThreadsafeFunction<T> {}
122
- unsafe impl<T> Sync for ThreadsafeFunction<T> {}
123
-
124
- impl<T: 'static> ThreadsafeFunction<T> {
125
- /// See [napi_create_threadsafe_function](https://nodejs.org/api/n-api.html#n_api_napi_create_threadsafe_function)
126
- /// for more information.
127
- pub(crate) fn create<R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<()>>(
128
- env: sys::napi_env,
129
- func: sys::napi_value,
130
- max_queue_size: usize,
131
- callback: R,
132
- ) -> Result<Self> {
133
- let mut async_resource_name = ptr::null_mut();
134
- let s = "napi_rs_threadsafe_function";
135
- let len = s.len();
136
- let s = CString::new(s)?;
137
- check_status!(unsafe {
138
- sys::napi_create_string_utf8(env, s.as_ptr(), len, &mut async_resource_name)
139
- })?;
140
-
141
- let initial_thread_count = 1usize;
142
- let mut raw_tsfn = ptr::null_mut();
143
- let ptr = Box::into_raw(Box::new(callback)).cast::<c_void>();
144
- check_status!(unsafe {
145
- sys::napi_create_threadsafe_function(
146
- env,
147
- func,
148
- ptr::null_mut(),
149
- async_resource_name,
150
- max_queue_size,
151
- initial_thread_count,
152
- ptr,
153
- Some(thread_finalize_cb::<T, R>),
154
- ptr,
155
- Some(call_js_cb::<T, R>),
156
- &mut raw_tsfn,
157
- )
158
- })?;
159
-
160
- let aborted = Arc::new(AtomicBool::new(false));
161
- let aborted_ptr = Arc::into_raw(aborted.clone()) as *mut c_void;
162
- check_status!(unsafe {
163
- sys::napi_add_env_cleanup_hook(env, Some(cleanup_cb), aborted_ptr)
164
- })?;
165
-
166
- Ok(ThreadsafeFunction {
167
- raw_tsfn,
168
- aborted,
169
- ref_count: Arc::new(AtomicUsize::new(initial_thread_count)),
170
- _phantom: PhantomData,
171
- })
172
- }
173
- }
174
-
175
- impl<T: 'static> ThreadsafeFunction<T> {
176
- /// See [napi_call_threadsafe_function](https://nodejs.org/api/n-api.html#n_api_napi_call_threadsafe_function)
177
- /// for more information.
178
- pub fn call(&self, value: T, mode: ThreadsafeFunctionCallMode) -> Status {
179
- if self.aborted.load(Ordering::Acquire) {
180
- return Status::Closing;
181
- }
182
- unsafe {
183
- sys::napi_call_threadsafe_function(
184
- self.raw_tsfn,
185
- Box::into_raw(Box::new(value)).cast(),
186
- mode.into(),
187
- )
188
- }
189
- .into()
190
- }
191
- }
192
-
193
- impl<T: 'static> Drop for ThreadsafeFunction<T> {
194
- fn drop(&mut self) {
195
- if !self.aborted.load(Ordering::Acquire) && self.ref_count.load(Ordering::Acquire) > 0usize
196
- {
197
- let release_status = unsafe {
198
- sys::napi_release_threadsafe_function(
199
- self.raw_tsfn,
200
- sys::ThreadsafeFunctionReleaseMode::release,
201
- )
202
- };
203
- assert!(
204
- release_status == sys::Status::napi_ok,
205
- "Threadsafe Function release failed"
206
- );
207
- }
208
- }
209
- }
210
-
211
- unsafe extern "C" fn cleanup_cb(cleanup_data: *mut c_void) {
212
- let aborted = Arc::<AtomicBool>::from_raw(cleanup_data.cast());
213
- aborted.store(true, Ordering::SeqCst);
214
- }
215
-
216
- unsafe extern "C" fn thread_finalize_cb<T: 'static, R>(
217
- _raw_env: sys::napi_env,
218
- finalize_data: *mut c_void,
219
- _finalize_hint: *mut c_void,
220
- ) where
221
- R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<()>,
222
- {
223
- // cleanup
224
- drop(Box::<R>::from_raw(finalize_data.cast()));
225
- }
226
-
227
- unsafe extern "C" fn call_js_cb<T: 'static, R>(
228
- raw_env: sys::napi_env,
229
- js_callback: sys::napi_value,
230
- context: *mut c_void,
231
- data: *mut c_void,
232
- ) where
233
- R: 'static + Send + FnMut(ThreadSafeCallContext<T>) -> Result<()>,
234
- {
235
- // env and/or callback can be null when shutting down
236
- if raw_env.is_null() || js_callback.is_null() {
237
- return;
238
- }
239
-
240
- let ctx: &mut R = &mut *context.cast::<R>();
241
- let val: Result<T> = Ok(*Box::<T>::from_raw(data.cast()));
242
-
243
- let mut recv = ptr::null_mut();
244
- sys::napi_get_undefined(raw_env, &mut recv);
245
-
246
- let ret = val.and_then(|v| {
247
- (ctx)(ThreadSafeCallContext {
248
- env: Env::from_raw(raw_env),
249
- value: v,
250
- callback: JsFunction::from_raw(raw_env, js_callback).unwrap(), // TODO: unwrap
251
- })
252
- });
253
-
254
- let status = match ret {
255
- Ok(()) => sys::Status::napi_ok,
256
- Err(e) => sys::napi_fatal_exception(raw_env, JsError::from(e).into_value(raw_env)),
257
- };
258
- if status == sys::Status::napi_ok {
259
- return;
260
- }
261
- if status == sys::Status::napi_pending_exception {
262
- let mut error_result = ptr::null_mut();
263
- assert_eq!(
264
- sys::napi_get_and_clear_last_exception(raw_env, &mut error_result),
265
- sys::Status::napi_ok
266
- );
267
-
268
- // When shutting down, napi_fatal_exception sometimes returns another exception
269
- let stat = sys::napi_fatal_exception(raw_env, error_result);
270
- assert!(stat == sys::Status::napi_ok || stat == sys::Status::napi_pending_exception);
271
- } else {
272
- let error_code: Status = status.into();
273
- let error_code_string = format!("{error_code:?}");
274
- let mut error_code_value = ptr::null_mut();
275
- assert_eq!(
276
- sys::napi_create_string_utf8(
277
- raw_env,
278
- error_code_string.as_ptr().cast(),
279
- error_code_string.len(),
280
- &mut error_code_value,
281
- ),
282
- sys::Status::napi_ok,
283
- );
284
- let error_msg = "Call JavaScript callback failed in thread safe function";
285
- let mut error_msg_value = ptr::null_mut();
286
- assert_eq!(
287
- sys::napi_create_string_utf8(
288
- raw_env,
289
- error_msg.as_ptr().cast(),
290
- error_msg.len(),
291
- &mut error_msg_value,
292
- ),
293
- sys::Status::napi_ok,
294
- );
295
- let mut error_value = ptr::null_mut();
296
- assert_eq!(
297
- sys::napi_create_error(raw_env, error_code_value, error_msg_value, &mut error_value),
298
- sys::Status::napi_ok,
299
- );
300
- assert_eq!(
301
- sys::napi_fatal_exception(raw_env, error_value),
302
- sys::Status::napi_ok
303
- );
304
- }
305
- }