Browse Source

Merge branch 'master' into lhf

Conflicts:
	js/IDE.js
	st/IDE.st
Herbert Vojčík 10 years ago
parent
commit
93f18bd23a

+ 2 - 0
API-CHANGES.txt

@@ -5,6 +5,7 @@
 
 * HashedCollection >> at:ifAbsentPut: pushed up to SequenceableCollection
 * HashedCollection >> , is now allowed (removed shouldNotImplement)
+* HashedCollection and Dictionary both subclasses of AssociativeCollection
 
 + CompiledMethod >>
   + defaultProtocol
@@ -21,6 +22,7 @@
 + PackageHandler >> load:
 + AmdPackageHandler >> load:
 + Set >> removeAll
++ AssociativeCollection class
 
 - CompiledMethod >>
   - category: (use #protocol:)

+ 63 - 5
cli/js/AmberCli.js

@@ -216,15 +216,15 @@ return smalltalk.withContext(function($ctx1) {
 var $2,$1;
 $2=self["@basePath"];
 if(($receiver = $2) == nil || $receiver == null){
-$1="./";
+$1=_st(self._class())._defaultBasePath();
 } else {
 $1=$2;
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"basePath",{},smalltalk.FileServer)})},
 args: [],
-source: "basePath\x0a\x09^basePath ifNil: ['./']",
-messageSends: ["ifNil:"],
+source: "basePath\x0a\x09^basePath ifNil: [self class defaultBasePath]",
+messageSends: ["ifNil:", "defaultBasePath", "class"],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -237,10 +237,11 @@ fn: function (aString){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@basePath"]=aString;
+self._validateBasePath();
 return self}, function($ctx1) {$ctx1.fill(self,"basePath:",{aString:aString},smalltalk.FileServer)})},
 args: ["aString"],
-source: "basePath: aString\x0a\x09basePath := aString",
-messageSends: [],
+source: "basePath: aString\x0a\x09basePath := aString.\x0a\x09self validateBasePath.",
+messageSends: ["validateBasePath"],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -928,6 +929,47 @@ referencedClasses: []
 }),
 smalltalk.FileServer);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "validateBasePath",
+protocol: 'private',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4,$7,$6,$5,$8,$9;
+$1=self["@fs"];
+$2=self._basePath();
+$ctx1.sendIdx["basePath"]=1;
+_st($1)._stat_then_($2,(function(err,stat){
+return smalltalk.withContext(function($ctx2) {
+if(($receiver = err) == nil || $receiver == null){
+$3=_st(stat)._isDirectory();
+if(! smalltalk.assert($3)){
+$4=console;
+$7=self._basePath();
+$ctx2.sendIdx["basePath"]=2;
+$6="Warning: --base-path parameter ".__comma($7);
+$ctx2.sendIdx[","]=2;
+$5=_st($6).__comma(" is not a directory.");
+$ctx2.sendIdx[","]=1;
+return _st($4)._warn_($5);
+$ctx2.sendIdx["warn:"]=1;
+};
+} else {
+$8=console;
+$9=_st("Warning: path at --base-path parameter ".__comma(self._basePath())).__comma(" does not exist.");
+$ctx2.sendIdx[","]=3;
+return _st($8)._warn_($9);
+};
+}, function($ctx2) {$ctx2.fillBlock({err:err,stat:stat},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"validateBasePath",{},smalltalk.FileServer)})},
+args: [],
+source: "validateBasePath\x0a\x09\x22The basePath must be an existing directory. \x22\x0a\x09fs stat: self basePath then: [ :err :stat | err\x0a\x09\x09ifNil: [ stat isDirectory ifFalse: [ console warn: 'Warning: --base-path parameter ' , self basePath , ' is not a directory.' ]]\x0a\x09\x09ifNotNil: [ console warn: 'Warning: path at --base-path parameter ' , self basePath , ' does not exist.'  ]].",
+messageSends: ["stat:then:", "basePath", "ifNil:ifNotNil:", "ifFalse:", "isDirectory", "warn:", ","],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "withBasePath:",
@@ -1076,6 +1118,22 @@ referencedClasses: ["Array"]
 }),
 smalltalk.FileServer.klass);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "defaultBasePath",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "./";
+}, function($ctx1) {$ctx1.fill(self,"defaultBasePath",{},smalltalk.FileServer.klass)})},
+args: [],
+source: "defaultBasePath\x0a\x09^ './'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.FileServer.klass);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "defaultHost",

+ 14 - 2
cli/st/AmberCli.st

@@ -126,11 +126,12 @@ Adding new commandline parameters to `FileServer` is as easy as adding a new sin
 !FileServer methodsFor: 'accessing'!
 
 basePath
-	^basePath ifNil: ['./']
+	^basePath ifNil: [self class defaultBasePath]
 !
 
 basePath: aString
-	basePath := aString
+	basePath := aString.
+	self validateBasePath.
 !
 
 fallbackPage
@@ -229,6 +230,13 @@ require: aModuleString
 	^require value: aModuleString
 !
 
+validateBasePath
+	"The basePath must be an existing directory. "
+	fs stat: self basePath then: [ :err :stat | err
+		ifNil: [ stat isDirectory ifFalse: [ console warn: 'Warning: --base-path parameter ' , self basePath , ' is not a directory.' ]]
+		ifNotNil: [ console warn: 'Warning: path at --base-path parameter ' , self basePath , ' does not exist.'  ]].
+!
+
 withBasePath: aBaseRelativePath
 	"return a file path which is relative to the basePath."
 	^	path join: self basePath with: aBaseRelativePath
@@ -399,6 +407,10 @@ commandLineSwitches
 	^switches
 !
 
+defaultBasePath
+	^ './'
+!
+
 defaultHost
 	^'127.0.0.1'
 !

+ 63 - 5
cli/support/amber-cli.js

@@ -38839,15 +38839,15 @@ return smalltalk.withContext(function($ctx1) {
 var $2,$1;
 $2=self["@basePath"];
 if(($receiver = $2) == nil || $receiver == null){
-$1="./";
+$1=_st(self._class())._defaultBasePath();
 } else {
 $1=$2;
 };
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"basePath",{},smalltalk.FileServer)})},
 args: [],
-source: "basePath\x0a\x09^basePath ifNil: ['./']",
-messageSends: ["ifNil:"],
+source: "basePath\x0a\x09^basePath ifNil: [self class defaultBasePath]",
+messageSends: ["ifNil:", "defaultBasePath", "class"],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -38860,10 +38860,11 @@ fn: function (aString){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self["@basePath"]=aString;
+self._validateBasePath();
 return self}, function($ctx1) {$ctx1.fill(self,"basePath:",{aString:aString},smalltalk.FileServer)})},
 args: ["aString"],
-source: "basePath: aString\x0a\x09basePath := aString",
-messageSends: [],
+source: "basePath: aString\x0a\x09basePath := aString.\x0a\x09self validateBasePath.",
+messageSends: ["validateBasePath"],
 referencedClasses: []
 }),
 smalltalk.FileServer);
@@ -39551,6 +39552,47 @@ referencedClasses: []
 }),
 smalltalk.FileServer);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "validateBasePath",
+protocol: 'private',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4,$7,$6,$5,$8,$9;
+$1=self["@fs"];
+$2=self._basePath();
+$ctx1.sendIdx["basePath"]=1;
+_st($1)._stat_then_($2,(function(err,stat){
+return smalltalk.withContext(function($ctx2) {
+if(($receiver = err) == nil || $receiver == null){
+$3=_st(stat)._isDirectory();
+if(! smalltalk.assert($3)){
+$4=console;
+$7=self._basePath();
+$ctx2.sendIdx["basePath"]=2;
+$6="Warning: --base-path parameter ".__comma($7);
+$ctx2.sendIdx[","]=2;
+$5=_st($6).__comma(" is not a directory.");
+$ctx2.sendIdx[","]=1;
+return _st($4)._warn_($5);
+$ctx2.sendIdx["warn:"]=1;
+};
+} else {
+$8=console;
+$9=_st("Warning: path at --base-path parameter ".__comma(self._basePath())).__comma(" does not exist.");
+$ctx2.sendIdx[","]=3;
+return _st($8)._warn_($9);
+};
+}, function($ctx2) {$ctx2.fillBlock({err:err,stat:stat},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"validateBasePath",{},smalltalk.FileServer)})},
+args: [],
+source: "validateBasePath\x0a\x09\x22The basePath must be an existing directory. \x22\x0a\x09fs stat: self basePath then: [ :err :stat | err\x0a\x09\x09ifNil: [ stat isDirectory ifFalse: [ console warn: 'Warning: --base-path parameter ' , self basePath , ' is not a directory.' ]]\x0a\x09\x09ifNotNil: [ console warn: 'Warning: path at --base-path parameter ' , self basePath , ' does not exist.'  ]].",
+messageSends: ["stat:then:", "basePath", "ifNil:ifNotNil:", "ifFalse:", "isDirectory", "warn:", ","],
+referencedClasses: []
+}),
+smalltalk.FileServer);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "withBasePath:",
@@ -39699,6 +39741,22 @@ referencedClasses: ["Array"]
 }),
 smalltalk.FileServer.klass);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "defaultBasePath",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "./";
+}, function($ctx1) {$ctx1.fill(self,"defaultBasePath",{},smalltalk.FileServer.klass)})},
+args: [],
+source: "defaultBasePath\x0a\x09^ './'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.FileServer.klass);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "defaultHost",

+ 59 - 0
js/Helios-Commands-Browser.js

@@ -442,6 +442,65 @@ referencedClasses: []
 smalltalk.HLGenerateCommand.klass);
 
 
+smalltalk.addClass('HLCategorizeUnclassifiedCommand', smalltalk.HLGenerateCommand, [], 'Helios-Commands-Browser');
+smalltalk.HLCategorizeUnclassifiedCommand.comment="I am the command used to categorize unclassified methods";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "execute",
+protocol: 'executing',
+fn: function (){
+var self=this;
+var targetClass,unclassified;
+function $HLMethodClassifier(){return smalltalk.HLMethodClassifier||(typeof HLMethodClassifier=="undefined"?nil:HLMethodClassifier)}
+return smalltalk.withContext(function($ctx1) { 
+targetClass=_st(self._model())._selectedClass();
+unclassified=_st(_st(targetClass)._methods())._select_((function(e){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(e)._protocol()).__eq("as yet unclassified");
+}, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1,1)})}));
+_st(_st($HLMethodClassifier())._new())._classifyAll_(unclassified);
+return self}, function($ctx1) {$ctx1.fill(self,"execute",{targetClass:targetClass,unclassified:unclassified},smalltalk.HLCategorizeUnclassifiedCommand)})},
+args: [],
+source: "execute\x0a\x09| targetClass unclassified |\x0a\x09targetClass := self model selectedClass.\x0a\x0a\x09unclassified := targetClass methods select:[ :e | e protocol = 'as yet unclassified' ].\x0a\x09\x09\x0a\x09HLMethodClassifier new\x0a\x09\x09classifyAll: unclassified",
+messageSends: ["selectedClass", "model", "select:", "methods", "=", "protocol", "classifyAll:", "new"],
+referencedClasses: ["HLMethodClassifier"]
+}),
+smalltalk.HLCategorizeUnclassifiedCommand);
+
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "key",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "c";
+}, function($ctx1) {$ctx1.fill(self,"key",{},smalltalk.HLCategorizeUnclassifiedCommand.klass)})},
+args: [],
+source: "key\x0a\x09^ 'c'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLCategorizeUnclassifiedCommand.klass);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "label",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "Categorize";
+}, function($ctx1) {$ctx1.fill(self,"label",{},smalltalk.HLCategorizeUnclassifiedCommand.klass)})},
+args: [],
+source: "label\x0a\x09^ 'Categorize'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLCategorizeUnclassifiedCommand.klass);
+
+
 smalltalk.addClass('HLGenerateAccessorsCommand', smalltalk.HLGenerateCommand, [], 'Helios-Commands-Browser');
 smalltalk.HLGenerateAccessorsCommand.comment="I am the command used to generate the `getter` and the `setter` methods depending of the selected class";
 smalltalk.addMethod(

+ 40 - 6
js/Helios-Core.js

@@ -4094,6 +4094,22 @@ referencedClasses: []
 }),
 smalltalk.HLModalWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "giveFocusToButton:",
+protocol: 'private',
+fn: function (aButton){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(aButton)._asJQuery())._focus();
+return self}, function($ctx1) {$ctx1.fill(self,"giveFocusToButton:",{aButton:aButton},smalltalk.HLModalWidget)})},
+args: ["aButton"],
+source: "giveFocusToButton: aButton\x0a\x09aButton asJQuery focus",
+messageSends: ["focus", "asJQuery"],
+referencedClasses: []
+}),
+smalltalk.HLModalWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "hasButtons",
@@ -4174,11 +4190,11 @@ confirmButton=$6;
 return confirmButton;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 $ctx1.sendIdx["with:"]=1;
-_st(_st(confirmButton)._asJQuery())._focus();
+self._giveFocusToButton_(confirmButton);
 return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html,confirmButton:confirmButton},smalltalk.HLModalWidget)})},
 args: ["html"],
-source: "renderButtonsOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button\x0a\x09\x09\x09\x09class: 'button';\x0a\x09\x09\x09\x09with: 'Cancel';\x0a\x09\x09\x09\x09onClick: [ self cancel ].\x0a\x09\x09\x09confirmButton := html button\x0a\x09\x09\x09\x09class: 'button default';\x0a\x09\x09\x09\x09with: 'Confirm';\x0a\x09\x09\x09\x09onClick: [ self confirm ] ].\x0a\x0a\x09confirmButton asJQuery focus",
-messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "focus", "asJQuery"],
+source: "renderButtonsOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button\x0a\x09\x09\x09\x09class: 'button';\x0a\x09\x09\x09\x09with: 'Cancel';\x0a\x09\x09\x09\x09onClick: [ self cancel ].\x0a\x09\x09\x09confirmButton := html button\x0a\x09\x09\x09\x09class: 'button default';\x0a\x09\x09\x09\x09with: 'Confirm';\x0a\x09\x09\x09\x09onClick: [ self confirm ] ].\x0a\x0a\x09self giveFocusToButton:confirmButton",
+messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "giveFocusToButton:"],
 referencedClasses: []
 }),
 smalltalk.HLModalWidget);
