<commit_before>//******************************************************************************
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//******************************************************************************

#import "NSKeyValueObserving-Internal.h"

#import <objc/encoding.h>

static Class NSKVO$class(id self, SEL);
static Class NSKVO$meta$superclass(Class self, SEL);
static void NSKVO$setObject$forKey$(id self, SEL _cmd, id object, NSString* key);
static void NSKVO$removeObjectForKey$(id self, SEL _cmd, NSString* key);
static void NSKVO$dealloc(id self, SEL);

namespace std {
// These specializations allow for the use of SEL in STL collections.
template <>
struct equal_to<SEL> {
    bool operator()(const SEL& lhs, const SEL& rhs) const {
        return sel_isEqual(lhs, rhs) == YES;
    }
};

template <>
struct hash<SEL> {
    std::size_t operator()(const SEL& s) const {
        return std::hash<decltype(sel_getName(s))>()(sel_getName(s));
    }
};
}

namespace {

// Swizzling stuff
struct NSKVOSwizzledMethod {
    std::string key;
    size_t valueSize;
    IMP origImp;
    NSKVOSwizzledMethod(const std::string& key) : key(key), valueSize(0), origImp(nullptr) {
    }
    NSKVOSwizzledMethod(const std::string& key, const char* objcType, IMP origImp)
        : key(key), valueSize(objc_sizeof_type(objcType)), origImp(origImp) {
    }
    NSKVOSwizzledMethod(IMP origImp) : origImp(origImp) {
    }
    template <typename... Args>
    void callOriginal(id self, SEL _cmd, Args... args) {
        ((void (*)(id, SEL, Args...))origImp)(self, _cmd, std::forward<Args>(args)...);
    }
    template <typename... Args>
    void callOriginalVariadic(id self, SEL _cmd, Args... args) {
        ((void (*)(id, SEL, ...))origImp)(self, _cmd, std::forward<Args>(args)...);
    }
};

class NSKVOSwizzledClass : public NSKVOClass {
private:
    std::unordered_map<SEL, NSKVOSwizzledMethod> _selectorMethodInfo;
    std::recursive_mutex _mutex;
    std::function<void(id)> _deallocInstance;

public:
    NSKVOSwizzledClass(Class originalClass, Class notifyingClass)
        : NSKVOClass(originalClass, notifyingClass), _selectorMethodInfo(), _mutex() {
        auto deallocIMP = (void (*)(id, SEL))class_getMethodImplementation(originalClass, @selector(dealloc));
        _deallocInstance = std::bind(deallocIMP, std::placeholders::_1, @selector(dealloc));
    }
    NSKVOSwizzledMethod& swizzledMethod(SEL sel) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        return _selectorMethodInfo.at(sel);
    }

    void dealloc(id instance) override {
        _deallocInstance(instance);
        NSKVOClass::dealloc(instance);
    }

    // Two entrypoints into method swizzling: one key-based, one imp-based.
    void swizzleMethod(SEL sel, IMP newImp);
    void ensureKeyWillNotify(const std::string& key) override;

private:
    void _swizzleMethodWithMethodInfo(SEL sel, IMP newImp, NSKVOSwizzledMethod&& methodInfo);
};
}

