74 nameString := newName asString. |
77 nameString := newName asString. |
75 classSymbol := newName asSymbol. |
78 classSymbol := newName asSymbol. |
76 newComment := commentString. |
79 newComment := commentString. |
77 |
80 |
78 "look, if it already exists as a class" |
81 "look, if it already exists as a class" |
79 (aSystemDictionary includesKey:classSymbol) ifTrue:[ |
82 oldClass := aSystemDictionary at:classSymbol ifAbsent:[nil]. |
80 oldClass := aSystemDictionary at:classSymbol. |
83 oldClass isBehavior ifFalse:[ |
81 (oldClass isBehavior not or:[oldClass name ~= newName]) ifTrue:[ |
84 oldClass := nil. |
82 oldClass := nil. |
85 ] ifTrue:[ |
83 ] ifFalse:[ |
86 oldClass superclass notNil ifTrue:[ |
84 oldClass allSuperclasses do:[:aClass | |
87 oldClass allSuperclasses do:[:cls | |
85 aClass name = newName ifTrue:[ |
88 cls name = nameString ifTrue:[ |
|
89 self error:'trying to create circular class definition'. |
|
90 ^ nil |
|
91 ] |
|
92 ] |
|
93 ]. |
|
94 |
|
95 aClass superclass notNil ifTrue:[ |
|
96 aClass allSuperclasses do:[:cls | |
|
97 cls name = nameString ifTrue:[ |
86 self error:'trying to create circular class definition'. |
98 self error:'trying to create circular class definition'. |
87 ^ nil |
99 ^ nil |
88 ] |
100 ] |
89 ]. |
101 ]. |
90 |
102 ]. |
91 newComment isNil ifTrue:[ |
103 |
92 newComment := oldClass comment |
104 newComment isNil ifTrue:[ |
93 ] |
105 newComment := oldClass comment |
94 ] |
106 ]. |
95 ]. |
107 |
96 |
108 " |
97 "I dont like the confirmers below - we need a notifying: argument, to give |
109 warn, if it exists with different category and different instvars, |
|
110 and the existing is not an autoload class. |
|
111 Usually, this indicates that someone wants to create a new class with |
|
112 a name, which already exists (it happened a few times to myself, while |
|
113 I wanted to create a new class called ReturnNode ...). |
|
114 This will be much less of a problem, once multiple name spaces are |
|
115 implemented and classes can be put into separate packages. |
|
116 " |
|
117 oldClass isLoaded ifTrue:[ |
|
118 oldClass category ~= categoryString ifTrue:[ |
|
119 oldClass instanceVariableString asCollectionOfWords |
|
120 ~= stringOfInstVarNames asCollectionOfWords ifTrue:[ |
|
121 (self confirm:'a class named ' , oldClass name , ' already exists - |
|
122 |
|
123 create (i.e. change) anyway ?' withCRs) |
|
124 ifFalse:[ |
|
125 ^ nil |
|
126 ] |
|
127 ] |
|
128 ] |
|
129 ] |
|
130 ]. |
|
131 |
|
132 " |
|
133 Check for some 'considered bad-style' things, like lower case names. |
|
134 But only do these checks for new classes - |
|
135 - thus, once confirmed, the warnings will not come again and again. |
|
136 |
|
137 NOTICE: |
|
138 I dont like the confirmers below - we need a notifying: argument, to give |
98 the outer codeview a chance to highlight the error. |
139 the outer codeview a chance to highlight the error. |
99 (but thats how PP defined it in the book - maybe it will change anyway" |
140 (but thats how its defined in the book - maybe I will change anyway). |
|
141 " |
100 |
142 |
101 oldClass isNil ifTrue:[ |
143 oldClass isNil ifTrue:[ |
102 "let user confirm, if the classname is no good" |
144 "let user confirm, if the classname is no good" |
103 newName first isUppercase ifFalse:[ |
145 newName first isUppercase ifFalse:[ |
104 (self confirm:'classenames should start with an uppercase letter |
146 (self confirm:'classenames should start with an uppercase letter |
105 (by convention) |
147 (by convention only) |
106 |
148 |
107 install anyway ?' withCRs) |
149 install anyway ?' withCRs) |
108 ifFalse:[ |
150 ifFalse:[ |
109 ^ nil |
151 ^ nil |
110 ] |
152 ] |
147 |
189 |
148 "create the metaclass first" |
190 "create the metaclass first" |
149 newMetaclass := Metaclass new. |
191 newMetaclass := Metaclass new. |
150 newMetaclass setSuperclass:(aClass class). |
192 newMetaclass setSuperclass:(aClass class). |
151 newMetaclass instSize:(aClass class instSize). |
193 newMetaclass instSize:(aClass class instSize). |
152 newMetaclass flags:0. "not indexed" |
|
153 newMetaclass setName:(nameString , 'class'). |
194 newMetaclass setName:(nameString , 'class'). |
154 newMetaclass classVariableString:'' "stringOfClassVarNames". |
195 newMetaclass classVariableString:'' "stringOfClassVarNames". |
155 newMetaclass setComment:newComment category:categoryString. |
196 newMetaclass setComment:newComment category:categoryString. |
156 |
197 |
|
198 "the let the meta create the class" |
157 newClass := newMetaclass new. |
199 newClass := newMetaclass new. |
158 newClass setSuperclass:aClass. |
200 newClass setSuperclass:aClass. |
159 newClass instSize:(aClass instSize + nInstVars). |
201 newClass instSize:(aClass instSize + nInstVars). |
160 |
202 newClass setName:nameString. |
|
203 |
|
204 " |
|
205 Allowing non-booleans as variableBoolean |
|
206 is a hack for backward (ST-80) compatibility: |
|
207 |
|
208 ST-80 code will pass true or false as variableBoolean, |
|
209 while ST/X also calls it with symbols such as #float, #double etc. |
|
210 " |
161 (variableBoolean == true) ifTrue:[ |
211 (variableBoolean == true) ifTrue:[ |
162 pointersBoolean ifTrue:[ |
212 pointersBoolean ifTrue:[ |
163 newFlags := 4 "pointerarray" |
213 newFlags := Behavior flagPointers |
164 ] ifFalse:[ |
214 ] ifFalse:[ |
165 wordsBoolean ifTrue:[ |
215 wordsBoolean ifTrue:[ |
166 newFlags := 2 "wordarray" |
216 newFlags := Behavior flagWords |
167 ] ifFalse:[ |
217 ] ifFalse:[ |
168 newFlags := 1 "bytearray" |
218 newFlags := Behavior flagBytes |
169 ] |
219 ] |
170 ] |
220 ] |
171 ] ifFalse:[ |
221 ] ifFalse:[ |
172 "this is a backward compatible hack" |
|
173 |
|
174 (variableBoolean == #float) ifTrue:[ |
222 (variableBoolean == #float) ifTrue:[ |
175 newFlags := 6 "float array" |
223 newFlags := Behavior flagFloats |
176 ] ifFalse:[ |
224 ] ifFalse:[ |
177 (variableBoolean == #double) ifTrue:[ |
225 (variableBoolean == #double) ifTrue:[ |
178 newFlags := 7 "double array" |
226 newFlags := Behavior flagDoubles |
179 ] ifFalse:[ |
227 ] ifFalse:[ |
180 (variableBoolean == #long) ifTrue:[ |
228 (variableBoolean == #long) ifTrue:[ |
181 newFlags := 3 "long array" |
229 newFlags := Behavior flagLongs |
182 ] ifFalse:[ |
230 ] ifFalse:[ |
183 newFlags := 0 |
231 newFlags := Behavior flagNotIndexed |
184 ] |
232 ] |
185 ] |
233 ] |
186 ]. |
234 ]. |
187 ]. |
235 ]. |
188 superFlags := aClass flags bitAnd:16rFFFF0. "everything except indexed-spec" |
236 superFlags := aClass flags bitAnd:(Behavior maskIndexType bitInvert). "preserve other bits" |
189 |
237 oldClass notNil ifTrue:[ |
190 newClass flags:(newFlags bitOr:superFlags). "keep the special bits around" |
238 oldClass isBuiltInClass ifTrue:[ |
191 |
239 " |
192 newClass setName:nameString. |
240 special care when redefining Method, Block and other built-in classes, |
|
241 which might have other flag bits ... |
|
242 " |
|
243 |
|
244 newFlags := newFlags bitOr:(oldClass flags bitAnd:(Behavior maskIndexType bitInvert)) |
|
245 ] |
|
246 ]. |
|
247 newClass flags:(newFlags bitOr:superFlags). "preserve inherited special bits" |
|
248 |
193 (nInstVars ~~ 0) ifTrue:[ |
249 (nInstVars ~~ 0) ifTrue:[ |
194 newClass instanceVariableString:stringOfInstVarNames |
250 newClass instanceVariableString:stringOfInstVarNames |
195 ]. |
251 ]. |
196 oldClass notNil ifTrue:[ |
252 oldClass notNil ifTrue:[ |
197 "setting first will make new class clear obsolete classvars" |
253 "setting first will make new class clear obsolete classvars" |
198 newClass setClassVariableString:(oldClass classVariableString) |
254 newClass setClassVariableString:(oldClass classVariableString) |
199 ]. |
255 ]. |
200 newClass classVariableString:stringOfClassVarNames. |
256 newClass classVariableString:stringOfClassVarNames. |
201 |
257 |
|
258 " |
|
259 for new classes, we are almost done here |
|
260 " |
202 oldClass isNil ifTrue:[ |
261 oldClass isNil ifTrue:[ |
203 self addChangeRecordForClass:newClass. |
262 self addChangeRecordForClass:newClass. |
204 |
263 |
205 commentString notNil ifTrue:[ |
264 commentString notNil ifTrue:[ |
206 newClass comment:commentString |
265 newClass comment:commentString |
207 ]. |
266 ]. |
|
267 |
208 aSystemDictionary at:classSymbol put:newClass. |
268 aSystemDictionary at:classSymbol put:newClass. |
209 Smalltalk changed. |
269 aSystemDictionary changed. |
210 ^ newClass |
270 ^ newClass |
211 ]. |
271 ]. |
212 |
272 |
|
273 |
|
274 " |
|
275 here comes the hard part - we are actually changing the |
|
276 definition of an existing class .... |
|
277 Try hard to get away WITHOUT recompiling, since it makes all |
|
278 compiled code into interpreted ... |
|
279 " |
213 |
280 |
214 oldInstVars := oldClass instanceVariableString asCollectionOfWords. |
281 oldInstVars := oldClass instanceVariableString asCollectionOfWords. |
215 newInstVars := newClass instanceVariableString asCollectionOfWords. |
282 newInstVars := newClass instanceVariableString asCollectionOfWords. |
216 oldClassVars := oldClass classVariableString asCollectionOfWords. |
283 oldClassVars := oldClass classVariableString asCollectionOfWords. |
217 newClassVars := newClass classVariableString asCollectionOfWords. |
284 newClassVars := newClass classVariableString asCollectionOfWords. |
218 |
285 |
219 "if only category/comment has changed, do not recompile .." |
286 " |
220 |
287 we are on the bright side of life, if the instance layout and |
|
288 inheritance do not change. |
|
289 In this case, we can go ahead and patch the class object. |
|
290 " |
221 (oldClass superclass == newClass superclass) ifTrue:[ |
291 (oldClass superclass == newClass superclass) ifTrue:[ |
222 (oldClass instSize == newClass instSize) ifTrue:[ |
292 (oldClass instSize == newClass instSize) ifTrue:[ |
223 (oldClass flags == newClass flags) ifTrue:[ |
293 (oldClass flags == newClass flags) ifTrue:[ |
224 (oldClass name = newClass name) ifTrue:[ |
294 (oldClass name = newClass name) ifTrue:[ |
225 (oldInstVars = newInstVars) ifTrue:[ |
295 (oldInstVars = newInstVars) ifTrue:[ |
|
296 |
|
297 (newComment ~= oldClass comment) ifTrue:[ |
|
298 oldClass comment:newComment. "writes a change-chunk" |
|
299 ]. |
|
300 |
226 (oldClassVars = newClassVars) ifTrue:[ |
301 (oldClassVars = newClassVars) ifTrue:[ |
|
302 " |
|
303 really no change (just comment and/or category) |
|
304 " |
227 anyChange := false. |
305 anyChange := false. |
228 |
|
229 |
306 |
230 oldClass instanceVariableString:(newClass instanceVariableString). |
307 oldClass instanceVariableString:(newClass instanceVariableString). |
231 oldClass setClassVariableString:(newClass classVariableString). |
308 oldClass setClassVariableString:(newClass classVariableString). |
232 |
309 |
233 (newComment ~= oldClass comment) ifTrue:[ |
|
234 oldClass comment:newComment. "already writes change-chunk" |
|
235 ]. |
|
236 oldClass category ~= categoryString ifTrue:[ |
310 oldClass category ~= categoryString ifTrue:[ |
237 "notify change of organization" |
311 "notify change of organization" |
|
312 |
238 oldClass category:categoryString. |
313 oldClass category:categoryString. |
239 self addChangeRecordForClass:newClass. |
314 self addChangeRecordForClass:newClass. |
240 Smalltalk changed |
315 aSystemDictionary changed |
241 ]. |
316 ]. |
242 "notify change of class" |
317 "notify change of class" |
243 oldClass changed. |
318 oldClass changed. |
244 ^ oldClass |
319 ^ oldClass |
245 ]. |
320 ]. |
246 |
321 |
247 "when we arrive here, class variables have changed" |
322 " |
248 |
323 when we arrive here, class variables have changed |
249 (newComment ~= oldClass comment) ifTrue:[ |
324 " |
250 oldClass comment:newComment. "already writes change-chunk" |
|
251 ]. |
|
252 oldClass category ~= categoryString ifTrue:[ |
325 oldClass category ~= categoryString ifTrue:[ |
253 "notify change of organization" |
326 "notify change of organization" |
254 oldClass category:categoryString. |
327 oldClass category:categoryString. |
255 Smalltalk changed |
328 aSystemDictionary changed |
256 ]. |
329 ]. |
257 |
330 |
|
331 " |
|
332 set class variable string; |
|
333 this also updates the set of class variables |
|
334 by creating new / deleting obsolete ones. |
|
335 " |
258 oldClass classVariableString:stringOfClassVarNames. |
336 oldClass classVariableString:stringOfClassVarNames. |
259 |
337 |
|
338 " |
|
339 get the set of changed class variables |
|
340 " |
260 changeSet1 := Set new. |
341 changeSet1 := Set new. |
261 oldClassVars do:[:nm | |
342 oldClassVars do:[:nm | |
262 (newClassVars includes:nm) ifFalse:[ |
343 (newClassVars includes:nm) ifFalse:[ |
263 changeSet1 add:nm |
344 changeSet1 add:nm |
264 ] |
345 ] |
292 ] |
381 ] |
293 ] |
382 ] |
294 ] |
383 ] |
295 ]. |
384 ]. |
296 |
385 |
297 "tell dependents of class ..." |
386 "tell dependents ..." |
|
387 "OLD: |
298 oldClass changed. |
388 oldClass changed. |
299 |
389 " |
300 "catch special case, where superclass changed its layout and thus |
390 |
301 forced redefinition of this class - this will not be logged here" |
391 |
|
392 " |
|
393 here we enter the darkness of mordor ... |
|
394 " |
302 |
395 |
303 (newComment ~= oldClass comment) ifTrue:[ |
396 (newComment ~= oldClass comment) ifTrue:[ |
304 newClass comment:newComment |
397 newClass comment:newComment |
305 ]. |
398 ]. |
306 |
399 |
307 superClassChange := oldClass superclass ~~ newClass superclass. |
400 superClassChange := oldClass superclass ~~ newClass superclass. |
308 |
401 |
309 "dont allow built-in classes to be modified" |
402 " |
|
403 dont allow built-in classes to be modified this way |
|
404 " |
310 (oldClass notNil and:[oldClass isBuiltInClass and:[superClassChange]]) ifTrue:[ |
405 (oldClass notNil and:[oldClass isBuiltInClass and:[superClassChange]]) ifTrue:[ |
311 self error:'the inheritance of this class is fixed - you cannot change it'. |
406 self error:'the inheritance of this class is fixed - you cannot change it'. |
312 ^ oldClass |
407 ^ oldClass |
313 ]. |
408 ]. |
314 |
409 |
|
410 " |
|
411 catch special case, where superclass changed its layout and thus |
|
412 forced redefinition of this class; |
|
413 only log if this is not the case. |
|
414 " |
315 (superClassChange |
415 (superClassChange |
316 and:[(oldClass superclass isNil or:[oldClass superclass name = newClass superclass name]) |
416 and:[(oldClass superclass isNil or:[oldClass superclass name = newClass superclass name]) |
317 and:[(oldClassVars = newClassVars) |
417 and:[(oldClassVars = newClassVars) |
318 and:[(oldInstVars = newInstVars) |
418 and:[(oldInstVars = newInstVars) |
319 and:[newComment = oldClass comment]]]]) ifFalse:[ |
419 and:[newComment = oldClass comment]]]]) ifFalse:[ |
320 self addChangeRecordForClass:newClass. |
420 self addChangeRecordForClass:newClass. |
321 ]. |
421 ]. |
322 |
422 |
|
423 " |
|
424 care for class methods ... |
|
425 " |
323 changeSet1 := Set new. |
426 changeSet1 := Set new. |
324 changeSet2 := Set new. |
|
325 |
427 |
326 classVarChange := false. |
428 classVarChange := false. |
327 |
429 |
328 superClassChange ifTrue:[ |
430 superClassChange ifTrue:[ |
329 "superclass changed, |
431 " |
330 must recompile all class methods accessing any classvar" |
432 superclass changed: |
|
433 must recompile all class methods accessing ANY classvar |
|
434 ( |
|
435 actually, we could be less strict and handle the case where |
|
436 both the old and the new superclass have a common ancestor, |
|
437 and both have no new classvariables in between. |
|
438 This would speedup the case when a class is inserted into |
|
439 the inheritance chain. |
|
440 ) |
|
441 " |
331 |
442 |
332 oldClass allClassVarNames do:[:nm | changeSet1 add:nm]. |
443 oldClass allClassVarNames do:[:nm | changeSet1 add:nm]. |
333 newClass allClassVarNames do:[:nm | changeSet1 add:nm]. |
444 newClass allClassVarNames do:[:nm | changeSet1 add:nm]. |
334 |
445 |
335 " " |
446 " " |
336 Transcript showCr:'recompiling class methods accessing any classvar'. |
447 Transcript showCr:'recompiling class methods accessing any classvar'. |
|
448 Transcript endEntry. |
337 " " |
449 " " |
338 self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass accessingAny:changeSet1. |
450 self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass accessingAny:changeSet1. |
339 newMetaclass recompileInvalidatedMethods. |
451 newMetaclass recompileInvalidatedMethods. |
340 ] ifFalse:[ |
452 ] ifFalse:[ |
341 "same superclass, find out which classvars have changed" |
453 " |
342 |
454 same superclass, find out which classvars have changed |
|
455 " |
343 classVarChange := oldClassVars ~= newClassVars. |
456 classVarChange := oldClassVars ~= newClassVars. |
344 classVarChange ifTrue:[ |
457 classVarChange ifTrue:[ |
345 oldClassVars do:[:nm | |
458 oldClassVars do:[:nm | |
346 (newClassVars includes:nm) ifFalse:[ |
459 (newClassVars includes:nm) ifFalse:[ |
347 changeSet1 add:nm |
460 changeSet1 add:nm |
352 changeSet1 add:nm |
465 changeSet1 add:nm |
353 ] |
466 ] |
354 ]. |
467 ]. |
355 ]. |
468 ]. |
356 |
469 |
357 " " |
|
358 Transcript showCr:'recompiling class methods accessing ' , changeSet1 printString. |
|
359 " " |
|
360 classVarChange ifTrue:[ |
470 classVarChange ifTrue:[ |
361 "must recompile class-methods" |
471 " |
|
472 must recompile some class-methods |
|
473 " |
|
474 " " |
|
475 Transcript showCr:'recompiling class methods accessing ' , changeSet1 printString. |
|
476 Transcript endEntry. |
|
477 " " |
362 self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass accessingAny:changeSet1. |
478 self copyInvalidatedMethodsFrom:(oldClass class) for:newMetaclass accessingAny:changeSet1. |
363 newMetaclass recompileInvalidatedMethods. |
479 newMetaclass recompileInvalidatedMethods. |
364 ] ifFalse:[ |
480 ] ifFalse:[ |
365 "class methods still work" |
481 " |
|
482 class methods still work |
|
483 " |
366 self copyMethodsFrom:(oldClass class) for:newMetaclass |
484 self copyMethodsFrom:(oldClass class) for:newMetaclass |
367 ]. |
485 ]. |
368 ]. |
486 ]. |
|
487 |
|
488 " |
|
489 care for instance methods ... |
|
490 " |
|
491 changeSet2 := Set new. |
369 |
492 |
370 superClassChange ifTrue:[ |
493 superClassChange ifTrue:[ |
371 "superclass changed, |
494 "superclass changed, |
372 must recompile all class methods accessing any class or instvar" |
495 must recompile all methods accessing any class or instvar. |
373 |
496 If number of instvars (i.e. the instances instSize) is the same, |
374 "no, if number of instvars is the same, only the changed ones ..." |
497 we can limit the set of recompiled instance methods to those methods, |
375 |
498 which refer to an instvar with a different inst-index |
376 "find set of changed instvars" |
499 " |
377 |
500 |
|
501 " |
|
502 collect the instvar-indices in the old and new class |
|
503 " |
378 offset := 0. oldOffsets := Dictionary new. |
504 offset := 0. oldOffsets := Dictionary new. |
379 oldClass allInstVarNames do:[:nm | offset := offset + 1. oldOffsets at:nm put:offset]. |
505 oldClass allInstVarNames do:[:nm | offset := offset + 1. oldOffsets at:nm put:offset]. |
380 offset := 0. newOffsets := Dictionary new. |
506 offset := 0. newOffsets := Dictionary new. |
381 newClass allInstVarNames do:[:nm | offset := offset + 1. newOffsets at:nm put:offset]. |
507 newClass allInstVarNames do:[:nm | offset := offset + 1. newOffsets at:nm put:offset]. |
382 |
508 |
|
509 " |
|
510 the changeset consists of instance variables, |
|
511 whith a different position |
|
512 " |
383 oldOffsets associationsDo:[:a | |
513 oldOffsets associationsDo:[:a | |
384 |k| |
514 |k| |
385 |
515 |
386 k := a key. |
516 k := a key. |
387 (newOffsets includesKey:k) ifFalse:[ |
517 (newOffsets includesKey:k) ifFalse:[ |
403 changeSet2 add:k |
533 changeSet2 add:k |
404 ] |
534 ] |
405 ] |
535 ] |
406 ]. |
536 ]. |
407 |
537 |
|
538 " |
|
539 merge in the changed class variables |
|
540 " |
408 changeSet1 do:[:nm | changeSet2 add:nm]. |
541 changeSet1 do:[:nm | changeSet2 add:nm]. |
409 " " |
542 |
410 Transcript showCr:'recompiling instance methods accessing ' , changeSet2 printString , ' |
543 " " |
411 ...'. |
544 Transcript showCr:'recompiling instance methods accessing ' , changeSet2 printString , ' ...'. |
|
545 Transcript endEntry. |
412 " " |
546 " " |
413 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet2. |
547 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet2. |
414 newClass recompileInvalidatedMethods. |
548 newClass recompileInvalidatedMethods. |
415 |
549 |
416 false ifTrue:[ |
|
417 oldClass allClassVarNames do:[:nm | changeSet1 add:nm]. |
|
418 newClass allClassVarNames do:[:nm | changeSet1 add:nm]. |
|
419 oldClass allInstVarNames do:[:nm | changeSet1 add:nm]. |
|
420 newClass allInstVarNames do:[:nm | changeSet1 add:nm]. |
|
421 |
|
422 " " |
|
423 Transcript showCr:'recompiling instance methods accessing any class or instvar' . |
|
424 " " |
|
425 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet1. |
|
426 newClass recompileInvalidatedMethods. |
|
427 ] |
|
428 ] ifFalse:[ |
550 ] ifFalse:[ |
|
551 " |
|
552 same inheritance ... |
|
553 " |
429 instVarChange := oldInstVars ~= newInstVars. |
554 instVarChange := oldInstVars ~= newInstVars. |
430 instVarChange ifFalse:[ |
555 instVarChange ifFalse:[ |
|
556 " |
|
557 same instance variables ... |
|
558 " |
431 classVarChange ifTrue:[ |
559 classVarChange ifTrue:[ |
432 "recompile all inst methods accessing classvars" |
560 "recompile all inst methods accessing changed classvars" |
433 |
561 |
434 " " |
562 " " |
435 Transcript showCr:'recompiling instance methods accessing ' , changeSet1 printString , ' ...'. |
563 Transcript showCr:'recompiling instance methods accessing ' , changeSet1 printString , ' ...'. |
|
564 Transcript endEntry. |
436 " " |
565 " " |
437 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet1. |
566 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet1. |
438 newClass recompileInvalidatedMethods. |
567 newClass recompileInvalidatedMethods. |
439 ] |
568 ] |
440 ] ifTrue:[ |
569 ] ifTrue:[ |
441 instVarChange := (oldInstVars ~= newInstVars). |
570 " |
442 |
571 dont allow built-in classes to be modified |
443 "dont allow built-in classes to be modified" |
572 " |
444 (oldClass notNil and:[oldClass isBuiltInClass and:[instVarChange]]) ifTrue:[ |
573 (oldClass notNil and:[oldClass isBuiltInClass and:[instVarChange]]) ifTrue:[ |
445 self error:'the layout of this class is fixed - you cannot change it'. |
574 self error:'the layout of this class is fixed - you cannot change it'. |
446 ^ oldClass |
575 ^ oldClass |
447 ]. |
576 ]. |
448 |
577 |
449 instVarChange ifTrue:[ |
578 ((oldInstVars size == 0) |
450 |
579 or:[newInstVars startsWith:oldInstVars]) ifTrue:[ |
451 ((oldInstVars size == 0) |
580 " |
452 or:[newInstVars startsWith:oldInstVars]) ifTrue:[ |
581 only new inst variable(s) has/have been added - |
453 "new variable(s) has/have been added - old methods still work" |
582 old methods still work (the existing inst-indices are still valid) |
454 |
583 " |
455 Transcript showCr:'copying methods ...'. |
584 " " |
456 self copyMethodsFrom:oldClass for:newClass. |
585 Transcript showCr:'copying methods ...'. |
457 |
586 Transcript endEntry. |
458 "but have to recompile methods accessing stuff now defined |
587 " " |
459 (it might have been a global before ...)" |
588 self copyMethodsFrom:oldClass for:newClass. |
460 |
589 |
461 addedNames := newInstVars select:[:nm | (oldInstVars includes:nm) not]. |
590 " |
462 changeSet1 do:[:nm | addedNames add:nm]. |
591 but: we have to recompile all methods accessing new instars |
463 |
592 (it might have been a classVar/global before ...) |
464 " " |
593 " |
465 Transcript showCr:'recompiling instance methods accessing ' , addedNames printString , '...'. |
594 addedNames := newInstVars select:[:nm | (oldInstVars includes:nm) not]. |
466 " " |
595 "merge in class variables" |
467 newClass recompileMethodsAccessingAny:addedNames. |
596 changeSet1 do:[:nm | addedNames add:nm]. |
468 ] ifFalse:[ |
597 |
469 "find set of changed instvars" |
598 " " |
470 |
599 Transcript showCr:'recompiling instance methods accessing ' , addedNames printString , '...'. |
471 offset := 0. oldOffsets := Dictionary new. |
600 Transcript endEntry. |
472 oldInstVars do:[:nm | offset := offset + 1. oldOffsets at:nm put:offset]. |
601 " " |
473 offset := 0. newOffsets := Dictionary new. |
602 newClass recompileMethodsAccessingAny:addedNames. |
474 newInstVars do:[:nm | offset := offset + 1. newOffsets at:nm put:offset]. |
603 ] ifFalse:[ |
475 |
604 " |
476 oldOffsets associationsDo:[:a | |
605 collect the instvar-indices in the old and new class |
477 |k| |
606 " |
478 |
607 offset := 0. oldOffsets := Dictionary new. |
479 k := a key. |
608 oldInstVars do:[:nm | offset := offset + 1. oldOffsets at:nm put:offset]. |
480 (newOffsets includesKey:k) ifFalse:[ |
609 offset := 0. newOffsets := Dictionary new. |
|
610 newInstVars do:[:nm | offset := offset + 1. newOffsets at:nm put:offset]. |
|
611 |
|
612 " |
|
613 the changeset consists of instance variables, |
|
614 whith a different position |
|
615 " |
|
616 oldOffsets associationsDo:[:a | |
|
617 |k| |
|
618 |
|
619 k := a key. |
|
620 (newOffsets includesKey:k) ifFalse:[ |
|
621 changeSet2 add:k |
|
622 ] ifTrue:[ |
|
623 (a value ~~ (newOffsets at:k)) ifTrue:[ |
481 changeSet2 add:k |
624 changeSet2 add:k |
482 ] ifTrue:[ |
|
483 (a value ~~ (newOffsets at:k)) ifTrue:[ |
|
484 changeSet2 add:k |
|
485 ] |
|
486 ] |
625 ] |
487 ]. |
626 ] |
488 newOffsets associationsDo:[:a | |
627 ]. |
489 |k| |
628 newOffsets associationsDo:[:a | |
490 |
629 |k| |
491 k := a key. |
630 |
492 (oldOffsets includesKey:k) ifFalse:[ |
631 k := a key. |
|
632 (oldOffsets includesKey:k) ifFalse:[ |
|
633 changeSet2 add:k |
|
634 ] ifTrue:[ |
|
635 (a value ~~ (oldOffsets at:k)) ifTrue:[ |
493 changeSet2 add:k |
636 changeSet2 add:k |
494 ] ifTrue:[ |
|
495 (a value ~~ (oldOffsets at:k)) ifTrue:[ |
|
496 changeSet2 add:k |
|
497 ] |
|
498 ] |
637 ] |
499 ]. |
638 ] |
500 |
|
501 changeSet1 do:[:nm | changeSet2 add:nm]. |
|
502 " " |
|
503 Transcript showCr:'recompiling instance methods accessing ' , changeSet2 printString , ' ...'. |
|
504 " " |
|
505 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet2. |
|
506 newClass recompileInvalidatedMethods. |
|
507 |
|
508 ]. |
639 ]. |
|
640 "merge in class variables" |
|
641 changeSet1 do:[:nm | changeSet2 add:nm]. |
|
642 " " |
|
643 Transcript showCr:'recompiling instance methods accessing ' , changeSet2 printString , ' ...'. |
|
644 Transcript endEntry. |
|
645 " " |
|
646 self copyInvalidatedMethodsFrom:oldClass for:newClass accessingAny:changeSet2. |
|
647 newClass recompileInvalidatedMethods. |
509 ]. |
648 ]. |
510 ]. |
649 ]. |
511 ]. |
650 ]. |
512 |
651 |
513 "get list of all subclasses - do before superclass is changed" |
652 " |
514 |
653 WOW, everything done for this class |
|
654 what about subclasses ? |
|
655 " |
|
656 |
|
657 " |
|
658 get list of all subclasses - do this before superclass is changed |
|
659 " |
|
660 "no longer needed" |
|
661 " |
515 allSubclasses := oldClass allSubclasses. |
662 allSubclasses := oldClass allSubclasses. |
516 |
663 " |
517 "update superclass of immediate subclasses - this forces recompilation if needed" |
664 |
518 |
665 " |
519 "dont update change file for the subclass changes" |
666 update superclass of immediate subclasses - |
|
667 this forces recompilation (recursively) if needed |
|
668 (dont update change file for the subclass changes) |
|
669 " |
520 upd := Class updateChanges:false. |
670 upd := Class updateChanges:false. |
521 [ |
671 [ |
522 oldClass subclassesDo:[:aClass | |
672 oldClass subclassesDo:[:aClass | |
523 " " |
673 " " |
524 Transcript showCr:'changing superclass of:' , aClass name. |
674 Transcript showCr:'changing superclass of:' , aClass name. |
|
675 Transcript endEntry. |
525 " " |
676 " " |
526 aClass superclass:newClass |
677 aClass superclass:newClass |
527 ] |
678 ] |
528 ] valueNowOrOnUnwindDo:[ |
679 ] valueNowOrOnUnwindDo:[ |
529 Class updateChanges:upd. |
680 Class updateChanges:upd. |
530 ]. |
681 ]. |
531 |
682 |
|
683 " |
|
684 change category in oldClass - so we see immediately what it is ... |
|
685 " |
|
686 oldClass category:'obsolete'. |
|
687 oldClass class category:'obsolete'. |
|
688 |
|
689 " |
|
690 and make the new class globally known |
|
691 " |
532 aSystemDictionary at:classSymbol put:newClass. |
692 aSystemDictionary at:classSymbol put:newClass. |
|
693 |
|
694 oldClass category ~= categoryString ifTrue:[ |
|
695 "notify change of organization" |
|
696 aSystemDictionary changed |
|
697 ]. |
|
698 |
|
699 " |
|
700 Not becoming the old class creates some update problems; |
|
701 the browsers must check carefully - a simple identity compare is |
|
702 not enought ... |
|
703 QUESTION: is this a good idea ? |
|
704 " |
|
705 |
|
706 newClass dependents:(oldClass dependents). |
|
707 newClass changed. |
|
708 |
|
709 "just to make certain ..." |
|
710 oldClass changed. |
|
711 |
533 ObjectMemory flushCaches. |
712 ObjectMemory flushCaches. |
|
713 |
534 ^ newClass |
714 ^ newClass |
535 ! |
715 ! |
536 |
716 |
537 new |
717 new |
538 "create & return a new metaclass (a classes class)" |
718 "create & return a new metaclass (a classes class)" |
678 |
862 |
679 ((oldNames size == 0) |
863 ((oldNames size == 0) |
680 or:[newNames startsWith:oldNames]) ifTrue:[ |
864 or:[newNames startsWith:oldNames]) ifTrue:[ |
681 "new variable(s) has/have been added - old methods still work" |
865 "new variable(s) has/have been added - old methods still work" |
682 |
866 |
|
867 " " |
683 Transcript showCr:'copying methods ...'. |
868 Transcript showCr:'copying methods ...'. |
|
869 Transcript endEntry. |
|
870 " " |
684 self copyMethodsFrom:self for:newMetaclass. |
871 self copyMethodsFrom:self for:newMetaclass. |
685 self copyMethodsFrom:oldClass for:newClass. |
872 self copyMethodsFrom:oldClass for:newClass. |
686 |
873 |
687 "but have to recompile methods accessing stuff now defined |
874 "but have to recompile methods accessing stuff now defined |
688 (it might have been a global before ...)" |
875 (it might have been a global before ...)" |
689 |
876 |
690 addedNames := newNames select:[:nm | (oldNames includes:nm) not]. |
877 addedNames := newNames select:[:nm | (oldNames includes:nm) not]. |
691 " |
878 " |
692 Transcript showCr:'recompiling methods accessing ' , |
879 Transcript showCr:'recompiling methods accessing ' , addedNames printString , '...'. |
693 addedNames printString , '...'. |
880 Transcript endEntry. |
694 " |
881 " |
695 "recompile class-methods" |
882 "recompile class-methods" |
696 newMetaclass recompileMethodsAccessingAny:addedNames. |
883 newMetaclass recompileMethodsAccessingAny:addedNames. |
697 ] ifFalse:[ |
884 ] ifFalse:[ |
698 " |
885 " |
699 Transcript showCr:'recompiling methods accessing ' , |
886 Transcript showCr:'recompiling methods accessing ' , changeSet printString , ' ...'. |
700 changeSet printString , ' ...'. |
887 Transcript endEntry. |
701 " |
888 " |
702 "recompile class-methods" |
889 "recompile class-methods" |
703 self copyInvalidatedMethodsFrom:self for:newMetaclass accessingAny:changeSet. |
890 self copyInvalidatedMethodsFrom:self for:newMetaclass accessingAny:changeSet. |
704 newMetaclass recompileInvalidatedMethods. |
891 newMetaclass recompileInvalidatedMethods. |
705 |
892 |
706 self copyMethodsFrom:oldClass for:newClass. |
893 self copyMethodsFrom:oldClass for:newClass. |
707 ]. |
894 ]. |
708 |
895 |
709 delta := newNames size - oldNames size. |
896 delta := newNames size - oldNames size. |
710 |
897 |
711 "get list of all subclasses - do before superclass is changed" |
898 " |
712 |
899 get list of all subclasses - do before superclass is changed |
|
900 " |
713 allSubclasses := oldClass allSubclasses. |
901 allSubclasses := oldClass allSubclasses. |
714 allSubclasses := allSubclasses asSortedCollection:[:a :b | |
902 allSubclasses := allSubclasses asSortedCollection:[:a :b | |
715 b isSubclassOf:a |
903 b isSubclassOf:a |
716 ]. |
904 ]. |
717 |
905 |
740 oldSuper == oldClass ifTrue:[ |
928 oldSuper == oldClass ifTrue:[ |
741 newSub setSuperclass:newClass. |
929 newSub setSuperclass:newClass. |
742 ] ifFalse:[ |
930 ] ifFalse:[ |
743 newSub setSuperclass:(oldToNew at:oldSuper). |
931 newSub setSuperclass:(oldToNew at:oldSuper). |
744 ]. |
932 ]. |
745 newSub setSelectors:(aSubclass selectors). |
933 newSub setSelectors:(aSubclass selectorArray). |
746 newSub setMethodDictionary:(aSubclass methodDictionary). |
934 newSub setMethodDictionary:(aSubclass methodArray). |
747 newSub setName:(aSubclass name). |
935 newSub setName:(aSubclass name). |
748 newSub classVariableString:(aSubclass classVariableString). |
936 newSub classVariableString:(aSubclass classVariableString). |
749 newSub setComment:(aSubclass comment). |
937 newSub setComment:(aSubclass comment). |
750 newSub category:(aSubclass category). |
938 newSub category:(aSubclass category). |
751 |
939 |
752 oldToNew at:aSubclass put:newSub. |
940 oldToNew at:aSubclass put:newSub. |
753 |
941 |
|
942 " |
754 aSubclass setName:(aSubclass name , '-old'). |
943 aSubclass setName:(aSubclass name , '-old'). |
755 aSubclass category:'obsolete classes' |
944 aSubclass category:'obsolete classes' |
|
945 " |
|
946 aSubclass category:'obsolete'. |
|
947 aSubclass class category:'obsolete'. |
756 ]. |
948 ]. |
757 |
949 |
758 "recompile what needs to be" |
950 "recompile what needs to be" |
759 |
951 |
760 delta == 0 ifTrue:[ |
952 delta == 0 ifTrue:[ |
761 "only have to recompile class methods accessing |
953 "only have to recompile class methods accessing |
762 class instvars from changeset" |
954 class instvars from changeset |
|
955 " |
763 |
956 |
764 allSubclasses do:[:aClass | |
957 allSubclasses do:[:aClass | |
765 aClass class recompileMethodsAccessingAny:changeSet. |
958 aClass class recompileMethodsAccessingAny:changeSet. |
766 ] |
959 ] |
767 ] ifFalse:[ |
960 ] ifFalse:[ |
768 "have to recompile all class methods accessing class instvars" |
961 " |
|
962 have to recompile all class methods accessing class instvars |
|
963 " |
769 |
964 |
770 allSubclasses do:[:aClass | |
965 allSubclasses do:[:aClass | |
771 |classInstVars| |
966 |classInstVars| |
772 |
967 |
773 classInstVars := aClass class allInstVarNames. |
968 classInstVars := aClass class allInstVarNames. |
802 ^ true |
998 ^ true |
803 ! ! |
999 ! ! |
804 |
1000 |
805 !Metaclass methodsFor:'private'! |
1001 !Metaclass methodsFor:'private'! |
806 |
1002 |
|
1003 invalidMethod |
|
1004 "When recompiling classes after a definition-change, all |
|
1005 uncompilable methods will be bound to this method here, |
|
1006 so that evaluating such an uncompilable method will trigger an error. |
|
1007 Can also happen when Compiler/runtime system is broken." |
|
1008 |
|
1009 self error:'invalid method - this method failed to compile when the class was changed' |
|
1010 ! |
|
1011 |
807 copyMethodsFrom:oldClass for:newClass |
1012 copyMethodsFrom:oldClass for:newClass |
808 "when a class has changed, but metaclass is unaffected (i.e. classVars |
1013 "when a class has changed, but metaclass is unaffected (i.e. classVars |
809 have not changed) there is no need to recompile them" |
1014 have not changed) there is no need to recompile them" |
810 |
1015 |
811 newClass selectors:(oldClass selectors copy) methods:(oldClass methodDictionary copy) |
1016 newClass selectors:(oldClass selectorArray copy) |
|
1017 methods:(oldClass methodArray copy) |
812 ! |
1018 ! |
813 |
1019 |
814 copyInvalidatedMethodsFrom:oldClass for:newClass |
1020 copyInvalidatedMethodsFrom:oldClass for:newClass |
815 "when a class has been changed, copy all old methods into the new class |
1021 "when a class has been changed, copy all old methods into the new class |
816 - changing code to a trap method giving an error message; |
1022 - changing code to a trap method giving an error message; |
817 this allows us to keep the source while trapping uncompilable (due to |
1023 this allows us to keep the source while trapping uncompilable (due to |
818 now undefined instvars) methods" |
1024 now undefined instvars) methods" |
819 |
1025 |
820 |trap trapCode trapByteCode newMethod oldMethodArray newMethodArray| |
1026 |trap trapCode trapByteCode newMethod oldMethodArray newMethodArray| |
821 |
1027 |
822 trap := Method compiledMethodAt:#invalidMethod. |
1028 trap := Metaclass compiledMethodAt:#invalidMethod. |
823 trapCode := trap code. |
1029 trapCode := trap code. |
824 trapByteCode := trap byteCode. |
1030 trapByteCode := trap byteCode. |
825 |
1031 |
826 oldMethodArray := oldClass methodDictionary. |
1032 oldMethodArray := oldClass methodArray. |
827 newMethodArray := Array new:(oldMethodArray size). |
1033 newMethodArray := Array new:(oldMethodArray size). |
828 newClass selectors:(oldClass selectors copy) methods:newMethodArray. |
1034 newClass selectors:(oldClass selectorArray copy) |
|
1035 methods:newMethodArray. |
829 1 to:oldMethodArray size do:[:i | |
1036 1 to:oldMethodArray size do:[:i | |
830 newMethod := (oldMethodArray at:i) copy. |
1037 newMethod := (oldMethodArray at:i) copy. |
831 newMethod code:trapCode. |
1038 newMethod code:trapCode. |
832 newMethod literals:nil. |
1039 newMethod literals:nil. |
833 newMethod byteCode:trapByteCode. |
1040 newMethod byteCode:trapByteCode. |