@@ -4482,6 +4498,21 @@ referencedClasses: []
 }),
 smalltalk.HLRequestWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "giveFocusToButton:",
+protocol: 'private',
+fn: function (aButton){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self}, function($ctx1) {$ctx1.fill(self,"giveFocusToButton:",{aButton:aButton},smalltalk.HLRequestWidget)})},
+args: ["aButton"],
+source: "giveFocusToButton: aButton",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLRequestWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderMainOn:",
@@ -4489,13 +4520,16 @@ protocol: 'rendering',
 fn: function (html){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
 smalltalk.HLRequestWidget.superclass.fn.prototype._renderMainOn_.apply(_st(self), [html]);
 self["@input"]=_st(html)._textarea();
-_st(_st(self["@input"])._asJQuery())._val_(self._value());
+$1=_st(self["@input"])._asJQuery();
+_st($1)._val_(self._value());
+$2=_st($1)._focus();
 return self}, function($ctx1) {$ctx1.fill(self,"renderMainOn:",{html:html},smalltalk.HLRequestWidget)})},
 args: ["html"],
-source: "renderMainOn: html\x0a\x09super renderMainOn: html.\x0a\x09input := html textarea.\x0a\x09input asJQuery val: self value",
-messageSends: ["renderMainOn:", "textarea", "val:", "asJQuery", "value"],
+source: "renderMainOn: html\x0a\x09super renderMainOn: html.\x0a\x09input := html textarea.\x0a\x09input asJQuery \x0a\x09\x09val: self value;\x0a\x09\x09focus",
+messageSends: ["renderMainOn:", "textarea", "val:", "asJQuery", "value", "focus"],
 referencedClasses: []
 }),
 smalltalk.HLRequestWidget);

+ 485 - 0
js/Helios-Helpers.js

@@ -2,6 +2,382 @@ define("amber_core/Helios-Helpers", ["amber_vm/smalltalk", "amber_vm/nil", "ambe
 smalltalk.addPackage('Helios-Helpers');
 smalltalk.packages["Helios-Helpers"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
+smalltalk.addClass('HLClassifierLink', smalltalk.Object, ['next', 'method'], 'Helios-Helpers');
+smalltalk.HLClassifierLink.comment="I am an abstract class implementing a link in a `chain of responsibility` pattern.\x0a\x0ay subclasses are in charge of classifying a method according to multiple strategies";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "classify",
+protocol: 'protocol',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$2;
+$1=self._next();
+$ctx1.sendIdx["next"]=1;
+if(($receiver = $1) == nil || $receiver == null){
+return false;
+} else {
+$1;
+};
+$3=self._doClassify();
+if(smalltalk.assert($3)){
+$2=true;
+} else {
+$2=_st(self._next())._execute();
+};
+return $2;
+}, function($ctx1) {$ctx1.fill(self,"classify",{},smalltalk.HLClassifierLink)})},
+args: [],
+source: "classify\x0a\x09self next ifNil: [ ^ false ].\x0a\x09\x0a\x09^ self doClassify\x0a\x09\x09ifTrue: [ true ]\x0a\x09\x09ifFalse: [ self next execute ]",
+messageSends: ["ifNil:", "next", "ifTrue:ifFalse:", "doClassify", "execute"],
+referencedClasses: []
+}),
+smalltalk.HLClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "doClassify",
+protocol: 'private',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"doClassify",{},smalltalk.HLClassifierLink)})},
+args: [],
+source: "doClassify\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
+referencedClasses: []
+}),
+smalltalk.HLClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "method",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@method"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"method",{},smalltalk.HLClassifierLink)})},
+args: [],
+source: "method\x0a\x09^ method",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "method:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+self["@method"]=anObject;
+$1=self._next();
+if(($receiver = $1) == nil || $receiver == null){
+$1;
+} else {
+var nextLink;
+nextLink=$receiver;
+_st(nextLink)._method_(anObject);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"method:",{anObject:anObject},smalltalk.HLClassifierLink)})},
+args: ["anObject"],
+source: "method: anObject\x0a\x09method := anObject.\x0a\x09self next\x0a\x09\x09ifNotNil: [ :nextLink | nextLink method: anObject ]",
+messageSends: ["ifNotNil:", "next", "method:"],
+referencedClasses: []
+}),
+smalltalk.HLClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "next",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@next"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"next",{},smalltalk.HLClassifierLink)})},
+args: [],
+source: "next\x0a\x09^ next",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "next:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@next"]=anObject;
+return self}, function($ctx1) {$ctx1.fill(self,"next:",{anObject:anObject},smalltalk.HLClassifierLink)})},
+args: ["anObject"],
+source: "next: anObject\x0a\x09next := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLClassifierLink);
+
+
+
+smalltalk.addClass('HLAccessorClassifierLink', smalltalk.HLClassifierLink, [], 'Helios-Helpers');
+smalltalk.HLAccessorClassifierLink.comment="I am a classifier checking the method selector matches an instance variable name";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "doClassify",
+protocol: 'private',
+fn: function (){
+var self=this;
+var names,selector;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+names=_st(_st(self["@method"])._methodClass())._allInstanceVariableNames();
+selector=_st(self["@method"])._selector();
+$1=_st(_st(selector)._last()).__eq(":");
+if(smalltalk.assert($1)){
+selector=_st(selector)._allButLast();
+selector;
+};
+$2=_st(names)._includes_(selector);
+if(! smalltalk.assert($2)){
+return false;
+};
+_st(self["@method"])._protocol_("accessing");
+return true;
+}, function($ctx1) {$ctx1.fill(self,"doClassify",{names:names,selector:selector},smalltalk.HLAccessorClassifierLink)})},
+args: [],
+source: "doClassify\x0a\x09| names selector |\x0a\x09\x0a\x09names := method methodClass allInstanceVariableNames.\x0a\x09selector := method selector.\x0a\x09\x0a\x09(selector last = ':')\x0a\x09\x09ifTrue: [ \x22selector might be a setter\x22\x0a\x09\x09\x09selector := selector allButLast ].\x0a\x09\x0a\x09(names includes: selector)\x0a\x09\x09ifFalse: [ ^ false ].\x0a\x09\x09\x0a\x09method protocol: 'accessing'.\x0a\x09^ true.",
+messageSends: ["allInstanceVariableNames", "methodClass", "selector", "ifTrue:", "=", "last", "allButLast", "ifFalse:", "includes:", "protocol:"],
+referencedClasses: []
+}),
+smalltalk.HLAccessorClassifierLink);
+
+
+
+smalltalk.addClass('HLImplementorClassifierLink', smalltalk.HLClassifierLink, [], 'Helios-Helpers');
+smalltalk.HLImplementorClassifierLink.comment="I am a classifier checking the other implementations of the same selector and choose the protocol the most populated";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "doClassify",
+protocol: 'private',
+fn: function (){
+var self=this;
+var currentClass;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$2;
+var $early={};
+try {
+currentClass=_st(self["@method"])._methodClass();
+_st((function(){
+return smalltalk.withContext(function($ctx2) {
+$1=_st(currentClass)._superclass();
+$ctx2.sendIdx["superclass"]=1;
+return _st($1)._isNil();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
+currentClass=_st(currentClass)._superclass();
+currentClass;
+$3=currentClass;
+$4=_st(self["@method"])._selector();
+$ctx2.sendIdx["selector"]=1;
+$2=_st($3)._includesSelector_($4);
+if(smalltalk.assert($2)){
+_st(self["@method"])._protocol_(_st(_st(currentClass).__gt_gt(_st(self["@method"])._selector()))._protocol());
+throw $early=[true];
+};
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
+return false;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"doClassify",{currentClass:currentClass},smalltalk.HLImplementorClassifierLink)})},
+args: [],
+source: "doClassify\x0a\x09| currentClass |\x0a\x09currentClass := method methodClass.\x0a\x09\x0a\x09[ currentClass superclass isNil ] whileFalse: [\x0a\x09\x09currentClass := currentClass superclass.\x0a\x09\x09(currentClass includesSelector: method selector)\x0a\x09\x09\x09ifTrue: [ \x0a\x09\x09\x09\x09method protocol: (currentClass >> method selector) protocol.\x0a\x09\x09\x09\x09^ true ]].\x0a\x09\x0a\x09^ false.",
+messageSends: ["methodClass", "whileFalse:", "isNil", "superclass", "ifTrue:", "includesSelector:", "selector", "protocol:", "protocol", ">>"],
+referencedClasses: []
+}),
+smalltalk.HLImplementorClassifierLink);
+
+
+
+smalltalk.addClass('HLPrefixClassifierLink', smalltalk.HLClassifierLink, ['prefixMapping'], 'Helios-Helpers');
+smalltalk.HLPrefixClassifierLink.comment="I am classifier checking the method selector to know if it begins with a known prefix";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "buildPrefixDictionary",
+protocol: 'initialization',
+fn: function (){
+var self=this;
+function $Dictionary(){return smalltalk.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+self["@prefixMapping"]=_st($Dictionary())._new();
+$1=self["@prefixMapping"];
+_st($1)._at_put_("test","tests");
+$ctx1.sendIdx["at:put:"]=1;
+_st($1)._at_put_("bench","benchmarking");
+$ctx1.sendIdx["at:put:"]=2;
+_st($1)._at_put_("copy","copying");
+$ctx1.sendIdx["at:put:"]=3;
+_st($1)._at_put_("initialize","initialization");
+$ctx1.sendIdx["at:put:"]=4;
+_st($1)._at_put_("accept","visitor");
+$ctx1.sendIdx["at:put:"]=5;
+_st($1)._at_put_("visit","visitor");
+$ctx1.sendIdx["at:put:"]=6;
+_st($1)._at_put_("signal","signalling");
+$ctx1.sendIdx["at:put:"]=7;
+_st($1)._at_put_("parse","parsing");
+$ctx1.sendIdx["at:put:"]=8;
+_st($1)._at_put_("add","adding");
+$ctx1.sendIdx["at:put:"]=9;
+_st($1)._at_put_("is","testing");
+$ctx1.sendIdx["at:put:"]=10;
+_st($1)._at_put_("as","converting");
+$ctx1.sendIdx["at:put:"]=11;
+$2=_st($1)._at_put_("new","instance creation");
+return self}, function($ctx1) {$ctx1.fill(self,"buildPrefixDictionary",{},smalltalk.HLPrefixClassifierLink)})},
+args: [],
+source: "buildPrefixDictionary\x0a\x09prefixMapping := Dictionary new.\x0a\x09prefixMapping \x0a\x09\x09at: 'test' put: 'tests';\x0a\x09 \x09at: 'bench' put: 'benchmarking';\x0a\x09 \x09at: 'copy' put: 'copying';\x0a\x09\x09at: 'initialize' put: 'initialization';\x0a\x09\x09at: 'accept' put: 'visitor';\x0a\x09\x09at: 'visit' put: 'visitor';\x0a\x09\x09at: 'signal' put: 'signalling';\x0a\x09\x09at: 'parse' put: 'parsing';\x0a\x09\x09at: 'add' put: 'adding';\x0a\x09\x09at: 'is' put: 'testing';\x0a\x09\x09at: 'as' put: 'converting';\x0a\x09\x09at: 'new' put: 'instance creation'.",
+messageSends: ["new", "at:put:"],
+referencedClasses: ["Dictionary"]
+}),
+smalltalk.HLPrefixClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "doClassify",
+protocol: 'private',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+var $early={};
+try {
+_st(self["@prefixMapping"])._keysAndValuesDo_((function(prefix,protocol){
+return smalltalk.withContext(function($ctx2) {
+$1=_st(_st(self["@method"])._selector())._beginsWith_(prefix);
+if(smalltalk.assert($1)){
+_st(self["@method"])._protocol_(protocol);
+throw $early=[true];
+};
+}, function($ctx2) {$ctx2.fillBlock({prefix:prefix,protocol:protocol},$ctx1,1)})}));
+return false;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"doClassify",{},smalltalk.HLPrefixClassifierLink)})},
+args: [],
+source: "doClassify\x0a\x09prefixMapping keysAndValuesDo: [ :prefix :protocol |\x0a\x09\x09(method selector beginsWith: prefix)\x0a\x09\x09\x09ifTrue: [\x0a\x09\x09\x09\x09method protocol: protocol.\x0a\x09\x09\x09\x09^ true ]].\x0a\x09^ false.",
+messageSends: ["keysAndValuesDo:", "ifTrue:", "beginsWith:", "selector", "protocol:"],
+referencedClasses: []
+}),
+smalltalk.HLPrefixClassifierLink);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initialize",
+protocol: 'initialization',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLPrefixClassifierLink.superclass.fn.prototype._initialize.apply(_st(self), []);
+self._buildPrefixDictionary();
+return self}, function($ctx1) {$ctx1.fill(self,"initialize",{},smalltalk.HLPrefixClassifierLink)})},
+args: [],
+source: "initialize\x0a\x09super initialize.\x0a\x0a\x09self buildPrefixDictionary",
+messageSends: ["initialize", "buildPrefixDictionary"],
+referencedClasses: []
+}),
+smalltalk.HLPrefixClassifierLink);
+
+
+
+smalltalk.addClass('HLSuperClassClassifierLink', smalltalk.HLClassifierLink, [], 'Helios-Helpers');
+smalltalk.HLSuperClassClassifierLink.comment="I am a classifier checking the superclass chain to find a matching selector";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "doClassify",
+protocol: 'private',
+fn: function (){
+var self=this;
+var protocolBag,methods,protocolToUse,counter;
+function $Dictionary(){return smalltalk.Dictionary||(typeof Dictionary=="undefined"?nil:Dictionary)}
+function $HLReferencesModel(){return smalltalk.HLReferencesModel||(typeof HLReferencesModel=="undefined"?nil:HLReferencesModel)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1,$4,$3,$5;
+var $early={};
+try {
+protocolBag=_st($Dictionary())._new();
+$ctx1.sendIdx["new"]=1;
+methods=_st(_st($HLReferencesModel())._new())._implementorsOf_(_st(self["@method"])._selector());
+_st(methods)._ifEmpty_ifNotEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
+throw $early=[false];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),(function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(methods)._do_((function(aMethod){
+var protocol;
+return smalltalk.withContext(function($ctx3) {
+protocol=_st(_st(aMethod)._method())._protocol();
+protocol;
+$2=_st(self["@method"])._methodClass();
+$ctx3.sendIdx["methodClass"]=1;
+$1=_st($2).__eq(_st(aMethod)._methodClass());
+$ctx3.sendIdx["="]=1;
+if(! smalltalk.assert($1)){
+$4=_st(_st(protocol)._first()).__eq("*");
+$ctx3.sendIdx["="]=2;
+$3=_st($4)._or_((function(){
+return smalltalk.withContext(function($ctx4) {
+return _st(protocol).__eq(_st(self["@method"])._defaultProtocol());
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,5)})}));
+if(! smalltalk.assert($3)){
+return _st(protocolBag)._at_put_(protocol,_st(_st(protocolBag)._at_ifAbsent_(protocol,(function(){
+return smalltalk.withContext(function($ctx4) {
+return (0);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,7)})}))).__plus((1)));
+};
+};
+}, function($ctx3) {$ctx3.fillBlock({aMethod:aMethod,protocol:protocol},$ctx2,3)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
+_st(protocolBag)._ifEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
+throw $early=[false];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,8)})}));
+protocolToUse=nil;
+counter=(0);
+_st(protocolBag)._keysAndValuesDo_((function(key,value){
+return smalltalk.withContext(function($ctx2) {
+$5=_st(value).__gt(counter);
+if(smalltalk.assert($5)){
+counter=value;
+counter;
+protocolToUse=key;
+return protocolToUse;
+};
+}, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,9)})}));
+_st(self["@method"])._protocol_(protocolToUse);
+return true;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+}, function($ctx1) {$ctx1.fill(self,"doClassify",{protocolBag:protocolBag,methods:methods,protocolToUse:protocolToUse,counter:counter},smalltalk.HLSuperClassClassifierLink)})},
+args: [],
+source: "doClassify\x0a\x09| protocolBag methods protocolToUse counter |\x0a\x09\x0a\x09protocolBag := Dictionary new.\x0a\x09methods := HLReferencesModel new implementorsOf: method selector.\x0a\x09methods\x0a\x09\x09ifEmpty: [ ^ false ]\x0a\x09\x09ifNotEmpty: [\x0a\x09\x09\x09methods \x0a\x09\x09\x09\x09do: [ :aMethod || protocol |\x0a\x09\x09\x09\x09\x09protocol := aMethod method protocol.\x0a\x09\x09\x09\x09\x09(method methodClass = aMethod methodClass)\x0a\x09\x09\x09\x09\x09\x09ifFalse: [\x0a\x09\x09\x09\x09\x09\x09((protocol first = '*') or: [ protocol = method defaultProtocol ])\x0a\x09\x09\x09\x09\x09\x09\x09ifFalse: [ \x0a\x09\x09\x09\x09\x09\x09\x09\x09protocolBag \x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09at: protocol \x0a\x09\x09\x09\x09\x09\x09\x09\x09\x09put: (protocolBag at: protocol ifAbsent: [ 0 ]) + 1 ] ] ] ].\x0a\x09\x09\x09\x0a\x09protocolBag ifEmpty: [ ^ false ].\x0a\x09protocolToUse := nil.\x0a\x09counter := 0.\x0a\x09protocolBag keysAndValuesDo: [ :key :value | value > counter \x0a\x09\x09ifTrue: [\x0a\x09\x09\x09counter := value.\x0a\x09\x09\x09protocolToUse := key ] ].\x0a\x09method protocol: protocolToUse.\x0a\x09^ true",
+messageSends: ["new", "implementorsOf:", "selector", "ifEmpty:ifNotEmpty:", "do:", "protocol", "method", "ifFalse:", "=", "methodClass", "or:", "first", "defaultProtocol", "at:put:", "+", "at:ifAbsent:", "ifEmpty:", "keysAndValuesDo:", "ifTrue:", ">", "protocol:"],
+referencedClasses: ["Dictionary", "HLReferencesModel"]
+}),
+smalltalk.HLSuperClassClassifierLink);
+
+
+
 smalltalk.addClass('HLGenerationOutput', smalltalk.Object, ['sourceCodes', 'protocol', 'targetClass'], 'Helios-Helpers');
 smalltalk.HLGenerationOutput.comment="I am a simple data object used to store the result of a generation process";
 smalltalk.addMethod(
@@ -627,6 +1003,115 @@ smalltalk.HLInitializeGenerator);
 
 
 
