Sfoglia il codice sorgente

kernel: .methods form an inheritance chain

Also change implementations of Behavior >> lookupSelector:
and Behavior >> canUnderstand: to use the shorter way.
Herby Vojčík 4 anni fa
parent
commit
13485da539

+ 18 - 9
lang/base/kernel-fundamentals.js

@@ -8,6 +8,8 @@ define(['./junk-drawer'], function ($goodies) {
     var addElement = $goodies.addElement;
     var addElement = $goodies.addElement;
     var removeElement = $goodies.removeElement;
     var removeElement = $goodies.removeElement;
 
 
+    var hop = Object.prototype.hasOwnProperty;
+
     function SelectorsBrik (brikz, st) {
     function SelectorsBrik (brikz, st) {
         var selectorSet = Object.create(null);
         var selectorSet = Object.create(null);
         var selectors = this.selectors = [];
         var selectors = this.selectors = [];
@@ -171,7 +173,8 @@ define(['./junk-drawer'], function ($goodies) {
 
 
             this.setupMethods = function (traitOrBehavior) {
             this.setupMethods = function (traitOrBehavior) {
                 traitOrBehavior.localMethods = Object.create(null);
                 traitOrBehavior.localMethods = Object.create(null);
-                traitOrBehavior.methods = Object.create(null);
+                var superclass = traitOrBehavior.superclass;
+                traitOrBehavior.methods = Object.create(superclass ? superclass.methods : null);
             };
             };
 
 
             function setLocalMethods (traitOrBehavior, newLocalMethods) {
             function setLocalMethods (traitOrBehavior, newLocalMethods) {
@@ -191,13 +194,19 @@ define(['./junk-drawer'], function ($goodies) {
             declareEvent("methodReplaced");
             declareEvent("methodReplaced");
 
 
             function updateMethod (selector, traitOrBehavior) {
             function updateMethod (selector, traitOrBehavior) {
-                var oldMethod = traitOrBehavior.methods[selector],
-                    newMethod = traitOrBehavior.localMethods[selector];
-                if (oldMethod == null && newMethod == null) {
-                    console.warn("Removal of nonexistent method " + traitOrBehavior + " >> " + selector);
-                    return;
+                var oldMethod,
+                    newMethod = traitOrBehavior.localMethods[selector],
+                    methods = traitOrBehavior.methods;
+                if (hop.call(methods, selector)) {
+                    oldMethod = methods[selector];
+                    if (newMethod === oldMethod) return;
+                } else {
+                    if (newMethod == null) {
+                        console.warn("Removal of nonexistent method " + traitOrBehavior + " >> " + selector);
+                        return;
+                    }
+                    oldMethod = null;
                 }
                 }
-                if (newMethod === oldMethod) return;
                 if (newMethod != null) {
                 if (newMethod != null) {
                     if (newMethod.methodClass && newMethod.methodClass !== traitOrBehavior) {
                     if (newMethod.methodClass && newMethod.methodClass !== traitOrBehavior) {
                         console.warn("Resetting methodClass of " + newMethod.methodClass.name + " >> " + selector + " to " + traitOrBehavior.name);
                         console.warn("Resetting methodClass of " + newMethod.methodClass.name + " >> " + selector + " to " + traitOrBehavior.name);
@@ -206,10 +215,10 @@ define(['./junk-drawer'], function ($goodies) {
                     if (newMethod.instantiateFn) {
                     if (newMethod.instantiateFn) {
                         newMethod.fn = newMethod.instantiateFn(traitOrBehavior);
                         newMethod.fn = newMethod.instantiateFn(traitOrBehavior);
                     }
                     }
-                    traitOrBehavior.methods[selector] = newMethod;
+                    methods[selector] = newMethod;
                     traitOrBehavior.methodAdded(newMethod);
                     traitOrBehavior.methodAdded(newMethod);
                 } else {
                 } else {
-                    delete traitOrBehavior.methods[selector];
+                    delete methods[selector];
                     traitOrBehavior.methodRemoved(oldMethod);
                     traitOrBehavior.methodRemoved(oldMethod);
                 }
                 }
                 emit.methodReplaced(newMethod, oldMethod, traitOrBehavior);
                 emit.methodReplaced(newMethod, oldMethod, traitOrBehavior);

+ 2 - 1
lang/base/kernel-language.js

@@ -243,13 +243,14 @@ define(['./junk-drawer'], function ($goodies) {
             var nilAsClass = this.nilAsClass = {
             var nilAsClass = this.nilAsClass = {
                 fn: SmalltalkRoot,
                 fn: SmalltalkRoot,
                 subclasses: [],
                 subclasses: [],
-                a$cls: {fn: SmalltalkClass}
+                a$cls: {fn: SmalltalkClass, methods: Object.create(null)}
             };
             };
 
 
             this.bootstrapHierarchy = function (realClass) {
             this.bootstrapHierarchy = function (realClass) {
                 nilAsClass.a$cls = realClass;
                 nilAsClass.a$cls = realClass;
                 nilAsClass.subclasses.forEach(function (each) {
                 nilAsClass.subclasses.forEach(function (each) {
                     each.a$cls.superclass = realClass;
                     each.a$cls.superclass = realClass;
+                    Object.setPrototypeOf(each.a$cls.methods, realClass.methods);
                     registerToSuperclass(each.a$cls);
                     registerToSuperclass(each.a$cls);
                 });
                 });
             };
             };

+ 3 - 1
lang/base/kernel-runtime.js

@@ -10,6 +10,8 @@ define(['./junk-drawer'], function ($goodies) {
     var deleteKeysFrom = $goodies.deleteKeysFrom;
     var deleteKeysFrom = $goodies.deleteKeysFrom;
     var extendWithMethods = $goodies.extendWithMethods;
     var extendWithMethods = $goodies.extendWithMethods;
 
 
+    var hop = Object.prototype.hasOwnProperty;
+
     function cleanMethodOfJsObjectEx (obj, name) {
     function cleanMethodOfJsObjectEx (obj, name) {
         var attachments;
         var attachments;
         var old = Object.getOwnPropertyDescriptor(obj, name);
         var old = Object.getOwnPropertyDescriptor(obj, name);
@@ -271,7 +273,7 @@ define(['./junk-drawer'], function ($goodies) {
                 var jsSelector = method.jsSelector;
                 var jsSelector = method.jsSelector;
                 st.traverseClassTree(klass, function (subclass, sentinel) {
                 st.traverseClassTree(klass, function (subclass, sentinel) {
                     if (subclass === exclude) return;
                     if (subclass === exclude) return;
-                    if (subclass.methods[selector]) return sentinel;
+                    if (hop.call(subclass.methods, selector)) return sentinel;
                     if (subclass.detachedRoot) {
                     if (subclass.detachedRoot) {
                         installMethodOfJsObjectEx(subclass.fn.prototype, jsSelector, subclass.superclass.fn.prototype[jsSelector]);
                         installMethodOfJsObjectEx(subclass.fn.prototype, jsSelector, subclass.superclass.fn.prototype[jsSelector]);
                     }
                     }

+ 9 - 47
lang/src/Kernel-Classes.js

@@ -324,29 +324,17 @@ selector: "canUnderstand:",
 protocol: "testing",
 protocol: "testing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aSelector"],
 args: ["aSelector"],
-source: "canUnderstand: aSelector\x0a\x09^ (self includesSelector: aSelector asString) or: [\x0a\x09\x09self superclass ifNil: [ false ] ifNotNil: [ :superClass | superClass canUnderstand: aSelector ]]",
+source: "canUnderstand: aSelector\x0a\x09^ (self lookupSelector: aSelector) notNil",
 referencedClasses: [],
 referencedClasses: [],
 //>>excludeEnd("ide");
 //>>excludeEnd("ide");
 pragmas: [],
 pragmas: [],
-messageSends: ["or:", "includesSelector:", "asString", "ifNil:ifNotNil:", "superclass", "canUnderstand:"]
+messageSends: ["notNil", "lookupSelector:"]
 }, function ($methodClass){ return function (aSelector){
 }, function ($methodClass){ return function (aSelector){
 var self=this,$self=this;
 var self=this,$self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 //>>excludeEnd("ctx");
-var $1;
-if($core.assert($self._includesSelector_($recv(aSelector)._asString()))){
-return true;
-} else {
-$1=$self._superclass();
-if($1 == null || $1.a$nil){
-return false;
-} else {
-var superClass;
-superClass=$1;
-return $recv(superClass)._canUnderstand_(aSelector);
-}
-}
+return $recv($self._lookupSelector_(aSelector))._notNil();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"canUnderstand:",{aSelector:aSelector})});
 }, function($ctx1) {$ctx1.fill(self,"canUnderstand:",{aSelector:aSelector})});
 //>>excludeEnd("ctx");
 //>>excludeEnd("ctx");
@@ -543,46 +531,20 @@ selector: "lookupSelector:",
 protocol: "accessing",
 protocol: "accessing",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["selector"],
 args: ["selector"],
-source: "lookupSelector: selector\x0a\x09\x22Look up the given selector in my methodDictionary.\x0a\x09Return the corresponding method if found.\x0a\x09Otherwise chase the superclass chain and try again.\x0a\x09Return nil if no method is found.\x22\x0a\x09\x0a\x09| lookupClass |\x0a\x09\x0a\x09lookupClass := self.\x0a\x09[ lookupClass = nil ] whileFalse: [\x0a\x09\x09(lookupClass includesSelector: selector)\x0a\x09\x09\x09\x09ifTrue: [ ^ lookupClass methodAt: selector ].\x0a\x09\x09\x09lookupClass := lookupClass superclass ].\x0a\x09^ nil",
+source: "lookupSelector: selector\x0a\x09\x22Look up the given selector in my methodDictionary.\x0a\x09Return the corresponding method if found.\x0a\x09Otherwise chase the superclass chain and try again.\x0a\x09Return nil if no method is found.\x22\x0a\x09\x0a\x09<inlineJS: 'return $self.methods[selector]'>",
 referencedClasses: [],
 referencedClasses: [],
 //>>excludeEnd("ide");
 //>>excludeEnd("ide");
-pragmas: [],
-messageSends: ["whileFalse:", "=", "ifTrue:", "includesSelector:", "methodAt:", "superclass"]
+pragmas: [["inlineJS:", ["return $self.methods[selector]"]]],
+messageSends: []
 }, function ($methodClass){ return function (selector){
 }, function ($methodClass){ return function (selector){
 var self=this,$self=this;
 var self=this,$self=this;
-var lookupClass;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 //>>excludeEnd("ctx");
-var $early={};
-try {
-lookupClass=self;
-$recv((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(lookupClass).__eq(nil);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
-//>>excludeEnd("ctx");
-}))._whileFalse_((function(){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-if($core.assert($recv(lookupClass)._includesSelector_(selector))){
-throw $early=[$recv(lookupClass)._methodAt_(selector)];
-}
-lookupClass=$recv(lookupClass)._superclass();
-return lookupClass;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
-//>>excludeEnd("ctx");
-}));
-return nil;
-}
-catch(e) {if(e===$early)return e[0]; throw e}
+return $self.methods[selector];
+return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"lookupSelector:",{selector:selector,lookupClass:lookupClass})});
+}, function($ctx1) {$ctx1.fill(self,"lookupSelector:",{selector:selector})});
 //>>excludeEnd("ctx");
 //>>excludeEnd("ctx");
 }; }),
 }; }),
 $globals.Behavior);
 $globals.Behavior);

+ 2 - 10
lang/src/Kernel-Classes.st

@@ -99,14 +99,7 @@ lookupSelector: selector
 	Otherwise chase the superclass chain and try again.
 	Otherwise chase the superclass chain and try again.
 	Return nil if no method is found."
 	Return nil if no method is found."
 	
 	
-	| lookupClass |
-	
-	lookupClass := self.
-	[ lookupClass = nil ] whileFalse: [
-		(lookupClass includesSelector: selector)
-				ifTrue: [ ^ lookupClass methodAt: selector ].
-			lookupClass := lookupClass superclass ].
-	^ nil
+	<inlineJS: 'return $self.methods[selector]'>
 !
 !
 
 
 prototype
 prototype
@@ -174,8 +167,7 @@ makeJavaScriptConstructorSubclassOf: javaScriptClass
 !Behavior methodsFor: 'testing'!
 !Behavior methodsFor: 'testing'!
 
 
 canUnderstand: aSelector
 canUnderstand: aSelector
-	^ (self includesSelector: aSelector asString) or: [
-		self superclass ifNil: [ false ] ifNotNil: [ :superClass | superClass canUnderstand: aSelector ]]
+	^ (self lookupSelector: aSelector) notNil
 !
 !
 
 
 includesBehavior: aClass
 includesBehavior: aClass