Browse Source

fixes and optimizations in Collection hierarchy

Herbert Vojčík 8 years ago
parent
commit
6c72a92e07
4 changed files with 505 additions and 237 deletions
  1. 156 113
      src/Kernel-Collections.js
  2. 38 33
      src/Kernel-Collections.st
  3. 278 82
      src/Kernel-Tests.js
  4. 33 9
      src/Kernel-Tests.st

+ 156 - 113
src/Kernel-Collections.js

@@ -871,6 +871,39 @@ messageSends: ["reject:", "includes:"]
 }),
 $globals.Collection);
 
+$core.addMethod(
+$core.method({
+selector: "deepCopy",
+protocol: 'copying',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=self._collect_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(each)._deepCopy();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return $1;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"deepCopy",{},$globals.Collection)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "deepCopy\x0a\x09^ self collect: [ :each | each deepCopy ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["collect:", "deepCopy"]
+}),
+$globals.Collection);
+
 $core.addMethod(
 $core.method({
 selector: "detect:",
@@ -1622,6 +1655,34 @@ messageSends: ["writeStream", "new", "class", "do:", "ifTrue:", "value:", "nextP
 }),
 $globals.Collection);
 
+$core.addMethod(
+$core.method({
+selector: "shallowCopy",
+protocol: 'copying',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=self._collect_((function(each){
+return each;
+
+}));
+return $1;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{},$globals.Collection)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "shallowCopy\x0a\x09^ self collect: [ :each | each ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["collect:"]
+}),
+$globals.Collection);
+
 $core.addMethod(
 $core.method({
 selector: "size",
@@ -4010,70 +4071,21 @@ selector: "copyFrom:to:",
 protocol: 'copying',
 fn: function (anIndex,anotherIndex){
 var self=this;
-var range,newCollection;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1;
-range=$recv(anIndex)._to_(anotherIndex);
-newCollection=$recv(self._class())._new_($recv(range)._size());
-$recv(range)._withIndexDo_((function(each,i){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(newCollection)._at_put_(i,self._at_(each));
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-$1=newCollection;
-return $1;
+self._subclassResponsibility();
+return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"copyFrom:to:",{anIndex:anIndex,anotherIndex:anotherIndex,range:range,newCollection:newCollection},$globals.SequenceableCollection)});
+}, function($ctx1) {$ctx1.fill(self,"copyFrom:to:",{anIndex:anIndex,anotherIndex:anotherIndex},$globals.SequenceableCollection)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["anIndex", "anotherIndex"],
-source: "copyFrom: anIndex to: anotherIndex\x0a\x09| range newCollection |\x0a\x09range := anIndex to: anotherIndex.\x0a\x09newCollection := self class new: range size.\x0a\x09range withIndexDo: [ :each :i |\x0a\x09\x09newCollection at: i put: (self at: each) ].\x0a\x09^ newCollection",
+source: "copyFrom: anIndex to: anotherIndex\x0a\x09self subclassResponsibility",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["to:", "new:", "class", "size", "withIndexDo:", "at:put:", "at:"]
-}),
-$globals.SequenceableCollection);
-
-$core.addMethod(
-$core.method({
-selector: "deepCopy",
-protocol: 'copying',
-fn: function (){
-var self=this;
-var newCollection;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1;
-newCollection=$recv(self._class())._new_(self._size());
-self._withIndexDo_((function(each,index){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(newCollection)._at_put_(index,$recv(each)._deepCopy());
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each,index:index},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-$1=newCollection;
-return $1;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"deepCopy",{newCollection:newCollection},$globals.SequenceableCollection)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "deepCopy\x0a\x09| newCollection |\x0a\x09newCollection := self class new: self size.\x0a\x09self withIndexDo: [ :each :index |\x0a\x09\x09newCollection at: index put: each deepCopy ].\x0a\x09^ newCollection",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["new:", "class", "size", "withIndexDo:", "at:put:", "deepCopy"]
+messageSends: ["subclassResponsibility"]
 }),
 $globals.SequenceableCollection);
 
@@ -4580,42 +4592,6 @@ messageSends: ["at:"]
 }),
 $globals.SequenceableCollection);
 