// NSKVOSwizzledNotifyingClassForClass constructs at runtime a notifying version of a given class.
// happen only under lock.
Class NSKVOSwizzledNotifyingClassForClass(Class originalClass) {
    const char* name = class_getName(originalClass);
    auto newName(woc::string::format("_NSKVONotifying_%s", name));
    Class notifyingClass = objc_allocateClassPair(originalClass, newName.c_str(), 0);
    Class notifyingMetaclass = object_getClass(notifyingClass);

    auto swizzledClassInfo(std::shared_ptr<NSKVOSwizzledClass>(new NSKVOSwizzledClass(originalClass, notifyingClass)));

    class_replaceMethod(notifyingClass, @selector(dealloc), reinterpret_cast<IMP>(NSKVO$dealloc), "v@:");
    class_replaceMethod(notifyingClass, @selector(class), reinterpret_cast<IMP>(NSKVO$class), "#@:");
    class_replaceMethod(notifyingMetaclass, @selector(superclass), reinterpret_cast<IMP>(NSKVO$meta$superclass), "##:");

    swizzledClassInfo->swizzleMethod(@selector(setObject:forKey:), reinterpret_cast<IMP>(NSKVO$setObject$forKey$));
    swizzledClassInfo->swizzleMethod(@selector(removeObjectForKey:), reinterpret_cast<IMP>(NSKVO$removeObjectForKey$));

    objc_registerClassPair(notifyingClass);

    NSKVOClass::registerClassPair(notifyingClass, originalClass, swizzledClassInfo);
    return notifyingClass;
}

template <typename T>
void notifyingSetImpl(id self, SEL _cmd, T val) {
    // These dynamic casts should never fail: we only have these methods
    // on classes we created a NSKVOSwizzledClass for.
    NSKVOSwizzledClass& trailer(dynamic_cast<NSKVOSwizzledClass&>(NSKVOClass::forInstance(self)));
    auto& obMethod(trailer.swizzledMethod(_cmd));
    NSString* key = [NSString stringWithUTF8String:obMethod.key.c_str()];

    [self willChangeValueForKey:key];
    obMethod.callOriginal(self, _cmd, val);
    [self didChangeValueForKey:key];
}

void notifyingVariadicSetImpl(id self, SEL _cmd, ...) {
    NSKVOSwizzledClass& trailer(dynamic_cast<NSKVOSwizzledClass&>(NSKVOClass::forInstance(self)));
    auto& obMethod(trailer.swizzledMethod(_cmd));
    auto argSz = obMethod.valueSize;
    auto nStackArgs = argSz / sizeof(unsigned);
    unsigned* raw = static_cast<unsigned*>(IwCalloc(sizeof(unsigned), nStackArgs));
    va_list ap;
    va_start(ap, _cmd);
    for (unsigned int i = 0; i < nStackArgs; ++i) {
        raw[i] = va_arg(ap, unsigned);
    }
    va_end(ap);

    NSString* key = [NSString stringWithUTF8String:obMethod.key.c_str()];

    [self willChangeValueForKey:key];

    switch (nStackArgs) {
        case 1:
            obMethod.callOriginalVariadic(self, _cmd, raw[0]);
            break;
        case 2:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1]);
            break;
        case 3:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2]);
            break;
        case 4:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2], raw[3]);
            break;
        case 5:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2], raw[3], raw[4]);
            break;
        case 6:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2], raw[3], raw[4], raw[5]);
            break;
    }

    [self didChangeValueForKey:key];
}

void NSKVOSwizzledClass::swizzleMethod(SEL sel, IMP newImp) {
    std::lock_guard<std::recursive_mutex> lock(_mutex);
    if (_selectorMethodInfo.find(sel) != _selectorMethodInfo.end()) {
        return;
    }

    auto method = class_getInstanceMethod(originalClass, sel);
    if (!method) {
        return;
    }
    auto origImp = method_getImplementation(method);
    _swizzleMethodWithMethodInfo(sel, newImp, NSKVOSwizzledMethod{ origImp });
}

