1
0
Преглед на файлове

Merge pull request #794 from herby/assocative-collection-restructure

Assocative collection restructure
Nicolas Petton преди 11 години
родител
ревизия
99169869ef
променени са 9 файла, в които са добавени 1058 реда и са изтрити 839 реда
  1. 2 0
      API-CHANGES.txt
  2. 30 30
      js/IDE.js
  3. 287 203
      js/Kernel-Collections.js
  4. 7 6
      js/Kernel-Infrastructure.js
  5. 516 414
      js/Kernel-Tests.js
  6. 14 14
      st/IDE.st
  7. 89 57
      st/Kernel-Collections.st
  8. 1 1
      st/Kernel-Infrastructure.st
  9. 112 114
      st/Kernel-Tests.st

+ 2 - 0
API-CHANGES.txt

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

+ 30 - 30
js/IDE.js

@@ -7405,21 +7405,27 @@ var $1;
 variables=_st($Dictionary())._new();
 variables=_st($Dictionary())._new();
 _st(variables)._at_put_("#self",self);
 _st(variables)._at_put_("#self",self);
 $ctx1.sendIdx["at:put:"]=1;
 $ctx1.sendIdx["at:put:"]=1;
-_st(variables)._at_put_("#keys",self._keys());
+_st(variables)._at_put_("#home",self._home());
 $ctx1.sendIdx["at:put:"]=2;
 $ctx1.sendIdx["at:put:"]=2;
-self._keysAndValuesDo_((function(key,value){
+_st(variables)._at_put_("#receiver",self._receiver());
+$ctx1.sendIdx["at:put:"]=3;
+_st(variables)._at_put_("#selector",self._selector());
+$ctx1.sendIdx["at:put:"]=4;
+_st(variables)._at_put_("#temps",self._temps());
+$ctx1.sendIdx["at:put:"]=5;
+_st(_st(self._class())._instanceVariableNames())._do_((function(each){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
-return _st(variables)._at_put_(key,value);
-}, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
+return _st(variables)._at_put_(each,self._instVarAt_(each));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 _st(anInspector)._setLabel_(self._printString());
 _st(anInspector)._setLabel_(self._printString());
 $1=_st(anInspector)._setVariables_(variables);
 $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.MethodContext)})},
 args: ["anInspector"],
 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:"],
+source: "inspectOn: anInspector\x0a\x09| variables |\x0a\x09variables := Dictionary new.\x0a\x09variables at: '#self' put: self.\x0a\x09variables at: '#home' put: self home.\x0a\x09variables at: '#receiver' put: self receiver.\x0a\x09variables at: '#selector' put: self selector.\x0a\x09variables at: '#temps' put: self temps.\x0a\x09self class instanceVariableNames do: [ :each |\x0a\x09\x09variables at: each put: (self instVarAt: each) ].\x0a\x09anInspector\x0a\x09\x09setLabel: self printString;\x0a\x09\x09setVariables: variables",
+messageSends: ["new", "at:put:", "home", "receiver", "selector", "temps", "do:", "instanceVariableNames", "class", "instVarAt:", "setLabel:", "printString", "setVariables:"],
 referencedClasses: ["Dictionary"]
 referencedClasses: ["Dictionary"]
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.MethodContext);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -7434,19 +7440,21 @@ var $1;
 variables=_st($Dictionary())._new();
 variables=_st($Dictionary())._new();
 _st(variables)._at_put_("#self",self);
 _st(variables)._at_put_("#self",self);
 $ctx1.sendIdx["at:put:"]=1;
 $ctx1.sendIdx["at:put:"]=1;
-_st(self["@elements"])._withIndexDo_((function(each,i){
+_st(variables)._at_put_("#keys",self._keys());
+$ctx1.sendIdx["at:put:"]=2;
+self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
-return _st(variables)._at_put_(i,each);
-}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1,1)})}));
+return _st(variables)._at_put_(key,value);
+}, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 _st(anInspector)._setLabel_(self._printString());
 _st(anInspector)._setLabel_(self._printString());
 $1=_st(anInspector)._setVariables_(variables);
 $1=_st(anInspector)._setVariables_(variables);
-return self}, function($ctx1) {$ctx1.fill(self,"inspectOn:",{anInspector:anInspector,variables:variables},smalltalk.Set)})},
+return self}, function($ctx1) {$ctx1.fill(self,"inspectOn:",{anInspector:anInspector,variables:variables},smalltalk.AssociativeCollection)})},
 args: ["anInspector"],
 args: ["anInspector"],
-source: "inspectOn: anInspector\x0a\x09| variables |\x0a\x09variables := Dictionary new.\x0a\x09variables at: '#self' put: self.\x0a\x09elements withIndexDo: [ :each :i |\x0a\x09\x09variables at: i put: each ].\x0a\x09anInspector\x0a\x09\x09setLabel: self printString;\x0a\x09\x09setVariables: variables",
-messageSends: ["new", "at:put:", "withIndexDo:", "setLabel:", "printString", "setVariables:"],
+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"]
 referencedClasses: ["Dictionary"]
 }),
 }),
