Skip to content

Commit 850e5e4

Browse files
committed
fix(runtime): Filter members which are unavailable in the current SDK
* Add `isImplementedInClass` and `isAvailableInClass` methods in `PropertyMeta` and `MethodMeta` which additionally check whether a Class instance supports a given optional method * Use `isAvailableInClass` instead of `isAvailable` in `getOwnPropertyNames` of `ObjCPrototype`, `ObjCConstructorBase`, `ObjCConstructorNative` and in `materializeProperties` of `ObjCConstructorNative` * Filter via `isAvailableInClass` in `BaseClassMeta`'s various method and property getters * Change `MembersCollection` to be a `HashSet` to avoid adding the same member more than once when collecting them from the inheritance chain * Remove unused functions from `BaseClassMeta` * Add `findInterfaceMeta` function with fallback to the base interface if the desired one is unavailable in the current SDK version (instead of the hardcoded `NSObject` so far) * Remove hacky patch from `ObjCMethodWrapper::preInvocation` which fixed the symptoms of #978 but not its root cause. Namely, that unimplemented optional methods were returned as available properties in `getOwnPropertyNames`. * Modify fixtures and add test cases for methods availability in `InheritanceTests.js` * Add test case for unavailable base class * Add test case for property with custom selector in ApiTests. The getter of `secureTextEntry`(`isSecureText`) overrides the default `secureTextEntry` * Add `encodeVersion` `getMajorVersion` and `getMinorVersion` helpers refs #1085
1 parent e123c28 commit 850e5e4

21 files changed

+488
-253
lines changed

src/NativeScript/GlobalObject.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989

