Skip to content

Commit

Permalink
Runtime/IRGen: Replace the _SwiftNativeNS*Base +load hack with a comp…
Browse files Browse the repository at this point in the history
…iler hack.

Rather than swizzle the superclass of these bridging classes at +load time, have the compiler set their ObjC runtime base classes, using a "@_swift_native_objc_runtime_base" attribute that tells the compiler to use a different implicit base class from SwiftObject. This lets the runtime shed its last lingering +loads, and should overall be more robust, since it doesn't rely on static initialization order or deprecated ObjC runtime calls.

Swift SVN r28219
  • Loading branch information
jckarter committed May 6, 2015
1 parent d51acde commit ab09922
Show file tree
Hide file tree
Showing 18 changed files with 192 additions and 44 deletions.
3 changes: 3 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ SIMPLE_DECL_ATTR(rethrows, Rethrows,

DECL_ATTR(warn_unused_result, WarnUnusedResult, OnFunc | OnConstructor, 58)

DECL_ATTR(_swift_native_objc_runtime_base, SwiftNativeObjCRuntimeBase,
OnClass | UserInaccessible, 59)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef SIMPLE_DECL_ATTR
Expand Down
26 changes: 26 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,32 @@ class AlignmentAttr : public DeclAttribute {
}
};

/// Defines the @_swift_native_objc_runtime_base attribute.
///
/// This attribute indicates a class that should be treated semantically
/// as a native Swift root class, but which inherits a specific Objective-C
/// class at runtime. For most classes this is the runtime's "SwiftObject"
/// root class. The compiler does not need to know about the class; it's the
/// build system's responsibility to link against the ObjC code that implements
/// the root class, and the ObjC implementation's responsibility to ensure
/// instances begin with a Swift-refcounting-compatible object header and
/// override all the necessary NSObject refcounting methods.
class SwiftNativeObjCRuntimeBaseAttr : public DeclAttribute {
public:
SwiftNativeObjCRuntimeBaseAttr(Identifier BaseClassName,
SourceLoc AtLoc, SourceRange Range,
bool Implicit)
: DeclAttribute(DAK_SwiftNativeObjCRuntimeBase, AtLoc, Range, Implicit),
BaseClassName(BaseClassName) {}

// The base class's name.
const Identifier BaseClassName;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_SwiftNativeObjCRuntimeBase;
}
};