+smalltalk.addClass('HLMethodClassifier', smalltalk.Object, ['firstLink'], 'Helios-Helpers');
+smalltalk.HLMethodClassifier.comment="I am in charge of categorizing methods following this strategy:\x0a\x0a- is it an accessor?\x0a- is it overriding a superclass method?\x0a- is it starting with a know prefix?\x0a- how are categorized the other implementations?";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "addLink:",
+protocol: 'private',
+fn: function (aLink){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(aLink)._next_(self["@firstLink"]);
+self["@firstLink"]=aLink;
+return self}, function($ctx1) {$ctx1.fill(self,"addLink:",{aLink:aLink},smalltalk.HLMethodClassifier)})},
+args: ["aLink"],
+source: "addLink: aLink\x0a\x09aLink next: firstLink.\x0a\x09firstLink := aLink",
+messageSends: ["next:"],
+referencedClasses: []
+}),
+smalltalk.HLMethodClassifier);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "buildChainOfResponsibility",
+protocol: 'initialization',
+fn: function (){
+var self=this;
+function $HLImplementorClassifierLink(){return smalltalk.HLImplementorClassifierLink||(typeof HLImplementorClassifierLink=="undefined"?nil:HLImplementorClassifierLink)}
+function $HLPrefixClassifierLink(){return smalltalk.HLPrefixClassifierLink||(typeof HLPrefixClassifierLink=="undefined"?nil:HLPrefixClassifierLink)}
+function $HLSuperclassClassifierLink(){return smalltalk.HLSuperclassClassifierLink||(typeof HLSuperclassClassifierLink=="undefined"?nil:HLSuperclassClassifierLink)}
+function $HLAccessorClassifierLink(){return smalltalk.HLAccessorClassifierLink||(typeof HLAccessorClassifierLink=="undefined"?nil:HLAccessorClassifierLink)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3;
+$1=_st($HLImplementorClassifierLink())._new();
+$ctx1.sendIdx["new"]=1;
+self._addLink_($1);
+$ctx1.sendIdx["addLink:"]=1;
+$2=_st($HLPrefixClassifierLink())._new();
+$ctx1.sendIdx["new"]=2;
+self._addLink_($2);
+$ctx1.sendIdx["addLink:"]=2;
+$3=_st($HLSuperclassClassifierLink())._new();
+$ctx1.sendIdx["new"]=3;
+self._addLink_($3);
+$ctx1.sendIdx["addLink:"]=3;
+self._addLink_(_st($HLAccessorClassifierLink())._new());
+return self}, function($ctx1) {$ctx1.fill(self,"buildChainOfResponsibility",{},smalltalk.HLMethodClassifier)})},
+args: [],
+source: "buildChainOfResponsibility\x0a\x09self addLink: HLImplementorClassifierLink new.\x0a\x09self addLink: HLPrefixClassifierLink new.\x0a\x09self addLink: HLSuperclassClassifierLink new.\x0a\x09self addLink: HLAccessorClassifierLink new",
+messageSends: ["addLink:", "new"],
+referencedClasses: ["HLImplementorClassifierLink", "HLPrefixClassifierLink", "HLSuperclassClassifierLink", "HLAccessorClassifierLink"]
+}),
+smalltalk.HLMethodClassifier);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "classify:",
+protocol: 'protocol',
+fn: function (aMethod){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=self["@firstLink"];
+_st($1)._method_(aMethod);
+$2=_st($1)._classify();
+return self}, function($ctx1) {$ctx1.fill(self,"classify:",{aMethod:aMethod},smalltalk.HLMethodClassifier)})},
+args: ["aMethod"],
+source: "classify: aMethod\x0a\x09firstLink\x0a\x09\x09method: aMethod;\x0a\x09\x09classify",
+messageSends: ["method:", "classify"],
+referencedClasses: []
+}),
+smalltalk.HLMethodClassifier);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "classifyAll:",
+protocol: 'protocol',
+fn: function (aCollectionOfMethods){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(aCollectionOfMethods)._do_((function(method){
+return smalltalk.withContext(function($ctx2) {
+return self._classify_(method);
+}, function($ctx2) {$ctx2.fillBlock({method:method},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"classifyAll:",{aCollectionOfMethods:aCollectionOfMethods},smalltalk.HLMethodClassifier)})},
+args: ["aCollectionOfMethods"],
+source: "classifyAll: aCollectionOfMethods\x0a\x09aCollectionOfMethods do: [ :method |\x0a\x09\x09self classify: method ]",
+messageSends: ["do:", "classify:"],
+referencedClasses: []
+}),
+smalltalk.HLMethodClassifier);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initialize",
+protocol: 'initialization',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLMethodClassifier.superclass.fn.prototype._initialize.apply(_st(self), []);
+self._buildChainOfResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"initialize",{},smalltalk.HLMethodClassifier)})},
+args: [],
+source: "initialize\x0a\x09super initialize.\x0a\x09\x0a\x09self buildChainOfResponsibility",
+messageSends: ["initialize", "buildChainOfResponsibility"],
+referencedClasses: []
+}),
+smalltalk.HLMethodClassifier);
+
+
+
 smalltalk.addClass('HLMethodSourceCode', smalltalk.Object, ['selector', 'sourceCode'], 'Helios-Helpers');
 smalltalk.HLMethodSourceCode.comment="I am a simple data object keeping track of the information about a method that will be compiled at the end of the generation process";
 smalltalk.addMethod(

+ 2 - 2
js/IDE.js

@@ -7439,13 +7439,13 @@ return _st(variables)._at_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 _st(anInspector)._setLabel_(self._printString());
 $1=_st(anInspector)._setVariables_(variables);
-return self}, function($ctx1) {$ctx1.fill(self,"inspectOn:",{anInspector:anInspector,variables:variables},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"inspectOn:",{anInspector:anInspector,variables:variables},smalltalk.AssociativeCollection)})},
 args: ["anInspector"],
 source: "inspectOn: anInspector\x0a\x09| variables |\x0a\x09variables := Dictionary new.\x0a\x09variables at: '#self' put: self.\x0a\x09variables at: '#keys' put: self keys.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09variables at: key put: value ].\x0a\x09anInspector\x0a\x09\x09setLabel: self printString;\x0a\x09\x09setVariables: variables",
 messageSends: ["new", "at:put:", "keys", "keysAndValuesDo:", "setLabel:", "printString", "setVariables:"],
 referencedClasses: ["Dictionary"]
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({

+ 296 - 213
js/Kernel-Collections.js

@@ -1301,41 +1301,41 @@ smalltalk.IndexableCollection);
 
 
 
-smalltalk.addClass('HashedCollection', smalltalk.IndexableCollection, [], 'Kernel-Collections');
-smalltalk.HashedCollection.comment="I am a traditional JavaScript object, or a Smalltalk `Dictionary`.\x0a\x0aUnlike a `Dictionary`, I can only have strings as keys.";
+smalltalk.addClass('AssociativeCollection', smalltalk.IndexableCollection, [], 'Kernel-Collections');
+smalltalk.AssociativeCollection.comment="I am a base class for object-indexed collections (Dictionary et.al.).";
 smalltalk.addMethod(
 smalltalk.method({
 selector: "=",
 protocol: 'comparing',
-fn: function (aHashedCollection){
+fn: function (anAssocitativeCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1,$4,$3,$6,$5;
 $2=self._class();
 $ctx1.sendIdx["class"]=1;
-$1=_st($2).__eq(_st(aHashedCollection)._class());
+$1=_st($2).__eq(_st(anAssocitativeCollection)._class());
 $ctx1.sendIdx["="]=1;
 if(! smalltalk.assert($1)){
 return false;
 };
 $4=self._size();
 $ctx1.sendIdx["size"]=1;
-$3=_st($4).__eq(_st(aHashedCollection)._size());
+$3=_st($4).__eq(_st(anAssocitativeCollection)._size());
 $ctx1.sendIdx["="]=2;
 if(! smalltalk.assert($3)){
 return false;
 };
 $6=self._associations();
 $ctx1.sendIdx["associations"]=1;
-$5=_st($6).__eq(_st(aHashedCollection)._associations());
+$5=_st($6).__eq(_st(anAssocitativeCollection)._associations());
 return $5;
-}, function($ctx1) {$ctx1.fill(self,"=",{aHashedCollection:aHashedCollection},smalltalk.HashedCollection)})},
-args: ["aHashedCollection"],
-source: "= aHashedCollection\x0a\x09self class = aHashedCollection class ifFalse: [ ^ false ].\x0a\x09self size = aHashedCollection size ifFalse: [ ^ false ].\x0a\x09^ self associations = aHashedCollection associations",
+}, function($ctx1) {$ctx1.fill(self,"=",{anAssocitativeCollection:anAssocitativeCollection},smalltalk.AssociativeCollection)})},
+args: ["anAssocitativeCollection"],
+source: "= anAssocitativeCollection\x0a\x09self class = anAssocitativeCollection class ifFalse: [ ^ false ].\x0a\x09self size = anAssocitativeCollection size ifFalse: [ ^ false ].\x0a\x09^ self associations = anAssocitativeCollection associations",
 messageSends: ["ifFalse:", "=", "class", "size", "associations"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1345,30 +1345,30 @@ fn: function (anAssociation){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self._at_put_(_st(anAssociation)._key(),_st(anAssociation)._value());
-return self}, function($ctx1) {$ctx1.fill(self,"add:",{anAssociation:anAssociation},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"add:",{anAssociation:anAssociation},smalltalk.AssociativeCollection)})},
 args: ["anAssociation"],
 source: "add: anAssociation\x0a\x09self at: anAssociation key put: anAssociation value",
 messageSends: ["at:put:", "key", "value"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
 selector: "addAll:",
 protocol: 'adding/removing',
-fn: function (aHashedCollection){
+fn: function (anAssocitativeCollection){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-smalltalk.HashedCollection.superclass.fn.prototype._addAll_.apply(_st(self), [_st(aHashedCollection)._associations()]);
-return aHashedCollection;
-}, function($ctx1) {$ctx1.fill(self,"addAll:",{aHashedCollection:aHashedCollection},smalltalk.HashedCollection)})},
-args: ["aHashedCollection"],
-source: "addAll: aHashedCollection\x0a\x09super addAll: aHashedCollection associations.\x0a\x09^ aHashedCollection",
+smalltalk.AssociativeCollection.superclass.fn.prototype._addAll_.apply(_st(self), [_st(anAssocitativeCollection)._associations()]);
+return anAssocitativeCollection;
+}, function($ctx1) {$ctx1.fill(self,"addAll:",{anAssocitativeCollection:anAssocitativeCollection},smalltalk.AssociativeCollection)})},
+args: ["anAssocitativeCollection"],
+source: "addAll: anAssocitativeCollection\x0a\x09super addAll: anAssocitativeCollection associations.\x0a\x09^ anAssocitativeCollection",
 messageSends: ["addAll:", "associations"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1381,13 +1381,32 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=_st($Dictionary())._from_(self._associations());
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"asDictionary",{},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"asDictionary",{},smalltalk.AssociativeCollection)})},
 args: [],
 source: "asDictionary\x0a\x09^ Dictionary from: self associations",
 messageSends: ["from:", "associations"],
 referencedClasses: ["Dictionary"]
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "asHashedCollection",
+protocol: 'converting',
+fn: function (){
+var self=this;
+function $HashedCollection(){return smalltalk.HashedCollection||(typeof HashedCollection=="undefined"?nil:HashedCollection)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st($HashedCollection())._from_(self._associations());
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"asHashedCollection",{},smalltalk.AssociativeCollection)})},
+args: [],
+source: "asHashedCollection\x0a\x09^ HashedCollection from: self associations",
+messageSends: ["from:", "associations"],
+referencedClasses: ["HashedCollection"]
+}),
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1396,22 +1415,23 @@ protocol: 'converting',
 fn: function (){
 var self=this;
 var c;
+function $HashedCollection(){return smalltalk.HashedCollection||(typeof HashedCollection=="undefined"?nil:HashedCollection)}
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-c=_st(self._class())._new();
+c=_st($HashedCollection())._new();
 self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return _st(c)._at_put_(key,_st(value)._asJSON());
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=c;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"asJSON",{c:c},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"asJSON",{c:c},smalltalk.AssociativeCollection)})},
 args: [],