-$core.addMethod(
-$core.method({
-selector: "shallowCopy",
-protocol: 'copying',
-fn: function (){
-var self=this;
-var newCollection;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1;
-newCollection=$recv(self._class())._new_(self._size());
-self._withIndexDo_((function(each,index){
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx2) {
-//>>excludeEnd("ctx");
-return $recv(newCollection)._at_put_(index,each);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({each:each,index:index},$ctx1,1)});
-//>>excludeEnd("ctx");
-}));
-$1=newCollection;
-return $1;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{newCollection:newCollection},$globals.SequenceableCollection)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "shallowCopy\x0a\x09| newCollection |\x0a\x09newCollection := self class new: self size.\x0a\x09self withIndexDo: [ :each :index |\x0a\x09\x09newCollection at: index put: each ].\x0a\x09^ newCollection",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["new:", "class", "size", "withIndexDo:", "at:put:"]
-}),
-$globals.SequenceableCollection);
-
 $core.addMethod(
 $core.method({
 selector: "stream",
@@ -4852,6 +4828,33 @@ messageSends: []
 }),
 $globals.Array);
 
+$core.addMethod(
+$core.method({
+selector: "addAll:",
+protocol: 'adding/removing',
+fn: function (aCollection){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+
+	if (Array.isArray(aCollection) && aCollection.length < 65000) self.push.apply(self, aCollection);
+	else $globals.Array.superclass.fn.prototype._addAll_.call(self, aCollection);
+	return aCollection;;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"addAll:",{aCollection:aCollection},$globals.Array)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection"],
+source: "addAll: aCollection\x0a<\x0a\x09if (Array.isArray(aCollection) && aCollection.length < 65000) self.push.apply(self, aCollection);\x0a\x09else $globals.Array.superclass.fn.prototype._addAll_.call(self, aCollection);\x0a\x09return aCollection;\x0a>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Array);
+
 $core.addMethod(
 $core.method({
 selector: "addFirst:",
@@ -5016,6 +5019,37 @@ messageSends: []
 }),
 $globals.Array);
 
+$core.addMethod(
+$core.method({
+selector: "copyFrom:to:",
+protocol: 'copying',
+fn: function (anIndex,anotherIndex){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+
+	if (anIndex >= 1 && anotherIndex <= self.length) {
+		return self.slice(anIndex - 1, anotherIndex);
+	} else {
+		self._at_(anIndex);
+		self._at_(self.length + 1);
+		throw new Error("Incorrect indexes in #copyFrom:to: not caught by #at:");
+	};
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"copyFrom:to:",{anIndex:anIndex,anotherIndex:anotherIndex},$globals.Array)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anIndex", "anotherIndex"],
+source: "copyFrom: anIndex to: anotherIndex\x0a<\x0a\x09if (anIndex >>= 1 && anotherIndex <= self.length) {\x0a\x09\x09return self.slice(anIndex - 1, anotherIndex);\x0a\x09} else {\x0a\x09\x09self._at_(anIndex);\x0a\x09\x09self._at_(self.length + 1);\x0a\x09\x09throw new Error(\x22Incorrect indexes in #copyFrom:to: not caught by #at:\x22);\x0a\x09}\x0a>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Array);
+
 $core.addMethod(
 $core.method({
 selector: "join:",
@@ -5258,7 +5292,7 @@ var self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return self._copy().reverse();
+return self.slice().reverse();
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"reversed",{},$globals.Array)});
@@ -5266,7 +5300,7 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "reversed\x0a\x09<return self._copy().reverse()>",
+source: "reversed\x0a\x09<return self.slice().reverse()>",
 referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: []
@@ -5282,15 +5316,7 @@ var self=this;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-
-		var result = self.klass._new();
-		for(var i=0; i<self.length; i++) {
-			if(aBlock._value_(self[i])) {
-				result.push(self[i]);
-			}
-		}
-		return result;
-	;
+return self.filter(function(each) {return aBlock._value_(each)});
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"select:",{aBlock:aBlock},$globals.Array)});
@@ -5298,7 +5324,31 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
-source: "select: aBlock\x0a\x09\x22Optimized version\x22\x0a\x09\x0a\x09<\x0a\x09\x09var result = self.klass._new();\x0a\x09\x09for(var i=0; i<self.length; i++) {\x0a\x09\x09\x09if(aBlock._value_(self[i])) {\x0a\x09\x09\x09\x09result.push(self[i]);\x0a\x09\x09\x09}\x0a\x09\x09}\x0a\x09\x09return result;\x0a\x09>",
+source: "select: aBlock\x0a\x09\x22Optimized version\x22\x0a\x09\x0a\x09<return self.filter(function(each) {return aBlock._value_(each)})>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Array);
+
+$core.addMethod(
+$core.method({
+selector: "shallowCopy",
+protocol: 'copying',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return self.slice();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{},$globals.Array)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "shallowCopy\x0a\x09<return self.slice()>",
 referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: []
@@ -7231,22 +7281,15 @@ selector: "shallowCopy",
 protocol: 'copying',
 fn: function (){
 var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $1;
-$1=$recv(self._class())._fromString_(self);
-return $1;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"shallowCopy",{},$globals.String)});
-//>>excludeEnd("ctx");
+return self;
+
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "shallowCopy\x0a\x09^ self class fromString: self",
+source: "shallowCopy\x0a\x09^ self",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["fromString:", "class"]
+messageSends: []
 }),
 $globals.String);
 

