<commit_before>#import <Foundation/Foundation.h>

#include <objc/runtime.h>

#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/Metadata.h"

@interface NSKeyedUnarchiver (SwiftAdditions)
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)cls
                                       operation:(int)operation
     NS_SWIFT_NAME(_swift_checkClassAndWarnForKeyedArchiving(_:operation:));
@end

static bool isASCIIIdentifierChar(char c) {
  if (c >= 'a' && c <= 'z') return true;
  if (c >= 'A' && c <= 'Z') return true;
  if (c >= '0' && c <= '9') return true;
  if (c == '_') return true;
  if (c == '$') return true;
  return false;
}

static void logIfFirstOccurrence(Class objcClass, void (^log)(void)) {
  static auto queue = dispatch_queue_create(
      "SwiftFoundation._checkClassAndWarnForKeyedArchivingQueue",
      DISPATCH_QUEUE_SERIAL);
  static NSHashTable *seenClasses = nil;

  dispatch_sync(queue, ^{
    // Will be NO when seenClasses is still nil.
    if ([seenClasses containsObject:objcClass])
      return;

    if (!seenClasses) {
      NSPointerFunctionsOptions options = 0;
      options |= NSPointerFunctionsOpaqueMemory;
      options |= NSPointerFunctionsObjectPointerPersonality;
      seenClasses = [[NSHashTable alloc] initWithOptions:options capacity:16];
    }
    [seenClasses addObject:objcClass];

    // Synchronize logging so that multiple lines aren't interleaved.
    log();
  });
}

namespace {
  class StringRefLite {
    StringRefLite(const char *data, size_t len) : data(data), length(len) {}
  public:
    const char *data;
    size_t length;

    StringRefLite() : data(nullptr), length(0) {}

    template <size_t N>
    StringRefLite(const char (&staticStr)[N]) : data(staticStr), length(N) {}

    StringRefLite(swift::TwoWordPair<const char *, uintptr_t> pair)
        : data(pair.first), length(pair.second) {}

    NS_RETURNS_RETAINED
    NSString *newNSStringNoCopy() const {
      return [[NSString alloc] initWithBytesNoCopy:const_cast<char *>(data)
                                            length:length
                                          encoding:NSUTF8StringEncoding
                                      freeWhenDone:NO];
    }

    const char &operator[](size_t offset) const {
      assert(offset < length);
      return data[offset];
    }

    StringRefLite slice(size_t from, size_t to) const {
      assert(from <= to);
      assert(to <= length);
      return {data + from, to - from};
    }

    const char *begin() const {
      return data;
    }
    const char *end() const {
      return data + length;
    }
  };
}

/// Assume that a non-generic demangled class name always ends in ".MyClass"
/// or ".(MyClass plus extra info)".
static StringRefLite findBaseName(StringRefLite demangledName) {
  size_t end = demangledName.length;
  size_t parenCount = 0;
  for (size_t i = end; i != 0; --i) {
    switch (demangledName[i - 1]) {
    case '.':
      if (parenCount == 0) {
        if (i != end && demangledName[i] == '(')
          ++i;
        return demangledName.slice(i, end);
      }
      break;
    case ')':
      parenCount += 1;
      break;
    case '(':
      if (parenCount > 0)
        parenCount -= 1;
      break;
    case ' ':
      end = i - 1;
      break;
    default:
      break;
    }
  }
  return {};
}

@implementation NSKeyedUnarchiver (SwiftAdditions)