-smalltalk.Set);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -7461,26 +7469,18 @@ var $1;
 variables=_st($Dictionary())._new();
 variables=_st($Dictionary())._new();
 _st(variables)._at_put_("#self",self);
 _st(variables)._at_put_("#self",self);
 $ctx1.sendIdx["at:put:"]=1;
 $ctx1.sendIdx["at:put:"]=1;
-_st(variables)._at_put_("#home",self._home());
-$ctx1.sendIdx["at:put:"]=2;
-_st(variables)._at_put_("#receiver",self._receiver());
-$ctx1.sendIdx["at:put:"]=3;
-_st(variables)._at_put_("#selector",self._selector());
-$ctx1.sendIdx["at:put:"]=4;
-_st(variables)._at_put_("#temps",self._temps());
-$ctx1.sendIdx["at:put:"]=5;
-_st(_st(self._class())._instanceVariableNames())._do_((function(each){
+_st(self["@elements"])._withIndexDo_((function(each,i){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
-return _st(variables)._at_put_(each,self._instVarAt_(each));
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
+return _st(variables)._at_put_(i,each);
+}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1,1)})}));
 _st(anInspector)._setLabel_(self._printString());
 _st(anInspector)._setLabel_(self._printString());
 $1=_st(anInspector)._setVariables_(variables);
 $1=_st(anInspector)._setVariables_(variables);
-return self}, function($ctx1) {$ctx1.fill(self,"inspectOn:",{anInspector:anInspector,variables:variables},smalltalk.MethodContext)})},
+return self}, function($ctx1) {$ctx1.fill(self,"inspectOn:",{anInspector:anInspector,variables:variables},smalltalk.Set)})},
 args: ["anInspector"],
 args: ["anInspector"],
-source: "inspectOn: anInspector\x0a\x09| variables |\x0a\x09variables := Dictionary new.\x0a\x09variables at: '#self' put: self.\x0a\x09variables at: '#home' put: self home.\x0a\x09variables at: '#receiver' put: self receiver.\x0a\x09variables at: '#selector' put: self selector.\x0a\x09variables at: '#temps' put: self temps.\x0a\x09self class instanceVariableNames do: [ :each |\x0a\x09\x09variables at: each put: (self instVarAt: each) ].\x0a\x09anInspector\x0a\x09\x09setLabel: self printString;\x0a\x09\x09setVariables: variables",
-messageSends: ["new", "at:put:", "home", "receiver", "selector", "temps", "do:", "instanceVariableNames", "class", "instVarAt:", "setLabel:", "printString", "setVariables:"],
+source: "inspectOn: anInspector\x0a\x09| variables |\x0a\x09variables := Dictionary new.\x0a\x09variables at: '#self' put: self.\x0a\x09elements withIndexDo: [ :each :i |\x0a\x09\x09variables at: i put: each ].\x0a\x09anInspector\x0a\x09\x09setLabel: self printString;\x0a\x09\x09setVariables: variables",
+messageSends: ["new", "at:put:", "withIndexDo:", "setLabel:", "printString", "setVariables:"],
 referencedClasses: ["Dictionary"]
 referencedClasses: ["Dictionary"]
 }),
 }),
-smalltalk.MethodContext);
+smalltalk.Set);
 
 
 });
 });

