-
Notifications
You must be signed in to change notification settings - Fork 76
/
vm.primitives.js
2240 lines (2239 loc) · 121 KB
/
vm.primitives.js
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
"use strict";
/*
* Copyright (c) 2013-2024 Vanessa Freudenberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
Object.subclass('Squeak.Primitives',
'initialization', {
initialize: function(vm, display) {
this.vm = vm;
this.oldPrims = !this.vm.image.hasClosures;
this.allowAccessBeyondSP = this.oldPrims;
this.deferDisplayUpdates = false;
this.semaphoresToSignal = [];
this.initDisplay(display);
this.initAtCache();
this.initModules();
this.initPlugins();
if (vm.image.isSpur) {
this.charFromInt = this.charFromIntSpur;
this.charToInt = this.charToIntSpur;
this.identityHash = this.identityHashSpur;
}
},
initDisplay: function(display) {
// Placeholder (can be replaced by a display module at runtime, before starting the Squeak interpreter)
this.display = display;
},
initModules: function() {
this.loadedModules = {};
this.builtinModules = {};
this.patchModules = {};
this.interpreterProxy = new Squeak.InterpreterProxy(this.vm);
},
initPlugins: function() {
// Empty placeholder (can be replaced by a plugins module at runtime, before starting the Squeak interpreter)
}
},
'dispatch', {
quickSendOther: function(rcvr, lobits) {
// returns true if it succeeds
this.success = true;
switch (lobits) {
case 0x0: return this.popNandPushIfOK(2, this.objectAt(true,true,false)); // at:
case 0x1: return this.popNandPushIfOK(3, this.objectAtPut(true,true,false)); // at:put:
case 0x2: return this.popNandPushIfOK(1, this.objectSize(true)); // size
//case 0x3: return false; // next
//case 0x4: return false; // nextPut:
//case 0x5: return false; // atEnd
case 0x6: return this.popNandPushBoolIfOK(2, this.vm.stackValue(1) === this.vm.stackValue(0)); // ==
case 0x7: return this.popNandPushIfOK(1,this.vm.getClass(this.vm.top())); // class
case 0x8: return this.popNandPushIfOK(2,this.doBlockCopy()); // blockCopy:
case 0x9: return this.primitiveBlockValue(0); // value
case 0xA: return this.primitiveBlockValue(1); // value:
//case 0xB: return false; // do:
//case 0xC: return false; // new
//case 0xD: return false; // new:
//case 0xE: return false; // x
//case 0xF: return false; // y
}
return false;
},
doPrimitive: function(index, argCount, primMethod) {
this.success = true;
switch (index) {
// Integer Primitives (0-19)
case 1: return this.popNandPushIntIfOK(argCount+1,this.stackInteger(1) + this.stackInteger(0)); // Integer.add
case 2: return this.popNandPushIntIfOK(argCount+1,this.stackInteger(1) - this.stackInteger(0)); // Integer.subtract
case 3: return this.popNandPushBoolIfOK(argCount+1, this.stackInteger(1) < this.stackInteger(0)); // Integer.less
case 4: return this.popNandPushBoolIfOK(argCount+1, this.stackInteger(1) > this.stackInteger(0)); // Integer.greater
case 5: return this.popNandPushBoolIfOK(argCount+1, this.stackInteger(1) <= this.stackInteger(0)); // Integer.leq
case 6: return this.popNandPushBoolIfOK(argCount+1, this.stackInteger(1) >= this.stackInteger(0)); // Integer.geq
case 7: return this.popNandPushBoolIfOK(argCount+1, this.stackInteger(1) === this.stackInteger(0)); // Integer.equal
case 8: return this.popNandPushBoolIfOK(argCount+1, this.stackInteger(1) !== this.stackInteger(0)); // Integer.notequal
case 9: return this.popNandPushIntIfOK(argCount+1,this.stackInteger(1) * this.stackInteger(0)); // Integer.multiply *
case 10: return this.popNandPushIntIfOK(argCount+1,this.vm.quickDivide(this.stackInteger(1),this.stackInteger(0))); // Integer.divide / (fails unless exact)
case 11: return this.popNandPushIntIfOK(argCount+1,this.vm.mod(this.stackInteger(1),this.stackInteger(0))); // Integer.mod \\
case 12: return this.popNandPushIntIfOK(argCount+1,this.vm.div(this.stackInteger(1),this.stackInteger(0))); // Integer.div //
case 13: return this.popNandPushIntIfOK(argCount+1,this.stackInteger(1) / this.stackInteger(0) | 0); // Integer.quo
case 14: return this.popNandPushIfOK(argCount+1,this.doBitAnd()); // SmallInt.bitAnd
case 15: return this.popNandPushIfOK(argCount+1,this.doBitOr()); // SmallInt.bitOr
case 16: return this.popNandPushIfOK(argCount+1,this.doBitXor()); // SmallInt.bitXor
case 17: return this.popNandPushIfOK(argCount+1,this.doBitShift()); // SmallInt.bitShift
case 18: return this.primitiveMakePoint(argCount, false);
case 19: return false; // Guard primitive for simulation -- *must* fail
// LargeInteger Primitives (20-39)
// 32-bit logic is aliased to Integer prims above
case 20: this.vm.warnOnce("missing primitive: 20 (primitiveRemLargeIntegers)"); return false;
case 21: this.vm.warnOnce("missing primitive: 21 (primitiveAddLargeIntegers)"); return false;
case 22: this.vm.warnOnce("missing primitive: 22 (primitiveSubtractLargeIntegers)"); return false;
case 23: return this.primitiveLessThanLargeIntegers(argCount);
case 24: return this.primitiveGreaterThanLargeIntegers(argCount);
case 25: return this.primitiveLessOrEqualLargeIntegers(argCount);
case 26: return this.primitiveGreaterOrEqualLargeIntegers(argCount);
case 27: return this.primitiveEqualLargeIntegers(argCount);
case 28: return this.primitiveNotEqualLargeIntegers(argCount);
case 29: this.vm.warnOnce("missing primitive: 29 (primitiveMultiplyLargeIntegers)"); return false;
case 30: this.vm.warnOnce("missing primitive: 30 (primitiveDivideLargeIntegers)"); return false;
case 31: this.vm.warnOnce("missing primitive: 31 (primitiveModLargeIntegers)"); return false;
case 32: this.vm.warnOnce("missing primitive: 32 (primitiveDivLargeIntegers)"); return false;
case 33: this.vm.warnOnce("missing primitive: 33 (primitiveQuoLargeIntegers)"); return false;
case 34: this.vm.warnOnce("missing primitive: 34 (primitiveBitAndLargeIntegers)"); return false;
case 35: this.vm.warnOnce("missing primitive: 35 (primitiveBitOrLargeIntegers)"); return false;
case 36: this.vm.warnOnce("missing primitive: 36 (primitiveBitXorLargeIntegers)"); return false;
case 37: this.vm.warnOnce("missing primitive: 37 (primitiveBitShiftLargeIntegers)"); return false;
case 38: return this.popNandPushIfOK(argCount+1, this.objectAt(false,false,false)); // Float basicAt
case 39: return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,false,false)); // Float basicAtPut
// Float Primitives (40-59)
case 40: return this.popNandPushFloatIfOK(argCount+1,this.stackInteger(0)); // primitiveAsFloat
case 41: return this.popNandPushFloatIfOK(argCount+1,this.stackFloat(1)+this.stackFloat(0)); // Float +
case 42: return this.popNandPushFloatIfOK(argCount+1,this.stackFloat(1)-this.stackFloat(0)); // Float -
case 43: return this.popNandPushBoolIfOK(argCount+1, this.stackFloat(1)<this.stackFloat(0)); // Float <
case 44: return this.popNandPushBoolIfOK(argCount+1, this.stackFloat(1)>this.stackFloat(0)); // Float >
case 45: return this.popNandPushBoolIfOK(argCount+1, this.stackFloat(1)<=this.stackFloat(0)); // Float <=
case 46: return this.popNandPushBoolIfOK(argCount+1, this.stackFloat(1)>=this.stackFloat(0)); // Float >=
case 47: return this.popNandPushBoolIfOK(argCount+1, this.stackFloat(1)===this.stackFloat(0)); // Float =
case 48: return this.popNandPushBoolIfOK(argCount+1, this.stackFloat(1)!==this.stackFloat(0)); // Float !=
case 49: return this.popNandPushFloatIfOK(argCount+1,this.stackFloat(1)*this.stackFloat(0)); // Float.mul
case 50: return this.popNandPushFloatIfOK(argCount+1,this.safeFDiv(this.stackFloat(1),this.stackFloat(0))); // Float.div
case 51: return this.popNandPushIfOK(argCount+1,this.floatAsSmallInt(this.stackFloat(0))); // Float.asInteger
case 52: return this.popNandPushFloatIfOK(argCount+1,this.floatFractionPart(this.stackFloat(0)));
case 53: return this.popNandPushIntIfOK(argCount+1, this.frexp_exponent(this.stackFloat(0)) - 1); // Float.exponent
case 54: return this.popNandPushFloatIfOK(argCount+1, this.ldexp(this.stackFloat(1), this.stackFloat(0))); // Float.timesTwoPower
case 55: return this.popNandPushFloatIfOK(argCount+1, Math.sqrt(this.stackFloat(0))); // SquareRoot
case 56: return this.popNandPushFloatIfOK(argCount+1, Math.sin(this.stackFloat(0))); // Sine
case 57: return this.popNandPushFloatIfOK(argCount+1, Math.atan(this.stackFloat(0))); // Arctan
case 58: return this.popNandPushFloatIfOK(argCount+1, Math.log(this.stackFloat(0))); // LogN
case 59: return this.popNandPushFloatIfOK(argCount+1, Math.exp(this.stackFloat(0))); // Exp
// Subscript and Stream Primitives (60-67)
case 60: return this.popNandPushIfOK(argCount+1, this.objectAt(false,false,false)); // basicAt:
case 61: return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,false,false)); // basicAt:put:
case 62: return this.popNandPushIfOK(argCount+1, this.objectSize(false)); // size
case 63: return this.popNandPushIfOK(argCount+1, this.objectAt(false,true,false)); // String.basicAt:
case 64: return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,true,false)); // String.basicAt:put:
case 65: this.vm.warnOnce("missing primitive: 65 (primitiveNext)"); return false;
case 66: this.vm.warnOnce("missing primitive: 66 (primitiveNextPut)"); return false;
case 67: this.vm.warnOnce("missing primitive: 67 (primitiveAtEnd)"); return false;
// StorageManagement Primitives (68-79)
case 68: return this.popNandPushIfOK(argCount+1, this.objectAt(false,false,true)); // Method.objectAt:
case 69: return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,false,true)); // Method.objectAt:put:
case 70: return this.popNandPushIfOK(argCount+1, this.instantiateClass(this.stackNonInteger(0), 0)); // Class.new
case 71: return this.popNandPushIfOK(argCount+1, this.instantiateClass(this.stackNonInteger(1), this.stackPos32BitInt(0))); // Class.new:
case 72: return this.primitiveArrayBecome(argCount, false, true); // one way, do copy hash
case 73: return this.popNandPushIfOK(argCount+1, this.objectAt(false,false,true)); // instVarAt:
case 74: return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,false,true)); // instVarAt:put:
case 75: return this.popNandPushIfOK(argCount+1, this.identityHash(this.stackNonInteger(0))); // Object.identityHash
case 76: return this.primitiveStoreStackp(argCount); // (Blue Book: primitiveAsObject)
case 77: return this.popNandPushIfOK(argCount+1, this.someInstanceOf(this.stackNonInteger(0))); // Class.someInstance
case 78: return this.popNandPushIfOK(argCount+1, this.nextInstanceAfter(this.stackNonInteger(0))); // Object.nextInstance
case 79: return this.primitiveNewMethod(argCount); // Compiledmethod.new
// Control Primitives (80-89)
case 80: return this.popNandPushIfOK(argCount+1,this.doBlockCopy()); // blockCopy:
case 81: return this.primitiveBlockValue(argCount); // BlockContext.value
case 82: return this.primitiveBlockValueWithArgs(argCount); // BlockContext.valueWithArguments:
case 83: return this.vm.primitivePerform(argCount); // Object.perform:(with:)*
case 84: return this.vm.primitivePerformWithArgs(argCount, false); // Object.perform:withArguments:
case 85: return this.primitiveSignal(); // Semaphore.wait
case 86: return this.primitiveWait(); // Semaphore.wait
case 87: return this.primitiveResume(); // Process.resume
case 88: return this.primitiveSuspend(); // Process.suspend
case 89: return this.vm.flushMethodCache(); //primitiveFlushCache
// Input/Output Primitives (90-109)
case 90: return this.primitiveMousePoint(argCount); // mousePoint
case 91: return this.primitiveTestDisplayDepth(argCount); // cursorLocPut in old images
case 92: this.vm.warnOnce("missing primitive: 92 (primitiveSetDisplayMode)"); return false;
case 93: return this.primitiveInputSemaphore(argCount);
case 94: return this.primitiveGetNextEvent(argCount);
case 95: return this.primitiveInputWord(argCount);
case 96: return this.namedPrimitive('BitBltPlugin', 'primitiveCopyBits', argCount);
case 97: return this.primitiveSnapshot(argCount);
case 98: return this.primitiveStoreImageSegment(argCount);
case 99: return this.primitiveLoadImageSegment(argCount);
case 100: return this.vm.primitivePerformWithArgs(argCount, true); // Object.perform:withArguments:inSuperclass: (Blue Book: primitiveSignalAtTick)
case 101: return this.primitiveBeCursor(argCount); // Cursor.beCursor
case 102: return this.primitiveBeDisplay(argCount); // DisplayScreen.beDisplay
case 103: return this.primitiveScanCharacters(argCount);
case 104: this.vm.warnOnce("missing primitive: 104 (primitiveDrawLoop)"); return false;
case 105: return this.popNandPushIfOK(argCount+1, this.doStringReplace()); // string and array replace
case 106: return this.primitiveScreenSize(argCount); // actualScreenSize
case 107: return this.primitiveMouseButtons(argCount); // Sensor mouseButtons
case 108: return this.primitiveKeyboardNext(argCount); // Sensor kbdNext
case 109: return this.primitiveKeyboardPeek(argCount); // Sensor kbdPeek
// System Primitives (110-119)
case 110: return this.popNandPushBoolIfOK(argCount+1, this.vm.stackValue(1) === this.vm.stackValue(0)); // ==
case 111: return this.popNandPushIfOK(argCount+1, this.vm.getClass(this.vm.top())); // Object.class
case 112: return this.popNandPushIfOK(argCount+1, this.vm.image.bytesLeft()); //primitiveBytesLeft
case 113: return this.primitiveQuit(argCount);
case 114: return this.primitiveExitToDebugger(argCount);
case 115: return this.primitiveChangeClass(argCount);
case 116: return this.vm.flushMethodCacheForMethod(this.vm.top()); // after Squeak 2.2 uses 119
case 117: return this.doNamedPrimitive(argCount, primMethod); // named prims
case 118: return this.primitiveDoPrimitiveWithArgs(argCount);
case 119: return this.vm.flushMethodCacheForSelector(this.vm.top()); // before Squeak 2.3 uses 116
// Miscellaneous Primitives (120-149)
case 120: return this.primitiveCalloutToFFI(argCount, primMethod);
case 121: return this.primitiveImageName(argCount); //get+set imageName
case 122: return this.primitiveReverseDisplay(argCount); // Blue Book: primitiveImageVolume
case 123: this.vm.warnOnce("missing primitive: 123 (primitiveValueUninterruptably)"); return false;
case 124: return this.popNandPushIfOK(argCount+1, this.registerSemaphore(Squeak.splOb_TheLowSpaceSemaphore));
case 125: return this.popNandPushIfOK(argCount+1, this.setLowSpaceThreshold());
case 126: return this.primitiveDeferDisplayUpdates(argCount);
case 127: return this.primitiveShowDisplayRect(argCount);
case 128: return this.primitiveArrayBecome(argCount, true, true); // both ways, do copy hash
case 129: return this.popNandPushIfOK(argCount+1, this.vm.image.specialObjectsArray); //specialObjectsOop
case 130: return this.primitiveFullGC(argCount);
case 131: return this.primitivePartialGC(argCount);
case 132: return this.popNandPushBoolIfOK(argCount+1, this.pointsTo(this.stackNonInteger(1), this.vm.top())); //Object.pointsTo
case 133: return this.popNIfOK(argCount); //TODO primitiveSetInterruptKey
case 134: return this.popNandPushIfOK(argCount+1, this.registerSemaphore(Squeak.splOb_TheInterruptSemaphore));
case 135: return this.popNandPushIfOK(argCount+1, this.millisecondClockValue());
case 136: return this.primitiveSignalAtMilliseconds(argCount); //Delay signal:atMs:();
case 137: return this.popNandPushIfOK(argCount+1, this.secondClock()); // seconds since Jan 1, 1901
case 138: return this.popNandPushIfOK(argCount+1, this.someObject()); // Object.someObject
case 139: return this.popNandPushIfOK(argCount+1, this.nextObject(this.vm.top())); // Object.nextObject
case 140: return this.primitiveBeep(argCount);
case 141: return this.primitiveClipboardText(argCount);
case 142: return this.popNandPushIfOK(argCount+1, this.makeStString(this.filenameToSqueak(Squeak.vmPath)));
case 143: // short at and shortAtPut
case 144: return this.primitiveShortAtAndPut(argCount);
case 145: return this.primitiveConstantFill(argCount);
case 146: return this.namedPrimitive('JoystickTabletPlugin', 'primitiveReadJoystick', argCount);
case 147: return this.namedPrimitive('BitBltPlugin', 'primitiveWarpBits', argCount);
case 148: return this.popNandPushIfOK(argCount+1, this.vm.image.clone(this.vm.top())); //shallowCopy
case 149: return this.primitiveGetAttribute(argCount);
// File Primitives (150-169)
case 150: if (this.oldPrims) return this.primitiveFileAtEnd(argCount);
case 151: if (this.oldPrims) return this.primitiveFileClose(argCount);
case 152: if (this.oldPrims) return this.primitiveFileGetPosition(argCount);
case 153: if (this.oldPrims) return this.primitiveFileOpen(argCount);
case 154: if (this.oldPrims) return this.primitiveFileRead(argCount);
case 155: if (this.oldPrims) return this.primitiveFileSetPosition(argCount);
case 156: if (this.oldPrims) return this.primitiveFileDelete(argCount);
case 157: if (this.oldPrims) return this.primitiveFileSize(argCount);
break; // fail 150-157 if fell through
case 158: if (this.oldPrims) return this.primitiveFileWrite(argCount);
else this.vm.warnOnce("missing primitive: 158 (primitiveCompareWith)"); return false;
case 159: if (this.oldPrims) return this.primitiveFileRename(argCount);
return this.popNandPushIntIfOK(argCount+1, this.stackSigned53BitInt(0) * 1664525 & 0xFFFFFFF); // primitiveHashMultiply
case 160: if (this.oldPrims) return this.primitiveDirectoryCreate(argCount);
else return this.primitiveAdoptInstance(argCount);
case 161: if (this.oldPrims) return this.primitiveDirectoryDelimitor(argCount);
this.vm.warnOnce("missing primitive: 161 (primitiveSetIdentityHash)"); return false;
case 162: if (this.oldPrims) return this.primitiveDirectoryLookup(argCount);
break; // fail
case 163: if (this.oldPrims) return this.primitiveDirectoryDelete(argCount);
else this.vm.warnOnce("missing primitive: 163 (primitiveGetImmutability)"); return false;
case 164: return this.popNandPushIfOK(argCount+1, this.vm.trueObj); // Fake primitiveSetImmutability
case 165:
case 166: return this.primitiveIntegerAtAndPut(argCount);
case 167: return false; // Processor.yield
case 168: return this.primitiveCopyObject(argCount);
case 169: if (this.oldPrims) return this.primitiveDirectorySetMacTypeAndCreator(argCount);
else return this.popNandPushBoolIfOK(argCount+1, this.vm.stackValue(1) !== this.vm.stackValue(0)); //new: primitiveNotIdentical
// Sound Primitives (170-199)
case 170: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundStart', argCount);
else return this.primitiveAsCharacter(argCount);
case 171: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundStartWithSemaphore', argCount);
else return this.popNandPushIfOK(argCount+1, this.stackNonInteger(0).hash); //primitiveImmediateAsInteger
case 172: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundStop', argCount);
this.vm.warnOnce("missing primitive: 172 (primitiveFetchMourner)");
return this.popNandPushIfOK(argCount, this.vm.nilObj); // do not fail
case 173: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundAvailableSpace', argCount);
else return this.popNandPushIfOK(argCount+1, this.objectAt(false,false,true)); // slotAt:
case 174: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundPlaySamples', argCount);
else return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,false,true)); // slotAt:put:
case 175: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundPlaySilence', argCount);
else if (!this.vm.image.isSpur) {
this.vm.warnOnce("primitive 175 called in non-spur image"); // workaround for Cuis
return this.popNandPushIfOK(argCount+1, this.identityHash(this.stackNonInteger(0)));
} else return this.popNandPushIfOK(argCount+1, this.behaviorHash(this.stackNonInteger(0)));
case 176: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primWaveTableSoundmixSampleCountintostartingAtpan', argCount);
else return this.popNandPushIfOK(argCount+1, this.vm.image.isSpur ? 0x3FFFFF : 0xFFF); // primitiveMaxIdentityHash
case 177: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primFMSoundmixSampleCountintostartingAtpan', argCount);
return this.popNandPushIfOK(argCount+1, this.allInstancesOf(this.stackNonInteger(0)));
case 178: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primPluckedSoundmixSampleCountintostartingAtpan', argCount);
return false; // allObjectsDo fallback code is just as fast and uses less memory
case 179: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primSampledSoundmixSampleCountintostartingAtpan', argCount);
break; // fail
case 180: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primitiveMixFMSound', argCount);
return false; // growMemoryByAtLeast
case 181: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primitiveMixPluckedSound', argCount);
return this.primitiveSizeInBytesOfInstance(argCount);
case 182: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'oldprimSampledSoundmixSampleCountintostartingAtleftVolrightVol', argCount);
return this.primitiveSizeInBytes(argCount);
case 183: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primitiveApplyReverb', argCount);
else return this.primitiveIsPinned(argCount);
case 184: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primitiveMixLoopedSampledSound', argCount);
else return this.primitivePin(argCount);
case 185: if (this.oldPrims) return this.namedPrimitive('SoundGenerationPlugin', 'primitiveMixSampledSound', argCount);
else return this.primitiveExitCriticalSection(argCount);
case 186: if (this.oldPrims) break; // unused
else return this.primitiveEnterCriticalSection(argCount);
case 187: if (this.oldPrims) break; // unused
else return this.primitiveTestAndSetOwnershipOfCriticalSection(argCount);
case 188: if (this.oldPrims) break; // unused
else return this.primitiveExecuteMethodArgsArray(argCount);
case 189: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundInsertSamples', argCount);
return false; // fail to fall back to primitiveExecuteMethodArgsArray (188)
case 190: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundStartRecording', argCount);
case 191: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundStopRecording', argCount);
case 192: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundGetRecordingSampleRate', argCount);
case 193: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundRecordSamples', argCount);
case 194: if (this.oldPrims) return this.namedPrimitive('SoundPlugin', 'primitiveSoundSetRecordLevel', argCount);
break; // fail 190-194 if fell through
case 195: return false; // Context.findNextUnwindContextUpTo:
case 196: return false; // Context.terminateTo:
case 197: return false; // Context.findNextHandlerContextStarting
case 198: return false; // MarkUnwindMethod (must fail)
case 199: return false; // MarkHandlerMethod (must fail)
// Networking Primitives (200-229)
case 200: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveInitializeNetwork', argCount);
else return this.primitiveClosureCopyWithCopiedValues(argCount);
case 201: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverStartNameLookup', argCount);
else return this.primitiveClosureValue(argCount);
case 202: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverNameLookupResult', argCount);
else return this.primitiveClosureValue(argCount);
case 203: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverStartAddressLookup', argCount);
else return this.primitiveClosureValue(argCount);
case 204: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverAddressLookupResult', argCount);
else return this.primitiveClosureValue(argCount);
case 205: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverAbortLookup', argCount);
else return this.primitiveClosureValue(argCount);
case 206: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverLocalAddress', argCount);
else return this.primitiveClosureValueWithArgs(argCount);
case 207: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverStatus', argCount);
else return this.primitiveFullClosureValue(argCount);
case 208: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveResolverError', argCount);
else return this.primitiveFullClosureValueWithArgs(argCount);
case 209: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketCreate', argCount);
else return this.primitiveFullClosureValueNoContextSwitch(argCount);
case 210: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketDestroy', argCount);
else return this.popNandPushIfOK(argCount+1, this.objectAt(false,false,false)); // contextAt:
case 211: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketConnectionStatus', argCount);
else return this.popNandPushIfOK(argCount+1, this.objectAtPut(false,false,false)); // contextAt:put:
case 212: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketError', argCount);
else return this.popNandPushIfOK(argCount+1, this.objectSize(false)); // contextSize
case 213: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketLocalAddress', argCount);
case 214: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketLocalPort', argCount);
case 215: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketRemoteAddress', argCount);
case 216: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketRemotePort', argCount);
case 217: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketConnectToPort', argCount);
case 218: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketListenWithOrWithoutBacklog', argCount);
else return this.primitiveDoNamedPrimitive(argCount);
case 219: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketCloseConnection', argCount);
case 220: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketAbortConnection', argCount);
break; // fail 212-220 if fell through
case 221: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketReceiveDataBufCount', argCount);
else return this.primitiveClosureValueNoContextSwitch(argCount);
case 222: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketReceiveDataAvailable', argCount);
else return this.primitiveClosureValueNoContextSwitch(argCount);
case 223: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketSendDataBufCount', argCount);
case 224: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketSendDone', argCount);
case 225: if (this.oldPrims) return this.namedPrimitive('SocketPlugin', 'primitiveSocketAccept', argCount);
break; // fail 223-229 if fell through
// 225-229: unused
// Other Primitives (230-249)
case 230: return this.primitiveRelinquishProcessorForMicroseconds(argCount);
case 231: return this.primitiveForceDisplayUpdate(argCount);
case 232: this.vm.warnOnce("missing primitive: 232 (primitiveFormPrint)"); return false;
case 233: return this.primitiveSetFullScreen(argCount);
case 234: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveDecompressFromByteArray', argCount);
case 235: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveCompareString', argCount);
case 236: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveConvert8BitSigned', argCount);
case 237: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveCompressToByteArray', argCount);
break; // fail 234-237 if fell through
case 238: if (this.oldPrims) return this.namedPrimitive('SerialPlugin', 'primitiveSerialPortOpen', argCount);
else return this.namedPrimitive('FloatArrayPlugin', 'primitiveAt', argCount);
case 239: if (this.oldPrims) return this.namedPrimitive('SerialPlugin', 'primitiveSerialPortClose', argCount);
else return this.namedPrimitive('FloatArrayPlugin', 'primitiveAtPut', argCount);
case 240: if (this.oldPrims) return this.namedPrimitive('SerialPlugin', 'primitiveSerialPortWrite', argCount);
else return this.popNandPushIfOK(argCount+1, this.microsecondClockUTC());
case 241: if (this.oldPrims) return this.namedPrimitive('SerialPlugin', 'primitiveSerialPortRead', argCount);
else return this.popNandPushIfOK(argCount+1, this.microsecondClockLocal());
case 242: if (this.oldPrims) break; // unused
else return this.primitiveSignalAtUTCMicroseconds(argCount);
case 243: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveTranslateStringWithTable', argCount);
else this.vm.warnOnce("missing primitive: 243 (primitiveUpdateTimeZone)"); return false;
case 244: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveFindFirstInString' , argCount);
case 245: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveIndexOfAsciiInString', argCount);
case 246: if (this.oldPrims) return this.namedPrimitive('MiscPrimitivePlugin', 'primitiveFindSubstring', argCount);
break; // fail 243-246 if fell through
// 247: unused
case 248: return this.primitiveArrayBecome(argCount, false, false); // one way, do not copy hash
case 249: return this.primitiveArrayBecome(argCount, false, true); // one way, opt. copy hash
case 254: return this.primitiveVMParameter(argCount);
//MIDI Primitives (520-539)
case 521: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIClosePort', argCount);
case 522: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIGetClock', argCount);
case 523: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIGetPortCount', argCount);
case 524: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIGetPortDirectionality', argCount);
case 525: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIGetPortName', argCount);
case 526: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIOpenPort', argCount);
case 527: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIParameterGetOrSet', argCount);
case 528: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIRead', argCount);
case 529: return this.namedPrimitive('MIDIPlugin', 'primitiveMIDIWrite', argCount);
// 530-539: reserved for extended MIDI primitives
// Sound Codec Primitives
case 550: return this.namedPrimitive('ADPCMCodecPlugin', 'primitiveDecodeMono', argCount);
case 551: return this.namedPrimitive('ADPCMCodecPlugin', 'primitiveDecodeStereo', argCount);
case 552: return this.namedPrimitive('ADPCMCodecPlugin', 'primitiveEncodeMono', argCount);
case 553: return this.namedPrimitive('ADPCMCodecPlugin', 'primitiveEncodeStereo', argCount);
// External primitive support primitives (570-574)
// case 570: return this.primitiveFlushExternalPrimitives(argCount);
case 571: return this.primitiveUnloadModule(argCount);
case 572: return this.primitiveListBuiltinModule(argCount);
case 573: return this.primitiveListLoadedModule(argCount);
case 575: this.vm.warnOnce("missing primitive: 575 (primitiveHighBit)"); return false;
// this is not really a primitive, see findSelectorInClass()
case 576: return this.vm.primitiveInvokeObjectAsMethod(argCount, primMethod);
case 578: this.vm.warnOnce("missing primitive: 578 (primitiveSuspendAndBackupPC)"); return false; // see bit 5 of vmParameterAt: 65
}
console.error("primitive " + index + " not implemented yet");
return false;
},
namedPrimitive: function(modName, functionName, argCount) {
// duplicated in loadFunctionFrom()
var mod = modName === "" ? this : this.loadedModules[modName];
var justLoaded = false;
if (mod === undefined) { // null if earlier load failed
mod = this.loadModule(modName);
this.loadedModules[modName] = mod;
justLoaded = true;
}
var result = false;
var sp = this.vm.sp;
if (mod) {
this.interpreterProxy.argCount = argCount;
this.interpreterProxy.primitiveName = functionName;
var primitive = mod[functionName];
if (typeof primitive === "function") {
result = mod[functionName](argCount);
} else if (typeof primitive === "string") {
// allow late binding for built-ins
result = this[primitive](argCount);
} else {
this.vm.warnOnce("missing primitive: " + modName + "." + functionName);
}
} else if (justLoaded) {
if (this.success) this.vm.warnOnce("missing module: " + modName + " (" + functionName + ")");
else this.vm.warnOnce("failed to load module: " + modName + " (" + functionName + ")");
}
if ((result === true || (result !== false && this.success)) && this.vm.sp !== sp - argCount && !this.vm.frozen) {
this.vm.warnOnce("stack unbalanced after primitive " + modName + "." + functionName, "error");
}
if (result === true || result === false) return result;
return this.success;
},
doNamedPrimitive: function(argCount, primMethod) {
if (primMethod.pointersSize() < 2) return false;
var firstLiteral = primMethod.pointers[1]; // skip method header
if (firstLiteral.pointersSize() !== 4) return false;
this.primMethod = primMethod;
var moduleName = firstLiteral.pointers[0].bytesAsString();
var functionName = firstLiteral.pointers[1].bytesAsString();
return this.namedPrimitive(moduleName, functionName, argCount);
},
fakePrimitive: function(prim, retVal, argCount) {
// fake a named primitive
// prim and retVal need to be curried when used:
// this.fakePrimitive.bind(this, "Module.primitive", 42)
this.vm.warnOnce("faking primitive: " + prim);
if (retVal === undefined) this.vm.popN(argCount);
else this.vm.popNandPush(argCount+1, this.makeStObject(retVal));
return true;
},
},
'modules', {
loadModule: function(modName) {
var mod = Squeak.externalModules[modName] || this.builtinModules[modName] || this.loadModuleDynamically(modName);
if (!mod) return null;
if (this.patchModules[modName])
this.patchModule(mod, modName);
if (mod.setInterpreter) {
if (!mod.setInterpreter(this.interpreterProxy)) {
console.log("Wrong interpreter proxy version: " + modName);
return null;
}
}
var initFunc = mod.initialiseModule;
if (typeof initFunc === 'function') {
mod.initialiseModule();
} else if (typeof initFunc === 'string') {
// allow late binding for built-ins
this[initFunc]();
}
if (this.interpreterProxy.failed()) {
console.log("Module initialization failed: " + modName);
return null;
}
if (mod.getModuleName) modName = mod.getModuleName();
console.log("Loaded module: " + modName);
return mod;
},
loadModuleDynamically: function(modName) {
// Placeholder (can be replaced by a module loader at runtime, before starting the Squeak interpreter)
return undefined;
},
patchModule: function(mod, modName) {
var patch = this.patchModules[modName];
for (var key in patch)
mod[key] = patch[key];
},
unloadModule: function(modName) {
var mod = this.loadedModules[modName];
if (!modName || !mod|| mod === this) return null;
delete this.loadedModules[modName];
var unloadFunc = mod.unloadModule;
if (typeof unloadFunc === 'function') {
mod.unloadModule(this);
} else if (typeof unloadFunc === 'string') {
// allow late binding for built-ins
this[unloadFunc](this);
}
console.log("Unloaded module: " + modName);
return mod;
},
loadFunctionFrom: function(functionName, modName) {
// copy of namedPrimitive() returning the bound function instead of calling it
var mod = modName === "" ? this : this.loadedModules[modName];
if (mod === undefined) { // null if earlier load failed
mod = this.loadModule(modName);
this.loadedModules[modName] = mod;
}
if (!mod) return null;
var func = mod[functionName];
if (typeof func === "function") {
return func.bind(mod);
} else if (typeof func === "string") {
return (this[func]).bind(this);
}
this.vm.warnOnce("missing primitive: " + modName + "." + functionName);
return null;
},
primitiveUnloadModule: function(argCount) {
var moduleName = this.stackNonInteger(0).bytesAsString();
if (!moduleName) return false;
this.unloadModule(moduleName);
return this.popNIfOK(argCount);
},
primitiveListBuiltinModule: function(argCount) {
var index = this.stackInteger(0) - 1;
if (!this.success) return false;
var moduleNames = Object.keys(this.builtinModules);
return this.popNandPushIfOK(argCount + 1, this.makeStObject(moduleNames[index]));
},
primitiveListLoadedModule: function(argCount) {
var index = this.stackInteger(0) - 1;
if (!this.success) return false;
var moduleNames = [];
for (var key in this.loadedModules) {
var module = this.loadedModules[key];
if (module) {
var moduleName = module.getModuleName ? module.getModuleName() : key;
moduleNames.push(moduleName);
}
}
return this.popNandPushIfOK(argCount + 1, this.makeStObject(moduleNames[index]));
},
},
'stack access', {
popNIfOK: function(nToPop) {
if (!this.success) return false;
this.vm.popN(nToPop);
return true;
},
pop2andPushBoolIfOK: function(bool) {
this.vm.success = this.success;
return this.vm.pop2AndPushBoolResult(bool);
},
popNandPushBoolIfOK: function(nToPop, bool) {
if (!this.success) return false;
this.vm.popNandPush(nToPop, bool ? this.vm.trueObj : this.vm.falseObj);
return true;
},
popNandPushIfOK: function(nToPop, returnValue) {
if (!this.success || returnValue == null) return false;
this.vm.popNandPush(nToPop, returnValue);
return true;
},
popNandPushIntIfOK: function(nToPop, returnValue) {
if (!this.success || !this.vm.canBeSmallInt(returnValue)) return false;
this.vm.popNandPush(nToPop, returnValue);
return true;
},
popNandPushFloatIfOK: function(nToPop, returnValue) {
if (!this.success) return false;
this.vm.popNandPush(nToPop, this.makeFloat(returnValue));
return true;
},
stackNonInteger: function(nDeep) {
return this.checkNonInteger(this.vm.stackValue(nDeep));
},
stackInteger: function(nDeep) {
return this.checkSmallInt(this.vm.stackValue(nDeep));
},
stackPos32BitInt: function(nDeep) {
return this.positive32BitValueOf(this.vm.stackValue(nDeep));
},
pos32BitIntFor: function(signed32) {
// Return the 32-bit quantity as an unsigned 32-bit integer
if (signed32 >= 0 && signed32 <= Squeak.MaxSmallInt) return signed32;
var lgIntClass = this.vm.specialObjects[Squeak.splOb_ClassLargePositiveInteger],
lgIntObj = this.vm.instantiateClass(lgIntClass, 4),
bytes = lgIntObj.bytes;
for (var i=0; i<4; i++)
bytes[i] = (signed32>>>(8*i)) & 255;
return lgIntObj;
},
pos53BitIntFor: function(longlong) {
// Return the quantity as an unsigned 64-bit integer
if (longlong <= 0xFFFFFFFF) return this.pos32BitIntFor(longlong);
if (longlong > 0x1FFFFFFFFFFFFF) {
console.warn("Out of range: pos53BitIntFor(" + longlong + ")");
this.success = false;
return 0;
};
var sz = longlong <= 0xFFFFFFFFFF ? 5 :
longlong <= 0xFFFFFFFFFFFF ? 6 :
7;
var lgIntClass = this.vm.specialObjects[Squeak.splOb_ClassLargePositiveInteger],
lgIntObj = this.vm.instantiateClass(lgIntClass, sz),
bytes = lgIntObj.bytes;
for (var i = 0; i < sz; i++) {
bytes[i] = longlong & 255;
longlong /= 256;
}
return lgIntObj;
},
stackSigned32BitInt: function(nDeep) {
var stackVal = this.vm.stackValue(nDeep);
if (typeof stackVal === "number") { // SmallInteger
return stackVal;
}
if (stackVal.bytesSize() !== 4) {
this.success = false;
return 0;
}
var bytes = stackVal.bytes,
value = 0;
for (var i = 0, f = 1; i < 4; i++, f *= 256)
value += bytes[i] * f;
if (this.isA(stackVal, Squeak.splOb_ClassLargePositiveInteger) && value <= 0x7FFFFFFF)
return value;
if (this.isA(stackVal, Squeak.splOb_ClassLargeNegativeInteger) && -value >= -0x80000000)
return -value;
this.success = false;
return 0;
},
signed32BitIntegerFor: function(signed32) {
// Return the 32-bit quantity as a signed 32-bit integer
if (signed32 >= Squeak.MinSmallInt && signed32 <= Squeak.MaxSmallInt) return signed32;
var negative = signed32 < 0,
unsigned = negative ? -signed32 : signed32,
lgIntClass = negative ? Squeak.splOb_ClassLargeNegativeInteger : Squeak.splOb_ClassLargePositiveInteger,
lgIntObj = this.vm.instantiateClass(this.vm.specialObjects[lgIntClass], 4),
bytes = lgIntObj.bytes;
for (var i=0; i<4; i++)
bytes[i] = (unsigned>>>(8*i)) & 255;
return lgIntObj;
},
stackFloat: function(nDeep) {
return this.checkFloat(this.vm.stackValue(nDeep));
},
stackBoolean: function(nDeep) {
return this.checkBoolean(this.vm.stackValue(nDeep));
},
stackSigned53BitInt:function(nDeep) {
var stackVal = this.vm.stackValue(nDeep);
if (typeof stackVal === "number") { // SmallInteger
return stackVal;
}
var n = stackVal.bytesSize();
if (n <= 7) {
var bytes = stackVal.bytes,
value = 0;
for (var i = 0, f = 1; i < n; i++, f *= 256)
value += bytes[i] * f;
if (value <= 0x1FFFFFFFFFFFFF) {
if (this.isA(stackVal, Squeak.splOb_ClassLargePositiveInteger))
return value;
if (this.isA(stackVal, Squeak.splOb_ClassLargeNegativeInteger))
return -value;
}
}
this.success = false;
return 0;
},
},
'numbers', {
doBitAnd: function() {
var rcvr = this.stackPos32BitInt(1);
var arg = this.stackPos32BitInt(0);
if (!this.success) return 0;
return this.pos32BitIntFor(rcvr & arg);
},
doBitOr: function() {
var rcvr = this.stackPos32BitInt(1);
var arg = this.stackPos32BitInt(0);
if (!this.success) return 0;
return this.pos32BitIntFor(rcvr | arg);
},
doBitXor: function() {
var rcvr = this.stackPos32BitInt(1);
var arg = this.stackPos32BitInt(0);
if (!this.success) return 0;
return this.pos32BitIntFor(rcvr ^ arg);
},
doBitShift: function() {
// SmallInts are handled by the bytecode,
// so rcvr is a LargeInteger
var rcvr = this.stackPos32BitInt(1);
var arg = this.stackInteger(0);
if (!this.success) return 0;
// we're not using safeShift() here because we want the full 32 bits
// and we know the receiver is unsigned
var result;
if (arg < 0) {
if (arg < -31) return 0; // JS would treat arg=32 as arg=0
result = rcvr >>> -arg;
} else {
if (arg > 31) {
this.success = false; // rcvr is never 0
return 0;
}
result = rcvr << arg;
// check for lost bits by seeing if computation is reversible
if ((result >>> arg) !== rcvr) {
this.success = false;
return 0;
}
}
return this.pos32BitIntFor(result);
},
safeFDiv: function(dividend, divisor) {
if (divisor === 0.0) {
this.success = false;
return 1.0;
}
return dividend / divisor;
},
floatAsSmallInt: function(float) {
var truncated = float >= 0 ? Math.floor(float) : Math.ceil(float);
return this.ensureSmallInt(truncated);
},
floatFractionPart: function(float) {
if (-9007199254740991 /* -((1 << 53) - 1) */ <= float && float <= 9007199254740991 /* (1 << 53) - 1 */) {
return float - Math.floor(float);
} else {
this.success = false;
return 0;
}
},
frexp_exponent: function(value) {
// frexp separates a float into its mantissa and exponent
if (value == 0.0) return 0; // zero is special
var data = new DataView(new ArrayBuffer(8));
data.setFloat64(0, value); // for accessing IEEE-754 exponent bits
var bits = (data.getUint32(0) >>> 20) & 0x7FF;
if (bits === 0) { // we have a subnormal float (actual zero was handled above)
// make it normal by multiplying a large number
data.setFloat64(0, value * Math.pow(2, 64));
// access its exponent bits, and subtract the large number's exponent
bits = ((data.getUint32(0) >>> 20) & 0x7FF) - 64;
}
var exponent = bits - 1022; // apply bias
// mantissa = this.ldexp(value, -exponent) // not needed for Squeak
return exponent;
},
ldexp: function(mantissa, exponent) {
// construct a float as mantissa * 2 ^ exponent
// avoid multiplying by Infinity and Zero and rounding errors
// by splitting the exponent (thanks to Nicolas Cellier)
// 3 multiplies needed for e.g. ldexp(5e-324, 1023+1074)
var steps = Math.min(3, Math.ceil(Math.abs(exponent) / 1023));
var result = mantissa;
for (var i = 0; i < steps; i++)
result *= Math.pow(2, Math.floor((exponent + i) / steps));
return result;
},
primitiveLessThanLargeIntegers: function(argCount) {
return this.popNandPushBoolIfOK(argCount+1, this.stackSigned53BitInt(1) < this.stackSigned53BitInt(0));
},
primitiveGreaterThanLargeIntegers: function(argCount) {
return this.popNandPushBoolIfOK(argCount+1, this.stackSigned53BitInt(1) > this.stackSigned53BitInt(0));
},
primitiveLessOrEqualLargeIntegers: function(argCount) {
return this.popNandPushBoolIfOK(argCount+1, this.stackSigned53BitInt(1) <= this.stackSigned53BitInt(0));
},
primitiveGreaterOrEqualLargeIntegers: function(argCount) {
return this.popNandPushBoolIfOK(argCount+1, this.stackSigned53BitInt(1) >= this.stackSigned53BitInt(0));
},
primitiveEqualLargeIntegers: function(argCount) {
return this.popNandPushBoolIfOK(argCount+1, this.stackSigned53BitInt(1) === this.stackSigned53BitInt(0));
},
primitiveNotEqualLargeIntegers: function(argCount) {
return this.popNandPushBoolIfOK(argCount+1, this.stackSigned53BitInt(1) !== this.stackSigned53BitInt(0));
},
},
'utils', {
floatOrInt: function(obj) {
if (obj.isFloat) return obj.float;
if (typeof obj === "number") return obj; // SmallInteger
return 0;
},
positive32BitValueOf: function(obj) {
if (typeof obj === "number") { // SmallInteger
if (obj >= 0)
return obj;
this.success = false;
return 0;
}
if (!this.isA(obj, Squeak.splOb_ClassLargePositiveInteger) || obj.bytesSize() !== 4) {
this.success = false;
return 0;
}
var bytes = obj.bytes,
value = 0;
for (var i = 0, f = 1; i < 4; i++, f *= 256)
value += bytes[i] * f;
return value;
},
checkFloat: function(maybeFloat) { // returns a number and sets success
if (maybeFloat.isFloat)
return maybeFloat.float;
if (typeof maybeFloat === "number") // SmallInteger
return maybeFloat;
this.success = false;
return 0.0;
},
checkSmallInt: function(maybeSmall) { // returns an int and sets success
if (typeof maybeSmall === "number")
return maybeSmall;
this.success = false;
return 0;
},
checkNonInteger: function(obj) { // returns a SqObj and sets success
if (typeof obj !== "number")
return obj;
this.success = false;
return this.vm.nilObj;
},
checkBoolean: function(obj) { // returns true/false and sets success
if (obj.isTrue) return true;
if (obj.isFalse) return false;
return this.success = false;
},
indexableSize: function(obj) {
if (typeof obj === "number") return -1; // -1 means not indexable
return obj.indexableSize(this);
},
isA: function(obj, knownClass) {
return obj.sqClass === this.vm.specialObjects[knownClass];
},
isKindOf: function(obj, knownClass) {
var classOrSuper = obj.sqClass;
var theClass = this.vm.specialObjects[knownClass];
while (!classOrSuper.isNil) {
if (classOrSuper === theClass) return true;
classOrSuper = classOrSuper.pointers[Squeak.Class_superclass];
}
return false;
},
isAssociation: function(obj) {
return typeof obj !== "number" && obj.pointersSize() == 2;
},
ensureSmallInt: function(number) {
if (number === (number|0) && this.vm.canBeSmallInt(number))
return number;
this.success = false;
return 0;
},
charFromInt: function(ascii) {
var charTable = this.vm.specialObjects[Squeak.splOb_CharacterTable];
var char = charTable.pointers[ascii];
if (char) return char;
var charClass = this.vm.specialObjects[Squeak.splOb_ClassCharacter];
char = this.vm.instantiateClass(charClass, 0);
char.pointers[0] = ascii;
return char;
},
charFromIntSpur: function(unicode) {
return this.vm.image.getCharacter(unicode);
},
charToInt: function(obj) {
return obj.pointers[0];
},
charToIntSpur: function(obj) {
return obj.hash;
},
makeFloat: function(value) {
var floatClass = this.vm.specialObjects[Squeak.splOb_ClassFloat];
var newFloat = this.vm.instantiateClass(floatClass, 2);
newFloat.float = value;
return newFloat;
},
makeLargeIfNeeded: function(integer) {
return this.vm.canBeSmallInt(integer) ? integer : this.makeLargeInt(integer);
},
makeLargeInt: function(integer) {
if (integer < 0) throw Error("negative large ints not implemented yet");
if (integer > 0xFFFFFFFF) throw Error("large large ints not implemented yet");
return this.pos32BitIntFor(integer);
},
makePointWithXandY: function(x, y) {
var pointClass = this.vm.specialObjects[Squeak.splOb_ClassPoint];
var newPoint = this.vm.instantiateClass(pointClass, 0);
newPoint.pointers[Squeak.Point_x] = x;
newPoint.pointers[Squeak.Point_y] = y;
return newPoint;
},
makeStArray: function(jsArray, proxyClass) {
var array = this.vm.instantiateClass(this.vm.specialObjects[Squeak.splOb_ClassArray], jsArray.length);
for (var i = 0; i < jsArray.length; i++)
array.pointers[i] = this.makeStObject(jsArray[i], proxyClass);
return array;
},
makeStByteArray: function(jsArray) {
var array = this.vm.instantiateClass(this.vm.specialObjects[Squeak.splOb_ClassByteArray], jsArray.length);
for (var i = 0; i < jsArray.length; i++)
array.bytes[i] = jsArray[i] & 0xff;
return array;
},
makeStString: function(jsString) {
var stString = this.vm.instantiateClass(this.vm.specialObjects[Squeak.splOb_ClassString], jsString.length);
for (var i = 0; i < jsString.length; ++i)
stString.bytes[i] = jsString.charCodeAt(i) & 0xFF;
return stString;
},
makeStStringFromBytes: function(bytes, zeroTerminated) {
var length = bytes.length;
if (zeroTerminated) {
length = bytes.indexOf(0);
if (length < 0) length = bytes.length;
}
var stString = this.vm.instantiateClass(this.vm.specialObjects[Squeak.splOb_ClassString], length);
for (var i = 0; i < length; ++i)
stString.bytes[i] = bytes[i];
return stString;
},
makeStObject: function(obj, proxyClass) {
if (obj === undefined || obj === null) return this.vm.nilObj;
if (obj === true) return this.vm.trueObj;
if (obj === false) return this.vm.falseObj;
if (obj.sqClass) return obj;
if (typeof obj === "number")
if (obj === (obj|0)) return this.makeLargeIfNeeded(obj);
else return this.makeFloat(obj);
if (proxyClass) { // wrap in JS proxy instance
var stObj = this.vm.instantiateClass(proxyClass, 0);
stObj.jsObject = obj;
return stObj;
}
// A direct test of the buffer's constructor doesn't work on Safari 10.0.
if (typeof obj === "string" || obj.constructor.name === "Uint8Array") return this.makeStString(obj);
if (obj.constructor.name === "Array") return this.makeStArray(obj);
throw Error("cannot make smalltalk object");
},
pointsTo: function(rcvr, arg) {
if (!rcvr.pointers) return false;
return rcvr.pointers.indexOf(arg) >= 0;
},
asUint8Array: function(buffer) {
// A direct test of the buffer's constructor doesn't work on Safari 10.0.
if (buffer.constructor.name === "Uint8Array") return buffer;
if (buffer.constructor.name === "ArrayBuffer") return new Uint8Array(buffer);
if (typeof buffer === "string") {
var array = new Uint8Array(buffer.length);
for (var i = 0; i < buffer.length; i++)
array[i] = buffer.charCodeAt(i);
return array;
}
throw Error("unknown buffer type");
},
filenameToSqueak: function(unixpath) {
var slash = unixpath[0] !== "/" ? "/" : "",
filepath = "/SqueakJS" + slash + unixpath; // add SqueakJS
if (this.emulateMac)
filepath = ("Macintosh HD" + filepath) // add Mac volume
.replace(/\//g, "€").replace(/:/g, "/").replace(/€/g, ":"); // substitute : for /
return filepath;
},
filenameFromSqueak: function(filepath) {
var unixpath = !this.emulateMac ? filepath :