/// Checks if class \p objcClass is good for archiving.
///
/// If not, a runtime warning is printed.
///
/// \param operation Specifies the archiving operation. Valid operations are:
/// 0:       archiving
/// 1:       unarchiving
/// \return Returns the status
/// 0:       not a problem class (either non-Swift or has an explicit name)
/// 1:       a Swift generic class
/// 2:       a Swift non-generic class where adding @objc is valid
/// Future versions of this API will return nonzero values for additional cases
/// that mean the class shouldn't be archived.
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)objcClass
                                       operation:(int)operation {
  using namespace swift;
  const ClassMetadata *theClass = (ClassMetadata *)objcClass;

  // Is it a (real) swift class?
  if (!theClass->isTypeMetadata() || theClass->isArtificialSubclass())
    return 0;

  // Does the class already have a custom name?
  if (theClass->getFlags() & ClassFlags::HasCustomObjCName)
    return 0;

  // Is it a mangled name?
  const char *className = class_getName(objcClass);
  if (!(className[0] == '_' && className[1] == 'T'))
    return 0;
  // Is it a name in the form <module>.<class>? Note: the module name could
  // start with "_T".
  if (strchr(className, '.'))
    return 0;

  // Is it a generic class?
  if (theClass->getDescription()->GenericParams.isGeneric()) {
    logIfFirstOccurrence(objcClass, ^{
      // Use actual NSStrings to force UTF-8.
      StringRefLite demangledName = swift_getTypeName(theClass,
                                                      /*qualified*/true);
      NSString *demangledString = demangledName.newNSStringNoCopy();
      NSString *mangledString = NSStringFromClass(objcClass);
      switch (operation) {
      case 1:
        NSLog(@"Attempting to unarchive generic Swift class '%@' with mangled "
               "runtime name '%@'. Runtime names for generic classes are "
               "unstable and may change in the future, leading to "
               "non-decodable data.", demangledString, mangledString);
        break;
      default:
        NSLog(@"Attempting to archive generic Swift class '%@' with mangled "
               "runtime name '%@'. Runtime names for generic classes are "
               "unstable and may change in the future, leading to "
               "non-decodable data.", demangledString, mangledString);
        break;
      }
      NSLog(@"To avoid this failure, create a concrete subclass and register "
             "it with NSKeyedUnarchiver.setClass(_:forClassName:) instead, "
             "using the name \"%@\".", mangledString);
      NSLog(@"If you need to produce archives compatible with older versions "
             "of your program, use NSKeyedArchiver.setClassName(_:for:) "
             "as well.");
      [demangledString release];
    });
    return 1;
  }

  // It's a swift class with a (compiler generated) mangled name, which should
  // be written into an NSArchive.
  logIfFirstOccurrence(objcClass, ^{
    // Use actual NSStrings to force UTF-8.
    StringRefLite demangledName = swift_getTypeName(theClass,/*qualified*/true);
    NSString *demangledString = demangledName.newNSStringNoCopy();
    NSString *mangledString = NSStringFromClass(objcClass);
    switch (operation) {
    case 1:
      NSLog(@"Attempting to unarchive Swift class '%@' with mangled runtime "
             "name '%@'. The runtime name for this class is unstable and may "
             "change in the future, leading to non-decodable data.",
             demangledString, mangledString);
      break;
    default:
      NSLog(@"Attempting to archive Swift class '%@' with mangled runtime "
             "name '%@'. The runtime name for this class is unstable and may "
             "change in the future, leading to non-decodable data.",
             demangledString, mangledString);
      break;
    }
    [demangledString release];
    NSLog(@"You can use the 'objc' attribute to ensure that the name will not "
           "change: \"@objc(%@)\"", mangledString);

    StringRefLite baseName = findBaseName(demangledName);
    // Offer a more generic message if the base name we found doesn't look like
    // an ASCII identifier. This avoids printing names like "ABCモデル".
    if (baseName.length == 0 ||
        !std::all_of(baseName.begin(), baseName.end(), isASCIIIdentifierChar)) {
      baseName = "MyModel";
    }

    NSLog(@"If there are no existing archives containing this class, you "
           "should choose a unique, prefixed name instead: "
           "\"@objc(ABC%1$.*2$s)\"", baseName.data, (int)baseName.length);
  });
  return 2;
}
@end
<commit_msg>Fix up 08c2a7a3ac for 32-bit platforms. (#10433)<commit_after>#import <Foundation/Foundation.h>

#include <objc/runtime.h>

#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/Metadata.h"

@interface NSKeyedUnarchiver (SwiftAdditions)
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)cls
                                       operation:(int)operation
     NS_SWIFT_NAME(_swift_checkClassAndWarnForKeyedArchiving(_:operation:));