+ 287 - 203
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.addMethod(
 smalltalk.method({
 smalltalk.method({
 selector: "=",
 selector: "=",
 protocol: 'comparing',
 protocol: 'comparing',
-fn: function (aHashedCollection){
+fn: function (anAssocitativeCollection){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1,$4,$3,$6,$5;
 var $2,$1,$4,$3,$6,$5;
 $2=self._class();
 $2=self._class();
 $ctx1.sendIdx["class"]=1;
 $ctx1.sendIdx["class"]=1;
-$1=_st($2).__eq(_st(aHashedCollection)._class());
+$1=_st($2).__eq(_st(anAssocitativeCollection)._class());
 $ctx1.sendIdx["="]=1;
 $ctx1.sendIdx["="]=1;
 if(! smalltalk.assert($1)){
 if(! smalltalk.assert($1)){
 return false;
 return false;
 };
 };
 $4=self._size();
 $4=self._size();
 $ctx1.sendIdx["size"]=1;
 $ctx1.sendIdx["size"]=1;
-$3=_st($4).__eq(_st(aHashedCollection)._size());
+$3=_st($4).__eq(_st(anAssocitativeCollection)._size());
 $ctx1.sendIdx["="]=2;
 $ctx1.sendIdx["="]=2;
 if(! smalltalk.assert($3)){
 if(! smalltalk.assert($3)){
 return false;
 return false;
 };
 };
 $6=self._associations();
 $6=self._associations();
 $ctx1.sendIdx["associations"]=1;
 $ctx1.sendIdx["associations"]=1;
-$5=_st($6).__eq(_st(aHashedCollection)._associations());
+$5=_st($6).__eq(_st(anAssocitativeCollection)._associations());
 return $5;
 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"],
 messageSends: ["ifFalse:", "=", "class", "size", "associations"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1345,30 +1345,30 @@ fn: function (anAssociation){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return smalltalk.withContext(function($ctx1) { 
 self._at_put_(_st(anAssociation)._key(),_st(anAssociation)._value());
 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"],
 args: ["anAssociation"],
 source: "add: anAssociation\x0a\x09self at: anAssociation key put: anAssociation value",
 source: "add: anAssociation\x0a\x09self at: anAssociation key put: anAssociation value",
 messageSends: ["at:put:", "key", "value"],
 messageSends: ["at:put:", "key", "value"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
 selector: "addAll:",
 selector: "addAll:",
 protocol: 'adding/removing',
 protocol: 'adding/removing',
-fn: function (aHashedCollection){
+fn: function (anAssocitativeCollection){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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"],
 messageSends: ["addAll:", "associations"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1381,13 +1381,32 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=_st($Dictionary())._from_(self._associations());
 $1=_st($Dictionary())._from_(self._associations());
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"asDictionary",{},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"asDictionary",{},smalltalk.AssociativeCollection)})},
 args: [],
 args: [],
 source: "asDictionary\x0a\x09^ Dictionary from: self associations",
 source: "asDictionary\x0a\x09^ Dictionary from: self associations",
 messageSends: ["from:", "associations"],
 messageSends: ["from:", "associations"],
 referencedClasses: ["Dictionary"]
 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.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1396,22 +1415,23 @@ protocol: 'converting',
 fn: function (){
 fn: function (){
 var self=this;
 var self=this;
 var c;
 var c;
+function $HashedCollection(){return smalltalk.HashedCollection||(typeof HashedCollection=="undefined"?nil:HashedCollection)}
 return smalltalk.withContext(function($ctx1) { 
 return smalltalk.withContext(function($ctx1) { 
 var $1;
 var $1;
-c=_st(self._class())._new();
+c=_st($HashedCollection())._new();
 self._keysAndValuesDo_((function(key,value){
 self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
 return _st(c)._at_put_(key,_st(value)._asJSON());
 return _st(c)._at_put_(key,_st(value)._asJSON());
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=c;
 $1=c;
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"asJSON",{c:c},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"asJSON",{c:c},smalltalk.AssociativeCollection)})},
 args: [],
 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.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1429,13 +1449,13 @@ return _st(associations)._add_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 $1=associations;
 $1=associations;
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"associations",{associations:associations},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"associations",{associations:associations},smalltalk.AssociativeCollection)})},
 args: [],
 args: [],
 source: "associations\x0a\x09| associations |\x0a\x09associations := #().\x0a\x09self associationsDo: [ :each | associations add: each ].\x0a\x09^ associations",
 source: "associations\x0a\x09| associations |\x0a\x09associations := #().\x0a\x09self associationsDo: [ :each | associations add: each ].\x0a\x09^ associations",
 messageSends: ["associationsDo:", "add:"],
 messageSends: ["associationsDo:", "add:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1449,35 +1469,13 @@ self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
 return _st(aBlock)._value_(_st($Association())._key_value_(key,value));
 return _st(aBlock)._value_(_st($Association())._key_value_(key,value));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 }, 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"],
 args: ["aBlock"],
 source: "associationsDo: aBlock\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09aBlock value: (Association key: key value: value) ]",
 source: "associationsDo: aBlock\x0a\x09self keysAndValuesDo: [ :key :value |\x0a\x09\x09aBlock value: (Association key: key value: value) ]",
 messageSends: ["keysAndValuesDo:", "value:", "key:value:"],
 messageSends: ["keysAndValuesDo:", "value:", "key:value:"],
 referencedClasses: ["Association"]
 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.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1488,36 +1486,19 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
 var $2,$1;
 $2=self._includesKey_(aKey);
 $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;
 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"],
 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: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 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)})}));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=newDict;
 $1=newDict;
 return $1;
 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"],
 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",
 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:"],
 messageSends: ["new", "class", "keysAndValuesDo:", "at:put:", "value:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 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)})}));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=copy;
 $1=copy;
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"deepCopy",{copy:copy},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"deepCopy",{copy:copy},smalltalk.AssociativeCollection)})},
 args: [],
 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",
 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"],
 messageSends: ["new", "class", "keysAndValuesDo:", "at:put:", "deepCopy"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1577,13 +1558,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=_st(self._values())._detect_ifNone_(aBlock,anotherBlock);
 $1=_st(self._values())._detect_ifNone_(aBlock,anotherBlock);
 return $1;
 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"],
 args: ["aBlock", "anotherBlock"],
 source: "detect: aBlock ifNone: anotherBlock\x0a\x09^ self values detect: aBlock ifNone: anotherBlock",
 source: "detect: aBlock ifNone: anotherBlock\x0a\x09^ self values detect: aBlock ifNone: anotherBlock",
 messageSends: ["detect:ifNone:", "values"],
 messageSends: ["detect:ifNone:", "values"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1593,13 +1574,13 @@ fn: function (aBlock){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return smalltalk.withContext(function($ctx1) { 
 self._valuesDo_(aBlock);
 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"],
 args: ["aBlock"],
 source: "do: aBlock\x0a\x09self valuesDo: aBlock",
 source: "do: aBlock\x0a\x09self valuesDo: aBlock",
 messageSends: ["valuesDo:"],
 messageSends: ["valuesDo:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1611,13 +1592,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=_st(self._values())._includes_(anObject);
 $1=_st(self._values())._includes_(anObject);
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"includes:",{anObject:anObject},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"includes:",{anObject:anObject},smalltalk.AssociativeCollection)})},
 args: ["anObject"],
 args: ["anObject"],
 source: "includes: anObject\x0a\x09^ self values includes: anObject",
 source: "includes: anObject\x0a\x09^ self values includes: anObject",
 messageSends: ["includes:", "values"],
 messageSends: ["includes:", "values"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1626,14 +1607,14 @@ protocol: 'testing',
 fn: function (aKey){
 fn: function (aKey){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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"],
 args: ["aKey"],
-source: "includesKey: aKey\x0a\x09<return self.hasOwnProperty(aKey)>",
-messageSends: [],
+source: "includesKey: aKey\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1648,13 +1629,13 @@ return smalltalk.withContext(function($ctx2) {
 return _st(self._at_(each)).__eq(anObject);
 return _st(self._at_(each)).__eq(anObject);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}),aBlock);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}),aBlock);
 return $1;
 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"],
 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:"],
 messageSends: ["detect:ifNone:", "keys", "=", "at:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1669,13 +1650,13 @@ return smalltalk.withContext(function($ctx2) {
 return self._errorNotFound();
 return self._errorNotFound();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 return $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"],
 args: ["anObject"],
 source: "keyAtValue: anObject\x0a\x09^ self keyAtValue: anObject ifAbsent: [ self errorNotFound ]",
 source: "keyAtValue: anObject\x0a\x09^ self keyAtValue: anObject ifAbsent: [ self errorNotFound ]",
 messageSends: ["keyAtValue:ifAbsent:", "errorNotFound"],
 messageSends: ["keyAtValue:ifAbsent:", "errorNotFound"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1687,13 +1668,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=self._indexOf_ifAbsent_(anObject,aBlock);
 $1=self._indexOf_ifAbsent_(anObject,aBlock);
 return $1;
 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"],
 args: ["anObject", "aBlock"],
 source: "keyAtValue: anObject ifAbsent: aBlock\x0a\x09^ self indexOf: anObject ifAbsent: aBlock",
 source: "keyAtValue: anObject ifAbsent: aBlock\x0a\x09^ self indexOf: anObject ifAbsent: aBlock",
 messageSends: ["indexOf:ifAbsent:"],
 messageSends: ["indexOf:ifAbsent:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1702,14 +1683,14 @@ protocol: 'accessing',
 fn: function (){
 fn: function (){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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: [],
 args: [],
-source: "keys\x0a\x09<return Object.keys(self)>",
-messageSends: [],
+source: "keys\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1722,13 +1703,13 @@ self._keysDo_((function(each){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
 return _st(aBlock)._value_value_(each,self._at_(each));
 return _st(aBlock)._value_value_(each,self._at_(each));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 }, 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"],
 args: ["aBlock"],
 source: "keysAndValuesDo: aBlock\x0a\x09self keysDo: [ :each |\x0a\x09\x09aBlock value: each value: (self at: each) ]",
 source: "keysAndValuesDo: aBlock\x0a\x09self keysDo: [ :each |\x0a\x09\x09aBlock value: each value: (self at: each) ]",
 messageSends: ["keysDo:", "value:value:", "at:"],
 messageSends: ["keysDo:", "value:value:", "at:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1737,14 +1718,14 @@ protocol: 'enumerating',
 fn: function (aBlock){
 fn: function (aBlock){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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"],
 args: ["aBlock"],
-source: "keysDo: aBlock\x0a\x09self keys do: aBlock",
-messageSends: ["do:", "keys"],
+source: "keysDo: aBlock\x0a\x09self subclassResponsibility",
+messageSends: ["subclassResponsibility"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1753,7 +1734,7 @@ protocol: 'printing',
 fn: function (aStream){
 fn: function (aStream){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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;
 $ctx1.sendIdx["printOn:"]=1;
 _st(aStream)._nextPutAll_(" (");
 _st(aStream)._nextPutAll_(" (");
 $ctx1.sendIdx["nextPutAll:"]=1;
 $ctx1.sendIdx["nextPutAll:"]=1;
@@ -1766,13 +1747,13 @@ return _st(aStream)._nextPutAll_(" , ");
 $ctx2.sendIdx["nextPutAll:"]=2;
 $ctx2.sendIdx["nextPutAll:"]=2;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
 _st(aStream)._nextPutAll_(")");
 _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"],
 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: ')'",
 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"],
 messageSends: ["printOn:", "nextPutAll:", "do:separatedBy:", "associations"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1784,13 +1765,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=self._removeKey_ifAbsent_(aKey,aBlock);
 $1=self._removeKey_ifAbsent_(aKey,aBlock);
 return $1;
 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"],
 args: ["aKey", "aBlock"],
 source: "remove: aKey ifAbsent: aBlock\x0a\x09^ self removeKey: aKey ifAbsent: aBlock",
 source: "remove: aKey ifAbsent: aBlock\x0a\x09^ self removeKey: aKey ifAbsent: aBlock",
 messageSends: ["removeKey:ifAbsent:"],
 messageSends: ["removeKey:ifAbsent:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1805,13 +1786,13 @@ return smalltalk.withContext(function($ctx2) {
 return self._removeKey_(each);
 return self._removeKey_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"removeAll",{},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"removeAll",{},smalltalk.AssociativeCollection)})},
 args: [],
 args: [],
 source: "removeAll\x0a\x09^ self keys do: [ :each | self removeKey: each ]",
 source: "removeAll\x0a\x09^ self keys do: [ :each | self removeKey: each ]",
 messageSends: ["do:", "keys", "removeKey:"],
 messageSends: ["do:", "keys", "removeKey:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1823,13 +1804,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=self._remove_(aKey);
 $1=self._remove_(aKey);
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"removeKey:",{aKey:aKey},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"removeKey:",{aKey:aKey},smalltalk.AssociativeCollection)})},
 args: ["aKey"],
 args: ["aKey"],
 source: "removeKey: aKey\x0a\x09^ self remove: aKey",
 source: "removeKey: aKey\x0a\x09^ self remove: aKey",
 messageSends: ["remove:"],
 messageSends: ["remove:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1838,21 +1819,14 @@ protocol: 'adding/removing',
 fn: function (aKey,aBlock){
 fn: function (aKey,aBlock){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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"],
 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: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1873,13 +1847,13 @@ return _st(newDict)._at_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $2=newDict;
 $2=newDict;
 return $2;
 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"],
 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",
 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:"],
 messageSends: ["new", "class", "keysAndValuesDo:", "ifTrue:", "value:", "at:put:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1897,13 +1871,13 @@ return _st(copy)._at_put_(key,value);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 $1=copy;
 $1=copy;
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{copy:copy},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{copy:copy},smalltalk.AssociativeCollection)})},
 args: [],
 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",
 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:"],
 messageSends: ["new", "class", "keysAndValuesDo:", "at:put:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1915,13 +1889,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=_st(self._keys())._size();
 $1=_st(self._keys())._size();
 return $1;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"size",{},smalltalk.HashedCollection)})},
+}, function($ctx1) {$ctx1.fill(self,"size",{},smalltalk.AssociativeCollection)})},
 args: [],
 args: [],
 source: "size\x0a\x09^ self keys size",
 source: "size\x0a\x09^ self keys size",
 messageSends: ["size", "keys"],
 messageSends: ["size", "keys"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1930,18 +1904,14 @@ protocol: 'accessing',
 fn: function (){
 fn: function (){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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: [],
 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: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1950,17 +1920,14 @@ protocol: 'enumerating',
 fn: function (aBlock){
 fn: function (aBlock){
 var self=this;
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 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"],
 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: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -1973,13 +1940,13 @@ self._keysAndValuesDo_((function(key,value){
 return smalltalk.withContext(function($ctx2) {
 return smalltalk.withContext(function($ctx2) {
 return _st(aBlock)._value_value_(value,key);
 return _st(aBlock)._value_value_(value,key);
 }, function($ctx2) {$ctx2.fillBlock({key:key,value:value},$ctx1,1)})}));
 }, 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"],
 args: ["aBlock"],
 source: "withIndexDo: aBlock\x0a\x09self keysAndValuesDo: [ :key :value | aBlock value: value value: key ]",
 source: "withIndexDo: aBlock\x0a\x09self keysAndValuesDo: [ :key :value | aBlock value: value value: key ]",
 messageSends: ["keysAndValuesDo:", "value:value:"],
 messageSends: ["keysAndValuesDo:", "value:value:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection);
+smalltalk.AssociativeCollection);
 
 
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
@@ -1998,13 +1965,13 @@ return _st(newCollection)._add_(each);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)})}));
 $1=newCollection;
 $1=newCollection;
 return $1;
 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"],
 args: ["aCollection"],
 source: "from: aCollection\x0a\x09| newCollection |\x0a\x09newCollection := self new.\x0a\x09aCollection do: [ :each | newCollection add: each ].\x0a\x09^ newCollection",
 source: "from: aCollection\x0a\x09| newCollection |\x0a\x09newCollection := self new.\x0a\x09aCollection do: [ :each | newCollection add: each ].\x0a\x09^ newCollection",
 messageSends: ["new", "do:", "add:"],
 messageSends: ["new", "do:", "add:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection.klass);
+smalltalk.AssociativeCollection.klass);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 smalltalk.method({
@@ -2016,13 +1983,13 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 var $1;
 $1=self._from_(aCollection);
 $1=self._from_(aCollection);
 return $1;
 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"],
 args: ["aCollection"],
 source: "fromPairs: aCollection\x0a\x09\x22This message is poorly named and has been replaced by #from:\x22\x0a\x09^ self from: 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:"],
 messageSends: ["from:"],
 referencedClasses: []
 referencedClasses: []
 }),
 }),