-source: "asJSON\x0a\x09| c |\x0a\x09c := self class new.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09c at: key put: value asJSON ].\x0a\x09^ c",
-messageSends: ["new", "class", "keysAndValuesDo:", "at:put:", "asJSON"],
-referencedClasses: []
+source: "asJSON\x0a\x09| c |\x0a\x09c := HashedCollection new.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09c at: key put: value asJSON ].\x0a\x09^ c",
+messageSends: ["new", "keysAndValuesDo:", "at:put:", "asJSON"],
+referencedClasses: ["HashedCollection"]
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1429,13 +1449,13 @@ return _st(associations)._add_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 $1=associations;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"associations",{associations:associations},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"associations",{associations:associations},smalltalk.AssociativeCollection)})},
 args: [],
 source: "associations\x0a\x09| associations |\x0a\x09associations := #().\x0a\x09self associationsDo: [ :each | associations add: each ].\x0a\x09^ associations",
 messageSends: ["associationsDo:", "add:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1449,35 +1469,13 @@ self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return _st(aBlock)._value_(_st($Association())._key_value_(key,value));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"associationsDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"associationsDo:",{aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
 source: "associationsDo: aBlock\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09aBlock value: (Association key: key value: value) ]",
 messageSends: ["keysAndValuesDo:", "value:", "key:value:"],
 referencedClasses: ["Association"]
 }),
-smalltalk.HashedCollection);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "at:ifAbsent:",
-protocol: 'accessing',
-fn: function (aKey,aBlock){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-var $2,$1;
-$2=self._includesKey_(aKey);
-$1=_st($2)._ifTrue_ifFalse_((function(){
-return smalltalk.withContext(function($ctx2) {
-return self._basicAt_(aKey);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),aBlock);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.HashedCollection)})},
-args: ["aKey", "aBlock"],
-source: "at: aKey ifAbsent: aBlock\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifTrue: [ self basicAt: aKey ]\x0a\x09\x09ifFalse: aBlock",
-messageSends: ["ifTrue:ifFalse:", "includesKey:", "basicAt:"],
-referencedClasses: []
-}),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1488,36 +1486,19 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
 $2=self._includesKey_(aKey);
-$1=_st($2)._ifTrue_ifFalse_((function(){
-return smalltalk.withContext(function($ctx2) {
-return _st(aBlock)._value_(self._at_(aKey));
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),anotherBlock);
+if(smalltalk.assert($2)){
+$1=_st(aBlock)._value_(self._at_(aKey));
+} else {
+$1=_st(anotherBlock)._value();
+};
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"at:ifPresent:ifAbsent:",{aKey:aKey,aBlock:aBlock,anotherBlock:anotherBlock},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"at:ifPresent:ifAbsent:",{aKey:aKey,aBlock:aBlock,anotherBlock:anotherBlock},smalltalk.AssociativeCollection)})},
 args: ["aKey", "aBlock", "anotherBlock"],
-source: "at: aKey ifPresent: aBlock ifAbsent: anotherBlock\x0a\x09\x22Lookup the given key in the receiver.\x0a\x09If it is present, answer the value of evaluating the oneArgBlock with the value associated with the key,\x0a\x09otherwise answer the value of absentBlock.\x22\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifTrue: [ aBlock value: (self at: aKey) ]\x0a\x09\x09ifFalse: anotherBlock",
-messageSends: ["ifTrue:ifFalse:", "includesKey:", "value:", "at:"],
-referencedClasses: []
-}),
-smalltalk.HashedCollection);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "at:put:",
-protocol: 'accessing',
-fn: function (aKey,aValue){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=self._basicAt_put_(aKey,aValue);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"at:put:",{aKey:aKey,aValue:aValue},smalltalk.HashedCollection)})},
-args: ["aKey", "aValue"],
-source: "at: aKey put: aValue\x0a\x09^ self basicAt: aKey put: aValue",
-messageSends: ["basicAt:put:"],
+source: "at: aKey ifPresent: aBlock ifAbsent: anotherBlock\x0a\x09\x22Lookup the given key in the receiver.\x0a\x09If it is present, answer the value of evaluating the oneArgBlock with the value associated with the key,\x0a\x09otherwise answer the value of absentBlock.\x22\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifTrue: [ aBlock value: (self at: aKey) ]\x0a\x09\x09ifFalse: [ anotherBlock value ]",
+messageSends: ["ifTrue:ifFalse:", "includesKey:", "value:", "at:", "value"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1535,13 +1516,13 @@ return _st(newDict)._at_put_(key,_st(aBlock)._value_(value));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=newDict;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"collect:",{aBlock:aBlock,newDict:newDict},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"collect:",{aBlock:aBlock,newDict:newDict},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
 source: "collect: aBlock\x0a\x09| newDict |\x0a\x09newDict := self class new.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09newDict at: key put: (aBlock value: value) ].\x0a\x09^ newDict",
 messageSends: ["new", "class", "keysAndValuesDo:", "at:put:", "value:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1559,13 +1540,13 @@ return _st(copy)._at_put_(key,_st(value)._deepCopy());
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=copy;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"deepCopy",{copy:copy},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"deepCopy",{copy:copy},smalltalk.AssociativeCollection)})},
 args: [],
 source: "deepCopy\x0a\x09| copy |\x0a\x09copy := self class new.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09copy at: key put: value deepCopy ].\x0a\x09^ copy",
 messageSends: ["new", "class", "keysAndValuesDo:", "at:put:", "deepCopy"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1577,13 +1558,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=_st(self._values())._detect_ifNone_(aBlock,anotherBlock);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"detect:ifNone:",{aBlock:aBlock,anotherBlock:anotherBlock},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"detect:ifNone:",{aBlock:aBlock,anotherBlock:anotherBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock", "anotherBlock"],
 source: "detect: aBlock ifNone: anotherBlock\x0a\x09^ self values detect: aBlock ifNone: anotherBlock",
 messageSends: ["detect:ifNone:", "values"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1593,13 +1574,13 @@ fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self._valuesDo_(aBlock);
-return self}, function($ctx1) {$ctx1.fill(self,"do:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"do:",{aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
 source: "do: aBlock\x0a\x09self valuesDo: aBlock",
 messageSends: ["valuesDo:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1611,13 +1592,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=_st(self._values())._includes_(anObject);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"includes:",{anObject:anObject},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"includes:",{anObject:anObject},smalltalk.AssociativeCollection)})},
 args: ["anObject"],
 source: "includes: anObject\x0a\x09^ self values includes: anObject",
 messageSends: ["includes:", "values"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1626,14 +1607,14 @@ protocol: 'testing',
 fn: function (aKey){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-return self.hasOwnProperty(aKey);
-return self}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey},smalltalk.HashedCollection)})},
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey},smalltalk.AssociativeCollection)})},
 args: ["aKey"],
-source: "includesKey: aKey\x0a\x09<return self.hasOwnProperty(aKey)>",
-messageSends: [],
+source: "includesKey: aKey\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1648,13 +1629,13 @@ return smalltalk.withContext(function($ctx2) {
 return _st(self._at_(each)).__eq(anObject);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}),aBlock);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"indexOf:ifAbsent:",{anObject:anObject,aBlock:aBlock},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"indexOf:ifAbsent:",{anObject:anObject,aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["anObject", "aBlock"],
-source: "indexOf: anObject ifAbsent: aBlock\x0a\x0a\x09^ self keys detect: [ :each | (self at: each) = anObject ] ifNone: aBlock",
+source: "indexOf: anObject ifAbsent: aBlock\x0a\x09^ self keys detect: [ :each | (self at: each) = anObject ] ifNone: aBlock",
 messageSends: ["detect:ifNone:", "keys", "=", "at:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1669,13 +1650,13 @@ return smalltalk.withContext(function($ctx2) {
 return self._errorNotFound();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"keyAtValue:",{anObject:anObject},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"keyAtValue:",{anObject:anObject},smalltalk.AssociativeCollection)})},
 args: ["anObject"],
 source: "keyAtValue: anObject\x0a\x09^ self keyAtValue: anObject ifAbsent: [ self errorNotFound ]",
 messageSends: ["keyAtValue:ifAbsent:", "errorNotFound"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1687,13 +1668,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=self._indexOf_ifAbsent_(anObject,aBlock);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"keyAtValue:ifAbsent:",{anObject:anObject,aBlock:aBlock},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"keyAtValue:ifAbsent:",{anObject:anObject,aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["anObject", "aBlock"],
 source: "keyAtValue: anObject ifAbsent: aBlock\x0a\x09^ self indexOf: anObject ifAbsent: aBlock",
 messageSends: ["indexOf:ifAbsent:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1702,14 +1683,14 @@ protocol: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-return Object.keys(self);
-return self}, function($ctx1) {$ctx1.fill(self,"keys",{},smalltalk.HashedCollection)})},
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"keys",{},smalltalk.AssociativeCollection)})},
 args: [],
