github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/node-syscall/syscall.cc (about)

     1  #include <memory>
     2  #include <stdexcept>
     3  #include <vector>
     4  #include <cstdlib>
     5  #include <node.h>
     6  #include <v8.h>
     7  #include <unistd.h>
     8  #include <sys/syscall.h>
     9  #include <errno.h>
    10  
    11  using namespace v8;
    12  
    13  #if NODE_MAJOR_VERSION == 0
    14  #define ARRAY_BUFFER_DATA_OFFSET 23
    15  #else
    16  #define ARRAY_BUFFER_DATA_OFFSET 31
    17  #endif
    18  
    19  // arena stores buffers we allocate for data passed to syscalls.
    20  //
    21  // This object lives for the duration of Syscall() or Syscall6() and correctly
    22  // frees all allocated buffers at the end. This is necessary to avoid memory
    23  // leaks on each call.
    24  class arena {
    25    std::vector<std::unique_ptr<intptr_t[]>> allocs_;
    26  public:
    27    arena() = default;
    28    virtual ~arena() = default;
    29    arena(const arena& a) = delete;
    30  
    31    intptr_t* allocate(size_t n) {
    32      allocs_.emplace_back(new intptr_t[n]);
    33      return allocs_.end()->get();
    34    }
    35  };
    36  
    37  // Cast value to integer or throw an exception.
    38  //
    39  // The exception must be explicitly caught up the call stack, since the Node API
    40  // this extension is using doesn't seem to handle exceptions.
    41  Local<Integer> integerOrDie(Local<Context> ctx, Local<Value> value) {
    42    Local<Integer> integer;
    43    if (value->ToInteger(ctx).ToLocal(&integer)) {
    44      return integer;
    45    }
    46    throw std::runtime_error("expected integer, got something else");
    47  }
    48  
    49  intptr_t toNative(Local<Context> ctx, arena& a, Local<Value> value) {
    50    if (value.IsEmpty()) {
    51      return 0;
    52    }
    53    if (value->IsArrayBufferView()) {
    54      Local<ArrayBufferView> view = Local<ArrayBufferView>::Cast(value);
    55      return *reinterpret_cast<intptr_t*>(*reinterpret_cast<char**>(*view->Buffer()) + ARRAY_BUFFER_DATA_OFFSET) + view->ByteOffset(); // ugly hack, because of https://codereview.chromium.org/25221002
    56    }
    57    if (value->IsArray()) {
    58      Local<Array> array = Local<Array>::Cast(value);
    59      intptr_t* native = a.allocate(array->Length());
    60      for (uint32_t i = 0; i < array->Length(); i++) {
    61        native[i] = toNative(ctx, a, array->Get(ctx, i).ToLocalChecked());
    62      }
    63      return reinterpret_cast<intptr_t>(native);
    64    }
    65  
    66    return static_cast<intptr_t>(static_cast<int32_t>(integerOrDie(ctx, value)->Value()));
    67  }
    68  
    69  void Syscall(const FunctionCallbackInfo<Value>& info) {
    70    arena a;
    71    Isolate* isolate = info.GetIsolate();
    72    Local<Context> ctx = isolate->GetCurrentContext();
    73  
    74    try {
    75      int trap = integerOrDie(ctx, info[0])->Value();
    76      int r1 = 0, r2 = 0;
    77      switch (trap) {
    78      case SYS_fork:
    79        r1 = fork();
    80        break;
    81      case SYS_pipe:
    82        int fd[2];
    83        r1 = pipe(fd);
    84        if (r1 == 0) {
    85          r1 = fd[0];
    86          r2 = fd[1];
    87        }
    88        break;
    89      default:
    90        r1 = syscall(
    91          trap,
    92          toNative(ctx, a, info[1]),
    93          toNative(ctx, a, info[2]),
    94          toNative(ctx, a, info[3])
    95        );
    96        break;
    97      }
    98      int err = 0;
    99      if (r1 < 0) {
   100        err = errno;
   101      }
   102      Local<Array> res = Array::New(isolate, 3);
   103      res->Set(ctx, 0, Integer::New(isolate, r1)).ToChecked();
   104      res->Set(ctx, 1, Integer::New(isolate, r2)).ToChecked();
   105      res->Set(ctx, 2, Integer::New(isolate, err)).ToChecked();
   106      info.GetReturnValue().Set(res);
   107    } catch (std::exception& e) {
   108      auto message = String::NewFromUtf8(isolate, e.what(), NewStringType::kNormal).ToLocalChecked();
   109      isolate->ThrowException(Exception::TypeError(message));
   110      return;
   111    }
   112  }
   113  
   114  void Syscall6(const FunctionCallbackInfo<Value>& info) {
   115    arena a;
   116    Isolate* isolate = info.GetIsolate();
   117    Local<Context> ctx = Context::New(isolate);
   118  
   119    try {
   120      int r = syscall(
   121        integerOrDie(ctx, info[0])->Value(),
   122        toNative(ctx, a, info[1]),
   123        toNative(ctx, a, info[2]),
   124        toNative(ctx, a, info[3]),
   125        toNative(ctx, a, info[4]),
   126        toNative(ctx, a, info[5]),
   127        toNative(ctx, a, info[6])
   128      );
   129      int err = 0;
   130      if (r < 0) {
   131        err = errno;
   132      }
   133      Local<Array> res = Array::New(isolate, 3);
   134      res->Set(ctx, 0, Integer::New(isolate, r)).ToChecked();
   135      res->Set(ctx, 1, Integer::New(isolate, 0)).ToChecked();
   136      res->Set(ctx, 2, Integer::New(isolate, err)).ToChecked();
   137      info.GetReturnValue().Set(res);
   138    } catch (std::exception& e) {
   139      auto message = String::NewFromUtf8(isolate, e.what(), NewStringType::kNormal).ToLocalChecked();
   140      isolate->ThrowException(Exception::TypeError(message));
   141      return;
   142    }
   143  }
   144  
   145  void init(Local<Object> exports) {
   146    NODE_SET_METHOD(exports, "Syscall", Syscall);
   147    NODE_SET_METHOD(exports, "Syscall6", Syscall6);
   148  }
   149  
   150  NODE_MODULE(syscall, init);