From 584807b4f5e1b831785c79eef5367f737589f29b Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Wed, 14 Dec 2022 16:06:04 +0000 Subject: [PATCH] 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. --- docs/changelog.md | 3 +++ src/common.cc | 8 ++++++++ src/common.h | 1 + src/metadata.cc | 10 +++++----- src/pipeline.cc | 4 ++-- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index cece1b13b..cbbff9f94 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -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) diff --git a/src/common.cc b/src/common.cc index c617cc710..99602f8ea 100644 --- a/src/common.cc +++ b/src/common.cc @@ -76,6 +76,14 @@ namespace sharp { } return vector; } + Napi::Buffer NewOrCopyBuffer(Napi::Env env, char* data, size_t len) { + try { + return Napi::Buffer::New(env, data, len, FreeCallback); + } catch (Napi::Error const &err) {} + Napi::Buffer buf = Napi::Buffer::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) { diff --git a/src/common.h b/src/common.h index d7cbe32de..5995f977d 100644 --- a/src/common.h +++ b/src/common.h @@ -133,6 +133,7 @@ namespace sharp { return static_cast( vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data())); } + Napi::Buffer 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); diff --git a/src/metadata.cc b/src/metadata.cc index 1dd01047b..09e9c931b 100644 --- a/src/metadata.cc +++ b/src/metadata.cc @@ -235,20 +235,20 @@ class MetadataWorker : public Napi::AsyncWorker { info.Set("orientation", baton->orientation); } if (baton->exifLength > 0) { - info.Set("exif", Napi::Buffer::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::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::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::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::New(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback)); + sharp::NewOrCopyBuffer(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength)); } Callback().MakeCallback(Receiver().Value(), { env.Null(), info }); } else { diff --git a/src/pipeline.cc b/src/pipeline.cc index 115a1b908..350689490 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -1206,8 +1206,8 @@ class PipelineWorker : public Napi::AsyncWorker { // Add buffer size to info info.Set("size", static_cast(baton->bufferOutLength)); // Pass ownership of output data to Buffer instance - Napi::Buffer data = Napi::Buffer::New(env, static_cast(baton->bufferOut), - baton->bufferOutLength, sharp::FreeCallback); + Napi::Buffer data = sharp::NewOrCopyBuffer(env, static_cast(baton->bufferOut), + baton->bufferOutLength); Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info }); } else { // Add file size to info