9090
static Strong<ObjCProtocolWrapper> createProtocolWrapper(GlobalObject* globalObject, const ProtocolMeta* protocolMeta, Protocol* aProtocol) {
9191
Structure* prototypeStructure = ObjCPrototype::createStructure(globalObject->vm(), globalObject, globalObject->objectPrototype());
92-
auto prototype = ObjCPrototype::create(globalObject->vm(), globalObject, prototypeStructure, protocolMeta);
92+
auto prototype = ObjCPrototype::create(globalObject->vm(), globalObject, prototypeStructure, protocolMeta, /*klass*/ nullptr);
9393
Structure* protocolWrapperStructure = ObjCProtocolWrapper::createStructure(globalObject->vm(), globalObject, globalObject->objectPrototype());
9494
auto protocolWrapper = ObjCProtocolWrapper::create(globalObject->vm(), protocolWrapperStructure, prototype.get(), protocolMeta, aProtocol);
9595
prototype->materializeProperties(globalObject->vm(), globalObject);

src/NativeScript/Metadata/Metadata.h

Lines changed: 102 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ static const V& getProperFunctionFromContainer(const std::vector<V>& container,
4646
return *callee;
4747
}
4848

49+
inline UInt8 encodeVersion(UInt8 majorVersion, UInt8 minorVersion) {
50+
return (majorVersion << 3) | minorVersion;
51+
}
52+
53+
inline UInt8 getMajorVersion(UInt8 encodedVersion) {
54+
return encodedVersion >> 3;
55+
}
56+
57+
inline UInt8 getMinorVersion(UInt8 encodedVersion) {
58+
return encodedVersion & 0b111;
59+
}
60+
4961
// Bit indices in flags section
5062
enum MetaFlags {
5163
HasName = 7,
@@ -126,6 +138,7 @@ enum BinaryTypeEncodingType : Byte {
126138
template <typename T>
127139
struct PtrTo;
128140
struct Meta;
141+
struct InterfaceMeta;
129142
struct ProtocolMeta;
130143
struct ModuleMeta;
131144
struct LibraryMeta;
@@ -273,6 +286,12 @@ struct GlobalTable {
273286

274287
ArrayOfPtrTo<ArrayOfPtrTo<Meta>> buckets;
275288

289+
const InterfaceMeta* findInterfaceMeta(WTF::StringImpl* identifier) const;
290+
291+
const InterfaceMeta* findInterfaceMeta(const char* identifierString) const;
292+
293+
const InterfaceMeta* findInterfaceMeta(const char* identifierString, size_t length, unsigned hash) const;
294+
276295
const Meta* findMeta(WTF::StringImpl* identifier, bool onlyIfAvailable = true) const;
277296

278297
const Meta* findMeta(const char* identifierString, bool onlyIfAvailable = true) const;
@@ -667,9 +686,24 @@ struct MethodMeta : MemberMeta {
667686
const char* constructorTokens() const {
668687
return this->_constructorTokens.valuePtr();
669688
}
689+
690+
bool isImplementedInClass(Class klass, bool isStatic) const {
691+
// class can be null for Protocol prototypes, treat all members in a protocol as implemented
692+
if (klass == nullptr) {
693+
return true;
694+
}
695+
696+
return nullptr != (isStatic ? class_getClassMethod(klass, this->selector()) : class_getInstanceMethod(klass, this->selector()));
697+
}
698+
699+
bool isAvailableInClass(Class klass, bool isStatic) const {
700+
return this->isAvailable() && this->isImplementedInClass(klass, isStatic);
701+
}
670702
};
671703

672-
std::unordered_map<std::string, std::vector<const MemberMeta*>> getMetasByJSNames(std::vector<const MemberMeta*> methods);
704+
typedef HashSet<const MemberMeta*> MembersCollection;
705+
706+
std::unordered_map<std::string, MembersCollection> getMetasByJSNames(MembersCollection methods);
673707

674708
struct PropertyMeta : MemberMeta {
675709
PtrTo<MethodMeta> method1;
@@ -691,6 +725,16 @@ struct PropertyMeta : MemberMeta {
691725
const MethodMeta* setter() const {
692726
return (this->hasSetter()) ? (this->hasGetter() ? method2.valuePtr() : method1.valuePtr()) : nullptr;
693727
}
728+
729+
bool isImplementedInClass(Class klass, bool isStatic) const {
730+
bool getterAvailable = this->hasGetter() && this->getter()->isImplementedInClass(klass, isStatic);
731+
bool setterAvailable = this->hasSetter() && this->setter()->isImplementedInClass(klass, isStatic);
732+
return getterAvailable || setterAvailable;
733+
}
734+
735+
bool isAvailableInClass(Class klass, bool isStatic) const {
736+
return this->isAvailable() && this->isImplementedInClass(klass, isStatic);
737+
}
694738
};
695739

696740
struct BaseClassMeta : Meta {
@@ -706,7 +750,7 @@ struct BaseClassMeta : Meta {
706750

707751
const MethodMeta* member(const char* identifier, size_t length, MemberType type, size_t paramsCount, bool includeProtocols = true, bool onlyIfAvailable = true) const;
708752

709-
const std::vector<const MemberMeta*> members(const char* identifier, size_t length, MemberType type, bool includeProtocols = true, bool onlyIfAvailable = true) const;
753+
const MembersCollection members(const char* identifier, size_t length, MemberType type, bool includeProtocols = true, bool onlyIfAvailable = true) const;
710754

711755
const MemberMeta* member(StringImpl* identifier, MemberType type, bool includeProtocols = true) const {
712756
const char* identif = reinterpret_cast<const char*>(identifier->characters8());
@@ -720,7 +764,7 @@ struct BaseClassMeta : Meta {
720764
return this->member(identif, length, type, paramsCount, includeProtocols);
721765
}
722766

723-
const std::vector<const MemberMeta*> members(StringImpl* identifier, MemberType type, bool includeProtocols = true) const {
767+
const MembersCollection members(StringImpl* identifier, MemberType type, bool includeProtocols = true) const {
724768
const char* identif = reinterpret_cast<const char*>(identifier->characters8());
725769
size_t length = (size_t)identifier->length();
726770
return this->members(identif, length, type, includeProtocols);
@@ -731,101 +775,110 @@ struct BaseClassMeta : Meta {
731775
}
732776

733777
/// instance methods
734-
const MethodMeta* instanceMethod(const char* identifier, size_t paramsCount, bool includeProtocols = true) const {
735-
return this->member(identifier, strlen(identifier), MemberType::InstanceMethod, paramsCount, includeProtocols);
736-
}
737778

738-
const MethodMeta* instanceMethod(StringImpl* identifier, size_t paramsCount, bool includeProtocols = true) const {
739-
return this->member(identifier, MemberType::InstanceMethod, paramsCount, includeProtocols);
779+
// Remove all optional methods/properties which are not implemented in the class
780+
template <typename TMemberMeta>
781+
static void filterUnavailableMembers(MembersCollection& members, Class klass, bool isStatic) {
782+
members.removeIf([klass, isStatic](const MemberMeta* memberMeta) {
783+
return !static_cast<const TMemberMeta*>(memberMeta)->isAvailableInClass(klass, isStatic);
784+
});
740785
}
741786

742-
const std::vector<const MemberMeta*> getInstanceMethods(StringImpl* identifier, bool includeProtocols = true) const {
743-
return this->members(identifier, MemberType::InstanceMethod, includeProtocols);
787+
const MembersCollection getInstanceMethods(StringImpl* identifier, Class klass, bool includeProtocols = true) const {
788+
MembersCollection methods = this->members(identifier, MemberType::InstanceMethod, includeProtocols);
789+
790+
filterUnavailableMembers<MethodMeta>(methods, klass, false);
791+
792+
return methods;
744793
}
745794

746795
/// static methods
747-
const MethodMeta* staticMethod(const char* identifier, size_t paramsCount, bool includeProtocols = true) const {
748-
return this->member(identifier, strlen(identifier), MemberType::StaticMethod, paramsCount, includeProtocols);
749-
}
796+
const MembersCollection getStaticMethods(StringImpl* identifier, Class klass, bool includeProtocols = true) const {
797+
MembersCollection methods = this->members(identifier, MemberType::StaticMethod, includeProtocols);
750798

751-
const std::vector<const MemberMeta*> getStaticMethods(StringImpl* identifier, bool includeProtocols = true) const {
752-
return this->members(identifier, MemberType::StaticMethod, includeProtocols);
753-
}
799+
filterUnavailableMembers<MethodMeta>(methods, klass, true);
754800

755-
const MethodMeta* staticMethod(StringImpl* identifier, size_t paramsCount, bool includeProtocols = true) const {
756-
return this->member(identifier, MemberType::StaticMethod, paramsCount, includeProtocols);
801+
return methods;
757802
}
758803

759804
/// instance properties
760-
const PropertyMeta* instanceProperty(const char* identifier, bool includeProtocols = true) const {
761-
return reinterpret_cast<const PropertyMeta*>(this->member(identifier, MemberType::InstanceProperty, includeProtocols));
805+
const PropertyMeta* instanceProperty(const char* identifier, Class klass, bool includeProtocols = true) const {
806+
auto propMeta = static_cast<const PropertyMeta*>(this->member(identifier, MemberType::InstanceProperty, includeProtocols));
807+
return propMeta && propMeta->isAvailableInClass(klass, /*isStatic*/ false) ? propMeta : nullptr;
762808
}
763809

764-
const PropertyMeta* instanceProperty(StringImpl* identifier, bool includeProtocols = true) const {
765-
return reinterpret_cast<const PropertyMeta*>(this->member(identifier, MemberType::InstanceProperty, includeProtocols));
810+
const PropertyMeta* instanceProperty(StringImpl* identifier, Class klass, bool includeProtocols = true) const {
811+
auto propMeta = static_cast<const PropertyMeta*>(this->member(identifier, MemberType::InstanceProperty, includeProtocols));
812+
return propMeta && propMeta->isAvailableInClass(klass, /*isStatic*/ false) ? propMeta : nullptr;
766813
}
767814

768815
/// static properties
769-
const PropertyMeta* staticProperty(const char* identifier, bool includeProtocols = true) const {
770-
return reinterpret_cast<const PropertyMeta*>(this->member(identifier, MemberType::StaticProperty, includeProtocols));
816+
const PropertyMeta* staticProperty(const char* identifier, Class klass, bool includeProtocols = true) const {
817+
auto propMeta = static_cast<const PropertyMeta*>(this->member(identifier, MemberType::StaticProperty, includeProtocols));
818+
return propMeta && propMeta->isAvailableInClass(klass, /*isStatic*/ true) ? propMeta : nullptr;
771819
}
772820

773-
const PropertyMeta* staticProperty(StringImpl* identifier, bool includeProtocols = true) const {
774-
return reinterpret_cast<const PropertyMeta*>(this->member(identifier, MemberType::StaticProperty, includeProtocols));
821+
const PropertyMeta* staticProperty(StringImpl* identifier, Class klass, bool includeProtocols = true) const {
822+
auto propMeta = static_cast<const PropertyMeta*>(this->member(identifier, MemberType::StaticProperty, includeProtocols));
823+
return propMeta && propMeta->isAvailableInClass(klass, /*isStatic*/ true) ? propMeta : nullptr;
775824
}
776825

777826
/// vectors
778-
std::vector<const PropertyMeta*> instanceProperties() const {
827+
std::vector<const PropertyMeta*> instanceProperties(Class klass) const {
779828
std::vector<const PropertyMeta*> properties;
780-
return this->instanceProperties(properties);
829+
return this->instanceProperties(properties, klass);
781830
}
782831

783-
std::vector<const PropertyMeta*> instancePropertiesWithProtocols() const {
832+
std::vector<const PropertyMeta*> instancePropertiesWithProtocols(Class klass) const {
784833
std::vector<const PropertyMeta*> properties;
785-
return this->instancePropertiesWithProtocols(properties);
834+
return this->instancePropertiesWithProtocols(properties, klass);
786835
}
787836

788-
std::vector<const PropertyMeta*> instanceProperties(std::vector<const PropertyMeta*>& container) const {
837+
std::vector<const PropertyMeta*> instanceProperties(std::vector<const PropertyMeta*>& container, Class klass) const {
789838
for (Array<PtrTo<PropertyMeta>>::iterator it = this->instanceProps->begin(); it != this->instanceProps->end(); it++) {
790-
container.push_back((*it).valuePtr());
839+
if ((*it)->isAvailableInClass(klass, /*isStatic*/ false)) {
840+
container.push_back((*it).valuePtr());
841+
}
791842
}
792843
return container;
793844
}
794845

795-
std::vector<const PropertyMeta*> instancePropertiesWithProtocols(std::vector<const PropertyMeta*>& container) const;
846+
std::vector<const PropertyMeta*> instancePropertiesWithProtocols(std::vector<const PropertyMeta*>& container, Class klass) const;
796847

797-
std::vector<const PropertyMeta*> staticProperties() const {
848+
std::vector<const PropertyMeta*> staticProperties(Class klass) const {
798849
std::vector<const PropertyMeta*> properties;
799-
return this->staticProperties(properties);
850+
return this->staticProperties(properties, klass);
800851
}
801852

802-
std::vector<const PropertyMeta*> staticPropertiesWithProtocols() const {
853+
std::vector<const PropertyMeta*> staticPropertiesWithProtocols(Class klass) const {
803854
std::vector<const PropertyMeta*> properties;
804-
return this->staticPropertiesWithProtocols(properties);
855+
return this->staticPropertiesWithProtocols(properties, klass);
805856
}
806857

807-
std::vector<const PropertyMeta*> staticProperties(std::vector<const PropertyMeta*>& container) const {
858+
std::vector<const PropertyMeta*> staticProperties(std::vector<const PropertyMeta*>& container, Class klass) const {
808859
for (Array<PtrTo<PropertyMeta>>::iterator it = this->staticProps->begin(); it != this->staticProps->end(); it++) {
809-
container.push_back((*it).valuePtr());
860+
if ((*it)->isAvailableInClass(klass, /*isStatic*/ true)) {
861+
container.push_back((*it).valuePtr());
862+
}
810863
}
811864
return container;
812865
}
813866

814-
std::vector<const PropertyMeta*> staticPropertiesWithProtocols(std::vector<const PropertyMeta*>& container) const;
867+
std::vector<const PropertyMeta*> staticPropertiesWithProtocols(std::vector<const PropertyMeta*>& container, Class klass) const;
815868

816-
std::vector<const MethodMeta*> initializers() const {
869+
std::vector<const MethodMeta*> initializers(Class klass) const {
817870
std::vector<const MethodMeta*> initializers;
818-
return this->initializers(initializers);
871+
return this->initializers(initializers, klass);
819872
}
820873

821-
std::vector<const MethodMeta*> initializersWithProtcols() const {
874+
std::vector<const MethodMeta*> initializersWithProtocols(Class klass) const {
822875
std::vector<const MethodMeta*> initializers;
823-
return this->initializersWithProtcols(initializers);
876+
return this->initializersWithProtocols(initializers, klass);
824877
}
825878

826-
std::vector<const MethodMeta*> initializers(std::vector<const MethodMeta*>& container) const;
879+
std::vector<const MethodMeta*> initializers(std::vector<const MethodMeta*>& container, Class klass) const;
827880

828-
std::vector<const MethodMeta*> initializersWithProtcols(std::vector<const MethodMeta*>& container) const;
881+
std::vector<const MethodMeta*> initializersWithProtocols(std::vector<const MethodMeta*>& container, Class klass) const;
829882
};
830883

831884
struct ProtocolMeta : BaseClassMeta {
@@ -843,9 +896,10 @@ struct InterfaceMeta : BaseClassMeta {
843896

844897
const InterfaceMeta* baseMeta() const {
845898
if (this->baseName() != nullptr) {
846-
const Meta* baseMeta = MetaFile::instance()->globalTable()->findMeta(this->baseName());
847-
return baseMeta->type() == MetaType::Interface ? reinterpret_cast<const InterfaceMeta*>(baseMeta) : nullptr;
899+
const InterfaceMeta* baseMeta = MetaFile::instance()->globalTable()->findInterfaceMeta(this->baseName());
900+
return baseMeta;
848901
}
902+
849903
return nullptr;
850904
}
851905
};

0 commit comments

Comments
 (0)