-source: "keys\x0a\x09<return Object.keys(self)>",
-messageSends: [],
+source: "keys\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1722,13 +1703,13 @@ self._keysDo_((function(each){
 return smalltalk.withContext(function($ctx2) {
 return _st(aBlock)._value_value_(each,self._at_(each));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"keysAndValuesDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"keysAndValuesDo:",{aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
 source: "keysAndValuesDo: aBlock\x0a\x09self keysDo: [ :each |\x0a\x09\x09aBlock value: each value: (self at: each) ]",
 messageSends: ["keysDo:", "value:value:", "at:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1737,14 +1718,14 @@ protocol: 'enumerating',
 fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(self._keys())._do_(aBlock);
-return self}, function($ctx1) {$ctx1.fill(self,"keysDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"keysDo:",{aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
-source: "keysDo: aBlock\x0a\x09self keys do: aBlock",
-messageSends: ["do:", "keys"],
+source: "keysDo: aBlock\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1753,7 +1734,7 @@ protocol: 'printing',
 fn: function (aStream){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-smalltalk.HashedCollection.superclass.fn.prototype._printOn_.apply(_st(self), [aStream]);
+smalltalk.AssociativeCollection.superclass.fn.prototype._printOn_.apply(_st(self), [aStream]);
 $ctx1.sendIdx["printOn:"]=1;
 _st(aStream)._nextPutAll_(" (");
 $ctx1.sendIdx["nextPutAll:"]=1;
@@ -1766,13 +1747,13 @@ return _st(aStream)._nextPutAll_(" , ");
 $ctx2.sendIdx["nextPutAll:"]=2;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
 _st(aStream)._nextPutAll_(")");
-return self}, function($ctx1) {$ctx1.fill(self,"printOn:",{aStream:aStream},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"printOn:",{aStream:aStream},smalltalk.AssociativeCollection)})},
 args: ["aStream"],
 source: "printOn: aStream\x0a\x09super printOn: aStream.\x0a\x09\x0a\x09aStream nextPutAll: ' ('.\x0a\x09self associations\x0a\x09\x09do: [ :each | each printOn: aStream ]\x0a\x09\x09separatedBy: [ aStream nextPutAll: ' , ' ].\x0a\x09aStream nextPutAll: ')'",
 messageSends: ["printOn:", "nextPutAll:", "do:separatedBy:", "associations"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1784,13 +1765,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=self._removeKey_ifAbsent_(aKey,aBlock);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"remove:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"remove:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aKey", "aBlock"],
 source: "remove: aKey ifAbsent: aBlock\x0a\x09^ self removeKey: aKey ifAbsent: aBlock",
 messageSends: ["removeKey:ifAbsent:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1805,13 +1786,13 @@ return smalltalk.withContext(function($ctx2) {
 return self._removeKey_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"removeAll",{},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"removeAll",{},smalltalk.AssociativeCollection)})},
 args: [],
 source: "removeAll\x0a\x09^ self keys do: [ :each | self removeKey: each ]",
 messageSends: ["do:", "keys", "removeKey:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1823,13 +1804,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=self._remove_(aKey);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"removeKey:",{aKey:aKey},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"removeKey:",{aKey:aKey},smalltalk.AssociativeCollection)})},
 args: ["aKey"],
 source: "removeKey: aKey\x0a\x09^ self remove: aKey",
 messageSends: ["remove:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1838,21 +1819,14 @@ protocol: 'adding/removing',
 fn: function (aKey,aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $2,$1;
-$2=self._includesKey_(aKey);
-if(smalltalk.assert($2)){
-$1=self._basicDelete_(aKey);
-} else {
-$1=_st(aBlock)._value();
-};
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"removeKey:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.HashedCollection)})},
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"removeKey:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aKey", "aBlock"],
-source: "removeKey: aKey ifAbsent: aBlock\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifFalse: [ aBlock value ]\x0a\x09\x09ifTrue: [ self basicDelete: aKey ]",
-messageSends: ["ifFalse:ifTrue:", "includesKey:", "value", "basicDelete:"],
+source: "removeKey: aKey ifAbsent: aBlock\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1873,13 +1847,13 @@ return _st(newDict)._at_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $2=newDict;
 return $2;
-}, function($ctx1) {$ctx1.fill(self,"select:",{aBlock:aBlock,newDict:newDict},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"select:",{aBlock:aBlock,newDict:newDict},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
 source: "select: aBlock\x0a\x09| newDict |\x0a\x09newDict := self class new.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09(aBlock value: value) ifTrue: [ newDict at: key put: value ]].\x0a\x09^ newDict",
 messageSends: ["new", "class", "keysAndValuesDo:", "ifTrue:", "value:", "at:put:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1897,13 +1871,13 @@ return _st(copy)._at_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=copy;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{copy:copy},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{copy:copy},smalltalk.AssociativeCollection)})},
 args: [],
 source: "shallowCopy\x0a\x09| copy |\x0a\x09copy := self class new.\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09copy at: key put: value ].\x0a\x09^ copy",
 messageSends: ["new", "class", "keysAndValuesDo:", "at:put:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1915,13 +1889,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=_st(self._keys())._size();
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"size",{},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"size",{},smalltalk.AssociativeCollection)})},
 args: [],
 source: "size\x0a\x09^ self keys size",
 messageSends: ["size", "keys"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1930,18 +1904,14 @@ protocol: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-
-		return self._keys().map(function(key){
-			return self._at_(key);
-		});
-	;
-return self}, function($ctx1) {$ctx1.fill(self,"values",{},smalltalk.HashedCollection)})},
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"values",{},smalltalk.AssociativeCollection)})},
 args: [],
-source: "values\x0a\x09<\x0a\x09\x09return self._keys().map(function(key){\x0a\x09\x09\x09return self._at_(key);\x0a\x09\x09});\x0a\x09>",
-messageSends: [],
+source: "values\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1950,17 +1920,14 @@ protocol: 'enumerating',
 fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(self._values())._do_((function(value){
-return smalltalk.withContext(function($ctx2) {
-return _st(aBlock)._value_(value);
-}, function($ctx2) {$ctx2.fillBlock({value:value},$ctx1,1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"valuesDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+self._subclassResponsibility();
+return self}, function($ctx1) {$ctx1.fill(self,"valuesDo:",{aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
-source: "valuesDo: aBlock\x0a\x09self values do: [ :value | aBlock value: value ]",
-messageSends: ["do:", "values", "value:"],
+source: "valuesDo: aBlock\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -1973,13 +1940,13 @@ self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return _st(aBlock)._value_value_(value,key);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"withIndexDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+return self}, function($ctx1) {$ctx1.fill(self,"withIndexDo:",{aBlock:aBlock},smalltalk.AssociativeCollection)})},
 args: ["aBlock"],
 source: "withIndexDo: aBlock\x0a\x09self keysAndValuesDo: [ :key :value | aBlock value: value value: key ]",
 messageSends: ["keysAndValuesDo:", "value:value:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
@@ -1998,13 +1965,13 @@ return _st(newCollection)._add_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 $1=newCollection;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"from:",{aCollection:aCollection,newCollection:newCollection},smalltalk.HashedCollection.klass)})},
+}, function($ctx1) {$ctx1.fill(self,"from:",{aCollection:aCollection,newCollection:newCollection},smalltalk.AssociativeCollection.klass)})},
 args: ["aCollection"],
 source: "from: aCollection\x0a\x09| newCollection |\x0a\x09newCollection := self new.\x0a\x09aCollection do: [ :each | newCollection add: each ].\x0a\x09^ newCollection",
 messageSends: ["new", "do:", "add:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection.klass);
+smalltalk.AssociativeCollection.klass);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -2016,13 +1983,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=self._from_(aCollection);
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"fromPairs:",{aCollection:aCollection},smalltalk.HashedCollection.klass)})},
+}, function($ctx1) {$ctx1.fill(self,"fromPairs:",{aCollection:aCollection},smalltalk.AssociativeCollection.klass)})},
 args: ["aCollection"],
 source: "fromPairs: aCollection\x0a\x09\x22This message is poorly named and has been replaced by #from:\x22\x0a\x09^ self from: aCollection",
 messageSends: ["from:"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection.klass);
+smalltalk.AssociativeCollection.klass);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -2049,54 +2016,17 @@ return _st($3)._at_put_($4,_st(aCollection)._at_(_st(each).__plus((1))));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,2)})}));
 $5=newCollection;
 return $5;
-}, function($ctx1) {$ctx1.fill(self,"newFromPairs:",{aCollection:aCollection,newCollection:newCollection},smalltalk.HashedCollection.klass)})},
+}, function($ctx1) {$ctx1.fill(self,"newFromPairs:",{aCollection:aCollection,newCollection:newCollection},smalltalk.AssociativeCollection.klass)})},
 args: ["aCollection"],
 source: "newFromPairs: aCollection\x0a\x09\x22Accept an array of elements where every two elements form an \x0a\x09association - the odd element being the key, and the even element the value.\x22\x0a\x09\x0a\x09| newCollection |\x0a\x09\x0a\x09aCollection size even ifFalse: [ \x0a\x09\x09self error: '#newFromPairs only accepts arrays of an even length' ].\x0a\x09\x09\x0a\x09newCollection := self new.\x0a\x09( 1 to: aCollection size by: 2 ) do: [ :each | \x0a\x09\x09newCollection at: (aCollection at: each) put: (aCollection at: each + 1) ].\x0a\x09\x09\x0a\x09^ newCollection",
 messageSends: ["ifFalse:", "even", "size", "error:", "new", "do:", "to:by:", "at:put:", "at:", "+"],
 referencedClasses: []
 }),
-smalltalk.HashedCollection.klass);
+smalltalk.AssociativeCollection.klass);
 
 
-smalltalk.addClass('Dictionary', smalltalk.HashedCollection, ['keys', 'values'], 'Kernel-Collections');
+smalltalk.addClass('Dictionary', smalltalk.AssociativeCollection, ['keys', 'values'], 'Kernel-Collections');
 smalltalk.Dictionary.comment="I represent a set of elements that can be viewed from one of two perspectives: a set of associations,\x0aor a container of values that are externally named where the name can be any object that responds to `=`.\x0a\x0aThe external name is referred to as the key.";
-smalltalk.addMethod(
-smalltalk.method({
-selector: "asHashedCollection",
-protocol: 'converting',
-fn: function (){
-var self=this;
-function $HashedCollection(){return smalltalk.HashedCollection||(typeof HashedCollection=="undefined"?nil:HashedCollection)}
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st($HashedCollection())._from_(self._associations());
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"asHashedCollection",{},smalltalk.Dictionary)})},
-args: [],
-source: "asHashedCollection\x0a\x09^ HashedCollection from: self associations",
-messageSends: ["from:", "associations"],
-referencedClasses: ["HashedCollection"]
-}),
-smalltalk.Dictionary);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "asJSON",
-protocol: 'converting',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self._asHashedCollection())._asJSON();
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"asJSON",{},smalltalk.Dictionary)})},
-args: [],
-source: "asJSON\x0a\x09^ self asHashedCollection asJSON",
-messageSends: ["asJSON", "asHashedCollection"],
-referencedClasses: []
-}),
-smalltalk.Dictionary);
-
 smalltalk.addMethod(
 smalltalk.method({
 selector: "at:ifAbsent:",
@@ -2364,6 +2294,160 @@ smalltalk.Dictionary);
 
 
 
+smalltalk.addClass('HashedCollection', smalltalk.AssociativeCollection, [], 'Kernel-Collections');
+smalltalk.HashedCollection.comment="I am a traditional JavaScript object, or a Smalltalk `Dictionary`.\x0a\x0aUnlike a `Dictionary`, I can only have strings as keys.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "at:ifAbsent:",
+protocol: 'accessing',
+fn: function (aKey,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self._includesKey_(aKey);
+if(smalltalk.assert($2)){
+$1=self._basicAt_(aKey);
+} else {
+$1=_st(aBlock)._value();
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.HashedCollection)})},
+args: ["aKey", "aBlock"],
+source: "at: aKey ifAbsent: aBlock\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifTrue: [ self basicAt: aKey ]\x0a\x09\x09ifFalse: [ aBlock value ]",
+messageSends: ["ifTrue:ifFalse:", "includesKey:", "basicAt:", "value"],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "at:put:",
+protocol: 'accessing',
+fn: function (aKey,aValue){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self._basicAt_put_(aKey,aValue);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"at:put:",{aKey:aKey,aValue:aValue},smalltalk.HashedCollection)})},
+args: ["aKey", "aValue"],
+source: "at: aKey put: aValue\x0a\x09^ self basicAt: aKey put: aValue",
+messageSends: ["basicAt:put:"],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "includesKey:",
+protocol: 'testing',
+fn: function (aKey){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self.hasOwnProperty(aKey);
+return self}, function($ctx1) {$ctx1.fill(self,"includesKey:",{aKey:aKey},smalltalk.HashedCollection)})},
+args: ["aKey"],
+source: "includesKey: aKey\x0a\x09<return self.hasOwnProperty(aKey)>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "keys",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return Object.keys(self);
+return self}, function($ctx1) {$ctx1.fill(self,"keys",{},smalltalk.HashedCollection)})},
+args: [],
+source: "keys\x0a\x09<return Object.keys(self)>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "keysDo:",
+protocol: 'enumerating',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._keys())._do_(aBlock);
+return self}, function($ctx1) {$ctx1.fill(self,"keysDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+args: ["aBlock"],
+source: "keysDo: aBlock\x0a\x09self keys do: aBlock",
+messageSends: ["do:", "keys"],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "removeKey:ifAbsent:",
+protocol: 'adding/removing',
+fn: function (aKey,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self._at_ifPresent_ifAbsent_(aKey,(function(removed){
+return smalltalk.withContext(function($ctx2) {
+self._basicDelete_(aKey);
+return removed;
+}, function($ctx2) {$ctx2.fillBlock({removed:removed},$ctx1,1)})}),(function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(aBlock)._value();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"removeKey:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.HashedCollection)})},
+args: ["aKey", "aBlock"],
+source: "removeKey: aKey ifAbsent: aBlock\x0a\x09^ self\x0a\x09\x09at: aKey\x0a\x09\x09ifPresent: [ :removed | self basicDelete: aKey. removed ]\x0a\x09\x09ifAbsent: [ aBlock value ]",
+messageSends: ["at:ifPresent:ifAbsent:", "basicDelete:", "value"],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "values",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+
+		return self._keys().map(function(key){
+			return self._at_(key);
+		});
+	;
+return self}, function($ctx1) {$ctx1.fill(self,"values",{},smalltalk.HashedCollection)})},
+args: [],
+source: "values\x0a\x09<\x0a\x09\x09return self._keys().map(function(key){\x0a\x09\x09\x09return self._at_(key);\x0a\x09\x09});\x0a\x09>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "valuesDo:",
+protocol: 'enumerating',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._values())._do_(aBlock);
+return self}, function($ctx1) {$ctx1.fill(self,"valuesDo:",{aBlock:aBlock},smalltalk.HashedCollection)})},
+args: ["aBlock"],
+source: "valuesDo: aBlock\x0a\x09self values do: aBlock",
+messageSends: ["do:", "values"],
+referencedClasses: []
+}),
+smalltalk.HashedCollection);
+
+
+
 smalltalk.addClass('SequenceableCollection', smalltalk.IndexableCollection, [], 'Kernel-Collections');
 smalltalk.SequenceableCollection.comment="I am an IndexableCollection\x0awith numeric indexes starting with 1.";
 smalltalk.addMethod(
@@ -2711,13 +2795,13 @@ return smalltalk.withContext(function($ctx1) {
 
 		self = self._numericallyIndexable();
 		for(var i=0; i < self.length; i++) {
-			if(self[i].__eq(anObject)) {return i+1}
+			if(_st(self[i]).__eq(anObject)) {return i+1}
 		};
 		return aBlock._value();
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"indexOf:ifAbsent:",{anObject:anObject,aBlock:aBlock},smalltalk.SequenceableCollection)})},
 args: ["anObject", "aBlock"],