@end

static bool isASCIIIdentifierChar(char c) {
  if (c >= 'a' && c <= 'z') return true;
  if (c >= 'A' && c <= 'Z') return true;
  if (c >= '0' && c <= '9') return true;
  if (c == '_') return true;
  if (c == '$') return true;
  return false;
}

static void logIfFirstOccurrence(Class objcClass, void (^log)(void)) {
  static auto queue = dispatch_queue_create(
      "SwiftFoundation._checkClassAndWarnForKeyedArchivingQueue",
      DISPATCH_QUEUE_SERIAL);
  static NSHashTable *seenClasses = nil;

  dispatch_sync(queue, ^{
    // Will be NO when seenClasses is still nil.
    if ([seenClasses containsObject:objcClass])
      return;

    if (!seenClasses) {
      NSPointerFunctionsOptions options = 0;
      options |= NSPointerFunctionsOpaqueMemory;
      options |= NSPointerFunctionsObjectPointerPersonality;
      seenClasses = [[NSHashTable alloc] initWithOptions:options capacity:16];
    }
    [seenClasses addObject:objcClass];

    // Synchronize logging so that multiple lines aren't interleaved.
    log();
  });
}

namespace {
  class StringRefLite {
    StringRefLite(const char *data, size_t len) : data(data), length(len) {}
  public:
    const char *data;
    size_t length;

    StringRefLite() : data(nullptr), length(0) {}

    template <size_t N>
    StringRefLite(const char (&staticStr)[N]) : data(staticStr), length(N) {}

    StringRefLite(swift::TwoWordPair<const char *, uintptr_t>::Return rawValue)
        : data(swift::TwoWordPair<const char *, uintptr_t>(rawValue).first),
          length(swift::TwoWordPair<const char *, uintptr_t>(rawValue).second){}

    NS_RETURNS_RETAINED
    NSString *newNSStringNoCopy() const {
      return [[NSString alloc] initWithBytesNoCopy:const_cast<char *>(data)
                                            length:length
                                          encoding:NSUTF8StringEncoding
                                      freeWhenDone:NO];
    }

    const char &operator[](size_t offset) const {
      assert(offset < length);
      return data[offset];
    }

    StringRefLite slice(size_t from, size_t to) const {
      assert(from <= to);
      assert(to <= length);
      return {data + from, to - from};
    }

    const char *begin() const {
      return data;
    }
    const char *end() const {
      return data + length;
    }
  };
}

/// Assume that a non-generic demangled class name always ends in ".MyClass"
/// or ".(MyClass plus extra info)".
static StringRefLite findBaseName(StringRefLite demangledName) {
  size_t end = demangledName.length;
  size_t parenCount = 0;
  for (size_t i = end; i != 0; --i) {
    switch (demangledName[i - 1]) {
    case '.':
      if (parenCount == 0) {
        if (i != end && demangledName[i] == '(')
          ++i;
        return demangledName.slice(i, end);
      }
      break;
    case ')':
      parenCount += 1;
      break;
    case '(':
      if (parenCount > 0)
        parenCount -= 1;
      break;
    case ' ':
      end = i - 1;
      break;
    default:
      break;
    }
  }
  return {};
}

@implementation NSKeyedUnarchiver (SwiftAdditions)