-smalltalk.HashedCollection.klass);
+smalltalk.AssociativeCollection.klass);
 
 
 smalltalk.addMethod(
 smalltalk.addMethod(
 smalltalk.method({
 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)})}));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,2)})}));
 $5=newCollection;
 $5=newCollection;
 return $5;
 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"],
 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",
 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:", "+"],
 messageSends: ["ifFalse:", "even", "size", "error:", "new", "do:", "to:by:", "at:put:", "at:", "+"],
 referencedClasses: []
 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.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.addMethod(
 smalltalk.method({
 smalltalk.method({
 selector: "at:ifAbsent:",
 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.addClass('SequenceableCollection', smalltalk.IndexableCollection, [], 'Kernel-Collections');
 smalltalk.SequenceableCollection.comment="I am an IndexableCollection\x0awith numeric indexes starting with 1.";
 smalltalk.SequenceableCollection.comment="I am an IndexableCollection\x0awith numeric indexes starting with 1.";
 smalltalk.addMethod(
 smalltalk.addMethod(

+ 7 - 6
js/Kernel-Infrastructure.js

@@ -2081,15 +2081,16 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
 var $2,$1;
 $2=self._includesKey_(aKey);
 $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;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.Smalltalk)})},
 }, function($ctx1) {$ctx1.fill(self,"at:ifAbsent:",{aKey:aKey,aBlock:aBlock},smalltalk.Smalltalk)})},
 args: ["aKey", "aBlock"],
 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: []
 referencedClasses: []
 }),
 }),
 smalltalk.Smalltalk);
 smalltalk.Smalltalk);

