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);