From d4e40faecc505bad79634540074beaf9e7a4a069 Mon Sep 17 00:00:00 2001 From: Martin Carlberg Date: Wed, 20 Feb 2019 23:44:00 +0100 Subject: [PATCH] Fixed: the class method +initialize was always sent even if the root class did not implement it. Forwarding and Invocation worked also on the method. To mimic Objective-C correctly the +initialize method should not be sent on a class hierarchy if it does not implement it. Also the +initialize method should not allowed to be forwarded to another target with Invocation or any other forwarding mechanism. Added test cases for this and cleaned up some test cases that succeeded for the wrong reason. This change will not affect many projects as all classes inherit from CPObject. --- Objective-J/Runtime.js | 8 ++- Tests/Objective-J/MethodDispatchTest.j | 77 +++++++++++++++++++++----- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/Objective-J/Runtime.js b/Objective-J/Runtime.js index 5a371e3aa..3d9e04440 100644 --- a/Objective-J/Runtime.js +++ b/Objective-J/Runtime.js @@ -504,9 +504,11 @@ var _class_initialize = function(/*Class*/ aClass) meta.objj_msgSend3 = objj_msgSendFast3; aClass.method_msgSend = aClass.method_dtable; - meta.method_msgSend = meta.method_dtable; + var metaMethodDTable = meta.method_msgSend = meta.method_dtable, + initializeImp = metaMethodDTable["initialize"]; - meta.objj_msgSend0(aClass, "initialize"); + if (initializeImp) + initializeImp(aClass, "initialize"); CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING); } @@ -583,7 +585,7 @@ GLOBAL(_objj_forward) = function(self, _cmd) if (implementation) return implementation(self, SEL_doesNotRecognizeSelector_, _cmd); - throw class_getName(isa) + " does not implement doesNotRecognizeSelector:. Did you forget a superclass for " + class_getName(isa) + "?"; + throw class_getName(isa) + " does not implement doesNotRecognizeSelector: when sending " + sel_getName(_cmd) + ". Did you forget a superclass for " + class_getName(isa) + "?"; }; // I think this forward:: may need to be a common method, instead of defined in CPObject. diff --git a/Tests/Objective-J/MethodDispatchTest.j b/Tests/Objective-J/MethodDispatchTest.j index 377d039eb..7e13b5264 100644 --- a/Tests/Objective-J/MethodDispatchTest.j +++ b/Tests/Objective-J/MethodDispatchTest.j @@ -13,6 +13,38 @@ @end +@implementation RootClassWithInitialize +{ +} + ++ (id)alloc +{ + return class_createInstance(self); +} + ++ (void)initialize +{ + throw "Initialize"; +} + +@end + +@implementation RootClassWithInitialize2 +{ +} + ++ (id)alloc +{ + return class_createInstance(self); +} + ++ (void)initialize +{ + throw "Initialize"; +} + +@end + @implementation RootClassWithDoesNotRecognizeSelector { } @@ -24,7 +56,7 @@ - (void)doesNotRecognizeSelector:(SEL)aSelector { - throw "ERROR"; + throw "ERROR: " + sel_getName(aSelector); } @end @@ -40,10 +72,6 @@ Class isa; } -+ (void)initialize -{ -} - + (id)alloc { return class_createInstance(self); @@ -88,10 +116,6 @@ { } -+ (void)initialize -{ -} - + (id)alloc { return class_createInstance(self); @@ -176,7 +200,7 @@ var GlobalMethodDispatchTest; } catch (anException) { - [self assert:anException equals:"RootClass does not implement doesNotRecognizeSelector:. Did you forget a superclass for RootClass?"]; + [self assert:anException equals:"RootClass does not implement doesNotRecognizeSelector: when sending doesNotExist. Did you forget a superclass for RootClass?"]; } } @@ -190,7 +214,34 @@ var GlobalMethodDispatchTest; } catch (anException) { - [self assert:anException equals:"RootClass does not implement doesNotRecognizeSelector:. Did you forget a superclass for RootClass?"]; + [self assert:anException equals:"RootClass does not implement doesNotRecognizeSelector: when sending doesNotExist. Did you forget a superclass for RootClass?"]; + } +} +- (void)test_RootClassWithInitialize_class_initialize +{ + try + { + [RootClassWithInitialize doesNotExist]; + } + catch (anException) + { + [self assert:anException equals:"Initialize"]; + } +} + +- (void)test_RootClassWithInitialize_instance_initialize +{ + // Here we create a new instance with Runtime function to not trigger the +initialize method. + // We also have to use a fresh new class or the +initialize method would have already been triggered. + var object = class_createInstance(RootClassWithInitialize2); + + try + { + [object doesNotExist]; + } + catch (anException) + { + [self assert:anException equals:"Initialize"]; } } @@ -202,7 +253,7 @@ var GlobalMethodDispatchTest; } catch (anException) { - [self assert:anException equals:"ERROR"]; + [self assert:anException equals:"ERROR: doesNotExist"]; } } @@ -216,7 +267,7 @@ var GlobalMethodDispatchTest; } catch (anException) { - [self assert:anException equals:"ERROR"]; + [self assert:anException equals:"ERROR: doesNotExist"]; } }