/// Checks if class \p objcClass is good for archiving.
///
/// If not, a runtime warning is printed.
///
/// \param operation Specifies the archiving operation. Valid operations are:
/// 0:       archiving
/// 1:       unarchiving
/// \return Returns the status
/// 0:       not a problem class (either non-Swift or has an explicit name)
/// 1:       a Swift generic class
/// 2:       a Swift non-generic class where adding @objc is valid
/// Future versions of this API will return nonzero values for additional cases
/// that mean the class shouldn't be archived.
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)objcClass
                                       operation:(int)operation {
  using namespace swift;
  const ClassMetadata *theClass = (ClassMetadata *)objcClass;

  // Is it a (real) swift class?
  if (!theClass->isTypeMetadata() || theClass->isArtificialSubclass())
    return 0;

  // Does the class already have a custom name?
  if (theClass->getFlags() & ClassFlags::HasCustomObjCName)
    return 0;

  // Is it a mangled name?
  const char *className = class_getName(objcClass);
  if (!(className[0] == '_' && className[1] == 'T'))
    return 0;
  // Is it a name in the form <module>.<class>? Note: the module name could
  // start with "_T".
  if (strchr(className, '.'))
    return 0;

  // Is it a generic class?
  if (theClass->getDescription()->GenericParams.isGeneric()) {
    logIfFirstOccurrence(objcClass, ^{
      // Use actual NSStrings to force UTF-8.
      StringRefLite demangledName = swift_getTypeName(theClass,
                                                      /*qualified*/true);
      NSString *demangledString = demangledName.newNSStringNoCopy();
      NSString *mangledString = NSStringFromClass(objcClass);
      switch (operation) {
      case 1:
        NSLog(@"Attempting to unarchive generic Swift class '%@' with mangled "
               "runtime name '%@'. Runtime names for generic classes are "
               "unstable and may change in the future, leading to "
               "non-decodable data.", demangledString, mangledString);
        break;
      default:
        NSLog(@"Attempting to archive generic Swift class '%@' with mangled "
               "runtime name '%@'. Runtime names for generic classes are "
               "unstable and may change in the future, leading to "
               "non-decodable data.", demangledString, mangledString);
        break;
      }
      NSLog(@"To avoid this failure, create a concrete subclass and register "
             "it with NSKeyedUnarchiver.setClass(_:forClassName:) instead, "
             "using the name \"%@\".", mangledString);
      NSLog(@"If you need to produce archives compatible with older versions "
             "of your program, use NSKeyedArchiver.setClassName(_:for:) "
             "as well.");
      [demangledString release];
    });
    return 1;
  }

  // It's a swift class with a (compiler generated) mangled name, which should
  // be written into an NSArchive.
  logIfFirstOccurrence(objcClass, ^{
    // Use actual NSStrings to force UTF-8.
    StringRefLite demangledName = swift_getTypeName(theClass,/*qualified*/true);
    NSString *demangledString = demangledName.newNSStringNoCopy();
    NSString *mangledString = NSStringFromClass(objcClass);
    switch (operation) {
    case 1:
      NSLog(@"Attempting to unarchive Swift class '%@' with mangled runtime "
             "name '%@'. The runtime name for this class is unstable and may "
             "change in the future, leading to non-decodable data.",
             demangledString, mangledString);
      break;
    default:
      NSLog(@"Attempting to archive Swift class '%@' with mangled runtime "
             "name '%@'. The runtime name for this class is unstable and may "
             "change in the future, leading to non-decodable data.",
             demangledString, mangledString);
      break;
    }
    [demangledString release];
    NSLog(@"You can use the 'objc' attribute to ensure that the name will not "
           "change: \"@objc(%@)\"", mangledString);

    StringRefLite baseName = findBaseName(demangledName);
    // Offer a more generic message if the base name we found doesn't look like
    // an ASCII identifier. This avoids printing names like "ABCモデル".
    if (baseName.length == 0 ||
        !std::all_of(baseName.begin(), baseName.end(), isASCIIIdentifierChar)) {
      baseName = "MyModel";
    }

    NSLog(@"If there are no existing archives containing this class, you "
           "should choose a unique, prefixed name instead: "
           "\"@objc(ABC%1$.*2$s)\"", baseName.data, (int)baseName.length);
  });
  return 2;
}
@end

{'fraction_non_alphanumeric': 0.11443488943488943, 'fraction_numerical': 0.005036855036855037, 'mean_word_length': 2.62283044058745, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6635554', 'n_tokens_mistral': 4896, 'n_tokens_neox': 4672, 'n_words': 1422}