-source: "indexOf: anObject ifAbsent: aBlock\x0a\x09<\x0a\x09\x09self = self._numericallyIndexable();\x0a\x09\x09for(var i=0; i < self.length; i++) {\x0a\x09\x09\x09if(self[i].__eq(anObject)) {return i+1}\x0a\x09\x09};\x0a\x09\x09return aBlock._value();\x0a\x09>",
+source: "indexOf: anObject ifAbsent: aBlock\x0a\x09<\x0a\x09\x09self = self._numericallyIndexable();\x0a\x09\x09for(var i=0; i < self.length; i++) {\x0a\x09\x09\x09if(_st(self[i]).__eq(anObject)) {return i+1}\x0a\x09\x09};\x0a\x09\x09return aBlock._value();\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -2754,13 +2838,13 @@ return smalltalk.withContext(function($ctx1) {
 
 		self = self._numericallyIndexable();
 		for(var i=start - 1; i < self.length; i++){
-			if(self[i].__eq(anObject)) {return i+1}
+			if(_st(self[i]).__eq(anObject)) {return i+1}
 		}
 		return aBlock._value();
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"indexOf:startingAt:ifAbsent:",{anObject:anObject,start:start,aBlock:aBlock},smalltalk.SequenceableCollection)})},
 args: ["anObject", "start", "aBlock"],
-source: "indexOf: anObject startingAt: start ifAbsent: aBlock\x0a\x09<\x0a\x09\x09self = self._numericallyIndexable();\x0a\x09\x09for(var i=start - 1; i < self.length; i++){\x0a\x09\x09\x09if(self[i].__eq(anObject)) {return i+1}\x0a\x09\x09}\x0a\x09\x09return aBlock._value();\x0a\x09>",
+source: "indexOf: anObject startingAt: start ifAbsent: aBlock\x0a\x09<\x0a\x09\x09self = self._numericallyIndexable();\x0a\x09\x09for(var i=start - 1; i < self.length; i++){\x0a\x09\x09\x09if(_st(self[i]).__eq(anObject)) {return i+1}\x0a\x09\x09}\x0a\x09\x09return aBlock._value();\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -3904,15 +3988,14 @@ fn: function (aString){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 
-		if(typeof aString === 'undefined') { return false }
-		if(!aString._isString || ! aString._isString()) {
-			return false;
-		}
-		return String(self) === String(aString)
+		return aString != null &&
+			typeof aString._isString === "function" &&
+			aString._isString() &&
+			String(self) === String(aString)
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"=",{aString:aString},smalltalk.String)})},
 args: ["aString"],
-source: "= aString\x0a\x09<\x0a\x09\x09if(typeof aString === 'undefined') { return false }\x0a\x09\x09if(!aString._isString || ! aString._isString()) {\x0a\x09\x09\x09return false;\x0a\x09\x09}\x0a\x09\x09return String(self) === String(aString)\x0a\x09>",
+source: "= aString\x0a\x09<\x0a\x09\x09return aString != null &&\x0a\x09\x09\x09typeof aString._isString === \x22function\x22 &&\x0a\x09\x09\x09aString._isString() &&\x0a\x09\x09\x09String(self) === String(aString)\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),

+ 7 - 6
js/Kernel-Infrastructure.js

@@ -2075,15 +2075,16 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
 $2=self._includesKey_(aKey);
-$1=_st($2)._ifTrue_ifFalse_((function(){
-return smalltalk.withContext(function($ctx2) {
-return self._at_(aKey);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),aBlock);
+if(smalltalk.assert($2)){
+$1=self._at_(aKey);
+} else {
+$1=_st(aBlock)._value();
+};
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.Smalltalk)})},
 args: ["aKey", "aBlock"],
-source: "at: aKey ifAbsent: aBlock\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifTrue: [ self at: aKey ]\x0a\x09\x09ifFalse: aBlock",
-messageSends: ["ifTrue:ifFalse:", "includesKey:", "at:"],
+source: "at: aKey ifAbsent: aBlock\x0a\x09^ (self includesKey: aKey)\x0a\x09\x09ifTrue: [ self at: aKey ]\x0a\x09\x09ifFalse: [ aBlock value ]",
+messageSends: ["ifTrue:ifFalse:", "includesKey:", "at:", "value"],
 referencedClasses: []
 }),
 smalltalk.Smalltalk);

+ 10 - 10
js/Kernel-Objects.js

@@ -1302,14 +1302,14 @@ fn: function (aBoolean){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 
-		if(! aBoolean._isBoolean || ! aBoolean._isBoolean()) {
-			return false;
-		}
-		return Boolean(self == true) == aBoolean
+		return aBoolean != null &&
+			typeof aBoolean._isBoolean === "function" &&
+			aBoolean._isBoolean() &&
+			Boolean(self == true) == aBoolean
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"=",{aBoolean:aBoolean},smalltalk.Boolean)})},
 args: ["aBoolean"],
-source: "= aBoolean\x0a\x09<\x0a\x09\x09if(! aBoolean._isBoolean || ! aBoolean._isBoolean()) {\x0a\x09\x09\x09return false;\x0a\x09\x09}\x0a\x09\x09return Boolean(self == true) == aBoolean\x0a\x09>",
+source: "= aBoolean\x0a\x09<\x0a\x09\x09return aBoolean != null &&\x0a\x09\x09\x09typeof aBoolean._isBoolean === \x22function\x22 &&\x0a\x09\x09\x09aBoolean._isBoolean() &&\x0a\x09\x09\x09Boolean(self == true) == aBoolean\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -2474,14 +2474,14 @@ fn: function (aNumber){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 
-		if(! aNumber._isNumber || ! aNumber._isNumber()) {
-			return false;
-		}
-		return Number(self) == aNumber
+		return aNumber != null &&
+			typeof aNumber._isNumber === "function" &&
+			aNumber._isNumber() &&
+			Number(self) == aNumber
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"=",{aNumber:aNumber},smalltalk.Number)})},
 args: ["aNumber"],
-source: "= aNumber\x0a\x09<\x0a\x09\x09if(! aNumber._isNumber || ! aNumber._isNumber()) {\x0a\x09\x09\x09return false;\x0a\x09\x09}\x0a\x09\x09return Number(self) == aNumber\x0a\x09>",
+source: "= aNumber\x0a\x09<\x0a\x09\x09return aNumber != null &&\x0a\x09\x09\x09typeof aNumber._isNumber === \x22function\x22 &&\x0a\x09\x09\x09aNumber._isNumber() &&\x0a\x09\x09\x09Number(self) == aNumber\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),

File diff suppressed because it is too large
+ 505 - 377
js/Kernel-Tests.js


+ 28 - 0
st/Helios-Commands-Browser.st

@@ -171,6 +171,34 @@ label
 	^ 'Generate'
 ! !
 
+HLGenerateCommand subclass: #HLCategorizeUnclassifiedCommand
+	instanceVariableNames: ''
+	package: 'Helios-Commands-Browser'!
+!HLCategorizeUnclassifiedCommand commentStamp!
+I am the command used to categorize unclassified methods!
+
+!HLCategorizeUnclassifiedCommand methodsFor: 'executing'!
+
+execute
+	| targetClass unclassified |
+	targetClass := self model selectedClass.
+
+	unclassified := targetClass methods select:[ :e | e protocol = 'as yet unclassified' ].
+		
+	HLMethodClassifier new
+		classifyAll: unclassified
+! !
+
+!HLCategorizeUnclassifiedCommand class methodsFor: 'accessing'!
+
+key
+	^ 'c'
+!
+
+label
+	^ 'Categorize'
+! !
+
 HLGenerateCommand subclass: #HLGenerateAccessorsCommand
 	instanceVariableNames: ''
 	package: 'Helios-Commands-Browser'!

+ 15 - 2
st/Helios-Core.st

@@ -1371,6 +1371,12 @@ show
 	self appendToJQuery: 'body' asJQuery
 ! !
 
+!HLModalWidget methodsFor: 'private'!
+
+giveFocusToButton: aButton
+	aButton asJQuery focus
+! !
+
 !HLModalWidget methodsFor: 'rendering'!
 
 hasButtons
@@ -1392,7 +1398,7 @@ renderButtonsOn: html
 				with: 'Confirm';
 				onClick: [ self confirm ] ].
 
-	confirmButton asJQuery focus
+	self giveFocusToButton:confirmButton
 !
 
 renderContentOn: html
@@ -1499,12 +1505,19 @@ confirm
 	self actionBlock value: input asJQuery val
 ! !
 
+!HLRequestWidget methodsFor: 'private'!
+
+giveFocusToButton: aButton
+! !
+
 !HLRequestWidget methodsFor: 'rendering'!
 
 renderMainOn: html
 	super renderMainOn: html.
 	input := html textarea.
-	input asJQuery val: self value
+	input asJQuery 
+		val: self value;
+		focus
 ! !
 
 HLModalWidget subclass: #HLProgressWidget

+ 217 - 0
st/Helios-Helpers.st

@@ -1,4 +1,175 @@
 Smalltalk current createPackage: 'Helios-Helpers'!
+Object subclass: #HLClassifierLink
+	instanceVariableNames: 'next method'
+	package: 'Helios-Helpers'!
+!HLClassifierLink commentStamp!
+I am an abstract class implementing a link in a `chain of responsibility` pattern.
+
+y subclasses are in charge of classifying a method according to multiple strategies!
+
+!HLClassifierLink methodsFor: 'accessing'!
+
+method
+	^ method
+!
+
+method: anObject
+	method := anObject.
+	self next
+		ifNotNil: [ :nextLink | nextLink method: anObject ]
+!
+
+next
+	^ next
+!
+
+next: anObject
+	next := anObject
+! !
+
+!HLClassifierLink methodsFor: 'private'!
+
+doClassify
+	self subclassResponsibility
+! !
+
+!HLClassifierLink methodsFor: 'protocol'!
+
+classify
+	self next ifNil: [ ^ false ].
+	
+	^ self doClassify
+		ifTrue: [ true ]
+		ifFalse: [ self next execute ]
+! !
+
+HLClassifierLink subclass: #HLAccessorClassifierLink
+	instanceVariableNames: ''
+	package: 'Helios-Helpers'!
+!HLAccessorClassifierLink commentStamp!
+I am a classifier checking the method selector matches an instance variable name!
+
+!HLAccessorClassifierLink methodsFor: 'private'!
+
+doClassify
+	| names selector |
+	
+	names := method methodClass allInstanceVariableNames.
+	selector := method selector.
+	
+	(selector last = ':')
+		ifTrue: [ "selector might be a setter"
+			selector := selector allButLast ].
+	
+	(names includes: selector)
+		ifFalse: [ ^ false ].
+		
+	method protocol: 'accessing'.
+	^ true.
+! !
+
+HLClassifierLink subclass: #HLImplementorClassifierLink
+	instanceVariableNames: ''
+	package: 'Helios-Helpers'!
+!HLImplementorClassifierLink commentStamp!
+I am a classifier checking the other implementations of the same selector and choose the protocol the most populated!
+
+!HLImplementorClassifierLink methodsFor: 'private'!
+
+doClassify
+	| currentClass |
+	currentClass := method methodClass.
+	
+	[ currentClass superclass isNil ] whileFalse: [
+		currentClass := currentClass superclass.
+		(currentClass includesSelector: method selector)
+			ifTrue: [ 
+				method protocol: (currentClass >> method selector) protocol.
+				^ true ]].
+	
+	^ false.
+! !
+
+HLClassifierLink subclass: #HLPrefixClassifierLink
+	instanceVariableNames: 'prefixMapping'
+	package: 'Helios-Helpers'!
+!HLPrefixClassifierLink commentStamp!
+I am classifier checking the method selector to know if it begins with a known prefix!
+
+!HLPrefixClassifierLink methodsFor: 'initialization'!
+
+buildPrefixDictionary
+	prefixMapping := Dictionary new.
+	prefixMapping 
+		at: 'test' put: 'tests';
+	 	at: 'bench' put: 'benchmarking';
+	 	at: 'copy' put: 'copying';
+		at: 'initialize' put: 'initialization';
+		at: 'accept' put: 'visitor';
+		at: 'visit' put: 'visitor';
+		at: 'signal' put: 'signalling';
+		at: 'parse' put: 'parsing';
+		at: 'add' put: 'adding';
+		at: 'is' put: 'testing';
+		at: 'as' put: 'converting';
+		at: 'new' put: 'instance creation'.
+!
+
+initialize
+	super initialize.
+
+	self buildPrefixDictionary
+! !
+
+!HLPrefixClassifierLink methodsFor: 'private'!
+
+doClassify
+	prefixMapping keysAndValuesDo: [ :prefix :protocol |
+		(method selector beginsWith: prefix)
+			ifTrue: [
+				method protocol: protocol.
+				^ true ]].
+	^ false.
+! !
+
+HLClassifierLink subclass: #HLSuperClassClassifierLink
+	instanceVariableNames: ''
+	package: 'Helios-Helpers'!
+!HLSuperClassClassifierLink commentStamp!
+I am a classifier checking the superclass chain to find a matching selector!
+
+!HLSuperClassClassifierLink methodsFor: 'private'!
+
+doClassify
+	| protocolBag methods protocolToUse counter |
+	
+	protocolBag := Dictionary new.
+	methods := HLReferencesModel new implementorsOf: method selector.
+	methods
+		ifEmpty: [ ^ false ]
+		ifNotEmpty: [
+			methods 
+				do: [ :aMethod || protocol |
+					protocol := aMethod method protocol.
+					(method methodClass = aMethod methodClass)
+						ifFalse: [
+						((protocol first = '*') or: [ protocol = method defaultProtocol ])
+							ifFalse: [ 
+								protocolBag 
+									at: protocol 
+									put: (protocolBag at: protocol ifAbsent: [ 0 ]) + 1 ] ] ] ].
+			
+	protocolBag ifEmpty: [ ^ false ].
+	protocolToUse := nil.
+	counter := 0.
+	protocolBag keysAndValuesDo: [ :key :value | value > counter 
+		ifTrue: [
+			counter := value.
+			protocolToUse := key ] ].
+	method protocol: protocolToUse.
+	^ true
+! !
+
 Object subclass: #HLGenerationOutput
 	instanceVariableNames: 'sourceCodes protocol targetClass'
 	package: 'Helios-Helpers'!