Файловите разлики са ограничени, защото са твърде много
+ 516 - 414
js/Kernel-Tests.js


+ 14 - 14
st/IDE.st

@@ -2343,45 +2343,45 @@ inspectOn: anInspector
 	anInspector setLabel: label
 	anInspector setLabel: label
 ! !
 ! !
 
 
-!HashedCollection methodsFor: '*IDE'!
+!MethodContext methodsFor: '*IDE'!
 
 
 inspectOn: anInspector
 inspectOn: anInspector
 	| variables |
 	| variables |
 	variables := Dictionary new.
 	variables := Dictionary new.
 	variables at: '#self' put: self.
 	variables at: '#self' put: self.
-	variables at: '#keys' put: self keys.
-	self keysAndValuesDo: [ :key :value |
-		variables at: key put: value ].
+	variables at: '#home' put: self home.
+	variables at: '#receiver' put: self receiver.
+	variables at: '#selector' put: self selector.
+	variables at: '#temps' put: self temps.
+	self class instanceVariableNames do: [ :each |
+		variables at: each put: (self instVarAt: each) ].
 	anInspector
 	anInspector
 		setLabel: self printString;
 		setLabel: self printString;
 		setVariables: variables
 		setVariables: variables
 ! !
 ! !
 
 
-!Set methodsFor: '*IDE'!
+!AssociativeCollection methodsFor: '*IDE'!
 
 
 inspectOn: anInspector
 inspectOn: anInspector
 	| variables |
 	| variables |
 	variables := Dictionary new.
 	variables := Dictionary new.
 	variables at: '#self' put: self.
 	variables at: '#self' put: self.
