Add runtime detection of V8 memory cage #3384

When using the V8 memory cage, Buffers cannot be wrapped and then
later freed via a callback. When the cage is detected via a throw,
instead fall back to copying Buffer contents to V8 memory.

This approach will be used by Electron 21+ and you should expect
reduced performance and increased memory consumption/fragmentation.
This commit is contained in:
Lovell Fuller 2022-12-14 16:06:04 +00:00
parent a7fa7014ef
commit 584807b4f5
5 changed files with 19 additions and 7 deletions

View File

@ -9,6 +9,9 @@ Requires libvips v8.13.3
* Add experimental support for JPEG-XL images. Requires libvips compiled with libjxl.
[#2731](https://github.com/lovell/sharp/issues/2731)
* Add runtime detection of V8 memory cage, ensures compatibility with Electron 21 onwards.
[#3384](https://github.com/lovell/sharp/issues/3384)
* Expose `interFrameMaxError` and `interPaletteMaxError` GIF optimisation properties.
[#3401](https://github.com/lovell/sharp/issues/3401)

View File

@ -76,6 +76,14 @@ namespace sharp {
}
return vector;
}
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len) {
try {
return Napi::Buffer<char>::New(env, data, len, FreeCallback);
} catch (Napi::Error const &err) {}
Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, data, len);
FreeCallback(nullptr, data);
return buf;
}
// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input) {

View File

@ -133,6 +133,7 @@ namespace sharp {
return static_cast<T>(
vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data()));
}
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len);
// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input);

View File

@ -235,20 +235,20 @@ class MetadataWorker : public Napi::AsyncWorker {
info.Set("orientation", baton->orientation);
}
if (baton->exifLength > 0) {
info.Set("exif", Napi::Buffer<char>::New(env, baton->exif, baton->exifLength, sharp::FreeCallback));
info.Set("exif", sharp::NewOrCopyBuffer(env, baton->exif, baton->exifLength));
}
if (baton->iccLength > 0) {
info.Set("icc", Napi::Buffer<char>::New(env, baton->icc, baton->iccLength, sharp::FreeCallback));
info.Set("icc", sharp::NewOrCopyBuffer(env, baton->icc, baton->iccLength));
}
if (baton->iptcLength > 0) {
info.Set("iptc", Napi::Buffer<char>::New(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
info.Set("iptc", sharp::NewOrCopyBuffer(env, baton->iptc, baton->iptcLength));
}
if (baton->xmpLength > 0) {
info.Set("xmp", Napi::Buffer<char>::New(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
info.Set("xmp", sharp::NewOrCopyBuffer(env, baton->xmp, baton->xmpLength));
}
if (baton->tifftagPhotoshopLength > 0) {
info.Set("tifftagPhotoshop",
Napi::Buffer<char>::New(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback));
sharp::NewOrCopyBuffer(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength));
}
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
} else {

View File

@ -1206,8 +1206,8 @@ class PipelineWorker : public Napi::AsyncWorker {
// Add buffer size to info
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
// Pass ownership of output data to Buffer instance
Napi::Buffer<char> data = Napi::Buffer<char>::New(env, static_cast<char*>(baton->bufferOut),
baton->bufferOutLength, sharp::FreeCallback);
Napi::Buffer<char> data = sharp::NewOrCopyBuffer(env, static_cast<char*>(baton->bufferOut),
baton->bufferOutLength);
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
} else {
// Add file size to info