@@ -243,6 +414,52 @@ generate
 		initializeProtocolWith: self
 ! !
 
+Object subclass: #HLMethodClassifier
+	instanceVariableNames: 'firstLink'
+	package: 'Helios-Helpers'!
+!HLMethodClassifier commentStamp!
+I am in charge of categorizing methods following this strategy:
+
+- is it an accessor?
+- is it overriding a superclass method?
+- is it starting with a know prefix?
+- how are categorized the other implementations?!
+
+!HLMethodClassifier methodsFor: 'initialization'!
+
+buildChainOfResponsibility
+	self addLink: HLImplementorClassifierLink new.
+	self addLink: HLPrefixClassifierLink new.
+	self addLink: HLSuperclassClassifierLink new.
+	self addLink: HLAccessorClassifierLink new
+!
+
+initialize
+	super initialize.
+	
+	self buildChainOfResponsibility
+! !
+
+!HLMethodClassifier methodsFor: 'private'!
+
+addLink: aLink
+	aLink next: firstLink.
+	firstLink := aLink
+! !
+
+!HLMethodClassifier methodsFor: 'protocol'!
+
+classify: aMethod
+	firstLink
+		method: aMethod;
+		classify
+!
+
+classifyAll: aCollectionOfMethods
+	aCollectionOfMethods do: [ :method |
+		self classify: method ]
+! !
+
 Object subclass: #HLMethodSourceCode
 	instanceVariableNames: 'selector sourceCode'
 	package: 'Helios-Helpers'!

+ 1 - 1
st/IDE.st

@@ -2359,7 +2359,7 @@ inspectOn: anInspector
 		setVariables: variables
 ! !
 
-!HashedCollection methodsFor: '*IDE'!
+!AssociativeCollection methodsFor: '*IDE'!
 
 inspectOn: anInspector
 	| variables |

+ 95 - 64
st/Kernel-Collections.st

@@ -435,15 +435,13 @@ withIndexDo: aBlock
 	self subclassResponsibility
 ! !
 
-IndexableCollection subclass: #HashedCollection
+IndexableCollection subclass: #AssociativeCollection
 	instanceVariableNames: ''
 	package: 'Kernel-Collections'!
-!HashedCollection commentStamp!
-I am a traditional JavaScript object, or a Smalltalk `Dictionary`.
+!AssociativeCollection commentStamp!
+I am a base class for object-indexed collections (Dictionary et.al.).!
 
-Unlike a `Dictionary`, I can only have strings as keys.!
-
-!HashedCollection methodsFor: 'accessing'!
+!AssociativeCollection methodsFor: 'accessing'!
 
 associations
 	| associations |
@@ -452,27 +450,16 @@ associations
 	^ associations
 !
 
-at: aKey ifAbsent: aBlock
-	^ (self includesKey: aKey)
-		ifTrue: [ self basicAt: aKey ]
-		ifFalse: aBlock
-!
-
 at: aKey ifPresent: aBlock ifAbsent: anotherBlock
 	"Lookup the given key in the receiver.
 	If it is present, answer the value of evaluating the oneArgBlock with the value associated with the key,
 	otherwise answer the value of absentBlock."
 	^ (self includesKey: aKey)
 		ifTrue: [ aBlock value: (self at: aKey) ]
-		ifFalse: anotherBlock
-!
-
-at: aKey put: aValue
-	^ self basicAt: aKey put: aValue
+		ifFalse: [ anotherBlock value ]
 !
 
 indexOf: anObject ifAbsent: aBlock
-
 	^ self keys detect: [ :each | (self at: each) = anObject ] ifNone: aBlock
 !
 
@@ -485,7 +472,7 @@ keyAtValue: anObject ifAbsent: aBlock
 !
 
 keys
-	<return Object.keys(self)>
+	self subclassResponsibility
 !
 
 size
@@ -493,22 +480,18 @@ size
 !
 
 values
-	<
-		return self._keys().map(function(key){
-			return self._at_(key);
-		});
-	>
+	self subclassResponsibility
 ! !
 
-!HashedCollection methodsFor: 'adding/removing'!
+!AssociativeCollection methodsFor: 'adding/removing'!
 
 add: anAssociation
 	self at: anAssociation key put: anAssociation value
 !
 
-addAll: aHashedCollection
-	super addAll: aHashedCollection associations.
-	^ aHashedCollection
+addAll: anAssocitativeCollection
+	super addAll: anAssocitativeCollection associations.
+	^ anAssocitativeCollection
 !
 
 remove: aKey ifAbsent: aBlock
@@ -524,34 +507,36 @@ removeKey: aKey
 !
 
 removeKey: aKey ifAbsent: aBlock
-	^ (self includesKey: aKey)
-		ifFalse: [ aBlock value ]
-		ifTrue: [ self basicDelete: aKey ]
+	self subclassResponsibility
 ! !
 
-!HashedCollection methodsFor: 'comparing'!
+!AssociativeCollection methodsFor: 'comparing'!
 
-= aHashedCollection
-	self class = aHashedCollection class ifFalse: [ ^ false ].
-	self size = aHashedCollection size ifFalse: [ ^ false ].
-	^ self associations = aHashedCollection associations
+= anAssocitativeCollection
+	self class = anAssocitativeCollection class ifFalse: [ ^ false ].
+	self size = anAssocitativeCollection size ifFalse: [ ^ false ].
+	^ self associations = anAssocitativeCollection associations
 ! !
 
-!HashedCollection methodsFor: 'converting'!
+!AssociativeCollection methodsFor: 'converting'!
 
 asDictionary
 	^ Dictionary from: self associations
 !
 
+asHashedCollection
+	^ HashedCollection from: self associations
+!
+
 asJSON
 	| c |
-	c := self class new.
+	c := HashedCollection new.
 	self keysAndValuesDo: [ :key :value |
 		c at: key put: value asJSON ].
 	^ c
 ! !
 
-!HashedCollection methodsFor: 'copying'!
+!AssociativeCollection methodsFor: 'copying'!
 
 deepCopy
 	| copy |
@@ -569,7 +554,7 @@ shallowCopy
 	^ copy
 ! !
 
-!HashedCollection methodsFor: 'enumerating'!
+!AssociativeCollection methodsFor: 'enumerating'!
 
 associationsDo: aBlock
 	self keysAndValuesDo: [ :key :value |
@@ -602,7 +587,7 @@ keysAndValuesDo: aBlock
 !
 
 keysDo: aBlock
-	self keys do: aBlock
+	self subclassResponsibility
 !
 
 select: aBlock
@@ -614,14 +599,14 @@ select: aBlock
 !
 
 valuesDo: aBlock
-	self values do: [ :value | aBlock value: value ]
+	self subclassResponsibility
 !
 
 withIndexDo: aBlock
 	self keysAndValuesDo: [ :key :value | aBlock value: value value: key ]
 ! !
 
-!HashedCollection methodsFor: 'printing'!
+!AssociativeCollection methodsFor: 'printing'!
 
 printOn: aStream
 	super printOn: aStream.
@@ -633,13 +618,13 @@ printOn: aStream
 	aStream nextPutAll: ')'
 ! !
 
-!HashedCollection methodsFor: 'testing'!
+!AssociativeCollection methodsFor: 'testing'!
 
 includesKey: aKey
-	<return self.hasOwnProperty(aKey)>
+	self subclassResponsibility
 ! !
 
-!HashedCollection class methodsFor: 'instance creation'!
+!AssociativeCollection class methodsFor: 'instance creation'!
 
 from: aCollection
 	| newCollection |
@@ -669,7 +654,7 @@ newFromPairs: aCollection
 	^ newCollection
 ! !
 
-HashedCollection subclass: #Dictionary
+AssociativeCollection subclass: #Dictionary
 	instanceVariableNames: 'keys values'
 	package: 'Kernel-Collections'!
 !Dictionary commentStamp!
@@ -742,16 +727,6 @@ removeKey: aKey ifAbsent: aBlock
 	>
 ! !
 
-!Dictionary methodsFor: 'converting'!
-
-asHashedCollection
-	^ HashedCollection from: self associations
-!
-
-asJSON
-	^ self asHashedCollection asJSON
-! !
-
 !Dictionary methodsFor: 'enumerating'!
 
 keysAndValuesDo: aBlock
@@ -792,6 +767,63 @@ includesKey: aKey
 	< return self._positionOfKey_(aKey) >>= 0; >
 ! !
 
+AssociativeCollection subclass: #HashedCollection
+	instanceVariableNames: ''
+	package: 'Kernel-Collections'!
+!HashedCollection commentStamp!
+I am a traditional JavaScript object, or a Smalltalk `Dictionary`.
+
+Unlike a `Dictionary`, I can only have strings as keys.!
+
+!HashedCollection methodsFor: 'accessing'!
+
+at: aKey ifAbsent: aBlock
+	^ (self includesKey: aKey)
+		ifTrue: [ self basicAt: aKey ]
+		ifFalse: [ aBlock value ]
+!
+
+at: aKey put: aValue
+	^ self basicAt: aKey put: aValue
+!
+
+keys
+	<return Object.keys(self)>
+!
+
+values
+	<
+		return self._keys().map(function(key){
+			return self._at_(key);
+		});
+	>
+! !
+
+!HashedCollection methodsFor: 'adding/removing'!
+
+removeKey: aKey ifAbsent: aBlock
+	^ self
+		at: aKey
+		ifPresent: [ :removed | self basicDelete: aKey. removed ]
+		ifAbsent: [ aBlock value ]
+! !
+
+!HashedCollection methodsFor: 'enumerating'!
+
+keysDo: aBlock
+	self keys do: aBlock
+!
+
+valuesDo: aBlock
+	self values do: aBlock
+! !
+
+!HashedCollection methodsFor: 'testing'!
+
+includesKey: aKey
+	<return self.hasOwnProperty(aKey)>
+! !
+
 IndexableCollection subclass: #SequenceableCollection
 	instanceVariableNames: ''
 	package: 'Kernel-Collections'!
@@ -834,7 +866,7 @@ indexOf: anObject ifAbsent: aBlock
 	<
 		self = self._numericallyIndexable();
 		for(var i=0; i < self.length; i++) {
-			if(self[i].__eq(anObject)) {return i+1}
+			if(_st(self[i]).__eq(anObject)) {return i+1}
 		};
 		return aBlock._value();
 	>
@@ -851,7 +883,7 @@ indexOf: anObject startingAt: start ifAbsent: aBlock
 	<
 		self = self._numericallyIndexable();
 		for(var i=start - 1; i < self.length; i++){
-			if(self[i].__eq(anObject)) {return i+1}
+			if(_st(self[i]).__eq(anObject)) {return i+1}
 		}
 		return aBlock._value();
 	>
@@ -1347,11 +1379,10 @@ size
 
 = aString
 	<
-		if(typeof aString === 'undefined') { return false }
-		if(!!aString._isString || !! aString._isString()) {
-			return false;
-		}
-		return String(self) === String(aString)
+		return aString !!= null &&
+			typeof aString._isString === "function" &&
+			aString._isString() &&
+			String(self) === String(aString)
 	>
 !
 

+ 1 - 1
st/Kernel-Infrastructure.st

@@ -785,7 +785,7 @@ at: aString
 at: aKey ifAbsent: aBlock
 	^ (self includesKey: aKey)
 		ifTrue: [ self at: aKey ]
-		ifFalse: aBlock
+		ifFalse: [ aBlock value ]
 !
 
 at: aString put: anObject

+ 8 - 8
st/Kernel-Objects.st

@@ -440,10 +440,10 @@ I am directly mapped to JavaScript Boolean. The `true` and `false` objects are t
 
 = aBoolean
 	<
-		if(!! aBoolean._isBoolean || !! aBoolean._isBoolean()) {
-			return false;
-		}
-		return Boolean(self == true) == aBoolean
+		return aBoolean !!= null &&
+			typeof aBoolean._isBoolean === "function" &&
+			aBoolean._isBoolean() &&
+			Boolean(self == true) == aBoolean
 	>
 !
 
@@ -839,10 +839,10 @@ negated
 
 = aNumber
 	<
-		if(!! aNumber._isNumber || !! aNumber._isNumber()) {
-			return false;
-		}
-		return Number(self) == aNumber
+		return aNumber !!= null &&
+			typeof aNumber._isNumber === "function" &&
+			aNumber._isNumber() &&
+			Number(self) == aNumber
 	>
 !
 

+ 151 - 119
st/Kernel-Tests.st

@@ -719,12 +719,28 @@ testAtPut
 	self assert: newCollection equals: self collectionWithNewValue
 !
 
+testEquality
+	self assert: self collectionClass new equals: self collectionClass new.
+	self assert: self collection equals: self collection.
+	self assert: self collectionWithNewValue equals: self collectionWithNewValue.
+	
+	self deny: self collectionClass new = self collection.
+	self deny: self collection = self collectionClass new
+!
+
 testIndexOf
 	self should: [ self collection indexOf: self sampleNewValue ] raise: Error.
 	self samplesDo: [ :index :value |
 		self assert: (self collection indexOf: value) equals: index ]
 !
 
+testIndexOfWithNull
+	| jsNull |
+	jsNull := JSON parse: 'null'.
+	self samplesDo: [ :index :value |
+		self assert: (self collection at: index put: jsNull; indexOf: jsNull) equals: index ]
+!
+
 testWithIndexDo
 	| collection |
 	collection := self collection.
@@ -733,30 +749,18 @@ testWithIndexDo
 		self assert: (collection at: index) equals: each ]
 ! !
 
