| #if HAVE_FFI |
| |
| #include "data.h" |
| #include "base_object-inl.h" |
| #include "node_errors.h" |
| #include "util.h" |
| #include "v8.h" |
| |
| #include <cmath> |
| #include <cstdint> |
| #include <cstring> |
| #include <limits> |
| #include <type_traits> |
| |
| using v8::ArrayBuffer; |
| using v8::ArrayBufferView; |
| using v8::BackingStore; |
| using v8::BigInt; |
| using v8::Context; |
| using v8::FunctionCallbackInfo; |
| using v8::Integer; |
| using v8::Isolate; |
| using v8::Just; |
| using v8::JustVoid; |
| using v8::Local; |
| using v8::Maybe; |
| using v8::MaybeLocal; |
| using v8::NewStringType; |
| using v8::Nothing; |
| using v8::Number; |
| using v8::Object; |
| using v8::SharedArrayBuffer; |
| using v8::String; |
| using v8::Value; |
| |
| namespace node { |
| |
| namespace ffi { |
| |
| Maybe<size_t> GetValidatedSize(Environment* env, |
| Local<Value> value, |
| const char* label) { |
| if (!value->IsNumber()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "The %s must be a number", label); |
| return Nothing<size_t>(); |
| } |
| |
| double length = value.As<Number>()->Value(); |
| if (!std::isfinite(length) || length < 0 || std::floor(length) != length) { |
| THROW_ERR_INVALID_ARG_VALUE( |
| env, "The %s must be a non-negative integer", label); |
| return Nothing<size_t>(); |
| } |
| |
| if (length > static_cast<double>(std::numeric_limits<size_t>::max())) { |
| THROW_ERR_OUT_OF_RANGE(env, "The %s is too large", label); |
| return Nothing<size_t>(); |
| } |
| |
| return Just(static_cast<size_t>(length)); |
| } |
| |
| Maybe<uintptr_t> GetValidatedPointerAddress(Environment* env, |
| Local<Value> value, |
| const char* label) { |
| if (!value->IsBigInt()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "The %s must be a bigint", label); |
| return Nothing<uintptr_t>(); |
| } |
| |
| bool lossless; |
| uint64_t address = value.As<BigInt>()->Uint64Value(&lossless); |
| if (!lossless) { |
| THROW_ERR_INVALID_ARG_VALUE( |
| env, "The %s must be a non-negative bigint", label); |
| return Nothing<uintptr_t>(); |
| } |
| |
| if (address > static_cast<uint64_t>(std::numeric_limits<uintptr_t>::max())) { |
| THROW_ERR_INVALID_ARG_VALUE( |
| env, "The %s exceeds the platform pointer range", label); |
| return Nothing<uintptr_t>(); |
| } |
| |
| return Just(static_cast<uintptr_t>(address)); |
| } |
| |
| Maybe<int64_t> GetValidatedSignedInt(Environment* env, |
| Local<Value> value, |
| int64_t min, |
| int64_t max, |
| const char* type_name) { |
| if (!value->IsNumber()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be an %s", type_name); |
| return Nothing<int64_t>(); |
| } |
| |
| double number = value.As<Number>()->Value(); |
| if (!std::isfinite(number) || std::floor(number) != number || number < min || |
| number > max) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be an %s", type_name); |
| return Nothing<int64_t>(); |
| } |
| |
| return Just(static_cast<int64_t>(number)); |
| } |
| |
| Maybe<uint64_t> GetValidatedUnsignedInt(Environment* env, |
| Local<Value> value, |
| uint64_t max, |
| const char* type_name) { |
| if (!value->IsNumber()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be a %s", type_name); |
| return Nothing<uint64_t>(); |
| } |
| |
| double number = value.As<Number>()->Value(); |
| if (!std::isfinite(number) || std::floor(number) != number || number < 0 || |
| number > static_cast<double>(max)) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be a %s", type_name); |
| return Nothing<uint64_t>(); |
| } |
| |
| return Just(static_cast<uint64_t>(number)); |
| } |
| |
| Maybe<void> ValidatePointerSpan(Environment* env, |
| uintptr_t raw_ptr, |
| size_t offset, |
| size_t length, |
| const char* error_message) { |
| if (offset > std::numeric_limits<uintptr_t>::max() - raw_ptr) { |
| THROW_ERR_INVALID_ARG_VALUE(env, error_message); |
| return Nothing<void>(); |
| } |
| |
| uintptr_t start = raw_ptr + offset; |
| if (length > 0 && |
| length - 1 > std::numeric_limits<uintptr_t>::max() - start) { |
| THROW_ERR_INVALID_ARG_VALUE(env, error_message); |
| return Nothing<void>(); |
| } |
| |
| return JustVoid(); |
| } |
| |
| Maybe<void> ValidateBufferLength(Environment* env, size_t len) { |
| if (len > Buffer::kMaxLength) { |
| THROW_ERR_BUFFER_TOO_LARGE(env, "Buffer is too large"); |
| return Nothing<void>(); |
| } |
| |
| return JustVoid(); |
| } |
| |
| Maybe<void> ValidateStringLength(Environment* env, size_t len) { |
| if (len > static_cast<size_t>(String::kMaxLength)) { |
| THROW_ERR_STRING_TOO_LONG(env, "String is too long"); |
| return Nothing<void>(); |
| } |
| |
| return JustVoid(); |
| } |
| |
| Maybe<std::pair<uint8_t*, size_t>> GetValidatedPointerAndOffset( |
| Environment* env, const FunctionCallbackInfo<Value>& args) { |
| uintptr_t raw_ptr; |
| if (args.Length() < 1 || |
| !GetValidatedPointerAddress(env, args[0], "pointer").To(&raw_ptr)) { |
| return {}; |
| } |
| |
| if (raw_ptr == 0) { |
| THROW_ERR_FFI_INVALID_POINTER(env, "Cannot dereference a null pointer"); |
| return {}; |
| } |
| |
| size_t offset = 0; |
| if (args.Length() > 1 && !args[1]->IsUndefined()) { |
| if (!GetValidatedSize(env, args[1], "offset").To(&offset)) { |
| return {}; |
| } |
| } |
| |
| if (ValidatePointerSpan( |
| env, |
| raw_ptr, |
| offset, |
| 1, |
| "The pointer and offset exceed the platform address range") |
| .IsNothing()) { |
| return {}; |
| } |
| |
| return Just(std::make_pair(reinterpret_cast<uint8_t*>(raw_ptr), offset)); |
| } |
| |
| struct PointerOffsetAndValue { |
| uint8_t* ptr; |
| size_t offset; |
| Local<Value> value; |
| }; |
| |
| Maybe<PointerOffsetAndValue> GetValidatedPointerOffsetAndValue( |
| Environment* env, const FunctionCallbackInfo<Value>& args) { |
| size_t offset; |
| Local<Value> value; |
| uintptr_t raw_ptr; |
| if (args.Length() < 1 || |
| !GetValidatedPointerAddress(env, args[0], "pointer").To(&raw_ptr)) { |
| return {}; |
| } |
| |
| if (raw_ptr == 0) { |
| THROW_ERR_FFI_INVALID_POINTER(env, "Cannot dereference a null pointer"); |
| return {}; |
| } |
| |
| if (args.Length() < 2 || args[1]->IsUndefined()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Expected an offset argument"); |
| return {}; |
| } |
| |
| if (!GetValidatedSize(env, args[1], "offset").To(&offset)) { |
| return {}; |
| } |
| |
| if (ValidatePointerSpan( |
| env, |
| raw_ptr, |
| offset, |
| 1, |
| "The pointer and offset exceed the platform address range") |
| .IsNothing()) { |
| return {}; |
| } |
| |
| if (args.Length() < 3 || args[2]->IsUndefined()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Expected a value argument"); |
| return {}; |
| } |
| |
| value = args[2]; |
| |
| uint8_t* ptr = reinterpret_cast<uint8_t*>(static_cast<uintptr_t>(raw_ptr)); |
| return Just(PointerOffsetAndValue{ptr, offset, value}); |
| } |
| |
| template <typename T> |
| void GetValue(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| Isolate* isolate = env->isolate(); |
| std::pair<uint8_t*, size_t> ptr_and_offset; |
| |
| if (!GetValidatedPointerAndOffset(env, args).To(&ptr_and_offset)) { |
| return; |
| } |
| |
| auto [ptr, offset] = ptr_and_offset; |
| uintptr_t raw_ptr = reinterpret_cast<uintptr_t>(ptr); |
| if (ValidatePointerSpan( |
| env, |
| raw_ptr, |
| offset, |
| sizeof(T), |
| "The accessed range exceeds the platform address range") |
| .IsNothing()) { |
| return; |
| } |
| |
| T value; |
| std::memcpy(&value, ptr + offset, sizeof(value)); |
| if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t> || |
| std::is_same_v<T, int32_t>) { |
| args.GetReturnValue().Set(Integer::New(isolate, value)); |
| } else if constexpr (std::is_same_v<T, uint8_t> || |
| std::is_same_v<T, uint16_t> || |
| std::is_same_v<T, uint32_t>) { |
| args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, value)); |
| } else if constexpr (std::is_same_v<T, int64_t>) { |
| args.GetReturnValue().Set(BigInt::New(isolate, value)); |
| } else if constexpr (std::is_same_v<T, uint64_t>) { |
| args.GetReturnValue().Set(BigInt::NewFromUnsigned(isolate, value)); |
| } else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) { |
| args.GetReturnValue().Set(Number::New(isolate, value)); |
| } else { |
| UNREACHABLE(); |
| } |
| } |
| |
| template <typename T> |
| void SetValue(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| PointerOffsetAndValue data; |
| |
| if (!GetValidatedPointerOffsetAndValue(env, args).To(&data)) { |
| return; |
| } |
| auto [ptr, offset, value] = data; |
| |
| uintptr_t raw_ptr = reinterpret_cast<uintptr_t>(ptr); |
| if (ValidatePointerSpan( |
| env, |
| raw_ptr, |
| offset, |
| sizeof(T), |
| "The accessed range exceeds the platform address range") |
| .IsNothing()) { |
| return; |
| } |
| |
| T converted; |
| Local<Context> context = env->context(); |
| |
| if constexpr (std::is_same_v<T, int8_t>) { |
| int64_t validated; |
| if (!GetValidatedSignedInt(env, value, INT8_MIN, INT8_MAX, "int8") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else if constexpr (std::is_same_v<T, uint8_t>) { |
| uint64_t validated; |
| if (!GetValidatedUnsignedInt(env, value, UINT8_MAX, "uint8") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else if constexpr (std::is_same_v<T, int16_t>) { |
| int64_t validated; |
| if (!GetValidatedSignedInt(env, value, INT16_MIN, INT16_MAX, "int16") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else if constexpr (std::is_same_v<T, uint16_t>) { |
| uint64_t validated; |
| if (!GetValidatedUnsignedInt(env, value, UINT16_MAX, "uint16") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else if constexpr (std::is_same_v<T, int32_t>) { |
| int64_t validated; |
| if (!GetValidatedSignedInt(env, value, INT32_MIN, INT32_MAX, "int32") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else if constexpr (std::is_same_v<T, uint32_t>) { |
| uint64_t validated; |
| if (!GetValidatedUnsignedInt(env, value, UINT32_MAX, "uint32") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else if constexpr (std::is_same_v<T, int64_t>) { |
| if (value->IsBigInt()) { |
| bool lossless; |
| converted = static_cast<T>(value.As<BigInt>()->Int64Value(&lossless)); |
| if (!lossless) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be an int64"); |
| return; |
| } |
| } else if (value->IsNumber()) { |
| int64_t validated; |
| if (!GetValidatedSignedInt(env, |
| value, |
| -static_cast<int64_t>(kMaxSafeJsInteger), |
| static_cast<int64_t>(kMaxSafeJsInteger), |
| "int64") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be a bigint or a number"); |
| return; |
| } |
| } else if constexpr (std::is_same_v<T, uint64_t>) { |
| if (value->IsBigInt()) { |
| bool lossless; |
| converted = static_cast<T>(value.As<BigInt>()->Uint64Value(&lossless)); |
| if (!lossless) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be a uint64"); |
| return; |
| } |
| } else if (value->IsNumber()) { |
| uint64_t validated; |
| if (!GetValidatedUnsignedInt( |
| env, value, static_cast<uint64_t>(kMaxSafeJsInteger), "uint64") |
| .To(&validated)) { |
| return; |
| } |
| converted = static_cast<T>(validated); |
| } else { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be a bigint or a number"); |
| return; |
| } |
| } else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) { |
| MaybeLocal<Number> number = value->ToNumber(context); |
| Local<Number> number_local; |
| |
| if (!number.ToLocal(&number_local)) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Value must be a number"); |
| return; |
| } |
| |
| converted = static_cast<T>(number_local->Value()); |
| } else { |
| UNREACHABLE(); |
| } |
| |
| std::memcpy(ptr + offset, &converted, sizeof(converted)); |
| } |
| |
| // Raw FFI memory helpers are low-level and unsafe. Pointer validity, |
| // lifetime, and zero-copy writable views are the caller's responsibility. |
| void GetInt8(const FunctionCallbackInfo<Value>& args) { |
| GetValue<int8_t>(args); |
| } |
| |
| void GetUint8(const FunctionCallbackInfo<Value>& args) { |
| GetValue<uint8_t>(args); |
| } |
| |
| void GetInt16(const FunctionCallbackInfo<Value>& args) { |
| GetValue<int16_t>(args); |
| } |
| |
| void GetUint16(const FunctionCallbackInfo<Value>& args) { |
| GetValue<uint16_t>(args); |
| } |
| |
| void GetInt32(const FunctionCallbackInfo<Value>& args) { |
| GetValue<int32_t>(args); |
| } |
| |
| void GetUint32(const FunctionCallbackInfo<Value>& args) { |
| GetValue<uint32_t>(args); |
| } |
| |
| void GetInt64(const FunctionCallbackInfo<Value>& args) { |
| GetValue<int64_t>(args); |
| } |
| |
| void GetUint64(const FunctionCallbackInfo<Value>& args) { |
| GetValue<uint64_t>(args); |
| } |
| |
| void GetFloat32(const FunctionCallbackInfo<Value>& args) { |
| GetValue<float>(args); |
| } |
| |
| void GetFloat64(const FunctionCallbackInfo<Value>& args) { |
| GetValue<double>(args); |
| } |
| |
| void SetInt8(const FunctionCallbackInfo<Value>& args) { |
| SetValue<int8_t>(args); |
| } |
| |
| void SetUint8(const FunctionCallbackInfo<Value>& args) { |
| SetValue<uint8_t>(args); |
| } |
| |
| void SetInt16(const FunctionCallbackInfo<Value>& args) { |
| SetValue<int16_t>(args); |
| } |
| |
| void SetUint16(const FunctionCallbackInfo<Value>& args) { |
| SetValue<uint16_t>(args); |
| } |
| |
| void SetInt32(const FunctionCallbackInfo<Value>& args) { |
| SetValue<int32_t>(args); |
| } |
| |
| void SetUint32(const FunctionCallbackInfo<Value>& args) { |
| SetValue<uint32_t>(args); |
| } |
| |
| void SetInt64(const FunctionCallbackInfo<Value>& args) { |
| SetValue<int64_t>(args); |
| } |
| |
| void SetUint64(const FunctionCallbackInfo<Value>& args) { |
| SetValue<uint64_t>(args); |
| } |
| |
| void SetFloat32(const FunctionCallbackInfo<Value>& args) { |
| SetValue<float>(args); |
| } |
| |
| void SetFloat64(const FunctionCallbackInfo<Value>& args) { |
| SetValue<double>(args); |
| } |
| |
| void ToString(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| Isolate* isolate = env->isolate(); |
| |
| // Raw FFI memory helpers are low-level and unsafe. Pointer validity, |
| // lifetime, and zero-copy writable views are the caller's responsibility. |
| // `toString()` requires a valid NUL-terminated C string pointer. |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| |
| if (args.Length() < 1 || !args[0]->IsBigInt()) { |
| THROW_ERR_INVALID_ARG_TYPE(env, "The first argument must be a bigint"); |
| return; |
| } |
| |
| uintptr_t ptr; |
| if (!GetValidatedPointerAddress(env, args[0], "first argument").To(&ptr)) { |
| return; |
| } |
| |
| if (ptr == 0) { |
| args.GetReturnValue().SetNull(); |
| return; |
| } |
| |
| const char* str = reinterpret_cast<const char*>(ptr); |
| size_t len = std::strlen(str); |
| if (ValidateStringLength(env, len).IsNothing()) { |
| return; |
| } |
| |
| Local<String> out; |
| if (!String::NewFromUtf8(isolate, str, NewStringType::kNormal, len) |
| .ToLocal(&out)) { |
| return; |
| } |
| |
| args.GetReturnValue().Set(out); |
| } |
| |
| void ToBuffer(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| Isolate* isolate = env->isolate(); |
| |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| |
| // `copy === false` exposes a zero-copy writable view over foreign memory. |
| // This is intentionally unsafe and the caller must guarantee pointer |
| // validity, lifetime, and bounds. |
| |
| if (args.Length() < 1 || !args[0]->IsBigInt()) { |
| THROW_ERR_INVALID_ARG_TYPE(env, "The first argument must be a bigint"); |
| return; |
| } |
| |
| uintptr_t ptr; |
| if (!GetValidatedPointerAddress(env, args[0], "first argument").To(&ptr)) { |
| return; |
| } |
| |
| size_t len; |
| if (args.Length() < 2 || !GetValidatedSize(env, args[1], "length").To(&len)) { |
| return; |
| } |
| |
| if (ptr == 0 && len > 0) { |
| THROW_ERR_FFI_INVALID_POINTER(env, |
| "Cannot create a buffer from a null pointer"); |
| return; |
| } |
| |
| if (ValidatePointerSpan( |
| env, |
| ptr, |
| 0, |
| len, |
| "The pointer and length exceed the platform address range") |
| .IsNothing()) { |
| return; |
| } |
| |
| if (ValidateBufferLength(env, len).IsNothing()) { |
| return; |
| } |
| |
| Local<Object> buf; |
| if (args.Length() < 3 || args[2]->IsUndefined() || |
| args[2]->BooleanValue(isolate)) { |
| if (!Buffer::Copy(env, reinterpret_cast<char*>(ptr), len).ToLocal(&buf)) { |
| return; |
| } |
| } else { |
| if (!Buffer::New( |
| env, |
| reinterpret_cast<char*>(ptr), |
| len, |
| [](char* data, void* hint) {}, |
| nullptr) |
| .ToLocal(&buf)) { |
| return; |
| } |
| } |
| |
| args.GetReturnValue().Set(buf); |
| } |
| |
| void ToArrayBuffer(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| Isolate* isolate = env->isolate(); |
| |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| |
| if (args.Length() < 1 || !args[0]->IsBigInt()) { |
| THROW_ERR_INVALID_ARG_TYPE(env, "The first argument must be a bigint"); |
| return; |
| } |
| |
| uintptr_t ptr; |
| if (!GetValidatedPointerAddress(env, args[0], "first argument").To(&ptr)) { |
| return; |
| } |
| |
| size_t len; |
| if (args.Length() < 2 || !GetValidatedSize(env, args[1], "length").To(&len)) { |
| return; |
| } |
| |
| if (ptr == 0 && len > 0) { |
| THROW_ERR_FFI_INVALID_POINTER( |
| env, "Cannot create an ArrayBuffer from a null pointer"); |
| return; |
| } |
| |
| if (ValidatePointerSpan( |
| env, |
| ptr, |
| 0, |
| len, |
| "The pointer and length exceed the platform address range") |
| .IsNothing()) { |
| return; |
| } |
| |
| if (ValidateBufferLength(env, len).IsNothing()) { |
| return; |
| } |
| |
| Local<ArrayBuffer> ab; |
| |
| if (args.Length() < 3 || args[2]->IsUndefined() || |
| args[2]->BooleanValue(isolate)) { |
| std::unique_ptr<BackingStore> store = |
| ArrayBuffer::NewBackingStore(isolate, len); |
| memcpy(store->Data(), reinterpret_cast<void*>(ptr), len); |
| ab = ArrayBuffer::New(isolate, std::move(store)); |
| } else { |
| std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore( |
| reinterpret_cast<void*>(ptr), |
| len, |
| [](void* data, size_t length, void* deleter_data) {}, |
| nullptr); |
| |
| ab = ArrayBuffer::New(isolate, std::move(store)); |
| } |
| |
| args.GetReturnValue().Set(ab); |
| } |
| |
| void ExportBytes(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| |
| if (args.Length() < 1) { |
| THROW_ERR_INVALID_ARG_TYPE( |
| env, |
| "The first argument must be a Buffer, ArrayBuffer, or ArrayBufferView"); |
| return; |
| } |
| |
| // This needs to be kept alive until the data |
| // is actually copied. |
| ArrayBufferViewContents<uint8_t> view; |
| |
| if (args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() || |
| args[0]->IsArrayBufferView()) { |
| view.ReadValue(args[0]); |
| if (view.WasDetached()) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBufferView backing store"); |
| return; |
| } |
| } else { |
| THROW_ERR_INVALID_ARG_TYPE( |
| env, |
| "The first argument must be a Buffer, ArrayBuffer, or ArrayBufferView"); |
| return; |
| } |
| |
| uintptr_t ptr; |
| if (args.Length() < 2 || |
| !GetValidatedPointerAddress(env, args[1], "pointer").To(&ptr)) { |
| return; |
| } |
| |
| size_t len; |
| if (args.Length() < 3 || !GetValidatedSize(env, args[2], "length").To(&len)) { |
| return; |
| } |
| |
| if (len < view.length()) { |
| THROW_ERR_OUT_OF_RANGE(env, "The length must be >= source byte length"); |
| return; |
| } |
| |
| if (ptr == 0 && view.length() > 0) { |
| THROW_ERR_FFI_INVALID_POINTER(env, |
| "Cannot create a buffer from a null pointer"); |
| return; |
| } |
| |
| if (ValidatePointerSpan( |
| env, |
| ptr, |
| 0, |
| len, |
| "The pointer and length exceed the platform address range") |
| .IsNothing()) { |
| return; |
| } |
| |
| std::memcpy(reinterpret_cast<void*>(ptr), view.data(), view.length()); |
| } |
| |
| void GetRawPointer(const FunctionCallbackInfo<Value>& args) { |
| Environment* env = Environment::GetCurrent(args); |
| Isolate* isolate = env->isolate(); |
| |
| THROW_IF_INSUFFICIENT_PERMISSIONS(env, permission::PermissionScope::kFFI, ""); |
| |
| if (args.Length() < 1) { |
| THROW_ERR_INVALID_ARG_TYPE( |
| env, |
| "The first argument must be a Buffer, ArrayBuffer, or ArrayBufferView"); |
| return; |
| } |
| |
| uintptr_t ptr = 0; |
| size_t offset = 0; |
| std::shared_ptr<BackingStore> store; |
| |
| if (args[0]->IsArrayBuffer()) { |
| store = args[0].As<ArrayBuffer>()->GetBackingStore(); |
| } else if (args[0]->IsSharedArrayBuffer()) { |
| store = args[0].As<SharedArrayBuffer>()->GetBackingStore(); |
| } else if (args[0]->IsArrayBufferView()) { |
| // Access the store here to ensure that it exists. Small typed arrays |
| // may not have a store until this point and can instead be stored |
| // entirely in-heap. |
| store = args[0].As<ArrayBufferView>()->Buffer()->GetBackingStore(); |
| offset = args[0].As<ArrayBufferView>()->ByteOffset(); |
| } else { |
| THROW_ERR_INVALID_ARG_TYPE(env, |
| "The first argument must be a Buffer, " |
| "ArrayBuffer, or ArrayBufferView"); |
| return; |
| } |
| |
| // WARNING: There is no inherent guarantee that the pointer returned |
| // from this function will be valid beyond the lifetime of the BackingStore |
| // instance! |
| if (!store) { |
| THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ArrayBuffer backing store"); |
| return; |
| } |
| ptr = reinterpret_cast<uintptr_t>(store->Data()) + offset; |
| |
| args.GetReturnValue().Set( |
| BigInt::NewFromUnsigned(isolate, static_cast<uint64_t>(ptr))); |
| } |
| |
| } // namespace ffi |
| |
| } // namespace node |
| |
| #endif // HAVE_FFI |