+ 38 - 33
src/Kernel-Collections.st

@@ -233,6 +233,14 @@ copyWithoutAll: aCollection
 	equal to those in aCollection."
 
 	^ self reject: [ :each | aCollection includes: each ]
+!
+
+deepCopy
+	^ self collect: [ :each | each deepCopy ]
+!
+
+shallowCopy
+	^ self collect: [ :each | each ]
 ! !
 
 !Collection methodsFor: 'enumerating'!
@@ -1029,28 +1037,7 @@ reversed
 !SequenceableCollection methodsFor: 'copying'!
 
 copyFrom: anIndex to: anotherIndex
-	| range newCollection |
-	range := anIndex to: anotherIndex.
-	newCollection := self class new: range size.
-	range withIndexDo: [ :each :i |
-		newCollection at: i put: (self at: each) ].
-	^ newCollection
-!
-
-deepCopy
-	| newCollection |
-	newCollection := self class new: self size.
-	self withIndexDo: [ :each :index |
-		newCollection at: index put: each deepCopy ].
-	^ newCollection
-!
-
-shallowCopy
-	| newCollection |
-	newCollection := self class new: self size.
-	self withIndexDo: [ :each :index |
-		newCollection at: index put: each ].
-	^ newCollection
+	self subclassResponsibility
 ! !
 
 !SequenceableCollection methodsFor: 'enumerating'!
@@ -1206,6 +1193,14 @@ add: anObject
 	<self.push(anObject); return anObject;>
 !
 
+addAll: aCollection
+<
+	if (Array.isArray(aCollection) && aCollection.length < 65000) self.push.apply(self, aCollection);
+	else $globals.Array.superclass.fn.prototype._addAll_.call(self, aCollection);
+	return aCollection;
+>
+!
+
 addFirst: anObject
 	<self.unshift(anObject); return anObject;>
 !
@@ -1241,7 +1236,25 @@ asJavascript
 !
 
 reversed
-	<return self._copy().reverse()>
+	<return self.slice().reverse()>
+! !
+
+!Array methodsFor: 'copying'!
+
+copyFrom: anIndex to: anotherIndex
+<
+	if (anIndex >>= 1 && anotherIndex <= self.length) {
+		return self.slice(anIndex - 1, anotherIndex);
+	} else {
+		self._at_(anIndex);
+		self._at_(self.length + 1);
+		throw new Error("Incorrect indexes in #copyFrom:to: not caught by #at:");
+	}
+>
+!
+
+shallowCopy
+	<return self.slice()>
 ! !
 
 !Array methodsFor: 'enumerating'!
@@ -1259,15 +1272,7 @@ join: aString
 select: aBlock
 	"Optimized version"
 	
-	<
-		var result = self.klass._new();
-		for(var i=0; i<self.length; i++) {
-			if(aBlock._value_(self[i])) {
-				result.push(self[i]);
-			}
-		}
-		return result;
-	>
+	<return self.filter(function(each) {return aBlock._value_(each)})>
 !
 
 sort
@@ -1583,7 +1588,7 @@ deepCopy
 !
 
 shallowCopy
