mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge remote-tracking branch 'origin/master' into master-next
This commit is contained in:
@@ -49,6 +49,7 @@ KNOWN_STDLIB_TYPE_DECL(Set, NominalTypeDecl, 1)
|
||||
KNOWN_STDLIB_TYPE_DECL(Sequence, NominalTypeDecl, 1)
|
||||
KNOWN_STDLIB_TYPE_DECL(Dictionary, NominalTypeDecl, 2)
|
||||
KNOWN_STDLIB_TYPE_DECL(AnyHashable, NominalTypeDecl, 0)
|
||||
KNOWN_STDLIB_TYPE_DECL(MutableCollection, ProtocolDecl, 1)
|
||||
|
||||
KNOWN_STDLIB_TYPE_DECL(PartialKeyPath, NominalTypeDecl, 1)
|
||||
KNOWN_STDLIB_TYPE_DECL(KeyPath, NominalTypeDecl, 2)
|
||||
|
||||
@@ -143,10 +143,6 @@ public:
|
||||
/// Emit compile-time diagnostics when the law of exclusivity is violated.
|
||||
bool EnforceExclusivityStatic = true;
|
||||
|
||||
/// Suppress static diagnostics for violations of exclusive access for calls
|
||||
/// to the Standard Library's swap() free function.
|
||||
bool SuppressStaticExclusivitySwap = false;
|
||||
|
||||
/// Emit checks to trap at run time when the law of exclusivity is violated.
|
||||
bool EnforceExclusivityDynamic = false;
|
||||
|
||||
|
||||
@@ -1369,8 +1369,6 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
|
||||
|= Args.hasArg(OPT_assume_parsing_unqualified_ownership_sil);
|
||||
Opts.EnableMandatorySemanticARCOpts |=
|
||||
!Args.hasArg(OPT_disable_mandatory_semantic_arc_opts);
|
||||
Opts.SuppressStaticExclusivitySwap |=
|
||||
Args.hasArg(OPT_suppress_static_exclusivity_swap);
|
||||
|
||||
if (Args.hasArg(OPT_debug_on_sil)) {
|
||||
// Derive the name of the SIL file for debugging from
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "swift/AST/Expr.h"
|
||||
#include "swift/AST/Stmt.h"
|
||||
#include "swift/Basic/SourceLoc.h"
|
||||
#include "swift/Parse/Lexer.h"
|
||||
#include "swift/SIL/CFG.h"
|
||||
#include "swift/SIL/SILArgument.h"
|
||||
#include "swift/SIL/SILInstruction.h"
|
||||
@@ -305,33 +306,48 @@ static ExclusiveOrShared_t getRequiredAccess(const BeginAccessInst *BAI) {
|
||||
return ExclusiveOrShared_t::ExclusiveAccess;
|
||||
}
|
||||
|
||||
/// Perform a syntactic suppression that returns true when the accesses are for
|
||||
/// inout arguments to a call that corresponds to one of the passed-in
|
||||
/// 'apply' instructions.
|
||||
static bool
|
||||
isConflictOnInoutArgumentsToSuppressed(const BeginAccessInst *Access1,
|
||||
const BeginAccessInst *Access2,
|
||||
ArrayRef<ApplyInst *> CallsToSuppress) {
|
||||
if (CallsToSuppress.empty())
|
||||
return false;
|
||||
/// Extract the text for the given expression.
|
||||
static StringRef extractExprText(const Expr *E, SourceManager &SM) {
|
||||
const auto CSR = Lexer::getCharSourceRangeFromSourceRange(SM,
|
||||
E->getSourceRange());
|
||||
return SM.extractText(CSR);
|
||||
}
|
||||
|
||||
/// Do a sytactic pattern match to try to safely suggest a Fix-It to rewrite
|
||||
/// calls like swap(&collection[index1], &collection[index2]) to
|
||||
///
|
||||
/// This method takes an array of all the ApplyInsts for calls to swap()
|
||||
/// in the function to avoid need to construct a parent map over the AST
|
||||
/// to find the CallExpr for the inout accesses.
|
||||
static void
|
||||
tryFixItWithCallToCollectionSwapAt(const BeginAccessInst *Access1,
|
||||
const BeginAccessInst *Access2,
|
||||
ArrayRef<ApplyInst *> CallsToSwap,
|
||||
ASTContext &Ctx,
|
||||
InFlightDiagnostic &Diag) {
|
||||
if (CallsToSwap.empty())
|
||||
return;
|
||||
|
||||
// Inout arguments must be modifications.
|
||||
if (Access1->getAccessKind() != SILAccessKind::Modify ||
|
||||
Access2->getAccessKind() != SILAccessKind::Modify) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
SILLocation Loc1 = Access1->getLoc();
|
||||
SILLocation Loc2 = Access2->getLoc();
|
||||
if (Loc1.isNull() || Loc2.isNull())
|
||||
return false;
|
||||
return;
|
||||
|
||||
auto *InOut1 = Loc1.getAsASTNode<InOutExpr>();
|
||||
auto *InOut2 = Loc2.getAsASTNode<InOutExpr>();
|
||||
if (!InOut1 || !InOut2)
|
||||
return false;
|
||||
return;
|
||||
|
||||
for (ApplyInst *AI : CallsToSuppress) {
|
||||
CallExpr *FoundCall = nullptr;
|
||||
// Look through all the calls to swap() recorded in the function to find
|
||||
// which one we're diagnosing.
|
||||
for (ApplyInst *AI : CallsToSwap) {
|
||||
SILLocation CallLoc = AI->getLoc();
|
||||
if (CallLoc.isNull())
|
||||
continue;
|
||||
@@ -340,21 +356,89 @@ isConflictOnInoutArgumentsToSuppressed(const BeginAccessInst *Access1,
|
||||
if (!CE)
|
||||
continue;
|
||||
|
||||
bool FoundTarget = false;
|
||||
CE->forEachChildExpr([=,&FoundTarget](Expr *Child) {
|
||||
if (Child == InOut1) {
|
||||
FoundTarget = true;
|
||||
// Stops the traversal.
|
||||
return (Expr *)nullptr;
|
||||
assert(CE->getCalledValue() == Ctx.getSwap(nullptr));
|
||||
// swap() takes two arguments.
|
||||
auto *ArgTuple = cast<TupleExpr>(CE->getArg());
|
||||
const Expr *Arg1 = ArgTuple->getElement(0);
|
||||
const Expr *Arg2 = ArgTuple->getElement(1);
|
||||
if ((Arg1 == InOut1 && Arg2 == InOut2)) {
|
||||
FoundCall = CE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!FoundCall)
|
||||
return;
|
||||
|
||||
// We found a call to swap(&e1, &e2). Now check to see whether it
|
||||
// matches the form swap(&someCollection[index1], &someCollection[index2]).
|
||||
auto *SE1 = dyn_cast<SubscriptExpr>(InOut1->getSubExpr());
|
||||
if (!SE1)
|
||||
return;
|
||||
auto *SE2 = dyn_cast<SubscriptExpr>(InOut2->getSubExpr());
|
||||
if (!SE2)
|
||||
return;
|
||||
|
||||
// Do the two subscripts refer to the same subscript declaration?
|
||||
auto *Decl1 = cast<SubscriptDecl>(SE1->getDecl().getDecl());
|
||||
auto *Decl2 = cast<SubscriptDecl>(SE2->getDecl().getDecl());
|
||||
if (Decl1 != Decl2)
|
||||
return;
|
||||
|
||||
ProtocolDecl *MutableCollectionDecl = Ctx.getMutableCollectionDecl();
|
||||
|
||||
// Is the subcript either (1) on MutableCollection itself or (2) a
|
||||
// a witness for a subscript on MutableCollection?
|
||||
bool IsSubscriptOnMutableCollection = false;
|
||||
ProtocolDecl *ProtocolForDecl =
|
||||
Decl1->getDeclContext()->getAsProtocolOrProtocolExtensionContext();
|
||||
if (ProtocolForDecl) {
|
||||
IsSubscriptOnMutableCollection = (ProtocolForDecl == MutableCollectionDecl);
|
||||
} else {
|
||||
for (ValueDecl *Req : Decl1->getSatisfiedProtocolRequirements()) {
|
||||
DeclContext *ReqDC = Req->getDeclContext();
|
||||
ProtocolDecl *ReqProto = ReqDC->getAsProtocolOrProtocolExtensionContext();
|
||||
assert(ReqProto && "Protocol requirement not in a protocol?");
|
||||
|
||||
if (ReqProto == MutableCollectionDecl) {
|
||||
IsSubscriptOnMutableCollection = true;
|
||||
break;
|
||||
}
|
||||
return Child;
|
||||
}
|
||||
);
|
||||
if (FoundTarget)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!IsSubscriptOnMutableCollection)
|
||||
return;
|
||||
|
||||
// We're swapping two subscripts on mutable collections -- but are they
|
||||
// the same collection? Approximate this by checking for textual
|
||||
// equality on the base expressions. This is just an approximation,
|
||||
// but is fine for a best-effort Fix-It.
|
||||
SourceManager &SM = Ctx.SourceMgr;
|
||||
StringRef Base1Text = extractExprText(SE1->getBase(), SM);
|
||||
StringRef Base2Text = extractExprText(SE2->getBase(), SM);
|
||||
|
||||
if (Base1Text != Base2Text)
|
||||
return;
|
||||
|
||||
auto *Index1 = dyn_cast<ParenExpr>(SE1->getIndex());
|
||||
if (!Index1)
|
||||
return;
|
||||
|
||||
auto *Index2 = dyn_cast<ParenExpr>(SE2->getIndex());
|
||||
if (!Index2)
|
||||
return;
|
||||
|
||||
StringRef Index1Text = extractExprText(Index1->getSubExpr(), SM);
|
||||
StringRef Index2Text = extractExprText(Index2->getSubExpr(), SM);
|
||||
|
||||
// Suggest replacing with call with a call to swapAt().
|
||||
SmallString<64> FixItText;
|
||||
{
|
||||
llvm::raw_svector_ostream Out(FixItText);
|
||||
Out << Base1Text << ".swapAt(" << Index1Text << ", " << Index2Text << ")";
|
||||
}
|
||||
|
||||
Diag.fixItReplace(FoundCall->getSourceRange(), FixItText);
|
||||
}
|
||||
|
||||
/// Emits a diagnostic if beginning an access with the given in-progress
|
||||
@@ -363,6 +447,7 @@ isConflictOnInoutArgumentsToSuppressed(const BeginAccessInst *Access1,
|
||||
static void diagnoseExclusivityViolation(const AccessedStorage &Storage,
|
||||
const BeginAccessInst *PriorAccess,
|
||||
const BeginAccessInst *NewAccess,
|
||||
ArrayRef<ApplyInst *> CallsToSwap,
|
||||
ASTContext &Ctx) {
|
||||
|
||||
DEBUG(llvm::dbgs() << "Conflict on " << *PriorAccess
|
||||
@@ -385,25 +470,29 @@ static void diagnoseExclusivityViolation(const AccessedStorage &Storage,
|
||||
|
||||
SourceRange rangeForMain =
|
||||
AccessForMainDiagnostic->getLoc().getSourceRange();
|
||||
unsigned AccessKindForMain =
|
||||
static_cast<unsigned>(AccessForMainDiagnostic->getAccessKind());
|
||||
|
||||
if (const ValueDecl *VD = Storage.getStorageDecl()) {
|
||||
// We have a declaration, so mention the identifier in the diagnostic.
|
||||
auto DiagnosticID = (Ctx.LangOpts.isSwiftVersion3() ?
|
||||
diag::exclusivity_access_required_swift3 :
|
||||
diag::exclusivity_access_required);
|
||||
diagnose(Ctx, AccessForMainDiagnostic->getLoc().getSourceLoc(),
|
||||
DiagnosticID,
|
||||
VD->getDescriptiveKind(),
|
||||
VD->getName(),
|
||||
static_cast<unsigned>(AccessForMainDiagnostic->getAccessKind()))
|
||||
.highlight(rangeForMain);
|
||||
auto D = diagnose(Ctx, AccessForMainDiagnostic->getLoc().getSourceLoc(),
|
||||
DiagnosticID,
|
||||
VD->getDescriptiveKind(),
|
||||
VD->getName(),
|
||||
AccessKindForMain);
|
||||
D.highlight(rangeForMain);
|
||||
tryFixItWithCallToCollectionSwapAt(PriorAccess, NewAccess,
|
||||
CallsToSwap, Ctx, D);
|
||||
} else {
|
||||
auto DiagnosticID = (Ctx.LangOpts.isSwiftVersion3() ?
|
||||
diag::exclusivity_access_required_unknown_decl_swift3 :
|
||||
diag::exclusivity_access_required_unknown_decl);
|
||||
diagnose(Ctx, AccessForMainDiagnostic->getLoc().getSourceLoc(),
|
||||
DiagnosticID,
|
||||
static_cast<unsigned>(AccessForMainDiagnostic->getAccessKind()))
|
||||
AccessKindForMain)
|
||||
.highlight(rangeForMain);
|
||||
}
|
||||
diagnose(Ctx, AccessForNote->getLoc().getSourceLoc(),
|
||||
@@ -500,7 +589,8 @@ static AccessedStorage findAccessedStorage(SILValue Source) {
|
||||
}
|
||||
|
||||
/// Returns true when the apply calls the Standard Library swap().
|
||||
/// Used for diagnostic suppression.
|
||||
/// Used for fix-its to suggest replacing with Collection.swapAt()
|
||||
/// on exclusivity violations.
|
||||
bool isCallToStandardLibrarySwap(ApplyInst *AI, ASTContext &Ctx) {
|
||||
SILFunction *SF = AI->getReferencedFunction();
|
||||
if (!SF)
|
||||
@@ -535,9 +625,8 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO) {
|
||||
if (Fn.empty())
|
||||
return;
|
||||
|
||||
// Collects calls to functions for which diagnostics about conflicting inout
|
||||
// arguments should be suppressed.
|
||||
llvm::SmallVector<ApplyInst *, 8> CallsToSuppress;
|
||||
// Collects calls the Standard Library swap() for Fix-Its.
|
||||
llvm::SmallVector<ApplyInst *, 8> CallsToSwap;
|
||||
|
||||
// Stores the accesses that have been found to conflict. Used to defer
|
||||
// emitting diagnostics until we can determine whether they should
|
||||
@@ -606,11 +695,9 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO) {
|
||||
}
|
||||
|
||||
if (auto *AI = dyn_cast<ApplyInst>(&I)) {
|
||||
// Suppress for the arguments to the Standard Library's swap()
|
||||
// function until we can recommend a safe alternative.
|
||||
if (Fn.getModule().getOptions().SuppressStaticExclusivitySwap &&
|
||||
isCallToStandardLibrarySwap(AI, Fn.getASTContext()))
|
||||
CallsToSuppress.push_back(AI);
|
||||
// Record calls to swap() for potential Fix-Its.
|
||||
if (isCallToStandardLibrarySwap(AI, Fn.getASTContext()))
|
||||
CallsToSwap.push_back(AI);
|
||||
}
|
||||
|
||||
// Sanity check to make sure entries are properly removed.
|
||||
@@ -624,12 +711,9 @@ static void checkStaticExclusivity(SILFunction &Fn, PostOrderFunctionInfo *PO) {
|
||||
for (auto &Violation : ConflictingAccesses) {
|
||||
const BeginAccessInst *PriorAccess = Violation.FirstAccess;
|
||||
const BeginAccessInst *NewAccess = Violation.SecondAccess;
|
||||
if (isConflictOnInoutArgumentsToSuppressed(PriorAccess, NewAccess,
|
||||
CallsToSuppress))
|
||||
continue;
|
||||
|
||||
diagnoseExclusivityViolation(Violation.Storage, PriorAccess, NewAccess,
|
||||
Fn.getASTContext());
|
||||
CallsToSwap, Fn.getASTContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,28 @@ double _swift_stdlib_squareRoot(double _self) {
|
||||
return __builtin_sqrt(_self);
|
||||
}
|
||||
|
||||
// TLS - thread local storage
|
||||
|
||||
#if defined(__linux__)
|
||||
typedef unsigned int __swift_pthread_key_t;
|
||||
#else
|
||||
typedef unsigned long __swift_pthread_key_t;
|
||||
#endif
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
int _swift_stdlib_pthread_key_create(
|
||||
__swift_pthread_key_t * _Nonnull key, void
|
||||
(* _Nullable destructor)(void * _Nullable )
|
||||
);
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
void * _Nullable _swift_stdlib_pthread_getspecific(__swift_pthread_key_t key);
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
int _swift_stdlib_pthread_setspecific(
|
||||
__swift_pthread_key_t key, const void * _Nullable value
|
||||
);
|
||||
|
||||
// TODO: Remove horrible workaround when importer does Float80 <-> long double.
|
||||
#if (defined __i386__ || defined __x86_64__) && !defined _MSC_VER
|
||||
static inline SWIFT_ALWAYS_INLINE
|
||||
|
||||
@@ -109,6 +109,218 @@ __swift_int32_t _swift_stdlib_unicode_strToLower(
|
||||
__swift_uint16_t *Destination, __swift_int32_t DestinationCapacity,
|
||||
const __swift_uint16_t *Source, __swift_int32_t SourceLength);
|
||||
|
||||
typedef enum __swift_stdlib_UErrorCode {
|
||||
__swift_stdlib_U_USING_FALLBACK_WARNING = -128,
|
||||
__swift_stdlib_U_ERROR_WARNING_START = -128,
|
||||
__swift_stdlib_U_USING_DEFAULT_WARNING = -127,
|
||||
__swift_stdlib_U_SAFECLONE_ALLOCATED_WARNING = -126,
|
||||
__swift_stdlib_U_STATE_OLD_WARNING = -125,
|
||||
__swift_stdlib_U_STRING_NOT_TERMINATED_WARNING = -124,
|
||||
__swift_stdlib_U_SORT_KEY_TOO_SHORT_WARNING = -123,
|
||||
__swift_stdlib_U_AMBIGUOUS_ALIAS_WARNING = -122,
|
||||
__swift_stdlib_U_DIFFERENT_UCA_VERSION = -121,
|
||||
__swift_stdlib_U_PLUGIN_CHANGED_LEVEL_WARNING = -120,
|
||||
__swift_stdlib_U_ERROR_WARNING_LIMIT,
|
||||
__swift_stdlib_U_ZERO_ERROR = 0,
|
||||
__swift_stdlib_U_ILLEGAL_ARGUMENT_ERROR = 1,
|
||||
__swift_stdlib_U_MISSING_RESOURCE_ERROR = 2,
|
||||
__swift_stdlib_U_INVALID_FORMAT_ERROR = 3,
|
||||
__swift_stdlib_U_FILE_ACCESS_ERROR = 4,
|
||||
__swift_stdlib_U_INTERNAL_PROGRAM_ERROR = 5,
|
||||
__swift_stdlib_U_MESSAGE_PARSE_ERROR = 6,
|
||||
__swift_stdlib_U_MEMORY_ALLOCATION_ERROR = 7,
|
||||
__swift_stdlib_U_INDEX_OUTOFBOUNDS_ERROR = 8,
|
||||
__swift_stdlib_U_PARSE_ERROR = 9,
|
||||
__swift_stdlib_U_INVALID_CHAR_FOUND = 10,
|
||||
__swift_stdlib_U_TRUNCATED_CHAR_FOUND = 11,
|
||||
__swift_stdlib_U_ILLEGAL_CHAR_FOUND = 12,
|
||||
__swift_stdlib_U_INVALID_TABLE_FORMAT = 13,
|
||||
__swift_stdlib_U_INVALID_TABLE_FILE = 14,
|
||||
__swift_stdlib_U_BUFFER_OVERFLOW_ERROR = 15,
|
||||
__swift_stdlib_U_UNSUPPORTED_ERROR = 16,
|
||||
__swift_stdlib_U_RESOURCE_TYPE_MISMATCH = 17,
|
||||
__swift_stdlib_U_ILLEGAL_ESCAPE_SEQUENCE = 18,
|
||||
__swift_stdlib_U_UNSUPPORTED_ESCAPE_SEQUENCE = 19,
|
||||
__swift_stdlib_U_NO_SPACE_AVAILABLE = 20,
|
||||
__swift_stdlib_U_CE_NOT_FOUND_ERROR = 21,
|
||||
__swift_stdlib_U_PRIMARY_TOO_LONG_ERROR = 22,
|
||||
__swift_stdlib_U_STATE_TOO_OLD_ERROR = 23,
|
||||
__swift_stdlib_U_TOO_MANY_ALIASES_ERROR = 24,
|
||||
__swift_stdlib_U_ENUM_OUT_OF_SYNC_ERROR = 25,
|
||||
__swift_stdlib_U_INVARIANT_CONVERSION_ERROR = 26,
|
||||
__swift_stdlib_U_INVALID_STATE_ERROR = 27,
|
||||
__swift_stdlib_U_COLLATOR_VERSION_MISMATCH = 28,
|
||||
__swift_stdlib_U_USELESS_COLLATOR_ERROR = 29,
|
||||
__swift_stdlib_U_NO_WRITE_PERMISSION = 30,
|
||||
__swift_stdlib_U_STANDARD_ERROR_LIMIT,
|
||||
__swift_stdlib_U_BAD_VARIABLE_DEFINITION = 0x10000,
|
||||
__swift_stdlib_U_PARSE_ERROR_START = 0x10000,
|
||||
__swift_stdlib_U_MALFORMED_RULE,
|
||||
__swift_stdlib_U_MALFORMED_SET,
|
||||
__swift_stdlib_U_MALFORMED_SYMBOL_REFERENCE,
|
||||
__swift_stdlib_U_MALFORMED_UNICODE_ESCAPE,
|
||||
__swift_stdlib_U_MALFORMED_VARIABLE_DEFINITION,
|
||||
__swift_stdlib_U_MALFORMED_VARIABLE_REFERENCE,
|
||||
__swift_stdlib_U_MISMATCHED_SEGMENT_DELIMITERS,
|
||||
__swift_stdlib_U_MISPLACED_ANCHOR_START,
|
||||
__swift_stdlib_U_MISPLACED_CURSOR_OFFSET,
|
||||
__swift_stdlib_U_MISPLACED_QUANTIFIER,
|
||||
__swift_stdlib_U_MISSING_OPERATOR,
|
||||
__swift_stdlib_U_MISSING_SEGMENT_CLOSE,
|
||||
__swift_stdlib_U_MULTIPLE_ANTE_CONTEXTS,
|
||||
__swift_stdlib_U_MULTIPLE_CURSORS,
|
||||
__swift_stdlib_U_MULTIPLE_POST_CONTEXTS,
|
||||
__swift_stdlib_U_TRAILING_BACKSLASH,
|
||||
__swift_stdlib_U_UNDEFINED_SEGMENT_REFERENCE,
|
||||
__swift_stdlib_U_UNDEFINED_VARIABLE,
|
||||
__swift_stdlib_U_UNQUOTED_SPECIAL,
|
||||
__swift_stdlib_U_UNTERMINATED_QUOTE,
|
||||
__swift_stdlib_U_RULE_MASK_ERROR,
|
||||
__swift_stdlib_U_MISPLACED_COMPOUND_FILTER,
|
||||
__swift_stdlib_U_MULTIPLE_COMPOUND_FILTERS,
|
||||
__swift_stdlib_U_INVALID_RBT_SYNTAX,
|
||||
__swift_stdlib_U_INVALID_PROPERTY_PATTERN,
|
||||
__swift_stdlib_U_MALFORMED_PRAGMA,
|
||||
__swift_stdlib_U_UNCLOSED_SEGMENT,
|
||||
__swift_stdlib_U_ILLEGAL_CHAR_IN_SEGMENT,
|
||||
__swift_stdlib_U_VARIABLE_RANGE_EXHAUSTED,
|
||||
__swift_stdlib_U_VARIABLE_RANGE_OVERLAP,
|
||||
__swift_stdlib_U_ILLEGAL_CHARACTER,
|
||||
__swift_stdlib_U_INTERNAL_TRANSLITERATOR_ERROR,
|
||||
__swift_stdlib_U_INVALID_ID,
|
||||
__swift_stdlib_U_INVALID_FUNCTION,
|
||||
__swift_stdlib_U_PARSE_ERROR_LIMIT,
|
||||
__swift_stdlib_U_UNEXPECTED_TOKEN = 0x10100,
|
||||
__swift_stdlib_U_FMT_PARSE_ERROR_START = 0x10100,
|
||||
__swift_stdlib_U_MULTIPLE_DECIMAL_SEPARATORS,
|
||||
__swift_stdlib_U_MULTIPLE_DECIMAL_SEPERATORS =
|
||||
__swift_stdlib_U_MULTIPLE_DECIMAL_SEPARATORS,
|
||||
__swift_stdlib_U_MULTIPLE_EXPONENTIAL_SYMBOLS,
|
||||
__swift_stdlib_U_MALFORMED_EXPONENTIAL_PATTERN,
|
||||
__swift_stdlib_U_MULTIPLE_PERCENT_SYMBOLS,
|
||||
__swift_stdlib_U_MULTIPLE_PERMILL_SYMBOLS,
|
||||
__swift_stdlib_U_MULTIPLE_PAD_SPECIFIERS,
|
||||
__swift_stdlib_U_PATTERN_SYNTAX_ERROR,
|
||||
__swift_stdlib_U_ILLEGAL_PAD_POSITION,
|
||||
__swift_stdlib_U_UNMATCHED_BRACES,
|
||||
__swift_stdlib_U_UNSUPPORTED_PROPERTY,
|
||||
__swift_stdlib_U_UNSUPPORTED_ATTRIBUTE,
|
||||
__swift_stdlib_U_ARGUMENT_TYPE_MISMATCH,
|
||||
__swift_stdlib_U_DUPLICATE_KEYWORD,
|
||||
__swift_stdlib_U_UNDEFINED_KEYWORD,
|
||||
__swift_stdlib_U_DEFAULT_KEYWORD_MISSING,
|
||||
__swift_stdlib_U_DECIMAL_NUMBER_SYNTAX_ERROR,
|
||||
__swift_stdlib_U_FORMAT_INEXACT_ERROR,
|
||||
__swift_stdlib_U_FMT_PARSE_ERROR_LIMIT,
|
||||
__swift_stdlib_U_BRK_INTERNAL_ERROR = 0x10200,
|
||||
__swift_stdlib_U_BRK_ERROR_START = 0x10200,
|
||||
__swift_stdlib_U_BRK_HEX_DIGITS_EXPECTED,
|
||||
__swift_stdlib_U_BRK_SEMICOLON_EXPECTED,
|
||||
__swift_stdlib_U_BRK_RULE_SYNTAX,
|
||||
__swift_stdlib_U_BRK_UNCLOSED_SET,
|
||||
__swift_stdlib_U_BRK_ASSIGN_ERROR,
|
||||
__swift_stdlib_U_BRK_VARIABLE_REDFINITION,
|
||||
__swift_stdlib_U_BRK_MISMATCHED_PAREN,
|
||||
__swift_stdlib_U_BRK_NEW_LINE_IN_QUOTED_STRING,
|
||||
__swift_stdlib_U_BRK_UNDEFINED_VARIABLE,
|
||||
__swift_stdlib_U_BRK_INIT_ERROR,
|
||||
__swift_stdlib_U_BRK_RULE_EMPTY_SET,
|
||||
__swift_stdlib_U_BRK_UNRECOGNIZED_OPTION,
|
||||
__swift_stdlib_U_BRK_MALFORMED_RULE_TAG,
|
||||
__swift_stdlib_U_BRK_ERROR_LIMIT,
|
||||
__swift_stdlib_U_REGEX_INTERNAL_ERROR = 0x10300,
|
||||
__swift_stdlib_U_REGEX_ERROR_START = 0x10300,
|
||||
__swift_stdlib_U_REGEX_RULE_SYNTAX,
|
||||
__swift_stdlib_U_REGEX_INVALID_STATE,
|
||||
__swift_stdlib_U_REGEX_BAD_ESCAPE_SEQUENCE,
|
||||
__swift_stdlib_U_REGEX_PROPERTY_SYNTAX,
|
||||
__swift_stdlib_U_REGEX_UNIMPLEMENTED,
|
||||
__swift_stdlib_U_REGEX_MISMATCHED_PAREN,
|
||||
__swift_stdlib_U_REGEX_NUMBER_TOO_BIG,
|
||||
__swift_stdlib_U_REGEX_BAD_INTERVAL,
|
||||
__swift_stdlib_U_REGEX_MAX_LT_MIN,
|
||||
__swift_stdlib_U_REGEX_INVALID_BACK_REF,
|
||||
__swift_stdlib_U_REGEX_INVALID_FLAG,
|
||||
__swift_stdlib_U_REGEX_LOOK_BEHIND_LIMIT,
|
||||
__swift_stdlib_U_REGEX_SET_CONTAINS_STRING,
|
||||
#ifndef __swift_stdlib_U_HIDE_DEPRECATED_API
|
||||
__swift_stdlib_U_REGEX_OCTAL_TOO_BIG,
|
||||
#endif
|
||||
__swift_stdlib_U_REGEX_MISSING_CLOSE_BRACKET =
|
||||
__swift_stdlib_U_REGEX_SET_CONTAINS_STRING + 2,
|
||||
__swift_stdlib_U_REGEX_INVALID_RANGE,
|
||||
__swift_stdlib_U_REGEX_STACK_OVERFLOW,
|
||||
__swift_stdlib_U_REGEX_TIME_OUT,
|
||||
__swift_stdlib_U_REGEX_STOPPED_BY_CALLER,
|
||||
#ifndef __swift_stdlib_U_HIDE_DRAFT_API
|
||||
__swift_stdlib_U_REGEX_PATTERN_TOO_BIG,
|
||||
__swift_stdlib_U_REGEX_INVALID_CAPTURE_GROUP_NAME,
|
||||
#endif
|
||||
__swift_stdlib_U_REGEX_ERROR_LIMIT =
|
||||
__swift_stdlib_U_REGEX_STOPPED_BY_CALLER + 3,
|
||||
__swift_stdlib_U_IDNA_PROHIBITED_ERROR = 0x10400,
|
||||
__swift_stdlib_U_IDNA_ERROR_START = 0x10400,
|
||||
__swift_stdlib_U_IDNA_UNASSIGNED_ERROR,
|
||||
__swift_stdlib_U_IDNA_CHECK_BIDI_ERROR,
|
||||
__swift_stdlib_U_IDNA_STD3_ASCII_RULES_ERROR,
|
||||
__swift_stdlib_U_IDNA_ACE_PREFIX_ERROR,
|
||||
__swift_stdlib_U_IDNA_VERIFICATION_ERROR,
|
||||
__swift_stdlib_U_IDNA_LABEL_TOO_LONG_ERROR,
|
||||
__swift_stdlib_U_IDNA_ZERO_LENGTH_LABEL_ERROR,
|
||||
__swift_stdlib_U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR,
|
||||
__swift_stdlib_U_IDNA_ERROR_LIMIT,
|
||||
__swift_stdlib_U_STRINGPREP_PROHIBITED_ERROR =
|
||||
__swift_stdlib_U_IDNA_PROHIBITED_ERROR,
|
||||
__swift_stdlib_U_STRINGPREP_UNASSIGNED_ERROR =
|
||||
__swift_stdlib_U_IDNA_UNASSIGNED_ERROR,
|
||||
__swift_stdlib_U_STRINGPREP_CHECK_BIDI_ERROR =
|
||||
__swift_stdlib_U_IDNA_CHECK_BIDI_ERROR,
|
||||
__swift_stdlib_U_PLUGIN_ERROR_START = 0x10500,
|
||||
__swift_stdlib_U_PLUGIN_TOO_HIGH = 0x10500,
|
||||
__swift_stdlib_U_PLUGIN_DIDNT_SET_LEVEL,
|
||||
__swift_stdlib_U_PLUGIN_ERROR_LIMIT,
|
||||
__swift_stdlib_U_ERROR_LIMIT = __swift_stdlib_U_PLUGIN_ERROR_LIMIT
|
||||
} __swift_stdlib_UErrorCode;
|
||||
|
||||
typedef enum __swift_stdlib_UBreakIteratorType {
|
||||
__swift_stdlib_UBRK_CHARACTER = 0,
|
||||
__swift_stdlib_UBRK_WORD = 1,
|
||||
__swift_stdlib_UBRK_LINE = 2,
|
||||
__swift_stdlib_UBRK_SENTENCE = 3,
|
||||
#ifndef U_HIDE_DEPRECATED_API
|
||||
__swift_stdlib_UBRK_TITLE = 4,
|
||||
#endif
|
||||
__swift_stdlib_UBRK_COUNT = 5
|
||||
} __swift_stdlib_UBreakIteratorType;
|
||||
|
||||
typedef struct __swift_stdlib_UBreakIterator __swift_stdlib_UBreakIterator;
|
||||
typedef __swift_uint16_t __swift_stdlib_UChar;
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
void __swift_stdlib_ubrk_close(__swift_stdlib_UBreakIterator *bi);
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
__swift_stdlib_UBreakIterator *
|
||||
__swift_stdlib_ubrk_open(__swift_stdlib_UBreakIteratorType type,
|
||||
const char *_Null_unspecified locale,
|
||||
const __swift_stdlib_UChar *_Null_unspecified text,
|
||||
__swift_int32_t textLength,
|
||||
__swift_stdlib_UErrorCode *status);
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
void __swift_stdlib_ubrk_setText(__swift_stdlib_UBreakIterator *bi,
|
||||
const __swift_stdlib_UChar *text,
|
||||
__swift_int32_t textLength,
|
||||
__swift_stdlib_UErrorCode *status);
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
__swift_int32_t __swift_stdlib_ubrk_preceding(__swift_stdlib_UBreakIterator *bi,
|
||||
__swift_int32_t offset);
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
__swift_int32_t __swift_stdlib_ubrk_following(__swift_stdlib_UBreakIterator *bi,
|
||||
__swift_int32_t offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}} // extern "C", namespace swift
|
||||
#endif
|
||||
|
||||
@@ -68,6 +68,7 @@ set(SWIFTLIB_ESSENTIAL
|
||||
HashedCollectionsAnyHashableExtensions.swift
|
||||
Hashing.swift
|
||||
HeapBuffer.swift
|
||||
ICU.swift
|
||||
ImplicitlyUnwrappedOptional.swift
|
||||
Index.swift
|
||||
Indices.swift.gyb
|
||||
@@ -132,6 +133,7 @@ set(SWIFTLIB_ESSENTIAL
|
||||
StringUTF8.swift
|
||||
Substring.swift.gyb
|
||||
SwiftNativeNSArray.swift
|
||||
ThreadLocalStorage.swift
|
||||
UIntBuffer.swift
|
||||
UnavailableStringAPIs.swift.gyb
|
||||
UnicodeEncoding.swift
|
||||
@@ -176,6 +178,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
list(APPEND swift_core_link_flags "-all_load")
|
||||
list(APPEND swift_core_framework_depends Foundation)
|
||||
list(APPEND swift_core_framework_depends CoreFoundation)
|
||||
list(APPEND swift_core_private_link_libraries icucore)
|
||||
else()
|
||||
# With the GNU linker the equivalent of -all_load is to tell the linker
|
||||
# --whole-archive before the archive and --no-whole-archive after (without
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"CString.swift",
|
||||
"Character.swift",
|
||||
"CharacterUnicodeScalars.swift",
|
||||
"ICU.swift",
|
||||
"StaticString.swift",
|
||||
"String.swift",
|
||||
"StringBridge.swift",
|
||||
@@ -161,6 +162,7 @@
|
||||
"REPL.swift",
|
||||
"Runtime.swift",
|
||||
"Shims.swift",
|
||||
"ThreadLocalStorage.swift",
|
||||
"Unmanaged.swift",
|
||||
"Availability.swift",
|
||||
"CommandLine.swift",
|
||||
|
||||
18
stdlib/public/core/ICU.swift
Normal file
18
stdlib/public/core/ICU.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//===--- ICU.swift --------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
import SwiftShims
|
||||
|
||||
extension __swift_stdlib_UErrorCode {
|
||||
var isFailure: Bool { return rawValue > __swift_stdlib_U_ZERO_ERROR.rawValue }
|
||||
var isWarning: Bool { return rawValue < __swift_stdlib_U_ZERO_ERROR.rawValue }
|
||||
var isSuccess: Bool { return rawValue <= __swift_stdlib_U_ZERO_ERROR.rawValue }
|
||||
}
|
||||
@@ -22,6 +22,8 @@
|
||||
internal let _CR: UInt8 = 0x0d
|
||||
internal let _LF: UInt8 = 0x0a
|
||||
|
||||
import SwiftShims
|
||||
|
||||
extension String {
|
||||
/// A view of a string's contents as a collection of characters.
|
||||
///
|
||||
@@ -316,29 +318,34 @@ extension String.CharacterView : BidirectionalCollection {
|
||||
internal func _measureExtendedGraphemeClusterForward(
|
||||
from start: UnicodeScalarView.Index
|
||||
) -> Int {
|
||||
var start = start
|
||||
let end = unicodeScalars.endIndex
|
||||
if start == end {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Our relative position (offset). If our _core is not a substring, this is
|
||||
// the same as start._position.
|
||||
let relativeOffset = start._position - _coreOffset
|
||||
|
||||
// Grapheme breaking is much simpler if known ASCII
|
||||
if _core.isASCII {
|
||||
_onFastPath() // Please aggressively inline
|
||||
let asciiBuffer = _core.asciiBuffer._unsafelyUnwrappedUnchecked
|
||||
let pos = start._position - _coreOffset
|
||||
|
||||
// With the exception of CR-LF, ASCII graphemes are single-scalar. Check
|
||||
// for that one exception.
|
||||
if _slowPath(
|
||||
asciiBuffer[pos] == _CR &&
|
||||
pos+1 < asciiBuffer.endIndex &&
|
||||
asciiBuffer[pos+1] == _LF
|
||||
asciiBuffer[relativeOffset] == _CR &&
|
||||
relativeOffset+1 < asciiBuffer.endIndex &&
|
||||
asciiBuffer[relativeOffset+1] == _LF
|
||||
) {
|
||||
return 2
|
||||
}
|
||||
|
||||
return 1
|
||||
} else {
|
||||
// TODO: Check for (potentially non-contiguous) ASCII NSStrings,
|
||||
// especially small tagged pointers.
|
||||
}
|
||||
|
||||
let startIndexUTF16 = start._position
|
||||
@@ -350,15 +357,49 @@ extension String.CharacterView : BidirectionalCollection {
|
||||
|
||||
// Perform a quick single-code-unit grapheme check
|
||||
if _core._baseAddress != nil {
|
||||
let pos = start._position - _coreOffset
|
||||
if String.CharacterView._quickCheckGraphemeBreakBetween(
|
||||
_core._nthContiguous(pos),
|
||||
_core._nthContiguous(pos+1)
|
||||
_core._nthContiguous(relativeOffset),
|
||||
_core._nthContiguous(relativeOffset+1)
|
||||
) {
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
// TODO: Check for (potentially non-contiguous) UTF16 NSStrings,
|
||||
// especially small tagged pointers
|
||||
}
|
||||
|
||||
if _core._baseAddress != nil {
|
||||
_onFastPath() // Please aggressively inline
|
||||
let breakIterator = _ThreadLocalStorage.getUBreakIterator(for: _core)
|
||||
let ubrkFollowing = __swift_stdlib_ubrk_following(
|
||||
breakIterator, Int32(relativeOffset)
|
||||
)
|
||||
// ubrk_following may return UBRK_DONE (-1). Treat that as the rest of the
|
||||
// string.
|
||||
let nextPosition =
|
||||
ubrkFollowing == -1 ? end._position : Int(ubrkFollowing)
|
||||
return nextPosition - relativeOffset
|
||||
} else {
|
||||
// TODO: See if we can get fast character contents.
|
||||
}
|
||||
|
||||
// FIXME: Need to handle the general case correctly with Unicode 9+
|
||||
// semantics, as opposed to this legacy Unicode 8 path. This gets hit for
|
||||
// e.g. non-contiguous NSStrings. In such cases, there may be an alternative
|
||||
// CFString API available, or worst case we can map over it via UTextFuncs.
|
||||
|
||||
return legacyGraphemeForward(
|
||||
start: start, end: end, startIndexUTF16: startIndexUTF16
|
||||
)
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
func legacyGraphemeForward(
|
||||
start: UnicodeScalarView.Index,
|
||||
end: UnicodeScalarView.Index,
|
||||
startIndexUTF16: Int
|
||||
) -> Int {
|
||||
var start = start
|
||||
let graphemeClusterBreakProperty =
|
||||
_UnicodeGraphemeClusterBreakPropertyTrie()
|
||||
let segmenter = _UnicodeExtendedGraphemeClusterSegmenter()
|
||||
@@ -400,21 +441,25 @@ extension String.CharacterView : BidirectionalCollection {
|
||||
return 0
|
||||
}
|
||||
|
||||
// The relative position (offset) to the last code unit.
|
||||
let lastOffset = end._position - _coreOffset - 1
|
||||
// The relative position (offset) that is one-past-the-last
|
||||
let endOffset = lastOffset + 1
|
||||
|
||||
// Grapheme breaking is much simpler if known ASCII
|
||||
if _core.isASCII {
|
||||
_onFastPath() // Please aggressively inline
|
||||
let asciiBuffer = _core.asciiBuffer._unsafelyUnwrappedUnchecked
|
||||
let pos = end._position - _coreOffset - 1
|
||||
_sanityCheck(
|
||||
pos >= asciiBuffer.startIndex,
|
||||
lastOffset >= asciiBuffer.startIndex,
|
||||
"should of been caught in earlier start-of-scalars check")
|
||||
|
||||
// With the exception of CR-LF, ASCII graphemes are single-scalar. Check
|
||||
// for that one exception.
|
||||
if _slowPath(
|
||||
asciiBuffer[pos] == _LF &&
|
||||
pos-1 >= asciiBuffer.startIndex &&
|
||||
asciiBuffer[pos-1] == _CR
|
||||
asciiBuffer[lastOffset] == _LF &&
|
||||
lastOffset-1 >= asciiBuffer.startIndex &&
|
||||
asciiBuffer[lastOffset-1] == _CR
|
||||
) {
|
||||
return 2
|
||||
}
|
||||
@@ -431,15 +476,45 @@ extension String.CharacterView : BidirectionalCollection {
|
||||
|
||||
// Perform a quick single-code-unit grapheme check
|
||||
if _core._baseAddress != nil {
|
||||
let pos = end._position - _coreOffset - 1
|
||||
if String.CharacterView._quickCheckGraphemeBreakBetween(
|
||||
_core._nthContiguous(pos-1),
|
||||
_core._nthContiguous(pos)
|
||||
_core._nthContiguous(lastOffset-1),
|
||||
_core._nthContiguous(lastOffset)
|
||||
) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if _core._baseAddress != nil {
|
||||
_onFastPath() // Please aggressively inline
|
||||
let breakIterator = _ThreadLocalStorage.getUBreakIterator(for: _core)
|
||||
let ubrkPreceding = __swift_stdlib_ubrk_preceding(
|
||||
breakIterator, Int32(endOffset)
|
||||
)
|
||||
// ubrk_following may return UBRK_DONE (-1). Treat that as the rest of the
|
||||
// string.
|
||||
let priorPosition =
|
||||
ubrkPreceding == -1 ? start._position : Int(ubrkPreceding)
|
||||
return endOffset - priorPosition
|
||||
} else {
|
||||
// TODO: See if we can get fast character contents.
|
||||
}
|
||||
|
||||
// FIXME: Need to handle the general case correctly with Unicode 9+
|
||||
// semantics, as opposed to this legacy Unicode 8 path. This gets hit for
|
||||
// e.g. non-contiguous NSStrings. In such cases, there may be an alternative
|
||||
// CFString API available, or worst case we can map over it via UTextFuncs.
|
||||
|
||||
return legacyGraphemeBackward(
|
||||
start: start, end: end, endIndexUTF16: endIndexUTF16
|
||||
)
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
func legacyGraphemeBackward(
|
||||
start: UnicodeScalarView.Index,
|
||||
end: UnicodeScalarView.Index,
|
||||
endIndexUTF16: Int
|
||||
) -> Int {
|
||||
let graphemeClusterBreakProperty =
|
||||
_UnicodeGraphemeClusterBreakPropertyTrie()
|
||||
let segmenter = _UnicodeExtendedGraphemeClusterSegmenter()
|
||||
|
||||
153
stdlib/public/core/ThreadLocalStorage.swift
Normal file
153
stdlib/public/core/ThreadLocalStorage.swift
Normal file
@@ -0,0 +1,153 @@
|
||||
import SwiftShims
|
||||
|
||||
// For testing purposes, a thread-safe counter to guarantee that destructors get
|
||||
// called by pthread.
|
||||
#if INTERNAL_CHECKS_ENABLED
|
||||
public // @testable
|
||||
let _destroyTLSCounter = _stdlib_AtomicInt()
|
||||
#endif
|
||||
|
||||
// Thread local storage for all of the Swift standard library
|
||||
//
|
||||
// @moveonly/@pointeronly: shouldn't be used as a value, only through its
|
||||
// pointer. Similarly, shouldn't be created, except by
|
||||
// _initializeThreadLocalStorage.
|
||||
//
|
||||
internal struct _ThreadLocalStorage {
|
||||
// TODO: might be best to absract uBreakIterator handling and caching into
|
||||
// separate struct. That would also make it easier to maintain multiple ones
|
||||
// and other TLS entries side-by-side.
|
||||
|
||||
// Save a pre-allocated UBreakIterator, as they are very expensive to set up.
|
||||
// Each thread can reuse their unique break iterator, being careful to reset
|
||||
// the text when it has changed (see below). Even with a naive always-reset
|
||||
// policy, grapheme breaking is 30x faster when using a pre-allocated
|
||||
// UBreakIterator than recreating one.
|
||||
//
|
||||
// private
|
||||
var uBreakIterator: OpaquePointer
|
||||
|
||||
// TODO: Consider saving two, e.g. for character-by-character comparison
|
||||
|
||||
// The below cache key tries to avoid resetting uBreakIterator's text when
|
||||
// operating on the same String as before. Avoiding the reset gives a 50%
|
||||
// speedup on grapheme breaking.
|
||||
//
|
||||
// As a invalidation check, save the base address from the last used
|
||||
// StringCore. We can skip resetting the uBreakIterator's text when operating
|
||||
// on a given StringCore when both of these associated references/pointers are
|
||||
// equal to the StringCore's. Note that the owner is weak, to force it to
|
||||
// compare unequal if a new StringCore happens to be created in the same
|
||||
// memory.
|
||||
//
|
||||
// private
|
||||
weak var associatedStringOwner: AnyObject? = nil
|
||||
// private
|
||||
var associatedBaseAddress: UnsafeMutableRawPointer? = nil
|
||||
// private
|
||||
var associatedCountAndFlags: UInt = 0
|
||||
|
||||
// private: Should only be called by _initializeThreadLocalStorage
|
||||
init(_uBreakIterator: OpaquePointer) {
|
||||
self.uBreakIterator = _uBreakIterator
|
||||
}
|
||||
|
||||
// Get the current thread's TLS pointer. On first call for a given thread,
|
||||
// creates and initializes a new one.
|
||||
static internal func getPointer()
|
||||
-> UnsafeMutablePointer<_ThreadLocalStorage>
|
||||
{
|
||||
let tlsRawPtr = _swift_stdlib_pthread_getspecific(_tlsKey)
|
||||
if _fastPath(tlsRawPtr != nil) {
|
||||
return tlsRawPtr._unsafelyUnwrappedUnchecked.assumingMemoryBound(
|
||||
to: _ThreadLocalStorage.self)
|
||||
}
|
||||
|
||||
return _initializeThreadLocalStorage()
|
||||
}
|
||||
|
||||
// Retrieve our thread's local uBreakIterator and set it up for the given
|
||||
// StringCore. Checks our TLS cache to avoid excess text resetting.
|
||||
static internal func getUBreakIterator(
|
||||
for core: _StringCore
|
||||
) -> OpaquePointer {
|
||||
let tlsPtr = getPointer()
|
||||
let brkIter = tlsPtr[0].uBreakIterator
|
||||
|
||||
_sanityCheck(core._owner != nil || core._baseAddress != nil,
|
||||
"invalid StringCore")
|
||||
|
||||
// Check if we must reset the text
|
||||
if tlsPtr[0].associatedStringOwner !== core._owner
|
||||
|| tlsPtr[0].associatedBaseAddress != core._baseAddress
|
||||
|| tlsPtr[0].associatedCountAndFlags != core._countAndFlags
|
||||
{
|
||||
// cache miss
|
||||
var err = __swift_stdlib_U_ZERO_ERROR
|
||||
let corePtr: UnsafeMutablePointer<UTF16.CodeUnit>
|
||||
corePtr = core.startUTF16
|
||||
__swift_stdlib_ubrk_setText(brkIter, corePtr, Int32(core.count), &err)
|
||||
_precondition(err.isSuccess, "unexpected ubrk_setUText failure")
|
||||
tlsPtr[0].associatedStringOwner = core._owner
|
||||
tlsPtr[0].associatedBaseAddress = core._baseAddress
|
||||
tlsPtr[0].associatedCountAndFlags = core._countAndFlags
|
||||
}
|
||||
|
||||
return brkIter
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor to register with pthreads. Responsible for deallocating any memory
|
||||
// owned.
|
||||
internal func _destroyTLS(_ ptr: UnsafeMutableRawPointer?) {
|
||||
_sanityCheck(ptr != nil,
|
||||
"_destroyTLS was called, but with nil...")
|
||||
let tlsPtr = ptr!.assumingMemoryBound(to: _ThreadLocalStorage.self)
|
||||
__swift_stdlib_ubrk_close(tlsPtr[0].uBreakIterator)
|
||||
tlsPtr[0].associatedStringOwner = nil
|
||||
tlsPtr[0].associatedBaseAddress = nil
|
||||
tlsPtr.deinitialize(count: 1)
|
||||
tlsPtr.deallocate(capacity: 1)
|
||||
|
||||
#if INTERNAL_CHECKS_ENABLED
|
||||
// Log the fact we've destroyed our storage
|
||||
_destroyTLSCounter.fetchAndAdd(1)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Lazily created global key for use with pthread TLS
|
||||
internal let _tlsKey: __swift_pthread_key_t = {
|
||||
let sentinelValue = __swift_pthread_key_t.max
|
||||
var key: __swift_pthread_key_t = sentinelValue
|
||||
let success = _swift_stdlib_pthread_key_create(&key, _destroyTLS)
|
||||
_sanityCheck(success == 0, "somehow failed to create TLS key")
|
||||
_sanityCheck(key != sentinelValue, "Didn't make a new key")
|
||||
return key
|
||||
}()
|
||||
|
||||
@inline(never)
|
||||
internal func _initializeThreadLocalStorage()
|
||||
-> UnsafeMutablePointer<_ThreadLocalStorage>
|
||||
{
|
||||
_sanityCheck(_swift_stdlib_pthread_getspecific(_tlsKey) == nil,
|
||||
"already initialized")
|
||||
|
||||
// Create and initialize one.
|
||||
var err = __swift_stdlib_U_ZERO_ERROR
|
||||
let newUBreakIterator = __swift_stdlib_ubrk_open(
|
||||
/*type:*/ __swift_stdlib_UBRK_CHARACTER, /*locale:*/ nil,
|
||||
/*text:*/ nil, /*textLength:*/ 0, /*status:*/ &err)
|
||||
_precondition(err.isSuccess, "unexpected ubrk_open failure")
|
||||
|
||||
let tlsPtr: UnsafeMutablePointer<_ThreadLocalStorage>
|
||||
= UnsafeMutablePointer<_ThreadLocalStorage>.allocate(
|
||||
capacity: 1
|
||||
)
|
||||
tlsPtr.initialize(
|
||||
to: _ThreadLocalStorage(_uBreakIterator: newUBreakIterator)
|
||||
)
|
||||
let success = _swift_stdlib_pthread_setspecific(_tlsKey, tlsPtr)
|
||||
_sanityCheck(success == 0, "setspecific failed")
|
||||
return tlsPtr
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -96,6 +98,35 @@ int swift::_swift_stdlib_close(int fd) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Guard compilation on the typedef for __swift_pthread_key_t in LibcShims.h
|
||||
// being identical to the platform's pthread_key_t
|
||||
static_assert(std::is_same<__swift_pthread_key_t, pthread_key_t>::value,
|
||||
"This platform's pthread_key_t differs. If you hit this assert, "
|
||||
"fix __swift_pthread_key_t's typedef in LibcShims.h by adding an "
|
||||
"#if guard and definition for your platform");
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
int swift::_swift_stdlib_pthread_key_create(
|
||||
__swift_pthread_key_t * _Nonnull key,
|
||||
void (* _Nullable destructor)(void *)
|
||||
) {
|
||||
return pthread_key_create(key, destructor);
|
||||
}
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
void * _Nullable swift::_swift_stdlib_pthread_getspecific(
|
||||
__swift_pthread_key_t key
|
||||
) {
|
||||
return pthread_getspecific(key);
|
||||
}
|
||||
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
int swift::_swift_stdlib_pthread_setspecific(
|
||||
__swift_pthread_key_t key, const void * _Nullable value
|
||||
) {
|
||||
return pthread_setspecific(key, value);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <malloc/malloc.h>
|
||||
SWIFT_RUNTIME_STDLIB_INTERFACE
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../SwiftShims/UnicodeShims.h"
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
#include "swift/Basic/Lazy.h"
|
||||
#include "swift/Runtime/Config.h"
|
||||
@@ -30,11 +32,10 @@
|
||||
#include <unicode/ucol.h>
|
||||
#include <unicode/ucoleitr.h>
|
||||
#include <unicode/uiter.h>
|
||||
#include <unicode/ubrk.h>
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include "../SwiftShims/UnicodeShims.h"
|
||||
|
||||
static const UCollator *MakeRootCollator() {
|
||||
UErrorCode ErrorCode = U_ZERO_ERROR;
|
||||
UCollator *root = ucol_open("", &ErrorCode);
|
||||
@@ -291,3 +292,60 @@ swift::_swift_stdlib_unicode_strToLower(uint16_t *Destination,
|
||||
swift::Lazy<ASCIICollation> ASCIICollation::theTable;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
template <typename T, typename U> T *ptr_cast(U *p) {
|
||||
return static_cast<T *>(static_cast<void *>(p));
|
||||
}
|
||||
template <typename T, typename U> const T *ptr_cast(const U *p) {
|
||||
return static_cast<const T *>(static_cast<const void *>(p));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <stdint.h>
|
||||
extern "C" {
|
||||
// Declare a few external functions to avoid a dependency on ICU headers.
|
||||
typedef struct UBreakIterator UBreakIterator;
|
||||
typedef enum UBreakIteratorType {} UBreakIteratorType;
|
||||
typedef enum UErrorCode {} UErrorCode;
|
||||
typedef uint16_t UChar;
|
||||
|
||||
void ubrk_close(UBreakIterator *);
|
||||
UBreakIterator *ubrk_open(UBreakIteratorType, const char *, const UChar *,
|
||||
int32_t, UErrorCode *);
|
||||
int32_t ubrk_preceding(UBreakIterator *, int32_t);
|
||||
int32_t ubrk_following(UBreakIterator *, int32_t);
|
||||
void ubrk_setText(UBreakIterator *, const UChar *, int32_t, UErrorCode *);
|
||||
}
|
||||
#endif // defined(__APPLE__)
|
||||
|
||||
void swift::__swift_stdlib_ubrk_close(
|
||||
swift::__swift_stdlib_UBreakIterator *bi) {
|
||||
ubrk_close(ptr_cast<UBreakIterator>(bi));
|
||||
}
|
||||
|
||||
swift::__swift_stdlib_UBreakIterator *swift::__swift_stdlib_ubrk_open(
|
||||
swift::__swift_stdlib_UBreakIteratorType type, const char *locale,
|
||||
const UChar *text, int32_t textLength, __swift_stdlib_UErrorCode *status) {
|
||||
return ptr_cast<swift::__swift_stdlib_UBreakIterator>(
|
||||
ubrk_open(static_cast<UBreakIteratorType>(type), locale, text, textLength,
|
||||
ptr_cast<UErrorCode>(status)));
|
||||
}
|
||||
|
||||
int32_t
|
||||
swift::__swift_stdlib_ubrk_preceding(swift::__swift_stdlib_UBreakIterator *bi,
|
||||
int32_t offset) {
|
||||
return ubrk_preceding(ptr_cast<UBreakIterator>(bi), offset);
|
||||
}
|
||||
|
||||
int32_t
|
||||
swift::__swift_stdlib_ubrk_following(swift::__swift_stdlib_UBreakIterator *bi,
|
||||
int32_t offset) {
|
||||
return ubrk_following(ptr_cast<UBreakIterator>(bi), offset);
|
||||
}
|
||||
void swift::__swift_stdlib_ubrk_setText(
|
||||
swift::__swift_stdlib_UBreakIterator *bi, const __swift_stdlib_UChar *text,
|
||||
__swift_int32_t textLength, __swift_stdlib_UErrorCode *status) {
|
||||
return ubrk_setText(ptr_cast<UBreakIterator>(bi), ptr_cast<UChar>(text),
|
||||
textLength, ptr_cast<UErrorCode>(status));
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ func violationWithGenericType<T>(_ p: T) {
|
||||
takesTwoInouts(&local, &local)
|
||||
}
|
||||
|
||||
|
||||
// Helper.
|
||||
struct StructWithTwoStoredProp {
|
||||
var f1: Int
|
||||
@@ -106,3 +107,74 @@ func violationWithUnsafePointer(_ s: inout StructWithTwoStoredProp) {
|
||||
_ = s.f2
|
||||
}
|
||||
}
|
||||
// Tests for Fix-Its to replace swap(&collection[a], &collection[b]) with
|
||||
// collection.swapAt(a, b)
|
||||
|
||||
struct StructWithField {
|
||||
var f = 12
|
||||
}
|
||||
|
||||
struct StructWithFixits {
|
||||
var arrayProp: [Int] = [1, 2, 3]
|
||||
var dictionaryProp: [Int : Int] = [0 : 10, 1 : 11]
|
||||
|
||||
mutating
|
||||
func shouldHaveFixIts<T>(_ i: Int, _ j: Int, _ param: T, _ paramIndex: T.Index) where T : MutableCollection {
|
||||
var array1 = [1, 2, 3]
|
||||
// expected-error@+2{{simultaneous accesses}}{{5-41=array1.swapAt(i + 5, j - 2)}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&array1[i + 5], &array1[j - 2])
|
||||
|
||||
// expected-error@+2{{simultaneous accesses}}{{5-49=self.arrayProp.swapAt(i, j)}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&self.arrayProp[i], &self.arrayProp[j])
|
||||
|
||||
var localOfGenericType = param
|
||||
// expected-error@+2{{simultaneous accesses}}{{5-75=localOfGenericType.swapAt(paramIndex, paramIndex)}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&localOfGenericType[paramIndex], &localOfGenericType[paramIndex])
|
||||
}
|
||||
|
||||
mutating
|
||||
func shouldHaveNoFixIts(_ i: Int, _ j: Int) {
|
||||
var s = StructWithField()
|
||||
// expected-error@+2{{simultaneous accesses}}{{none}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&s.f, &s.f)
|
||||
|
||||
var array1 = [1, 2, 3]
|
||||
var array2 = [1, 2, 3]
|
||||
|
||||
// Swapping between different arrays should cannot have the
|
||||
// Fix-It.
|
||||
swap(&array1[i], &array2[j]) // no-warning no-fixit
|
||||
swap(&array1[i], &self.arrayProp[j]) // no-warning no-fixit
|
||||
|
||||
// Dictionaries aren't MutableCollections so don't support swapAt().
|
||||
// expected-error@+2{{simultaneous accesses}}{{none}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&dictionaryProp[i], &dictionaryProp[j])
|
||||
|
||||
// We could safely Fix-It this but don't now because the left and
|
||||
// right bases are not textually identical.
|
||||
// expected-error@+2{{simultaneous accesses}}{{none}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&self.arrayProp[i], &arrayProp[j])
|
||||
|
||||
// We could safely Fix-It this but we're not that heroic.
|
||||
// We don't suppress when swap() is used as a value
|
||||
let mySwap: (inout Int, inout Int) -> () = swap
|
||||
|
||||
// expected-error@+2{{simultaneous accesses}}{{none}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
mySwap(&array1[i], &array1[j])
|
||||
|
||||
func myOtherSwap<T>(_ a: inout T, _ b: inout T) {
|
||||
swap(&a, &b) // no-warning
|
||||
}
|
||||
|
||||
// expected-error@+2{{simultaneous accesses}}{{none}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
mySwap(&array1[i], &array1[j])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
// RUN: %target-swift-frontend -enforce-exclusivity=checked -suppress-static-exclusivity-swap -emit-sil -primary-file %s -o /dev/null -verify
|
||||
|
||||
import Swift
|
||||
|
||||
func takesTwoInouts(_ p1: inout Int, _ p2: inout Int) { }
|
||||
|
||||
func swapSuppression(_ i: Int, _ j: Int) {
|
||||
var a: [Int] = [1, 2, 3]
|
||||
|
||||
swap(&a[i], &a[j]) // no-warning
|
||||
|
||||
// expected-warning@+2{{simultaneous accesses to var 'a', but modification requires exclusive access; consider copying to a local variable}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
takesTwoInouts(&a[i], &a[j])
|
||||
}
|
||||
|
||||
func missedSwapSuppression(_ i: Int, _ j: Int) {
|
||||
var a: [Int] = [1, 2, 3]
|
||||
|
||||
// We don't suppress when swap() is used as a value
|
||||
let mySwap: (inout Int, inout Int) -> () = swap
|
||||
|
||||
// expected-warning@+2{{simultaneous accesses to var 'a', but modification requires exclusive access; consider copying to a local variable}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
mySwap(&a[i], &a[j])
|
||||
}
|
||||
|
||||
func dontSuppressUserSwap(_ i: Int, _ j: Int) {
|
||||
var a: [Int] = [1, 2, 3]
|
||||
|
||||
// Don't suppress on user-defined swap.
|
||||
func swap<T>(_ p1: inout T, _ p2: inout T) {
|
||||
return (p1, p2) = (p2, p1)
|
||||
}
|
||||
|
||||
// expected-warning@+2{{simultaneous accesses to var 'a', but modification requires exclusive access; consider copying to a local variable}}
|
||||
// expected-note@+1{{conflicting access is here}}
|
||||
swap(&a[i], &a[j])
|
||||
}
|
||||
@@ -196,7 +196,24 @@ CharacterTests.test("CR-LF") {
|
||||
let finalAlphaCharacters = unicodeAlphabetString.characters[unicodeAlphabetString.characters.index(unicodeAlphabetString.characters.endIndex, offsetBy: -3)..<unicodeAlphabetString.characters.endIndex]
|
||||
expectEqualSequence(finalAlphaCharacters, unicodeAlphabetString_final.characters)
|
||||
expectEqualSequence(finalAlphaCharacters.reversed(), unicodeAlphabetString_final_rev.characters)
|
||||
}
|
||||
|
||||
CharacterTests.test("Unicode 9 grapheme breaking") {
|
||||
// Only run it on ObjC platforms. Supported Linux versions do not have a
|
||||
// recent enough ICU for Unicode 9 support.
|
||||
#if _runtime(_ObjC)
|
||||
let flags = "🇺🇸🇨🇦🇩🇰🏳️🌈"
|
||||
expectEqual(4, flags.count)
|
||||
expectEqual(flags.reversed().count, flags.count)
|
||||
|
||||
let family = "👪👨👧👧👩👩👧👦👨👨👦👦👨👧👩👦👦"
|
||||
expectEqual(6, family.count)
|
||||
expectEqual(family.reversed().count, family.count)
|
||||
|
||||
let skinTone = "👋👋🏻👋🏼👋🏽👋🏾👋🏿"
|
||||
expectEqual(6, skinTone.count)
|
||||
expectEqual(skinTone.reversed().count, skinTone.count)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Test that a given `String` can be transformed into a `Character` and back
|
||||
|
||||
40
test/stdlib/ThreadLocalStorage.swift
Normal file
40
test/stdlib/ThreadLocalStorage.swift
Normal file
@@ -0,0 +1,40 @@
|
||||
// RUN: %target-run-stdlib-swift
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
import StdlibUnittest
|
||||
import Swift
|
||||
import SwiftPrivate
|
||||
|
||||
import Darwin
|
||||
|
||||
var str = "Lorem ipsum dolor sit amet\r\n 🌎 🇺🇸🇨🇦🍁"
|
||||
|
||||
func checkString(_unused: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
|
||||
expectEqual(str.count, str.reversed().count)
|
||||
return nil
|
||||
}
|
||||
|
||||
var PThreadTests = TestSuite("Thread Local Storage")
|
||||
|
||||
// Test that the destructor for our TLS is invoked
|
||||
PThreadTests.test("destructor") {
|
||||
#if INTERNAL_CHECKS_ENABLED
|
||||
let numIters = 100
|
||||
for _ in 0..<numIters {
|
||||
var thread : pthread_t? = nil
|
||||
guard pthread_create(&thread, nil, checkString, &thread) == 0 else {
|
||||
fatalError("pthread_create failed")
|
||||
}
|
||||
guard pthread_join(thread!, nil) == 0 else {
|
||||
fatalError("pthread_join failed")
|
||||
}
|
||||
}
|
||||
expectEqual(numIters, _destroyTLSCounter.load())
|
||||
#endif
|
||||
var x = 1
|
||||
checkString(_unused: &x)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
@@ -414,10 +414,10 @@
|
||||
÷ D800 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
|
||||
÷ 0061 ÷ 1F1E6 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (Regional_Indicator) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3]
|
||||
÷ 1F1F7 × 1F1FA ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER R (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER U (Regional_Indicator) ÷ [0.3]
|
||||
÷ 1F1F7 × 1F1FA × 1F1F8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER R (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER U (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER S (Regional_Indicator) ÷ [0.3]
|
||||
÷ 1F1F7 × 1F1FA × 1F1F8 × 1F1EA ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER R (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER U (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER S (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER E (Regional_Indicator) ÷ [0.3]
|
||||
# NOTE: disabled because Unicode 9.0. TODO: splat in proper file here. Original: ÷ 1F1F7 × 1F1FA × 1F1F8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER R (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER U (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER S (Regional_Indicator) ÷ [0.3]
|
||||
# NOTE: disabled because Unicode 9.0. TODO: splat in proper file here. Original: ÷ 1F1F7 × 1F1FA × 1F1F8 × 1F1EA ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER R (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER U (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER S (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER E (Regional_Indicator) ÷ [0.3]
|
||||
÷ 1F1F7 × 1F1FA ÷ 200B ÷ 1F1F8 × 1F1EA ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER R (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER U (Regional_Indicator) ÷ [5.0] ZERO WIDTH SPACE (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER S (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER E (Regional_Indicator) ÷ [0.3]
|
||||
÷ 1F1E6 × 1F1E7 × 1F1E8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER B (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER C (Regional_Indicator) ÷ [0.3]
|
||||
# NOTE: disabled because Unicode 9.0. TODO: splat in proper file here. Original: ÷ 1F1E6 × 1F1E7 × 1F1E8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER B (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER C (Regional_Indicator) ÷ [0.3]
|
||||
÷ 1F1E6 × 200D ÷ 1F1E7 × 1F1E8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (Regional_Indicator) × [9.0] ZERO WIDTH JOINER (Extend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER C (Regional_Indicator) ÷ [0.3]
|
||||
÷ 1F1E6 × 1F1E7 × 200D ÷ 1F1E8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (Regional_Indicator) × [8.1] REGIONAL INDICATOR SYMBOL LETTER B (Regional_Indicator) × [9.0] ZERO WIDTH JOINER (Extend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (Regional_Indicator) ÷ [0.3]
|
||||
÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (Extend) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3]
|
||||
|
||||
Reference in New Issue
Block a user