void NSKVOSwizzledClass::_swizzleMethodWithMethodInfo(SEL sel, IMP newImp, NSKVOSwizzledMethod&& methodInfo) {
    _selectorMethodInfo.emplace(std::piecewise_construct, std::forward_as_tuple(sel), std::forward_as_tuple(std::move(methodInfo)));<commit_msg>6598577: Memory leak in KVO on variadic setters<commit_after>//******************************************************************************
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//******************************************************************************

#import "NSKeyValueObserving-Internal.h"

#import <objc/encoding.h>

static Class NSKVO$class(id self, SEL);
static Class NSKVO$meta$superclass(Class self, SEL);
static void NSKVO$setObject$forKey$(id self, SEL _cmd, id object, NSString* key);
static void NSKVO$removeObjectForKey$(id self, SEL _cmd, NSString* key);
static void NSKVO$dealloc(id self, SEL);

namespace std {
// These specializations allow for the use of SEL in STL collections.
template <>
struct equal_to<SEL> {
    bool operator()(const SEL& lhs, const SEL& rhs) const {
        return sel_isEqual(lhs, rhs) == YES;
    }
};

template <>
struct hash<SEL> {
    std::size_t operator()(const SEL& s) const {
        return std::hash<decltype(sel_getName(s))>()(sel_getName(s));
    }
};
}

namespace {

// Swizzling stuff
struct NSKVOSwizzledMethod {
    std::string key;
    size_t valueSize;
    IMP origImp;
    NSKVOSwizzledMethod(const std::string& key) : key(key), valueSize(0), origImp(nullptr) {
    }
    NSKVOSwizzledMethod(const std::string& key, const char* objcType, IMP origImp)
        : key(key), valueSize(objc_sizeof_type(objcType)), origImp(origImp) {
    }
    NSKVOSwizzledMethod(IMP origImp) : origImp(origImp) {
    }
    template <typename... Args>
    void callOriginal(id self, SEL _cmd, Args... args) {
        ((void (*)(id, SEL, Args...))origImp)(self, _cmd, std::forward<Args>(args)...);
    }
    template <typename... Args>
    void callOriginalVariadic(id self, SEL _cmd, Args... args) {
        ((void (*)(id, SEL, ...))origImp)(self, _cmd, std::forward<Args>(args)...);
    }
};

class NSKVOSwizzledClass : public NSKVOClass {
private:
    std::unordered_map<SEL, NSKVOSwizzledMethod> _selectorMethodInfo;
    std::recursive_mutex _mutex;
    std::function<void(id)> _deallocInstance;

public:
    NSKVOSwizzledClass(Class originalClass, Class notifyingClass)
        : NSKVOClass(originalClass, notifyingClass), _selectorMethodInfo(), _mutex() {
        auto deallocIMP = (void (*)(id, SEL))class_getMethodImplementation(originalClass, @selector(dealloc));
        _deallocInstance = std::bind(deallocIMP, std::placeholders::_1, @selector(dealloc));
    }
    NSKVOSwizzledMethod& swizzledMethod(SEL sel) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);
        return _selectorMethodInfo.at(sel);
    }

    void dealloc(id instance) override {
        _deallocInstance(instance);
        NSKVOClass::dealloc(instance);
    }

    // Two entrypoints into method swizzling: one key-based, one imp-based.
    void swizzleMethod(SEL sel, IMP newImp);
    void ensureKeyWillNotify(const std::string& key) override;

private:
    void _swizzleMethodWithMethodInfo(SEL sel, IMP newImp, NSKVOSwizzledMethod&& methodInfo);
};
}

// NSKVOSwizzledNotifyingClassForClass constructs at runtime a notifying version of a given class.
// happen only under lock.
Class NSKVOSwizzledNotifyingClassForClass(Class originalClass) {
    const char* name = class_getName(originalClass);
    auto newName(woc::string::format("_NSKVONotifying_%s", name));
    Class notifyingClass = objc_allocateClassPair(originalClass, newName.c_str(), 0);
    Class notifyingMetaclass = object_getClass(notifyingClass);

    auto swizzledClassInfo(std::shared_ptr<NSKVOSwizzledClass>(new NSKVOSwizzledClass(originalClass, notifyingClass)));

    class_replaceMethod(notifyingClass, @selector(dealloc), reinterpret_cast<IMP>(NSKVO$dealloc), "v@:");
    class_replaceMethod(notifyingClass, @selector(class), reinterpret_cast<IMP>(NSKVO$class), "#@:");
    class_replaceMethod(notifyingMetaclass, @selector(superclass), reinterpret_cast<IMP>(NSKVO$meta$superclass), "##:");

    swizzledClassInfo->swizzleMethod(@selector(setObject:forKey:), reinterpret_cast<IMP>(NSKVO$setObject$forKey$));
    swizzledClassInfo->swizzleMethod(@selector(removeObjectForKey:), reinterpret_cast<IMP>(NSKVO$removeObjectForKey$));

    objc_registerClassPair(notifyingClass);

    NSKVOClass::registerClassPair(notifyingClass, originalClass, swizzledClassInfo);
    return notifyingClass;
}