-IndexableCollectionTest subclass: #HashedCollectionTest
+IndexableCollectionTest subclass: #AssociativeCollectionTest
 	instanceVariableNames: ''
 	package: 'Kernel-Tests'!
 
-!HashedCollectionTest methodsFor: 'fixture'!
-
-collection
-	^ #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4 }
-!
-
-collectionOfPrintStrings
-	^ #{ 'b' -> '1'. 'a' -> '2'. 'c' -> '3'. 'd' -> '-4' }
-!
-
-collectionSize
-	^ 4
-!
+!AssociativeCollectionTest methodsFor: 'fixture'!
 
-collectionWithDuplicates
-	^ #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4. 'e' -> 1. 'f' -> 2. 'g' -> 10 }
+collectionKeys
+	self subclassResponsibility
 !
 
-collectionWithNewValue
-	^ #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4. 'new' -> 'N' }
+collectionValues
+	self subclassResponsibility
 !
 
 nonIndexesDo: aBlock
@@ -768,15 +772,11 @@ sampleNewIndex
 	^ 'new'
 !
 
-sampleNewValueAsCollection
-	^ #{ 'new' -> 'N' }
-!
-
 samplesDo: aBlock
 	aBlock value: 'a' value: 2
 ! !
 
-!HashedCollectionTest methodsFor: 'tests'!
+!AssociativeCollectionTest methodsFor: 'tests'!
 
 testAddAll
 	super testAddAll.
@@ -789,6 +789,10 @@ testAsDictionary
 self assert: ( self collectionClass new asDictionary isMemberOf: Dictionary ).
 !
 
+testAsHashedCollection
+self assert: ( self collectionClass new asHashedCollection isMemberOf: HashedCollection ).
+!
+
 testComma
 	super testComma.
 	self assert: self collection, self collection equals: self collection.
@@ -803,20 +807,67 @@ associations := { 'a' -> 1. 'b' -> 2 }.
 self assertSameContents: ( self class collectionClass from: associations ) as: #{ 'a' -> 1. 'b' -> 2 }.
 !
 
+testKeys
+	self assert:self collectionClass new keys isEmpty.
+	self assertSameContents:self collection keys as: self collectionKeys.
+	self assertSameContents:self collectionWithNewValue keys as: self collectionKeys, { self sampleNewIndex }
+!
+
 testNewFromPairs
 "Accept an array in which all odd indexes are keys and evens are values."
 | flattenedAssociations |
 flattenedAssociations := { 'a'. 1. 'b'. 2 }.
 self assertSameContents: ( self class collectionClass newFromPairs: flattenedAssociations ) as: #{ 'a' -> 1. 'b' -> 2 }.
-! !
+!
 
-!HashedCollectionTest class methodsFor: 'fixture'!
+testPrintString
+	self
+		assert: (self collectionClass new
+							at:'firstname' put: 'James';
+							at:'lastname' put: 'Bond';
+							printString)
+		equals: 'a ', self collectionClass name, ' (''firstname'' -> ''James'' , ''lastname'' -> ''Bond'')'
+!
 
-collectionClass
-	^ HashedCollection
+testRemoveKey
+	self nonIndexesDo: [ :each |
+		| collection |
+		collection := self collection.
+		self should: [ collection removeKey: each ] raise: Error.
+		self assert: collection equals: self collection ].
+	self samplesDo: [ :index :value |
+		| collection |
+		collection := self collection.
+		self assert: (collection removeKey: index) equals: value.
+		self deny: collection = self collection ].
+	self
+		assert: (self collectionWithNewValue removeKey: self sampleNewIndex; yourself)
+		equals: self collection
+!
+
+testRemoveKeyIfAbsent
+	self nonIndexesDo: [ :each |
+		| collection |
+		collection := self collection.
+		self assert: (collection removeKey: each ifAbsent: [ self sampleNewValue ]) equals: self sampleNewValue.
+		self assert: collection equals: self collection ].
+	self samplesDo: [ :index :value |
+		| collection |
+		collection := self collection.
+		self assert: (collection removeKey: index ifAbsent: [ self sampleNewValue ]) equals: value.
+		self deny: collection = self collection ].
+	self
+		assert: (self collectionWithNewValue removeKey: self sampleNewIndex ifAbsent: [ self assert: false ]; yourself)
+		equals: self collection
+!
+
+testValues
+	self assert:self collectionClass new values isEmpty.
+	self assertSameContents:self collection values as: self collectionValues.
+	self assertSameContents:self collectionWithNewValue values as: self collectionValues, { self sampleNewValue }
 ! !
 
-HashedCollectionTest subclass: #DictionaryTest
+AssociativeCollectionTest subclass: #DictionaryTest
 	instanceVariableNames: ''
 	package: 'Kernel-Tests'!
 
@@ -831,6 +882,10 @@ collection
 		yourself
 !
 
+collectionKeys
+	^ {1. 'a'. true. 1@3}
+!
+
 collectionOfPrintStrings
 	^ Dictionary new
 		at: 1 put: '1';
@@ -844,6 +899,10 @@ collectionSize
 	^ 4
 !
 
+collectionValues
+	^ {1. 2. 3. -4}
+!
+
 collectionWithDuplicates
 	^ Dictionary new
 		at: 1 put: 1;
@@ -903,118 +962,64 @@ testAccessing
 	self deny: (d includesKey: 3@1)
 !
 
-testAsHashedCollection
-self assert: ( self collectionClass new asHashedCollection isMemberOf: HashedCollection ).
-!
-
 testDynamicDictionaries
 	self assert: #{'hello' -> 1} asDictionary equals: (Dictionary with: 'hello' -> 1)
-!
+! !
 
-testEquality
-	| d1 d2 |
+!DictionaryTest class methodsFor: 'fixture'!
 
-	self assert: (Dictionary new = Dictionary new).
-		
-	d1 := Dictionary new at: 1 put: 2; yourself.
-	d2 := Dictionary new at: 1 put: 2; yourself.
-	self assert: (d1 = d2).
+collectionClass
+	^ Dictionary
+! !
 
-	d2 := Dictionary new at: 1 put: 3; yourself.
-	self deny: d1 = d2.
+AssociativeCollectionTest subclass: #HashedCollectionTest
+	instanceVariableNames: ''
+	package: 'Kernel-Tests'!
 
-	d2 := Dictionary new at: 2 put: 2; yourself.
-	self deny: d1 = d2.
+!HashedCollectionTest methodsFor: 'fixture'!
 
-	d2 := Dictionary new at: 1 put: 2; at: 3 put: 4; yourself.
-	self deny: d1 = d2.
+collection
+	^ #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4 }
 !
 
-testKeys
-	| d |
-
-	d := Dictionary new.
-	d at: 1 put: 2.
-	d at: 2 put: 3.
-	d at: 3 put: 4.
-
-	self assert: d keys equals: #(1 2 3)
+collectionKeys
+	^ { 'b'. 'a'. 'c'. 'd' }
 !
 
-testPointKey
-	| d |
-
-	d := Dictionary new.
-	
-	d at: 1@1 put: 'foo'.
-	self assert: (d at: 1@1) equals: 'foo'.
-	d at: 1@1 put: 'bar'.
-	self assert: (d at: 1@1) equals: 'bar'.
-	d removeKey: 1@1.
-	self assert: (d at: 1@1 ifAbsent: [ 'baz' ]) equals: 'baz'.
-	self deny: (d includesKey: 1@1)
+collectionOfPrintStrings
+	^ #{ 'b' -> '1'. 'a' -> '2'. 'c' -> '3'. 'd' -> '-4' }
 !
 
-testPrintString
-	self
-		assert: (Dictionary new
-							at:'firstname' put: 'James';
-							at:'lastname' put: 'Bond';
-							printString)
-		equals: 'a Dictionary (''firstname'' -> ''James'' , ''lastname'' -> ''Bond'')'
+collectionSize
+	^ 4
 !
 
-testRemoveKey
-	| d key |
-
-	d := Dictionary new.
-	d at: 1 put: 2.
-	d at: 2 put: 3.
-	d at: 3 put: 4.
-
-	key := 2.
-
-	self assert: d keys equals: #(1 2 3).
-
-	d removeKey: key.
-	self assert: d keys equals: #(1 3).
-	self assert: d values equals: #(2 4).
-	self deny: (d includesKey: 2)
+collectionValues
+	^ { 1. 2. 3. -4 }
 !
 
-testRemoveKeyIfAbsent
-	| d key |
-
-	d := Dictionary new.
-	d at: 1 put: 2.
-	d at: 2 put: 3.
-	d at: 3 put: 4.
-
-	key := 2.
-	self assert: (d removeKey: key) equals: 3.
-
-	key := 3.
-	self assert: (d removeKey: key ifAbsent: [ 42 ]) equals: 4.
+collectionWithDuplicates
+	^ #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4. 'e' -> 1. 'f' -> 2. 'g' -> 10 }
+!
 
-	key := 'why'.
-	self assert: (d removeKey: key ifAbsent: [ 42 ] ) equals: 42.
+collectionWithNewValue
+	^ #{ 'b' -> 1. 'a' -> 2. 'c' -> 3. 'd' -> -4. 'new' -> 'N' }
 !
 
-testValues
-	| d |
+sampleNewValueAsCollection
+	^ #{ 'new' -> 'N' }
+! !
 
-	d := Dictionary new.
-	d at: 1 put: 2.
-	d at: 2 put: 3.
-	d at: 3 put: 4.
+!HashedCollectionTest methodsFor: 'tests'!
 
-	self assert: d values equals: #(2 3 4)
+testDynamicDictionaries
+	self assert: #{'hello' -> 1} asHashedCollection equals: (HashedCollection with: 'hello' -> 1)
 ! !
 
-!DictionaryTest class methodsFor: 'fixture'!
+!HashedCollectionTest class methodsFor: 'fixture'!
 
 collectionClass
-	^ Dictionary
+	^ HashedCollection
 ! !
 
 IndexableCollectionTest subclass: #SequenceableCollectionTest
@@ -1090,6 +1095,26 @@ testFourth
 	self assert: (self collection fourth) equals: (self collection at: 4)
 !
 
+testIndexOfStartingAt
+	| jsNull |
+	jsNull := JSON parse: 'null'.
+	self samplesDo: [ :index :value |
+		self assert: (self collection indexOf: value startingAt: 1) equals: index.
+		self assert: (self collection indexOf: value startingAt: index) equals: index.
+		self assert: (self collection indexOf: value startingAt: index+1) equals: 0 ]
+!
+
+testIndexOfStartingAtWithNull
+	| jsNull |
+	jsNull := JSON parse: 'null'.
+	self samplesDo: [ :index :value | | collection |
+		collection := self collection.
+		collection at: index put: jsNull.
+		self assert: (collection indexOf: jsNull startingAt: 1) equals: index.
+		self assert: (collection indexOf: jsNull startingAt: index) equals: index.
+		self assert: (collection indexOf: jsNull startingAt: index+1) equals: 0 ]
+!
+
 testLast
 	self assert: self collection last equals: self collectionLast
 !
@@ -1256,7 +1281,7 @@ SequenceableCollectionTest subclass: #StringTest
 !StringTest methodsFor: 'fixture'!
 
 collection
-	^ 'hello'
+	^ 'helLo'
 !
 
 collectionFirst
@@ -1272,11 +1297,11 @@ collectionLast
 !
 
 collectionLastTwo
-	^ 'lo'
+	^ 'Lo'
 !
 
 collectionOfPrintStrings
-	^ '''h''''e''''l''''l''''o'''
+	^ '''h''''e''''l''''L''''o'''
 !
 
 collectionSize
@@ -1288,11 +1313,10 @@ collectionWithDuplicates
 !
 
 collectionWithNewValue
-	^ 'helloN'
+	^ 'helLoN'
 !
 
 sampleNewValueAsCollection
-	
 	^ 'N'
 !
 
@@ -1412,6 +1436,14 @@ testIncludesSubString
 	self deny: ('amber' includesSubString: 'zork').
 !
 
+testIndexOfStartingAtWithNull
+	"String cannot hold JS null"
+!
+
+testIndexOfWithNull
+	"String cannot hold JS null"
+!
+
 testIsVowel
     |vowel consonant|
     vowel := 'u'.

Some files were not shown because too many files changed in this diff