-	elements withIndexDo: [ :each :i |
-		variables at: i put: each ].
+	variables at: '#keys' put: self keys.
+	self keysAndValuesDo: [ :key :value |
+		variables at: key put: value ].
 	anInspector
 	anInspector
 		setLabel: self printString;
 		setLabel: self printString;
 		setVariables: variables
 		setVariables: variables
 ! !
 ! !
 
 
-!MethodContext methodsFor: '*IDE'!
+!Set methodsFor: '*IDE'!
 
 
 inspectOn: anInspector
 inspectOn: anInspector
 	| variables |
 	| variables |
 	variables := Dictionary new.
 	variables := Dictionary new.
 	variables at: '#self' put: self.
 	variables at: '#self' put: self.
-	variables at: '#home' put: self home.
-	variables at: '#receiver' put: self receiver.
-	variables at: '#selector' put: self selector.
-	variables at: '#temps' put: self temps.
-	self class instanceVariableNames do: [ :each |
-		variables at: each put: (self instVarAt: each) ].
+	elements withIndexDo: [ :each :i |
+		variables at: i put: each ].
 	anInspector
 	anInspector
 		setLabel: self printString;
 		setLabel: self printString;
 		setVariables: variables
 		setVariables: variables

+ 89 - 57
st/Kernel-Collections.st

@@ -435,15 +435,13 @@ withIndexDo: aBlock
 	self subclassResponsibility
 	self subclassResponsibility
 ! !
 ! !
 
 
-IndexableCollection subclass: #HashedCollection
+IndexableCollection subclass: #AssociativeCollection
 	instanceVariableNames: ''
 	instanceVariableNames: ''
 	package: 'Kernel-Collections'!
 	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
 	| associations |
 	| associations |
@@ -452,27 +450,16 @@ associations
 	^ associations
 	^ associations
 !
 !
 
 