template <typename T>
void notifyingSetImpl(id self, SEL _cmd, T val) {
    // These dynamic casts should never fail: we only have these methods
    // on classes we created a NSKVOSwizzledClass for.
    NSKVOSwizzledClass& trailer(dynamic_cast<NSKVOSwizzledClass&>(NSKVOClass::forInstance(self)));
    auto& obMethod(trailer.swizzledMethod(_cmd));
    NSString* key = [NSString stringWithUTF8String:obMethod.key.c_str()];

    [self willChangeValueForKey:key];
    obMethod.callOriginal(self, _cmd, val);
    [self didChangeValueForKey:key];
}

void notifyingVariadicSetImpl(id self, SEL _cmd, ...) {
    NSKVOSwizzledClass& trailer(dynamic_cast<NSKVOSwizzledClass&>(NSKVOClass::forInstance(self)));
    auto& obMethod(trailer.swizzledMethod(_cmd));
    auto argSz = obMethod.valueSize;
    auto nStackArgs = argSz / sizeof(unsigned);
    unsigned* raw = static_cast<unsigned*>(IwCalloc(sizeof(unsigned), nStackArgs));
    va_list ap;
    va_start(ap, _cmd);
    for (unsigned int i = 0; i < nStackArgs; ++i) {
        raw[i] = va_arg(ap, unsigned);
    }
    va_end(ap);

    NSString* key = [NSString stringWithUTF8String:obMethod.key.c_str()];

    [self willChangeValueForKey:key];

    switch (nStackArgs) {
        case 1:
            obMethod.callOriginalVariadic(self, _cmd, raw[0]);
            break;
        case 2:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1]);
            break;
        case 3:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2]);
            break;
        case 4:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2], raw[3]);
            break;
        case 5:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2], raw[3], raw[4]);
            break;
        case 6:
            obMethod.callOriginalVariadic(self, _cmd, raw[0], raw[1], raw[2], raw[3], raw[4], raw[5]);
            break;
    }

    [self didChangeValueForKey:key];

    IwFree(raw);
}

void NSKVOSwizzledClass::swizzleMethod(SEL sel, IMP newImp) {
    std::lock_guard<std::recursive_mutex> lock(_mutex);
    if (_selectorMethodInfo.find(sel) != _selectorMethodInfo.end()) {
        return;
    }

    auto method = class_getInstanceMethod(originalClass, sel);
    if (!method) {
        return;
    }
    auto origImp = method_getImplementation(method);
    _swizzleMethodWithMethodInfo(sel, newImp, NSKVOSwizzledMethod{ origImp });
}

void NSKVOSwizzledClass::_swizzleMethodWithMethodInfo(SEL sel, IMP newImp, NSKVOSwizzledMethod&& methodInfo) {
    _selectorMethodInfo.emplace(std::piecewise_construct, std::forward_as_tuple(sel), std::forward_as_tuple(std::move(methodInfo)));
{'fraction_non_alphanumeric': 0.14204244031830238, 'fraction_numerical': 0.005371352785145889, 'mean_word_length': 4.397637795275591, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6984262', 'n_tokens_mistral': 4920, 'n_tokens_neox': 4787, 'n_words': 1077}