-	^ self class fromString: self
+	^ self
 ! !
 
 !String methodsFor: 'evaluating'!

+ 278 - 82
src/Kernel-Tests.js

@@ -3338,6 +3338,155 @@ messageSends: ["assert:equals:", ",", "collection", "new", "collectionClass", "s
 }),
 $globals.CollectionTest);
 
+$core.addMethod(
+$core.method({
+selector: "testCopy",
+protocol: 'tests',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $3,$2,$1,$5,$4,$7,$6,$8,$10,$9,$11,$15,$14,$13,$16,$12,$19,$18,$17;
+$3=self._collectionClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionClass"]=1;
+//>>excludeEnd("ctx");
+$2=$recv($3)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=1;
+//>>excludeEnd("ctx");
+$1=$recv($2)._copy();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["copy"]=1;
+//>>excludeEnd("ctx");
+$5=self._collectionClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionClass"]=2;
+//>>excludeEnd("ctx");
+$4=$recv($5)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=2;
+//>>excludeEnd("ctx");
+self._assert_equals_($1,$4);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["assert:equals:"]=1;
+//>>excludeEnd("ctx");
+$7=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=1;
+//>>excludeEnd("ctx");
+$6=$recv($7)._copy();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["copy"]=2;
+//>>excludeEnd("ctx");
+$8=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=2;
+//>>excludeEnd("ctx");
+self._assert_equals_($6,$8);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["assert:equals:"]=2;
+//>>excludeEnd("ctx");
+$10=self._collectionWithNewValue();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionWithNewValue"]=1;
+//>>excludeEnd("ctx");
+$9=$recv($10)._copy();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["copy"]=3;
+//>>excludeEnd("ctx");
+$11=self._collectionWithNewValue();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionWithNewValue"]=2;
+//>>excludeEnd("ctx");
+self._assert_equals_($9,$11);
+$15=self._collectionClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionClass"]=3;
+//>>excludeEnd("ctx");
+$14=$recv($15)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=3;
+//>>excludeEnd("ctx");
+$13=$recv($14)._copy();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["copy"]=4;
+//>>excludeEnd("ctx");
+$16=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=3;
+//>>excludeEnd("ctx");
+$12=$recv($13).__eq($16);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["="]=1;
+//>>excludeEnd("ctx");
+self._deny_($12);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["deny:"]=1;
+//>>excludeEnd("ctx");
+$19=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=4;
+//>>excludeEnd("ctx");
+$18=$recv($19)._copy();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["copy"]=5;
+//>>excludeEnd("ctx");
+$17=$recv($18).__eq($recv(self._collectionClass())._new());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["="]=2;
+//>>excludeEnd("ctx");
+self._deny_($17);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["deny:"]=2;
+//>>excludeEnd("ctx");
+self._deny_($recv($recv(self._collection())._copy()).__eq(self._collectionWithNewValue()));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testCopy",{},$globals.CollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testCopy\x0a\x09self assert: self collectionClass new copy equals: self collectionClass new.\x0a\x09self assert: self collection copy equals: self collection.\x0a\x09self assert: self collectionWithNewValue copy equals: self collectionWithNewValue.\x0a\x09\x0a\x09self deny: self collectionClass new copy = self collection.\x0a\x09self deny: self collection copy = self collectionClass new.\x0a\x09self deny: self collection copy = self collectionWithNewValue",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["assert:equals:", "copy", "new", "collectionClass", "collection", "collectionWithNewValue", "deny:", "="]
+}),
+$globals.CollectionTest);
+
+$core.addMethod(
+$core.method({
+selector: "testCopySeparates",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var original,copy;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+original=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=1;
+//>>excludeEnd("ctx");
+copy=$recv(original)._copy();
+$recv(copy)._addAll_(self._sampleNewValueAsCollection());
+self._assert_($recv(original).__eq(self._collection()));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testCopySeparates",{original:original,copy:copy},$globals.CollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testCopySeparates\x0a\x09| original copy |\x0a\x09original := self collection.\x0a\x09copy := original copy.\x0a\x09copy addAll: self sampleNewValueAsCollection.\x0a\x09self assert: original = self collection",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["collection", "copy", "addAll:", "sampleNewValueAsCollection", "assert:", "="]
+}),
+$globals.CollectionTest);
+
 $core.addMethod(
 $core.method({
 selector: "testDetect",
@@ -3649,6 +3798,104 @@ messageSends: ["new", "do:", "collection", "add:", "assertSameContents:as:", "co
 }),
 $globals.CollectionTest);
 
+$core.addMethod(
+$core.method({
+selector: "testEquality",
+protocol: 'tests',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $2,$1,$4,$3,$5,$6,$7,$8,$11,$10,$12,$9,$14,$13;
+$2=self._collectionClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionClass"]=1;
+//>>excludeEnd("ctx");
+$1=$recv($2)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=1;
+//>>excludeEnd("ctx");
+$4=self._collectionClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionClass"]=2;
+//>>excludeEnd("ctx");
+$3=$recv($4)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=2;
+//>>excludeEnd("ctx");
+self._assert_equals_($1,$3);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["assert:equals:"]=1;
+//>>excludeEnd("ctx");
+$5=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=1;
+//>>excludeEnd("ctx");
+$6=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=2;
+//>>excludeEnd("ctx");
+self._assert_equals_($5,$6);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["assert:equals:"]=2;
+//>>excludeEnd("ctx");
+$7=self._collectionWithNewValue();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionWithNewValue"]=1;
+//>>excludeEnd("ctx");
+$8=self._collectionWithNewValue();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionWithNewValue"]=2;
+//>>excludeEnd("ctx");
+self._assert_equals_($7,$8);
+$11=self._collectionClass();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collectionClass"]=3;
+//>>excludeEnd("ctx");
+$10=$recv($11)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["new"]=3;
+//>>excludeEnd("ctx");
+$12=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=3;
+//>>excludeEnd("ctx");
+$9=$recv($10).__eq($12);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["="]=1;
+//>>excludeEnd("ctx");
+self._deny_($9);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["deny:"]=1;
+//>>excludeEnd("ctx");
+$14=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=4;
+//>>excludeEnd("ctx");
+$13=$recv($14).__eq($recv(self._collectionClass())._new());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["="]=2;
+//>>excludeEnd("ctx");
+self._deny_($13);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["deny:"]=2;
+//>>excludeEnd("ctx");
+self._deny_($recv(self._collection()).__eq(self._collectionWithNewValue()));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testEquality",{},$globals.CollectionTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testEquality\x0a\x09self assert: self collectionClass new equals: self collectionClass new.\x0a\x09self assert: self collection equals: self collection.\x0a\x09self assert: self collectionWithNewValue equals: self collectionWithNewValue.\x0a\x09\x0a\x09self deny: self collectionClass new = self collection.\x0a\x09self deny: self collection = self collectionClass new.\x0a\x09self deny: self collection = self collectionWithNewValue",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["assert:equals:", "new", "collectionClass", "collection", "collectionWithNewValue", "deny:", "="]
+}),
+$globals.CollectionTest);
+
 $core.addMethod(
 $core.method({
 selector: "testIfEmptyFamily",
@@ -4766,88 +5013,6 @@ messageSends: ["collection", "samplesDo:", "at:put:", "assert:equals:", "sampleN
 }),
 $globals.IndexableCollectionTest);
 
-$core.addMethod(
-$core.method({
-selector: "testEquality",
-protocol: 'tests',
-fn: function (){
-var self=this;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-return $core.withContext(function($ctx1) {
-//>>excludeEnd("ctx");
-var $2,$1,$4,$3,$5,$6,$7,$10,$9,$11,$8;
-$2=self._collectionClass();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collectionClass"]=1;
-//>>excludeEnd("ctx");
-$1=$recv($2)._new();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["new"]=1;
-//>>excludeEnd("ctx");
-$4=self._collectionClass();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collectionClass"]=2;
-//>>excludeEnd("ctx");
-$3=$recv($4)._new();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["new"]=2;
-//>>excludeEnd("ctx");
-self._assert_equals_($1,$3);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["assert:equals:"]=1;
-//>>excludeEnd("ctx");
-$5=self._collection();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collection"]=1;
-//>>excludeEnd("ctx");
-$6=self._collection();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collection"]=2;
-//>>excludeEnd("ctx");
-self._assert_equals_($5,$6);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["assert:equals:"]=2;
-//>>excludeEnd("ctx");
-$7=self._collectionWithNewValue();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collectionWithNewValue"]=1;
-//>>excludeEnd("ctx");
-self._assert_equals_($7,self._collectionWithNewValue());
-$10=self._collectionClass();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collectionClass"]=3;
-//>>excludeEnd("ctx");
-$9=$recv($10)._new();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["new"]=3;
-//>>excludeEnd("ctx");
-$11=self._collection();
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["collection"]=3;
-//>>excludeEnd("ctx");
-$8=$recv($9).__eq($11);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["="]=1;
-//>>excludeEnd("ctx");
-self._deny_($8);
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-$ctx1.sendIdx["deny:"]=1;
-//>>excludeEnd("ctx");
-self._deny_($recv(self._collection()).__eq($recv(self._collectionClass())._new()));
-return self;
-//>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"testEquality",{},$globals.IndexableCollectionTest)});
-//>>excludeEnd("ctx");
-},
-//>>excludeStart("ide", pragmas.excludeIdeData);
-args: [],
-source: "testEquality\x0a\x09self assert: self collectionClass new equals: self collectionClass new.\x0a\x09self assert: self collection equals: self collection.\x0a\x09self assert: self collectionWithNewValue equals: self collectionWithNewValue.\x0a\x09\x0a\x09self deny: self collectionClass new = self collection.\x0a\x09self deny: self collection = self collectionClass new",
-referencedClasses: [],
-//>>excludeEnd("ide");
-messageSends: ["assert:equals:", "new", "collectionClass", "collection", "collectionWithNewValue", "deny:", "="]
-}),
-$globals.IndexableCollectionTest);
-
 $core.addMethod(
 $core.method({
 selector: "testIndexOf",
@@ -8318,6 +8483,37 @@ messageSends: ["assert:equals:", "copyFrom:to:"]
 }),
 $globals.StringTest);
 
+$core.addMethod(
+$core.method({
+selector: "testCopySeparates",
+protocol: 'tests',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $3,$2,$1;
+$3=self._collection();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["collection"]=1;
+//>>excludeEnd("ctx");
+$2=$recv($3)._copy();
+$1=$recv($2).__eq_eq(self._collection());
+self._assert_($1);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testCopySeparates",{},$globals.StringTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testCopySeparates\x0a\x09\x22String instances are immutable\x22\x0a\x09self assert: self collection copy == self collection",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["assert:", "==", "copy", "collection"]
+}),
+$globals.StringTest);
+
 $core.addMethod(
 $core.method({
 selector: "testCopyWithoutAll",

+ 33 - 9
src/Kernel-Tests.st

@@ -612,6 +612,24 @@ testComma
 	self assertSameContents: self sampleNewValueAsCollection, self collection as: self collectionWithNewValue
 !
 
+testCopy
+	self assert: self collectionClass new copy equals: self collectionClass new.
+	self assert: self collection copy equals: self collection.
+	self assert: self collectionWithNewValue copy equals: self collectionWithNewValue.
+	
+	self deny: self collectionClass new copy = self collection.
+	self deny: self collection copy = self collectionClass new.
+	self deny: self collection copy = self collectionWithNewValue
+!
+
+testCopySeparates
+	| original copy |
+	original := self collection.
+	copy := original copy.
+	copy addAll: self sampleNewValueAsCollection.
+	self assert: original = self collection
+!
+
 testDetect
 	self
 		shouldnt: [ self collection detect: [ true ] ]
@@ -652,6 +670,16 @@ testDo
 		as: newCollection
 !
 
+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.
+	self deny: self collection = self collectionWithNewValue
+!
+
 testIfEmptyFamily
 	self assert: (self collectionClass new ifEmpty: [ 42 ]) equals: 42.
 	self assert: (self collection ifEmpty: [ 42 ]) equals: self collection.
@@ -813,15 +841,6 @@ 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 |
@@ -1489,6 +1508,11 @@ testCopyFromTo
 	self assert: ('jackie' copyFrom: 4 to: 6) equals: 'kie'.
 !
 
+testCopySeparates
+	"String instances are immutable"
+	self assert: self collection copy == self collection
+!
+
 testCopyWithoutAll
 	self
 		assert: ('*hello* *world*' copyWithoutAll: '*')