-at: aKey ifAbsent: aBlock
-	^ (self includesKey: aKey)
-		ifTrue: [ self basicAt: aKey ]
-		ifFalse: aBlock
-!
-
 at: aKey ifPresent: aBlock ifAbsent: anotherBlock
 at: aKey ifPresent: aBlock ifAbsent: anotherBlock
 	"Lookup the given key in the receiver.
 	"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,
 	If it is present, answer the value of evaluating the oneArgBlock with the value associated with the key,
 	otherwise answer the value of absentBlock."
 	otherwise answer the value of absentBlock."
 	^ (self includesKey: aKey)
 	^ (self includesKey: aKey)
 		ifTrue: [ aBlock value: (self at: 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
 indexOf: anObject ifAbsent: aBlock
-
 	^ self keys detect: [ :each | (self at: each) = anObject ] ifNone: aBlock
 	^ self keys detect: [ :each | (self at: each) = anObject ] ifNone: aBlock
 !
 !
 
 
@@ -485,7 +472,7 @@ keyAtValue: anObject ifAbsent: aBlock
 !
 !
 
 
 keys
 keys
-	<return Object.keys(self)>
+	self subclassResponsibility
 !
 !
 
 
 size
 size
@@ -493,22 +480,18 @@ size
 !
 !
 
 
 values
 values
-	<
-		return self._keys().map(function(key){
-			return self._at_(key);
-		});
-	>
+	self subclassResponsibility
 ! !
 ! !
 
 
-!HashedCollection methodsFor: 'adding/removing'!
+!AssociativeCollection methodsFor: 'adding/removing'!
 
 
 add: anAssociation
 add: anAssociation
 	self at: anAssociation key put: anAssociation value
 	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
 remove: aKey ifAbsent: aBlock
@@ -524,34 +507,36 @@ removeKey: aKey
 !
 !
 
 
 removeKey: aKey ifAbsent: aBlock
 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
 asDictionary
 	^ Dictionary from: self associations
 	^ Dictionary from: self associations
 !
 !
 
 
+asHashedCollection
+	^ HashedCollection from: self associations
+!
+
 asJSON
 asJSON
 	| c |
 	| c |
-	c := self class new.
+	c := HashedCollection new.
 	self keysAndValuesDo: [ :key :value |
 	self keysAndValuesDo: [ :key :value |
 		c at: key put: value asJSON ].
 		c at: key put: value asJSON ].
 	^ c
 	^ c
 ! !
 ! !
 
 
-!HashedCollection methodsFor: 'copying'!
+!AssociativeCollection methodsFor: 'copying'!
 
 
 deepCopy
 deepCopy
 	| copy |
 	| copy |
@@ -569,7 +554,7 @@ shallowCopy
 	^ copy
 	^ copy
 ! !
 ! !
 
 
-!HashedCollection methodsFor: 'enumerating'!
+!AssociativeCollection methodsFor: 'enumerating'!
 
 
 associationsDo: aBlock
 associationsDo: aBlock
 	self keysAndValuesDo: [ :key :value |
 	self keysAndValuesDo: [ :key :value |
@@ -602,7 +587,7 @@ keysAndValuesDo: aBlock
 !
 !
 
 
 keysDo: aBlock
 keysDo: aBlock
-	self keys do: aBlock
+	self subclassResponsibility
 !
 !
 
 
 select: aBlock
 select: aBlock
@@ -614,14 +599,14 @@ select: aBlock
 !
 !
 
 
 valuesDo: aBlock
 valuesDo: aBlock
-	self values do: [ :value | aBlock value: value ]
+	self subclassResponsibility
 !
 !
 
 
 withIndexDo: aBlock
 withIndexDo: aBlock
 	self keysAndValuesDo: [ :key :value | aBlock value: value value: key ]
 	self keysAndValuesDo: [ :key :value | aBlock value: value value: key ]
 ! !
 ! !
 
 
-!HashedCollection methodsFor: 'printing'!
+!AssociativeCollection methodsFor: 'printing'!
 
 
 printOn: aStream
 printOn: aStream
 	super printOn: aStream.
 	super printOn: aStream.
@@ -633,13 +618,13 @@ printOn: aStream
 	aStream nextPutAll: ')'
 	aStream nextPutAll: ')'
 ! !
 ! !
 
 
-!HashedCollection methodsFor: 'testing'!
+!AssociativeCollection methodsFor: 'testing'!
 
 
 includesKey: aKey
 includesKey: aKey
-	<return self.hasOwnProperty(aKey)>
+	self subclassResponsibility
 ! !
 ! !
 
 
-!HashedCollection class methodsFor: 'instance creation'!
+!AssociativeCollection class methodsFor: 'instance creation'!
 
 
 from: aCollection
 from: aCollection
 	| newCollection |
 	| newCollection |
@@ -669,7 +654,7 @@ newFromPairs: aCollection
 	^ newCollection
 	^ newCollection
 ! !
 ! !
 
 
-HashedCollection subclass: #Dictionary
+AssociativeCollection subclass: #Dictionary
 	instanceVariableNames: 'keys values'
 	instanceVariableNames: 'keys values'
 	package: 'Kernel-Collections'!
 	package: 'Kernel-Collections'!
 !Dictionary commentStamp!
 !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'!
 !Dictionary methodsFor: 'enumerating'!
 
 
 keysAndValuesDo: aBlock
 keysAndValuesDo: aBlock
@@ -792,6 +767,63 @@ includesKey: aKey
 	< return self._positionOfKey_(aKey) >>= 0; >
 	< 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
 IndexableCollection subclass: #SequenceableCollection
 	instanceVariableNames: ''
 	instanceVariableNames: ''
 	package: 'Kernel-Collections'!
 	package: 'Kernel-Collections'!

+ 1 - 1
st/Kernel-Infrastructure.st

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

+ 112 - 114
st/Kernel-Tests.st

@@ -719,6 +719,15 @@ testAtPut
 	self assert: newCollection equals: self collectionWithNewValue
 	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
 testIndexOf
 	self should: [ self collection indexOf: self sampleNewValue ] raise: Error.
 	self should: [ self collection indexOf: self sampleNewValue ] raise: Error.
 	self samplesDo: [ :index :value |
 	self samplesDo: [ :index :value |
@@ -733,30 +742,18 @@ testWithIndexDo
 		self assert: (collection at: index) equals: each ]
 		self assert: (collection at: index) equals: each ]
 ! !
 ! !
 
 
-IndexableCollectionTest subclass: #HashedCollectionTest
+IndexableCollectionTest subclass: #AssociativeCollectionTest
 	instanceVariableNames: ''
 	instanceVariableNames: ''
 	package: 'Kernel-Tests'!
 	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
 nonIndexesDo: aBlock
@@ -768,15 +765,11 @@ sampleNewIndex
 	^ 'new'
 	^ 'new'
 !
 !
 
 
-sampleNewValueAsCollection
-	^ #{ 'new' -> 'N' }
-!
-
 samplesDo: aBlock
 samplesDo: aBlock
 	aBlock value: 'a' value: 2
 	aBlock value: 'a' value: 2
 ! !
 ! !
 
 
-!HashedCollectionTest methodsFor: 'tests'!
+!AssociativeCollectionTest methodsFor: 'tests'!
 
 
 testAddAll
 testAddAll
 	super testAddAll.
 	super testAddAll.
@@ -789,6 +782,10 @@ testAsDictionary
 self assert: ( self collectionClass new asDictionary isMemberOf: Dictionary ).
 self assert: ( self collectionClass new asDictionary isMemberOf: Dictionary ).
 !
 !
 
 
+testAsHashedCollection
+self assert: ( self collectionClass new asHashedCollection isMemberOf: HashedCollection ).
+!
+
 testComma
 testComma
 	super testComma.
 	super testComma.
 	self assert: self collection, self collection equals: self collection.
 	self assert: self collection, self collection equals: self collection.
@@ -803,20 +800,67 @@ associations := { 'a' -> 1. 'b' -> 2 }.
 self assertSameContents: ( self class collectionClass from: associations ) as: #{ '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
 testNewFromPairs
 "Accept an array in which all odd indexes are keys and evens are values."
 "Accept an array in which all odd indexes are keys and evens are values."
 | flattenedAssociations |
 | flattenedAssociations |
 flattenedAssociations := { 'a'. 1. 'b'. 2 }.
 flattenedAssociations := { 'a'. 1. 'b'. 2 }.
 self assertSameContents: ( self class collectionClass newFromPairs: flattenedAssociations ) as: #{ '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: ''
 	instanceVariableNames: ''
 	package: 'Kernel-Tests'!
 	package: 'Kernel-Tests'!
 
 
@@ -831,6 +875,10 @@ collection
 		yourself
 		yourself
 !
 !
 
 
+collectionKeys
+	^ {1. 'a'. true. 1@3}
+!
+
 collectionOfPrintStrings
 collectionOfPrintStrings
 	^ Dictionary new
 	^ Dictionary new
 		at: 1 put: '1';
 		at: 1 put: '1';
@@ -844,6 +892,10 @@ collectionSize
 	^ 4
 	^ 4
 !
 !
 
 
+collectionValues
+	^ {1. 2. 3. -4}
+!
+
 collectionWithDuplicates
 collectionWithDuplicates
 	^ Dictionary new
 	^ Dictionary new
 		at: 1 put: 1;
 		at: 1 put: 1;
@@ -903,118 +955,64 @@ testAccessing
 	self deny: (d includesKey: 3@1)
 	self deny: (d includesKey: 3@1)
 !
 !
 
 
-testAsHashedCollection
-self assert: ( self collectionClass new asHashedCollection isMemberOf: HashedCollection ).
-!
-
 testDynamicDictionaries
 testDynamicDictionaries
 	self assert: #{'hello' -> 1} asDictionary equals: (Dictionary with: 'hello' -> 1)
 	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
 collectionClass
-	^ Dictionary
+	^ HashedCollection
 ! !
 ! !
 
 
 IndexableCollectionTest subclass: #SequenceableCollectionTest
 IndexableCollectionTest subclass: #SequenceableCollectionTest

Някои файлове не бяха показани, защото твърде много файлове са промени