/// Determine the result of comparing an availability attribute to a specific
/// minimum platform version.
enum class MinVersionComparison {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,9 @@ ERROR(attr_expected_string_literal,attribute_parsing,none,
ERROR(alignment_must_be_positive_integer,attribute_parsing,none,
"alignment value must be a positive integer literal", ())

ERROR(swift_native_objc_runtime_base_must_be_identifier,attribute_parsing,none,
"@_swift_native_objc_runtime_base class name must be an identifier", ())

ERROR(attr_interpolated_string,attribute_parsing,none,
"%0 cannot be an interpolated string literal", (StringRef))

Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,9 @@ ERROR(ibaction_nonobject_argument,sema_tcd,none,
ERROR(no_objc_tagged_pointer_not_class_protocol,sema_tcd,none,
"@unsafe_no_objc_tagged_pointer can only be applied to class protocols",
())
ERROR(swift_native_objc_runtime_base_not_on_root_class,sema_tcd,none,
"@_swift_native_objc_runtime_base_not_on_root_class can only be applied "
"to root classes", ())
ERROR(access_control_in_protocol,sema_tcd,none,
"%0 modifier cannot be used in protocols", (DeclAttribute))
ERROR(access_control_setter,sema_tcd,none,
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ IDENTIFIER(setObject)
IDENTIFIER(simd)
IDENTIFIER(Some)
IDENTIFIER(subscript)
IDENTIFIER(SwiftObject)
IDENTIFIER(toRaw)
IDENTIFIER(Type)
IDENTIFIER(value)
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,12 @@ namespace decls_block {
BCFixed<1>, // implicit flag
BCFixed<31> // alignment
>;

using SwiftNativeObjCRuntimeBaseDeclAttrLayout = BCRecordLayout<
SwiftNativeObjCRuntimeBase_DECL_ATTR,
BCFixed<1>, // implicit flag
IdentifierIDField // name
>;

using SemanticsDeclAttrLayout = BCRecordLayout<
Semantics_DECL_ATTR,
Expand Down
9 changes: 9 additions & 0 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ void DeclAttribute::print(ASTPrinter &Printer,
case DAK_SetterAccessibility:
Printer << getAttrName() << "(set)";
break;

case DAK_SwiftNativeObjCRuntimeBase: {
auto *attr = cast<SwiftNativeObjCRuntimeBaseAttr>(this);
Printer << "@_swift_native_objc_runtime_base("
<< attr->BaseClassName.str() << ")";
break;
}

case DAK_RawDocComment:
// Not printed.
Expand Down Expand Up @@ -353,6 +360,8 @@ StringRef DeclAttribute::getAttrName() const {
return "asmname";
case DAK_Alignment:
return "_alignment";
case DAK_SwiftNativeObjCRuntimeBase:
return "_swift_native_objc_runtime_base";
case DAK_Semantics:
return "_semantics";
case DAK_Availability:
Expand Down
38 changes: 31 additions & 7 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,12 @@ namespace {
// which will be visible in the Objective-C type system.
if (Root->hasClangNode()) return Root;

return IGM.getSwiftRootClass();
// FIXME: If the root class specifies its own runtime ObjC base class,
// assume that that base class ultimately inherits NSObject.
if (Root->getAttrs().hasAttribute<SwiftNativeObjCRuntimeBaseAttr>())
return IGM.getObjCRuntimeBaseClass(IGM.Context.Id_NSObject);

return IGM.getObjCRuntimeBaseClass(IGM.Context.Id_SwiftObject);
}

const FieldEntry &getFieldEntry(VarDecl *field) const {
Expand Down Expand Up @@ -1810,14 +1815,14 @@ const TypeInfo *TypeConverter::convertClassType(ClassDecl *D) {
D, refcount);
}

/// Lazily declare the Swift root-class, SwiftObject.
ClassDecl *IRGenModule::getSwiftRootClass() {
if (SwiftRootClass) return SwiftRootClass;

auto name = Context.getIdentifier("SwiftObject");
/// Lazily declare a fake-looking class to represent an ObjC runtime base class.
ClassDecl *IRGenModule::getObjCRuntimeBaseClass(Identifier name) {
auto found = SwiftRootClasses.find(name);
if (found != SwiftRootClasses.end())
return found->second;

// Make a really fake-looking class.
SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(),
auto SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(),
MutableArrayRef<TypeLoc>(),
/*generics*/ nullptr,
Context.TheBuiltinModule);
Expand All @@ -1827,9 +1832,28 @@ ClassDecl *IRGenModule::getSwiftRootClass() {
/*implicit=*/true));
SwiftRootClass->setImplicit();
SwiftRootClass->setAccessibility(Accessibility::Public);

SwiftRootClasses.insert({name, SwiftRootClass});
return SwiftRootClass;
}

/// Lazily declare the ObjC runtime base class for a Swift root class.
ClassDecl *
IRGenModule::getObjCRuntimeBaseForSwiftRootClass(ClassDecl *theClass) {
assert(!theClass->hasSuperclass() && "must pass a root class");

Identifier name;
// If the class declares its own ObjC runtime base, use it.
if (auto baseAttr = theClass->getAttrs()
.getAttribute<SwiftNativeObjCRuntimeBaseAttr>()) {
name = baseAttr->BaseClassName;
} else {
// Otherwise, use the standard SwiftObject class.
name = Context.Id_SwiftObject;
}
return getObjCRuntimeBaseClass(name);
}

ClassDecl *irgen::getRootClassForMetaclass(IRGenModule &IGM, ClassDecl *C) {
LayoutClass layout(IGM, ResilienceScope::Local, C, getSelfType(C));

Expand Down
10 changes: 6 additions & 4 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2574,10 +2574,11 @@ namespace {
}

// We have to do getAddrOfObjCClass ourselves here because
// getSwiftRootClass needs to be ObjC-mangled but isn't
// the ObjC runtime base needs to be ObjC-mangled but isn't
// actually imported from a clang module.
addWord(IGM.getAddrOfObjCClass(IGM.getSwiftRootClass(),
NotForDefinition));
addWord(IGM.getAddrOfObjCClass(
IGM.getObjCRuntimeBaseForSwiftRootClass(Target),
NotForDefinition));
return;
}

Expand Down Expand Up @@ -2817,7 +2818,8 @@ namespace {
emitClassHeapMetadataRef(IGF, superclass->getCanonicalType(),
MetadataValueType::ObjCClass);
} else if (IGM.ObjCInterop) {
superMetadata = emitObjCHeapMetadataRef(IGF, IGM.getSwiftRootClass());
superMetadata = emitObjCHeapMetadataRef(IGF,
IGM.getObjCRuntimeBaseForSwiftRootClass(Target));
} else {
superMetadata
= llvm::ConstantPointerNull::get(IGF.IGM.ObjCClassPtrTy);
Expand Down
7 changes: 4 additions & 3 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,8 @@ class IRGenModule {
llvm::Constant *getObjCEmptyCachePtr();
llvm::Constant *getObjCEmptyVTablePtr();
llvm::Value *getObjCRetainAutoreleasedReturnValueMarker();
ClassDecl *getSwiftRootClass();
ClassDecl *getObjCRuntimeBaseForSwiftRootClass(ClassDecl *theClass);
ClassDecl *getObjCRuntimeBaseClass(Identifier name);
llvm::Module *getModule() const;
llvm::Module *releaseModule();
llvm::AttributeSet getAllocAttrs();
Expand All @@ -542,8 +543,8 @@ class IRGenModule {
llvm::Constant *ObjCEmptyVTablePtr = nullptr;
llvm::Constant *ObjCISAMaskPtr = nullptr;
Optional<llvm::Value*> ObjCRetainAutoreleasedReturnValueMarker;
ClassDecl *SwiftRootClass = nullptr;
llvm::AttributeSet AllocAttrs;
llvm::DenseMap<Identifier, ClassDecl*> SwiftRootClasses;
llvm::AttributeSet AllocAttrs;

#define FUNCTION_ID(Id) \
public: \
Expand Down
29 changes: 29 additions & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,35 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
break;
}

case DAK_SwiftNativeObjCRuntimeBase: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::swift_native_objc_runtime_base_must_be_identifier);
return false;
}

Identifier name = Context.getIdentifier(Tok.getText());

consumeToken(tok::identifier);

auto range = SourceRange(Loc, Tok.getRange().getStart());

if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}

Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(name,
AtLoc, range, /*implicit*/ false));
break;
}

case DAK_Semantics: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
Expand Down
22 changes: 22 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
IGNORED_ATTR(Convenience)
IGNORED_ATTR(Semantics)
IGNORED_ATTR(UnsafeNoObjCTaggedPointer)
IGNORED_ATTR(SwiftNativeObjCRuntimeBase)
IGNORED_ATTR(ObjCNonLazyRealization)
IGNORED_ATTR(Inline)
IGNORED_ATTR(Effects)
Expand Down Expand Up @@ -625,6 +626,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
void visitUIApplicationMainAttr(UIApplicationMainAttr *attr);

void visitUnsafeNoObjCTaggedPointerAttr(UnsafeNoObjCTaggedPointerAttr *attr);
void visitSwiftNativeObjCRuntimeBaseAttr(
SwiftNativeObjCRuntimeBaseAttr *attr);

void checkOperatorAttribute(DeclAttribute *attr);

Expand Down Expand Up @@ -843,6 +846,25 @@ void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr(
}
}

void AttributeChecker::visitSwiftNativeObjCRuntimeBaseAttr(
SwiftNativeObjCRuntimeBaseAttr *attr) {
// Only root classes can have the attribute.
auto theClass = dyn_cast<ClassDecl>(D);
if (!theClass) {
TC.diagnose(attr->getLocation(),
diag::swift_native_objc_runtime_base_not_on_root_class);
attr->setInvalid();
return;
}

if (theClass->hasSuperclass()) {
TC.diagnose(attr->getLocation(),
diag::swift_native_objc_runtime_base_not_on_root_class);
attr->setInvalid();
return;
}
}

void AttributeChecker::visitFinalAttr(FinalAttr *attr) {
// final on classes marks all members with final.
if (isa<ClassDecl>(D))
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4892,6 +4892,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
UNINTERESTING_ATTR(UIApplicationMain)
UNINTERESTING_ATTR(ObjCNonLazyRealization)
UNINTERESTING_ATTR(UnsafeNoObjCTaggedPointer)
UNINTERESTING_ATTR(SwiftNativeObjCRuntimeBase)

// These can't appear on overridable declarations.
UNINTERESTING_ATTR(AutoClosure)
Expand Down
13 changes: 13 additions & 0 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,19 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
isImplicit);
break;
}

case decls_block::SwiftNativeObjCRuntimeBase_DECL_ATTR: {
bool isImplicit;
IdentifierID nameID;
serialization::decls_block::SwiftNativeObjCRuntimeBaseDeclAttrLayout
::readRecord(scratch, isImplicit, nameID);

auto name = getIdentifier(nameID);
Attr = new (ctx) SwiftNativeObjCRuntimeBaseAttr(name, SourceLoc(),
SourceRange(),
isImplicit);
break;
}

case decls_block::Semantics_DECL_ATTR: {
bool isImplicit;
Expand Down
13 changes: 13 additions & 0 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,19 @@ void Serializer::writeDeclAttribute(const DeclAttribute *DA) {
return;
}

case DAK_SwiftNativeObjCRuntimeBase: {
auto *theBase = cast<SwiftNativeObjCRuntimeBaseAttr>(DA);
auto abbrCode
= DeclTypeAbbrCodes[SwiftNativeObjCRuntimeBaseDeclAttrLayout::Code];
auto nameID = addIdentifierRef(theBase->BaseClassName);

SwiftNativeObjCRuntimeBaseDeclAttrLayout::emitRecord(Out, ScratchRecord,
abbrCode,
theBase->isImplicit(),
nameID);
return;
}

case DAK_Semantics: {
auto *theAttr = cast<SemanticsAttr>(DA);
auto abbrCode = DeclTypeAbbrCodes[SemanticsDeclAttrLayout::Code];
Expand Down
21 changes: 15 additions & 6 deletions stdlib/public/core/Runtime.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -407,15 +407,24 @@ func _rawPointerToString(value: Builtin.RawPointer) -> String {
}

#if _runtime(_ObjC)
// These classes are derived from `_SwiftNativeNSXXXBase` (through runtime magic),
// At runtime, these classes are derived from `_SwiftNativeNSXXXBase`,
// which are derived from `NSXXX`.
//
// This allows us to subclass an Objective-C class and use the fast Swift
// The @swift_native_objc_runtime_base attribute
// allows us to subclass an Objective-C class and still use the fast Swift
// memory allocator.
@objc class _SwiftNativeNSArray {}
@objc class _SwiftNativeNSDictionary {}
@objc class _SwiftNativeNSSet {}
@objc class _SwiftNativeNSEnumerator {}

@objc @_swift_native_objc_runtime_base(_SwiftNativeNSArrayBase)
class _SwiftNativeNSArray {}

@objc @_swift_native_objc_runtime_base(_SwiftNativeNSDictionaryBase)
class _SwiftNativeNSDictionary {}

@objc @_swift_native_objc_runtime_base(_SwiftNativeNSSetBase)
class _SwiftNativeNSSet {}

@objc @_swift_native_objc_runtime_base(_SwiftNativeNSEnumeratorBase)
class _SwiftNativeNSEnumerator {}

//===----------------------------------------------------------------------===//
// Support for reliable testing of the return-autoreleased optimization
Expand Down
13 changes: 7 additions & 6 deletions stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,13 @@ extension String {
}
}

/// This class is derived from `_SwiftNativeNSStringBase` (through
/// runtime magic), which is derived from `NSString`.
///
/// This allows us to subclass an Objective-C class and use the fast Swift
/// memory allocator.
@objc
// At runtime, this class is derived from `_SwiftNativeNSStringBase`,
// which is derived from `NSString`.
//
// The @_swift_native_objc_runtime_base attribute
// This allows us to subclass an Objective-C class and use the fast Swift
// memory allocator.
@objc @_swift_native_objc_runtime_base(_SwiftNativeNSStringBase)
public class _SwiftNativeNSString {}

@objc
Expand Down

0 comments on commit ab09922

Please sign in to comment.