forked from newspeaklanguage/newspeak
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMirrorsForSqueak.ns
1771 lines (1649 loc) · 60.5 KB
/
MirrorsForSqueak.ns
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Newspeak3
'Mirrors'
class MirrorsForSqueak usingPlatform: platform <Platform> vmMirror: vmm <VMMirror> = (
(* The new Newspeak mirrors API. It is a work in progress that attempts to address weaknesses of the current API and of past mirror APIs. The mirrors here are high level mirrors: they represent language constructs in NS2 and above.
The API follows several general guidelines:
1) Operations return mirrors (an exception is getting the reflectee).
2) Operations take non-mirrors as arguments.
3) Operations take a failure block.
The motivation for (3) is that mirrors should be useful in both local and distributed settings. Forcing the user to confront the possibility of failure helps make code more robust in the distributed case. This also contributes to (1); returned results may refer to remote values, and going through the mirror API to deal with them will help ensure failure scenarios are dealt with.
On the other hand, arguments can always be converted to mirrors by the API. Much of the awkwardness in mirror APIs stems from the need to package arguments as mirrors - with the API often immediately extracting the reflectee afterwards. The API should endeavor to deal with either mirrors or non-mirrors when this makes sense (e.g., when applying a mixin to a superclass, the superclass argument could be either a class or a class mirror) or to provide a separate call for mirrors (say, when adding a method - one call might accept source as a string, another a MethodMirror).
The implementation will likely change. As we reform the reflective interface, we are likely to reduce our reliance on existing code like SqueakVMMirror and NS2Reflection; either their code will migrate here or vice versa.
The API is divided into immutable and mutable parts. Mirrors are basically immutable. As such they support introspection directly. In order to mutate code, one uses MirrorBuilders. These are created based on a mirror, and allow modifications to be accumulated without having any effect on the system. The builder can be asked to provide a mirror reflecting its current state at any time. This allows the results of multiple builders to be batched and submitted to the atomic installer as well.
All this brings up the question of how mirrors differ from ASTs. Mirrors and ASTs should ideally be viewed as different implementations of the same interface. Mirrors differ in how they are constructed and how they compute their subtrees. Mirrors may be connected to a live representation, or to a source base or whatever.
MirrorBuilders also differ in supporting mutability and in what inputs can drive them (e.g, addFromSource:) so they extend the base API of mirrors and ASTs.
It may be a while before this module realizes the ideal description given above. Also, the implementation still relies heavily on earlier reflective APIs - be they the built-in Squeak reflection classes or other efforts. Ultimately, the actual logic for this should reside here.
Copyright 2008 Cadence Design Systems, Inc.
Copyright (c) 2009-2010 Gilad Bracha
Copyright 2011 Gilad Bracha, Ryan Macnak and Cadence Design Systems
Licensed under the Apache License, Version 2.0 (the ''License''); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 *)
|
private Map = platform collections Map.
private List = platform collections List.
private IdentitySet = platform squeak IdentitySet.
private IdentityMap = platform squeak IdentityDictionary.
private UnhandledError = platform squeak UnhandledError.
private Semaphore = platform squeak Semaphore.
private Duct = platform ducts Duct.
private WeakStorage = platform ducts WeakStorage.
private Metaclass = platform kernel Metaclass.
private atomicInstaller = platform namespace AtomicInstaller usingPlatform: platform vmMirror: vmm.
private groups = platform namespace MirrorGroups usingPlatform: platform.
private intermediates = platform namespace Intermediates usingPlatform: platform.
private ImmutableMirrorGroup = groups ImmutableMirrorGroup.
private vmmirror = vmm.
public (* bogus *) parserLib = Future computing: [platform namespace CombinatorialParsing usingPlatform: platform].
(* Often imported by other module because it is annoying to get and assemble the parts to instaniate it. *)
public (* bogus *) grammar = Future computing: [platform namespace NewspeakGrammar usingPlatform: platform parsers: parserLib].
(* Used by the language adaptor for the colorizer. *)
protected asts = Future computing: [platform namespace NewspeakASTs usingPlatform: platform].
protected parsing = Future computing: [platform namespace NewspeakParsing
usingPlatform: platform
grammar: grammar
asts: asts].
protected parser = Future computing: [parsing Parser new].
public (* bogus *) compilation = Future computing: [platform namespace Newspeak2SqueakCompilation
usingPlatform: platform
asts: asts
newspeakParser: parsing
intermediates: intermediates].
protected storedCompiler (* cached compiler and module *)
private mixinBasedMirrors <WeakIdentitySet[MixinMirror]> = WeakStorage new.
public channelForMemoryHole = (Duct owner: self) beWeak. (* bogus *)
|) (
public class ClassDeclarationBuilder fromIntermediate: ir <IntermediateClassDeclaration> forExistingMixin: mixin <InstanceMixin> = (
(*Builders do not give an ordinary mirror representing their current state: ask the builder itself. (Although this could be done: submit to atomic install without a namespace, don't install into Smalltalk, don't update the existingMixin, and reflect on the result.)
Builders internally keep a CompiledMixinMirror that reflects the current state, including edits made but not yet installed. Queries reflect the state of this CMM.
Builders are connected up and down. If you get a builder on a declaration, if you ask *the builder* for an enclosing or nested declaration and edit, installing any of them means installing all of them. Builders created de novo (via reflecting: or fromSource:) remain independent.*)
|
public prvtIntermediate <CompiledMixinMirror> = ir.
public header <ClassHeaderBuilder> = ClassHeaderBuilder forIntermediate: ir.
|prvtIntermediate existingMixin: mixin) (
public accessModifier = (
^prvtIntermediate accessModifier
)
checkForHeaderConflicts: newIntermediate <IntermediateClassDeclaration> = (
newIntermediate instanceSide slots do:
[:slot <IntermediateSlotDeclaration> |
instanceSide
checkNameConflictsForSlot: slot name
mutable: slot isMutable].
prvtIntermediate classSide checkNameConflictsForFactory: newIntermediate factoryName.
)
checkForNameChange: newCompiledMixinMirror = (
(fullyQualifiedNameToSimple: newCompiledMixinMirror name) = self simpleName
ifFalse: [Error signal: 'Cannot change name this way'].
)
public classSide ^<MixinBuilder> = (
| ir builder |
prvtIntermediate builder = nil ifTrue: [halt].
ir:: prvtIntermediate classSide.
builder:: ir builder.
nil = builder ifTrue:
[builder:: MixinBuilder forClassDeclaration: self intermediate: ir.
ir builder: builder].
^builder
)
public collectExistingMixinsInto: map <Map[IntermediateClassDeclaration, InstanceMixin]> ^<IntermediateClassDeclaration> = (
prvtExistingMixin ifNotNil: [map at: prvtIntermediate put: prvtExistingMixin].
instanceSide nestedClasses do: [:ea | ea collectExistingMixinsInto: map].
^prvtIntermediate
)
public enclosingClass ^<ClassDeclarationBuilder> = (
| enclosingMixin <Mixin> enclosingDecl |
nil = prvtIntermediate enclosingClass ifFalse:
[^prvtIntermediate enclosingClass declaration builder].
nil = prvtIntermediate existingMixin ifTrue:
[^nil].
enclosingMixin:: prvtIntermediate existingMixin enclosingMixin.
nil = enclosingMixin ifTrue:
[^nil].
enclosingDecl:: ClassDeclarationBuilder reflecting: enclosingMixin.
enclosingDecl prvtIntermediate instanceSide nestedClasses do:
[:e | e simpleName = prvtIntermediate simpleName ifTrue:
[prvtIntermediate accessor: e accessor]].
enclosingDecl prvtIntermediate instanceSide nestedClasses removeAllSuchThat:
[:e | e simpleName = prvtIntermediate simpleName].
enclosingDecl prvtIntermediate instanceSide nestedClasses
add: prvtIntermediate.
enclosingDecl instanceSide nestedClasses addMirror: self.
prvtIntermediate enclosingClass: enclosingDecl prvtIntermediate instanceSide.
^enclosingDecl
)
public extractReflecteeFrom: mixin <Mixin> = (
(* For builders that already had a non-nil reflectee, this is not necessary since the old reflectee has been become:d to the new one (at least if atomic install is working right...). But this is required for builders that have been created from source and didn't have a reflectee yet. This will connect them to their reflectees. *)
prvtExistingMixin:: mixin.
mixin nestedMixins keysAndValuesDo: [:n :nestedMixin |
| m |
m:: instanceSide nestedClasses findMirrorNamed: n.
m extractReflecteeFrom: nestedMixin
].
)
public headerFromSource: newHeader <String> = (
| prefix newIR |
(* only top-level classes can specify categories *)
prefix:: prvtIntermediate isTopLevel
ifTrue: [ 'Newspeak3 ''', header category, ''' ' ]
ifFalse: [ ' ' ].
self enclosingClass. (* For side effect. *)
newIR:: compiler
compileClassHeader: (prefix, newHeader)
within: prvtIntermediate enclosingClass.
checkForHeaderConflicts: newIR.
assert: [prvtIntermediate name = newIR name] message: ''.
prvtIntermediate headerSource: newIR headerSource.
prvtIntermediate factoryName: newIR factoryName.
prvtIntermediate comment: newIR comment.
prvtIntermediate accessor: newIR accessor.
prvtIntermediate factory: newIR factory.
prvtIntermediate initializers: newIR initializers.
prvtIntermediate instanceSide slots: newIR instanceSide slots.
)
public install ^<ClassDeclarationMirror> = (
^(installAtomically: {self}) first
)
public instanceSide ^<MixinBuilder> = (
| ir builder |
prvtIntermediate builder = nil ifTrue: [halt].
ir:: prvtIntermediate instanceSide.
builder:: ir builder.
nil = builder ifTrue:
[builder:: MixinBuilder forClassDeclaration: self intermediate: ir.
ir builder: builder].
^builder
)
public isClassDeclarationMirror ^<Boolean> = (
^true
)
public isKindOfClassDeclarationMirror ^<Boolean> = (
^true
)
public name ^<Symbol> = (
^prvtIntermediate simpleName
)
public notifyExistingMirrors = (
instanceSide notifyExistingMirrors.
classSide notifyExistingMirrors.
)
public printOn: stm = (
stm nextPutAll: 'ClassDeclarationBuilder:'; nextPutAll: name
)
public prvtExistingMixin = (
^prvtIntermediate existingMixin
)
public prvtExistingMixin: m = (
^prvtIntermediate existingMixin: m
)
public qualifiedName ^<Symbol> = (
^prvtIntermediate qualifiedName
)
public reflectee ^<InstanceMixin | nil> = (
#BOGUS yourself.
(* If this builder corresponds to a class declaration that was already installed, we're okay. But this builder might represent a new class declaration that doesn't have a corresponding Mixin yet (#install will ensure it is filled, though). So, do we return a potentially nil reflectee or not support accessing a reflectee at all? *)
^prvtExistingMixin
)
public simpleName ^<Symbol> = (
self warnObsolete.
^self name
)
public source ^<String> = (
^String streamContents: [:stream | printClassDeclaration: self sourceOn: stream]
)
public ultimateInstallee ^<ClassDeclarationBuilder> = (
| topMostDirtyIntermediate <IntermediateClassDeclaration> |
topMostDirtyIntermediate:: prvtIntermediate.
[nil = topMostDirtyIntermediate enclosingClass] whileFalse:
[topMostDirtyIntermediate:: topMostDirtyIntermediate enclosingClass declaration].
^topMostDirtyIntermediate builder
)
) : (
public fromSource: src <String> ^<ClassDeclarationBuilder> = (
^self fromUnitSource: 'Newspeak3 ''Uncategorized'' ', src
)
public fromUnitSource: source <String> ^<ClassDeclarationBuilder> = (
| ir <IntermediateClassDeclaration> builder |
ir:: compiler compileClassSource: source within: nil.
builder:: fromIntermediate: ir forExistingMixin: nil.
ir builder: builder.
^builder
)
public reflecting: mixin <InstanceMixin> ^<ClassDeclarationBuilder> = (
| ir builder |
assert: [mixin isMixin & mixin isMeta not] message: 'Provide an instance-side mixin'.
ir:: buildIntermediateFor: mixin.
builder:: self fromIntermediate: ir forExistingMixin: mixin.
ir builder: builder.
^builder
)
)
public class ClassDeclarationMirror reflecting: mixin <Mixin> = Mirror reflecting: mixin (
(* A class declaration defines the instance and class sides, and a header. Each side comprises methods and nested classes. The header provides a superclass clauses, a primary factory a class comment and an instance initializer.
This mirror provides a view of a class declaration based on its runtime representation in the Newspeak image running on Squeak. To create an instance, provide the instance-side mixin. The mirror can obtain all necessary information from that. *)
|
instanceSideLazy
classSideLazy
|assert: [mixin isMixin & mixin isMeta not] message: 'Provide an instance-side mixin') (
public = other = (
^other isKindOfClassDeclarationMirror and: [self reflectee == other reflectee]
)
public accessModifier ^ <Symbol> = (
(* Finding the accessModifier of a class Declaration is hell. ClassDeclarationBuilders are
constructed from a mixin. Mixins, by definition, do not know with which accessModifier they
are applied with. So, we ask the enclosing mixin instead and assume that top-level mixins can
only be applied as public. *)
^reflectee accessModifier
)
public applyToObject ^<ClassMirror> = (
nil = reflectee enclosingMixin ifFalse: [^notTopLevel].
^ClassMirror reflecting: (reflectee apply: Object withName: reflectee simpleName)
)
public asBuilder = (
^ClassDeclarationBuilder reflecting: reflectee
)
public classSide ^ <MixinMirror> = (
classSideLazy ifNil: [
classSideLazy:: MixinMirror reflecting: reflectee classMixin].
^classSideLazy
)
public compilationUnitSource ^<String> = (
^String streamContents: [:stream |
stream nextPutAll: 'Newspeak3'; cr.
stream nextPutAll: header category asString printString; cr.
printClassDeclaration: self sourceOn: stream]
)
public definingMixin ^ <MixinMirror> = (
| enclosing <InstanceMixin> |
enclosing:: reflectee enclosingMixin.
nil == enclosing ifTrue: [^nil].
^MixinMirror reflecting: enclosing mixin
)
public enclosingClass ^ <ClassDeclarationMirror> = (
| enclosing <InstanceMixin> |
enclosing:: reflectee enclosingMixin.
nil == enclosing ifTrue: [^nil].
^ClassDeclarationMirror reflecting: enclosing mixin
)
public hasBody ^ <Boolean> = (
(* Does my reflectee have a class body *)
(* A bit of a hack, until we have clean per-mixin metadata *)
^instanceSide initializer metadata at: #hasBody ifAbsent: [true].
)
public hash = (
^reflectee identityHash
)
public header ^ <ClassHeaderMirror> = (
^ClassHeaderMirror reflecting: reflectee
)
public instanceSide ^ <MixinMirror> = (
instanceSideLazy ifNil: [
instanceSideLazy:: MixinMirror reflecting: reflectee].
^instanceSideLazy
)
public isClassDeclarationMirror ^<Boolean> = (
^true
)
public isKindOfClassDeclarationMirror ^<Boolean> = (
^true
)
public name ^ <Symbol> = (
^self simpleName
)
public qualifiedName ^ <Symbol> = (
^reflectee qualifiedName
)
public simpleName ^ <Symbol> = (
^reflectee simpleName
)
public source ^<String> = (
^String streamContents: [:stream | printClassDeclaration: self sourceOn: stream]
)
) : (
)
class ClassHeaderBuilder forIntermediate: ir = (|
protected intermediate <ClassDeclarationBuilder> = ir.
|) (
public category ^<Symbol> = (
^intermediate category ifNil: ['Uncategorized']
)
public classComment ^<String> = (
^(parser classHeader parseString: source) classComment
)
public classComment: newClassComment <String> = (
| ast token newHeaderSource |
ast:: grammar TypedNS3Grammar new classHeader parseString: source.
token:: (ast at: 8) at: 2.
newHeaderSource::
(source copyFrom: 1 to: token start + 2),
newClassComment,
(source copyFrom: token end - 1 to: source size).
intermediate
headerSource: newHeaderSource;
comment: newClassComment.
)
public name ^<Symbol> = (
^intermediate name
)
public name: newSimpleName <Symbol> = (
| declaration <ClassDeclarationBuilder> token newClassSource newIR |
declaration:: intermediate builder.
(* check for name conflict with sibling members *)
intermediate isTopLevel ifFalse:
[ | existing |
existing:: intermediate builder enclosingClass instanceSide nestedClasses findMirrorNamed: newSimpleName.
(nil = existing or: [existing = intermediate builder])
ifFalse: [^Error signal: 'A sibling already exists with the name ', newSimpleName]].
(* patch header source with the new name *)
token:: (grammar TypedNS3Grammar new classHeader parseString: source) at: 3.
newClassSource::
(intermediate isTopLevel
(* only top-level classes can specify categories *)
ifTrue: [ 'Newspeak3 ''', category, ''' ' ]
ifFalse: [ ' ' ]),
(source copyFrom: 1 to: token start - 1) ,
newSimpleName ,
(source copyFrom: token end + 1 to: source size).
(* compile etc *)
declaration enclosingClass. (* For side effect. *)
newIR:: compiler
compileClassHeader: newClassSource
within: intermediate enclosingClass.
intermediate simpleName: newIR simpleName.
intermediate headerSource: newIR headerSource.
intermediate factoryName: newIR factoryName.
intermediate comment: newIR comment.
intermediate accessor: newIR accessor.
intermediate factory: newIR factory.
intermediate initializers: newIR initializers.
intermediate instanceSide slots: newIR instanceSide slots.
)
public primaryFactory ^ <MethodBuiler> = (
| factoryName intermediateMethod |
factoryName:: intermediate factoryName.
intermediateMethod:: intermediate classSide methods
detect: [:im | im simpleName = factoryName].
^MethodBuilder reflecting: intermediateMethod in: intermediate builder classSide
)
public source ^<String> = (
^intermediate headerSource
)
public source: newHeaderSource <String> = (
intermediate builder headerFromSource: newHeaderSource
)
) : (
)
class ClassHeaderMirror reflecting: mixin <Mixin> = Mirror reflecting: mixin (
(* A class header defines the class' name, primary factory, superclass clause, class comment and instance initializer (slots and init expressions).
This mirror provides access to a class header based on the runtime representation. *)
) (
public category ^ <Symbol> = (
^reflectee category
)
public classComment ^ <String> = (
^(parser classHeader parseString: source) classComment
)
public declaration ^<ClassDeclarationMirror> = (
^ClassDeclarationMirror reflecting: self reflectee
)
public initializer = (
#BOGUS.
reflectee methodDictionary keysAndValuesDo:
[:selector :method |
(isSubinitializerSelector: selector) ifTrue:
[^MethodMirror reflecting: method]].
halt.
)
public isClassHeaderMirror ^<Boolean> = (
^true
)
public isKindOfClassHeaderMirror ^<Boolean> = (
^true
)
public name ^ <Symbol> = (
^reflectee name
)
public preamble ^<Symbol> = (
(* Foo factory = SuperFoo superFactory *)
| headerAst |
headerAst:: parser classHeader parseString: source.
^source copyFrom: headerAst start to: headerAst superConstructorCall end
)
public primaryFactory ^ <MethodMirror> = (
^MethodMirror reflecting: (reflectee classMixin
methodDict at: primaryFactoryName)
)
public primaryFactoryName ^ <Symbol> = (
^reflectee cachedConstructorName
)
public qualifiedName ^ <Symbol> = (
^reflectee qualifiedName
)
public sendsSelector: sel <Symbol> ^<Boolean> = (
(* This is incomplete for the quick send selectors. *)
(* Look through instance initializers. *)
reflectee methodDictionary keysAndValuesDo:
[:selector :method |
(isSubinitializerSelector: selector) ifTrue:
[(method hasLiteral: sel) ifTrue: [^true]]].
(* Look at the nested class accessor, if nested. *)
reflectee enclosingMixin ifNotNil: [:parent |
((parent compiledMethodAt: simpleName) hasLiteral: sel) ifTrue: [^true]].
^false
)
public simpleName ^ <Symbol> = (
^fullyQualifiedNameToSimple: name
)
public slots ^ <MirrorGroup[SlotDeclarationMirror]> = (
^ImmutableMirrorGroup group:
((reflectee slots
reject: [:ea | ea first includes: "`"])
collect: [:ea |
(SlotDeclarationMirror named: ea first mutable: ea second accessModifier: ea last)
mixin: reflectee; yourself
])
)
public source ^ <String> = (
^reflectee cachedHeaderSource
)
public superclassClause ^ <SendAST> = (
| ast <ClassHeaderAST> |
ast:: parser classHeader parseString: source.
^ast superCall.
)
) : (
)
public class ClassMirror reflecting: c <Behavior> = Mirror reflecting: c () (
public = other = (
^other isKindOfClassMirror and: [vmmirror is: reflectee identicalTo: other reflectee]
)
public allSuperclasses ^ <List[ClassMirror]> = (
| klass <ClassMirror> superclasses <List[ClassMirror]> |
superclasses:: List new.
klass:: superclass.
[klass isNil] whileFalse: [
superclasses add: klass.
klass:: klass superclass.
].
^superclasses
)
public canUnderstand: selector <Symbol> ^<Boolean> = (
(mixin canUnderstand: selector) ifTrue: [^true].
superclass
ifNil: [^false]
ifNotNil: [^superclass canUnderstand: selector].
)
computeMirrorGroup: mgAccessor <[:Mirror | MirrorGroup]> ^ <MirrorGroup> = (
(* Return a mirror group based on the mixins group and those of all superclasses. The argument mgAccessor extracts a mirror group from any mirror provided to it. *)
| mg <MirrorGroup> |
mg:: MutableMirrorGroup group: {} within: self.
(allSuperclasses reverse add: self; yourself) do:
[:klass <ClassMirror> |
mg addAllMirrors: (mgAccessor value: klass mixin)].
^ImmutableMirrorGroup group: mg
)
public enclosingObject ^<ObjectMirror> = (
^ObjectMirror reflecting:
(reflectee language isNewspeakLanguage3
ifTrue: [reflectee enclosingObjectSlot]
ifFalse: [nil])
)
public hash = (
^vmmirror identityHashOf: reflectee
)
public isKindOfClassMirror = (
^true
)
public isMeta ^ <Boolean> = (
^mixin isMeta
)
public methods ^<MirrorGroup[MethodMirror]> = (
^computeMirrorGroup: [:r | r methods]
)
public mixin ^<MixinMirror> = (
^MixinMirror reflecting: reflectee mixin
)
public name ^ <Symbol> = (
^reflectee name
)
public nestedClasses ^ <MirrorGroup[ClassDeclarationMirror]> = (
^computeMirrorGroup: [:r | r nestedClasses]
)
public simpleName ^<Symbol> = (
^mixin simpleName
)
public slots ^<MirrorGroup[SlotMirror]> = (
^computeMirrorGroup: [:r <MixinMirror> | r slots].
)
public superclass ^<ClassMirror> = (
reflectee superclass isNil ifTrue: [^nil].
^ClassMirror reflecting: reflectee superclass
)
public transientSlots ^<MirrorGroup[TransientSlotMirror]> = (
^computeMirrorGroup: [:r | r transientSlots].
)
) : (
)
class ImmutableMirrorGroupInMixin group: mirrorz in: mixinMirror = ImmutableMirrorGroup group: mirrorz (|
public enclosingMixin <MixinMirror> = mixinMirror.
public definingMixin <MixinMirror> = mixinMirror.
public channelForChanges <Duct> = Duct new.
|channelForChanges beWeak owner: self) (
private notifyAddedMirror: newMirror = (
channelForChanges send: (MirrorAddedEvent forNewMirror: newMirror)
)
private notifyRemovedMirror: oldMirror = (
channelForChanges send: (MirrorRemovedEvent forOldMirror: oldMirror)
)
private notifyReplacedMirror: oldMirror with: newMirror = (
channelForChanges send: (MirrorReplacedEvent from: oldMirror to: newMirror)
)
public updateToContain: actualMirrors <Collection[Mirror]> = (
| mirrorNames |
#ACCESSBOGUS.
mirrorNames:: actualMirrors collect: [:newMirror |
| oldMirror |
oldMirror:: findMirrorNamed: newMirror simpleName.
oldMirror == nil ifTrue: [
mirrors addLast: newMirror.
notifyAddedMirror: newMirror.
] ifFalse: [
oldMirror = newMirror ifTrue: [
(* Unchanged *)
] ifFalse: [
mirrors at: (mirrors indexOf: oldMirror) put: newMirror.
notifyReplacedMirror: oldMirror with: newMirror.
]
].
newMirror simpleName.
].
mirrors copy do: [:oldMirror |
(mirrorNames includes: oldMirror simpleName) ifFalse: [
mirrors remove: oldMirror.
notifyRemovedMirror: oldMirror.
].
].
)
) : (
)
class InitializerMethodMirror reflecting: m <MethodReference> = MethodMirror reflecting: m (
(* Initializers are distinnct in Newspeak and therefore require distinct mirrors.
A mirror on an initilaizer is based upon a method, but in practice the implementation of
the initializer is often spread across multiple methods. The exact scheme will vary based
on details of the implementation. The knowledge of these details should be limited to the
compiler and the mirror system. Indeed, the purpose of this class is to abstract those
details so that tools like the IDE need not be aware of them.
*)
) (
public isInitializerMethodMirror ^ <Boolean> = (
^true
)
public source ^ <String> = (
^definingMixin declaration header source
)
) : (
)
public class MethodBuilder reflecting: ir in: mb = (|
public prvtIntermediate <IntermediateMethod> = ir.
prvtMixinBuilder <MixinBuilder> = mb.
|) (
public accessModifier ^<Symbol> = (
^prvtIntermediate accessModifier
)
public category ^<Symbol> = (
^prvtIntermediate category
)
public category: cat <String | Symbol> = (
^prvtIntermediate category: cat asSymbol
)
public definingMixin ^<MixinBuilder> = (
^prvtMixinBuilder
)
public name ^<Symbol> = (
^prvtIntermediate selector
)
public reflectee ^<CompiledMethod> = (
#BOGUS yourself.
(* Poorly defined: If this a new method, the compiled method retrieved from the low level method would not be able to answer with its source. This is because in Squeak, the compiled method has to ask for it from its class, which will fail if the method is not yet installed. Perhaps it would be better to not give access to this. *)
(* ^lowLevelMethodMirror compiledMethod *)
FAIL.
)
public selector ^<Symbol> = (
^name
)
public simpleName ^<Symbol> = (
^name
)
public source ^<String> = (
^prvtIntermediate method
ifNil: [prvtIntermediate source]
ifNotNil: [:it | it getSource]
)
) : (
)
public class MethodMirror mirroring: m <CompiledMethod> = Mirror reflecting: m (
(* A basic mirror for a language level method. *)
) (
public = other = (
^other isKindOfMethodMirror
and: [reflectee == other reflectee]
)
public accessModifier ^<Symbol> = (
reflectee isProtected ifTrue: [^#protected].
reflectee isPrivate ifTrue: [^#private].
^#public
)
public category ^<Symbol> = (
^reflectee methodClass ifNotNil: [:mc| mc organization categoryOfElement: name]
)
public definingClass = (
^reflectee methodClass
)
public definingMixin ^<MixinMirror> = (
^MixinMirror reflecting: reflectee methodClass mixin
)
public hash = (
^reflectee identityHash
)
public isExpression ^<Boolean> = (
^reflectee properties at: #isExpression ifAbsent: [false].
)
public isInitializer ^ <Boolean> = (
^(isSubinitializerSelector: reflectee selector)
)
public isKindOfMethodMirror ^<Boolean> = (
^true
)
public isMethodMirror ^<Boolean> = (
^true
)
public metadata = (
^reflectee properties
)
public name ^<Symbol> = (
^reflectee selector
)
public sendsSelector: sel <Symbol> ^<Boolean> = (
(* This is incomplete for the quick send selectors. *)
^reflectee hasLiteral: sel
)
public simpleName = (
^name
)
public source ^<String> = (
| sourceAvailable |
sourceAvailable:: (reflectee properties includesKey: #source) or: [(reflectee trailer hasSourcePointer)].
^sourceAvailable ifTrue: [reflectee getSource asString] ifFalse: [nil]
)
) : (
public reflecting: method <CompiledMethod> = (
^(isSubinitializerSelector: method selector)
ifTrue: [InitializerMethodMirror reflecting: method]
ifFalse: [self mirroring: method]
)
)
class Mirror reflecting: r <Object> = (
(* Top of the Mirror hierarchy. An abstract class. *)
|
public reflectee <Object> = r.
|) (
public = other = (
^self class = other class and: [self reflectee == other reflectee]
)
public hash = (
^reflectee identityHash
)
public isMirror ^<Boolean> = (
^true
)
public printOn: stm = (
stm nextPutAll: class simpleName.
stm nextPutAll: ' reflecting: '.
reflectee printOn: stm.
)
) : (
)
class MirrorAddedEvent forNewMirror: m = MirrorEvent (
(* Sent by a mirror group on its channelForUpdates when a new mirror has been added. *)
|
public newMirror = m.
|) (
isMirrorAddedEvent ^<Boolean> = (
^true
)
) : (
)
class MirrorEvent = (
|
|) (
isMirrorEvent ^<Boolean> = (
^true
)
) : (
)
class MirrorRemovedEvent forOldMirror: m = MirrorEvent (
(* Sent by a mirror group on its channelForUpdates when an existing mirror has been removed. *)
|
public oldMirror = m.
|) (
isMirrorRemovedEvent ^<Boolean> = (
^true
)
) : (
)
class MirrorReplacedEvent from: oldM to: newM = MirrorEvent (
(* Sent by a mirror group on its channelForUpdates when an existing mirror has been replaced by a new mirror. *)
|
public oldMirror = oldM.
public newMirror = newM.
|) (
isMirrorReplacedEvent ^<Boolean> = (
^true
)
) : (
)
public class MixinBuilder forClassDeclaration: cbd intermediate: mixinIR = (
(* The mirror builder for mixins. See MixinMirror. *)
|
private intermediate <IntermediateMixin> = mixinIR.
public methods <MutableMethodGroup> = MutableMethodGroup group: (methodsFrom: mixinIR) within: mixinIR.
public nestedClasses <MutableNestedClassGroup> = MutableNestedClassGroup group: (nestedClassesFrom: mixinIR) within: mixinIR.
public transientSlots = MutableTransientSlotGroup group: (transientSlotsFrom: mixinIR) within: mixinIR.
|) (
public canUnderstand: selector <Symbol> ^<Boolean> = (
(* Slot, method, nested class, or factory method *)
(methods includesMirrorNamed: selector) ifTrue: [^true].
(nestedClasses includesMirrorNamed: selector) ifTrue: [^true].
(slots includesMirrorNamed: selector) ifTrue: [^true].
(selector last = ":") ifTrue: [
#BOGUS. (* Someday immutable slots will be enforced and this won't be quite right *)
(slots includesMirrorNamed: (selector allButLast: 1)) ifTrue: [^true].
].
isMeta ifTrue: [
declaration header primaryFactory simpleName = selector ifTrue: [^true].
].
^false
)
public checkNameConflictsForSlot: slotName <Symbol> mutable: isMutable <Boolean> = (
(nestedClasses includesMirrorNamed: slotName)
ifTrue: [^Error signal: 'Class already has nested class named ', slotName].
(methods includesMirrorNamed: slotName)
ifTrue: [^Error signal: 'Class already has method named ', slotName].
(isMutable and: [methods includesMirrorNamed: slotName, ':'])
ifTrue: [^Error signal: 'Class already has method named ', slotName].
)
public declaration ^<ClassDeclarationBuilder> = (
^intermediate declaration builder
)
public isKindOfMixinMirror ^<Boolean> = (
^true
)
public isMeta ^<Boolean> = (
^intermediate isMeta
)
methodsFrom: mixinIR <IntermediateMixin> ^<List[MethodBuilder]> = (
^(mixinIR methods
reject: [:m <LowLevelMethodMirror> | m isSynthetic])
collect: [:m <LowLevelMethodMirror> | MethodBuilder reflecting: m in: self]
)
nestedClassesFrom: mixinIR <IntermediateMixin> ^<List[ClassDeclarationBuilder]> = (
^mixinIR nestedClasses collect: [:nestedClassIR <IntermediateNestedClass> | | nc nestedMixin |
(* The existingMixin will be nil if this is a new (uninstalled) class declaration. *)
(* MUST COMPUTE THIS BEFORE CONSTRUCTING! Cannot be filled in after constructing the nested CDB or CDs with more than one level of nested will not be attached to their existing mixin! *)
declaration reflectee ifNotNil: [:mixin |
nestedMixin:: mixin nestedMixins at: nestedClassIR simpleName ifAbsent: [].
].
nc:: (ClassDeclarationBuilder fromIntermediate: nestedClassIR forExistingMixin: nestedMixin).
nestedClassIR builder: nc.
nc
]
)
public notifyExistingMirrors = (
isMeta
ifTrue: [mixinChanged: declaration reflectee classMixin]
ifFalse: [mixinChanged: declaration reflectee].
nestedClasses do: [:ea | ea notifyExistingMirrors].
)
public slots ^ <ImmutableMirrorGroup[SlotMirror]> = (
| slotMirrors <Collection[SlotMirror]> |
slotMirrors:: (intermediate slots collect:
[:each <IntermediateSlotDeclaration> |
SlotDeclarationMirror
named: each name
mutable: each isMutable
accessModifier: each accessModifier])
reject:
[:each <SlotDeclarationMirror> | each name includes: "`"].
^ImmutableMirrorGroup group: slotMirrors
)
transientSlotsFrom: mixinIR <IntermediateMixin> ^<List[TransientSlotBuilder]> = (
^mixinIR transientSlots
collect: [:each | TransientSlotBuilder reflecting: each in: self]
)
) : (
public reflecting: mxn <Mixin | ClassMixin> ^<MixinBuilder> = (
^mxn isMeta
ifTrue: [(ClassDeclarationBuilder reflecting: mxn instanceMixin) classSide]
ifFalse: [(ClassDeclarationBuilder reflecting: mxn) instanceSide]
)
)
public class MixinMirror reflecting: m <Mixin> = Mirror reflecting: m (
(* A mixin is the difference between a class and its superclass: a set of additional methods, slots and nested class declarations. Newspeak class declarations define an instance-side mixin and a class-side mixin, and Newspeak classes (other than Top) are all the result of mixin application.
What about mirroring the initializer?
Need to decide who does these things - the class declaration mirror or the mixin mirror. One should delegate to the other. *)
|
slotsLazy
methodsLazy
nestedClassesLazy
accessModifierLazy
transientSlotsLazy
|mixinBasedMirrors add: self) (
public = other <Object> ^<Boolean> = (
^other isKindOfMixinMirror and: [reflectee = other reflectee]
)
public accessModifier ^ <Symbol> = (
warnObsolete.
accessModifierLazy ifNil: [
accessModifierLazy:: computeAccessModifiers].
^accessModifierLazy
)
public applications ^<Set[ClassMirror]> = (
^reflectee applications collect: [:ea | ClassMirror reflecting: ea]
)
public buildIntermediateTransientSlots = (
^transientSlots collect: [:each | each buildIntermediate]
)
public canUnderstand: selector <Symbol> ^<Boolean> = (
(* Slot, method, nested class, or factory method *)
(methods includesMirrorNamed: selector) ifTrue: [^true].
(nestedClasses includesMirrorNamed: selector) ifTrue: [^true].
(slots includesMirrorNamed: selector) ifTrue: [^true].
(selector last = ":") ifTrue: [
#BOGUS. (* Someday immutable slots will be enforced and this won't be quite right *)
(slots includesMirrorNamed: (selector allButLast: 1)) ifTrue: [^true].
].
isMeta ifTrue: [
declaration header primaryFactory simpleName = selector ifTrue: [^true].
].
^false
)
public classMixin ^ <MixinMirror> = (
isMeta
ifFalse: [^MixinMirror reflecting: reflectee classMixin]
ifTrue: [^MixinMirror reflecting: Metaclass mixin].
)
computeAccessModifiers = (
^((ClassDeclarationMirror reflecting: reflectee) accessModifier)
)
computeMethods ^ <Collection[MethodMirror]> = (
^((reflectee methodDictionary values
reject: [:each <CompiledMethod> | TransientSlotMirror isMethodForTransientSlot: each in: reflectee ])
collect: [:each <CompiledMethod> | MethodMirror reflecting: each])
reject: [:each <MethodMirror> | each reflectee isSynthetic].
)
computeNestedClasses ^<Collection[ClassDeclarationMirror]> = (
| metadata |
isMeta ifTrue: [^{}].
^reflectee nestedMixins values
collect: [:each | ClassDeclarationMirror reflecting: each]
)
computeSlots ^ <Collection[SlotDeclarationMirror]> = (
^(reflectee slots
reject: [:iv <{String. Boolean. Symbol}> | iv first includes: "`"])
collect: [:iv <{String. Boolean. Symbol}> |
(SlotDeclarationMirror named: iv first mutable: iv second accessModifier: iv last)
mixin: reflectee;
yourself
].
)
computeTransientSlots ^ <Collection[TransientSlotMirror]> = (
^TransientSlotMirror mirrorsIn: self.
)
public declaration ^ <ClassDeclarationMirror> = (
^ClassDeclarationMirror
reflecting: (isMeta ifFalse: [reflectee] ifTrue: [reflectee instanceMixin])
)
public enclosingMixin ^ <MixinMirror> = (
| enclosing |
enclosing:: reflectee enclosingMixin.
nil = enclosing ifTrue: [^nil].
^MixinMirror reflecting: enclosing
)
public initializer ^ <MethodMirror> = (
reflectee methodDictionary keysAndValuesDo:
[:selector :method |
(isSubinitializerSelector: selector) ifTrue:
[^MethodMirror reflecting: method]].
^nil
)
public isKindOfMixinMirror ^<Boolean> = (
^true
)
public isMeta ^ <Boolean> = (
^self reflectee isMeta
)
public isMethodMirrorForTransientSlot: aMethodMirror = (
^TransientSlotMirror isMethodForTransientSlot: aMethodMirror reflectee in: aMethodMirror reflectee methodClass.
)
public methods ^ <MirrorGroup[MethodMirror]> = (
methodsLazy ifNil: [
methodsLazy:: ImmutableMirrorGroupInMixin group: computeMethods in: self].
^methodsLazy
)
public mixinChanged = (
#ACCESSBOGUS.
slotsLazy ifNotNil: [:it | it updateToContain: computeSlots].
methodsLazy ifNotNil: [:it | it updateToContain: computeMethods].
nestedClassesLazy ifNotNil: [:it | it updateToContain: computeNestedClasses].
accessModifierLazy ifNotNil: [:it | accessModifierLazy:: computeAccessModifiers].
transientSlotsLazy ifNotNil: [:it | it updateToContain: computeTransientSlots].
)
public name ^ <Symbol> = (
^reflectee simpleName
)
public nestedClasses ^ <MirrorGroup[ClassDeclarationMirror]> = (
nestedClassesLazy ifNil: [
nestedClassesLazy:: ImmutableMirrorGroupInMixin group: computeNestedClasses in: self].