Christoph Thiede uploaded a new version of Kernel to project The Trunk: http://source.squeak.org/trunk/Kernel-ct.1556.mcz
==================== Summary ====================
Name: Kernel-ct.1556 Author: ct Time: 25 February 2024, 8:26:48.901593 pm UUID: 929047e6-a708-3540-97c5-aec212d87203 Ancestors: Kernel-ct.1555, Kernel-ct.1420, Kernel-ct.1451, Kernel-ct.1449, Kernel-ct.1490, Kernel-ct.1546, Kernel-ct.1547, Kernel-ct.1548
Merges several simulation fixes from the inbox. As always, please refer to the original versions to read their full messages.
Kernel-ct.1420: Aligns the error code for primitive 118 (primitiveDoPrimitiveWithArgs) during simulation with what the VM returns.
Kernel-ct.1451: Revises fallback code for primitive 188 (primitiveExecuteMethodArgsArray) in Object>>#withArgs:executeMethod: to eliminate any side effects to the method dictionary and add support for Objects as Methods.
Kernel-ct.1449: Fixes arity checks for objects as methods in simulation of primitive 83 (primitivePerform) and primitive 84 (primitivePerformWithArgs).
Kernel-ct.1490: In code simulation of perform primitives (primitive 83 (primitivePerform) and primitive 84 (primitivePerformWithArgs)), do not check numArgs of the selector or method to align behavior with VM. Revision: Remove the same check from simulation of primitive 100 (primitivePerformInSuperclass) as well.
Kernel-ct.1546: In the debugger/simulator, fixes stepping through #mustBeBoolean errors and restarting from primitive methods that no longer fail.
Kernel-ct.1547: When simulating #mustBeBoolean errors, send #mustBeBoolean instead of #mustBeBooleanIn: just like the VM.
Kernel-ct.1548: Resolves #mustBeBoolean errors for copies or proxies of true and false. Revision: Improve documentation.
=============== Diff against Kernel-ct.1555 ===============
Item was changed: ----- Method: Context>>doPrimitive:method:receiver:args: (in category 'private') ----- doPrimitive: primitiveIndex method: meth receiver: receiver args: arguments "Simulate a primitive method whose index is primitiveIndex. The simulated receiver and arguments are given as arguments to this message. If successful, push result and return resuming context, else ^ {errCode, PrimitiveFailToken}. Any primitive which provokes execution needs to be intercepted and simulated to avoid execution running away."
| value | "Judicious use of primitive 19 (a null primitive that doesn't do anything) prevents the debugger from entering various run-away activities such as spawning a new process, etc. Injudicious use results in the debugger not being able to debug interesting code, such as the debugger itself. Hence use primitive 19 with care :-)" "SystemNavigation new browseAllSelect: [:m| m primitive = 19]" primitiveIndex = 19 ifTrue: [ [self notify: ('The code being simulated is trying to control a process ({1}). Process controlling cannot be simulated. If you proceed, things may happen outside the observable area of the simulator.' translated format: {meth reference})] ifCurtailed: [self push: nil "Cheap fix of the context's internal state"]]. ((primitiveIndex between: 201 and: 222) and: [(self objectClass: receiver) includesBehavior: BlockClosure]) ifTrue: [(primitiveIndex = 206 or: [primitiveIndex = 208]) ifTrue: "[Full]BlockClosure>>valueWithArguments:" [^receiver simulateValueWithArguments: arguments first caller: self]. ((primitiveIndex between: 201 and: 209) "[Full]BlockClosure>>value[:value:...]" or: [primitiveIndex between: 221 and: 222]) ifTrue: "[Full]BlockClosure>>valueNoContextSwitch[:]" [^receiver simulateValueWithArguments: arguments caller: self]].
primitiveIndex = 83 ifTrue: "afr 9/11/1998 19:50" "Object>>perform:[with:...]" [| selector | selector := arguments at: 1 ifAbsent: [^ self class primitiveFailTokenFor: #'bad argument']. - (selector isSymbol and: [arguments size - 1 ~= selector numArgs]) ifTrue: - [^ self class primitiveFailTokenFor: #'bad number of arguments']. ^self send: selector to: receiver with: arguments allButFirst]. primitiveIndex = 84 ifTrue: "afr 9/11/1998 19:50 & eem 8/18/2009 17:04" "Object>>perform:withArguments:" [| selector args | arguments size = 2 ifFalse: [^ self class primitiveFailTokenFor: #'bad argument']. selector := arguments first. args := arguments second. args isArray ifFalse: [^ self class primitiveFailTokenFor: #'bad argument']. - (selector isSymbol and: [args size ~= selector numArgs]) ifTrue: - [^ self class primitiveFailTokenFor: #'bad number of arguments']. ^self send: selector to: receiver with: args]. primitiveIndex = 100 ifTrue: "eem 8/18/2009 16:57" "Object>>perform:withArguments:inSuperclass:" [| rcvr selector args superclass | arguments size caseOf: { [3] -> [ rcvr := receiver. selector := arguments first. args := arguments second. superclass := arguments third]. [4] -> ["mirror primitive" rcvr := arguments first. selector := arguments second. args := arguments third. superclass := arguments fourth] } otherwise: [^ self class primitiveFailTokenFor: #'bad number of arguments']. args isArray ifFalse: [^ self class primitiveFailTokenFor: #'bad argument']. - (selector isSymbol and: [args size ~= selector numArgs]) ifTrue: - [^ self class primitiveFailTokenFor: #'bad number of arguments']. ((self objectClass: rcvr) includesBehavior: superclass) ifFalse: [^ self class primitiveFailTokenFor: #'bad argument']. ^self send: selector to: rcvr with: args lookupIn: superclass].
"Mutex>>primitiveEnterCriticalSection Mutex>>primitiveTestAndSetOwnershipOfCriticalSection" (primitiveIndex = 186 or: [primitiveIndex = 187]) ifTrue: [| effective | effective := Processor activeProcess effectiveProcess. "active == effective" value := primitiveIndex = 186 ifTrue: [receiver primitiveEnterCriticalSectionOnBehalfOf: effective] ifFalse: [receiver primitiveTestAndSetOwnershipOfCriticalSectionOnBehalfOf: effective]. ^(self isPrimFailToken: value) ifTrue: [value] ifFalse: [self push: value]]. + + (primitiveIndex = 188 or: [primitiveIndex = 189]) ifTrue: - - primitiveIndex = 188 ifTrue: "Object>>withArgs:executeMethod: - CompiledMethod class>>receiver:withArguments:executeMethod: - VMMirror>>ifFail:object:with:executeMethod: et al" [| n args methodArg thisReceiver | + primitiveIndex caseOf: + {[188 "primitiveExecuteMethodArgsArray"] -> + ["Object>>withArgs:executeMethod: + CompiledMethod class>>receiver:withArguments:executeMethod: + VMMirror>>ifFail:object:with:executeMethod: et al" + ((n := arguments size) between: 2 and: 4) ifFalse: + [^self class primitiveFailTokenFor: #'unsupported operation']. + ((self objectClass: (args := arguments at: n - 1)) == Array + and: [(self objectClass: (methodArg := arguments at: n)) includesBehavior: CompiledMethod]) ifFalse: + [^self class primitiveFailTokenFor: #'bad argument']. + thisReceiver := arguments at: n - 2 ifAbsent: [receiver]]. + [189 "primitiveExecuteMethod"] -> + ["Object>>executeMethod: + Object>>with:...executeMethod:" + (arguments size > 0) ifFalse: + [^self class primitiveFailTokenFor: #'bad argument']. + ((self objectClass: (methodArg := arguments atLast: 1)) includesBehavior: CompiledMethod) ifFalse: + [^self class primitiveFailTokenFor: #'bad argument']. + args := arguments allButLast. + thisReceiver := receiver]}. - ((n := arguments size) between: 2 and: 4) ifFalse: - [^self class primitiveFailTokenFor: #'unsupported operation']. - ((self objectClass: (args := arguments at: n - 1)) == Array - and: [(self objectClass: (methodArg := arguments at: n)) includesBehavior: CompiledMethod]) ifFalse: - [^self class primitiveFailTokenFor: #'bad argument']. methodArg numArgs = args size ifFalse: [^self class primitiveFailTokenFor: #'bad number of arguments']. - thisReceiver := arguments at: n - 2 ifAbsent: [receiver]. methodArg primitive > 0 ifTrue: [methodArg isQuick ifTrue: [^self push: (methodArg valueWithReceiver: thisReceiver arguments: args)]. ^self doPrimitive: methodArg primitive method: methodArg receiver: thisReceiver args: args]. ^self activateMethod: methodArg withArgs: args receiver: thisReceiver].
primitiveIndex = 118 ifTrue: "[receiver:]tryPrimitive:withArgs:; avoid recursing in the VM" [(arguments size = 3 and: [(self objectClass: arguments second) == SmallInteger and: [(self objectClass: arguments last) == Array]]) ifTrue: [^self doPrimitive: arguments second method: meth receiver: arguments first args: arguments last]. (arguments size = 2 and: [(self objectClass: arguments first) == SmallInteger and: [(self objectClass: arguments last) == Array]]) ifFalse: + [^self class primitiveFailTokenFor: -3]. - [^self class primitiveFailTokenFor: nil]. ^self doPrimitive: arguments first method: meth receiver: receiver args: arguments last].
value := primitiveIndex = 120 "FFI method" ifTrue: [(meth literalAt: 1) tryInvokeWithArguments: arguments] ifFalse: [primitiveIndex = 117 "named primitives" ifTrue: [self tryNamedPrimitiveIn: meth for: receiver withArgs: arguments] ifFalse: "should use self receiver: receiver tryPrimitive: primitiveIndex withArgs: arguments but this is only in later VMs (and appears to be broken)" [receiver tryPrimitive: primitiveIndex withArgs: arguments]].
^(self isPrimFailToken: value) ifTrue: [value] ifFalse: [self push: value]!
Item was added: + ----- Method: Context>>executeMethod:forSelector:withArgs:receiver: (in category 'controlling') ----- + executeMethod: meth forSelector: selector withArgs: arguments receiver: rcvr + + | primIndex val ctxt | + (self objectClass: meth) isCompiledCodeClass ifFalse: + ["Object as Methods (OaM) protocol: 'The contract is that, when the VM encounters an ordinary object (rather than a compiled method) in the method dictionary during lookup, it sends it the special selector #run:with:in: providing the original selector, arguments, and receiver.'. DOI: 10.1145/2991041.2991062." + ^self send: #run:with:in: + to: meth + with: {selector. arguments. rcvr}]. + + meth numArgs = arguments size ifFalse: + [^ self class primitiveFailTokenFor: #'bad number of arguments']. + (primIndex := meth primitive) > 0 ifTrue: + [val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments. + (self isPrimFailToken: val) ifFalse: + [^val]]. + + ctxt := self activateMethod: meth withArgs: arguments receiver: rcvr. + (primIndex isInteger and: [primIndex > 0]) ifTrue: + [ctxt failPrimitiveWith: val]. + + ^ctxt!
Item was changed: ----- Method: Context>>jump:if: (in category 'instruction decoding') ----- jump: distance if: condition "Simulate the action of a 'conditional jump' bytecode whose offset is the argument, distance, and whose condition is the argument, condition."
| bool | bool := self pop. condition == bool ifTrue: [self jump: distance] ifFalse: [(true == bool or: [false == bool]) ifFalse: + [^self send: #mustBeBoolean to: bool with: #()]]! - [^self send: #mustBeBooleanIn: to: bool with: {self}]]!
Item was changed: ----- Method: Context>>send:to:with:lookupIn: (in category 'controlling') ----- send: selector to: rcvr with: arguments lookupIn: lookupClass "Simulate the action of sending a message with selector and arguments to rcvr. The argument, lookupClass, is the class in which to lookup the message. This is the receiver's class for normal messages, but for super messages it will be some specific class related to the source method."
+ | meth | - | meth primIndex val ctxt | (meth := lookupClass lookupSelector: selector) ifNil: [selector == #doesNotUnderstand: ifTrue: [self error: 'Recursive message not understood!!' translated]. ^self send: #doesNotUnderstand: to: rcvr with: {(Message selector: selector arguments: arguments) lookupClass: lookupClass} lookupIn: lookupClass]. + ^ self + executeMethod: meth + forSelector: selector + withArgs: arguments + receiver: rcvr! - (self objectClass: meth) isCompiledCodeClass ifFalse: - ["Object as Methods (OaM) protocol: 'The contract is that, when the VM encounters an ordinary object (rather than a compiled method) in the method dictionary during lookup, it sends it the special selector #run:with:in: providing the original selector, arguments, and receiver.'. DOI: 10.1145/2991041.2991062." - ^self send: #run:with:in: - to: meth - with: {selector. arguments. rcvr}]. - - meth numArgs = arguments size ifFalse: - [^ self error: ('Wrong number of arguments in simulated message {1}' translated format: {selector})]. - (primIndex := meth primitive) > 0 ifTrue: - [val := self doPrimitive: primIndex method: meth receiver: rcvr args: arguments. - (self isPrimFailToken: val) ifFalse: - [^val]]. - - ctxt := self activateMethod: meth withArgs: arguments receiver: rcvr. - (primIndex isInteger and: [primIndex > 0]) ifTrue: - [ctxt failPrimitiveWith: val]. - - ^ctxt!
Item was changed: ----- Method: Context>>stepToSendOrReturn (in category 'system simulation') ----- stepToSendOrReturn "Simulate the execution of bytecodes until either sending a message or returning a value to the receiver (that is, until switching contexts)."
| ctxt | [self willReallySend or: [self willReturn or: [self willReallyStore]]] whileFalse: [ctxt := self step. ctxt == self ifFalse: + ["Caused by mustBeBoolean handling or callPrimitive:" + ^ctxt stepToSendOrReturn]]! - [self halt. - "Caused by mustBeBoolean handling" - ^ctxt]]!
Item was added: + ----- Method: False>>mustBeBooleanIn: (in category 'converting') ----- + mustBeBooleanIn: aContext + "Overwritten to support conditional jumps (ifTrue:/ifFalse:) based on a proxy to true/false. The jump will first fail, send #mustBeBoolean to the proxy, which will forward hither. Here, we correct the context by replacing the proxy on the stack through the canonical instance of ourselves and reattempting the jump. See ObjectTracer>>mustBeBoolean for an example." + + aContext skipBackBeforeJump. + ^ false!
Item was changed: ----- Method: Object>>withArgs:executeMethod: (in category 'message handling') ----- withArgs: argArray executeMethod: compiledMethod "Execute compiledMethod against the receiver and args in argArray"
+ | context | - | selector | <primitive: 188> + context := thisContext + executeMethod: compiledMethod + forSelector: Symbol new + withArgs: argArray + receiver: self. + ^ context == thisContext + ifTrue: ["quick return" thisContext top] + ifFalse: [context jump]! - selector := Symbol new. - self class addSelectorSilently: selector withMethod: compiledMethod. - ^ [self perform: selector withArguments: argArray] - ensure: [self class basicRemoveSelector: selector]!
Item was added: + ----- Method: ObjectTracer>>mustBeBoolean (in category 'very few messages') ----- + mustBeBoolean + "Overwritten to preserve original sender context. + + This is required to support conditional jumps (ifTrue:/ifFalse:) based on traced booleans, e.g.: + (ObjectTracer on: false) ifTrue: [1] ifFalse: [2]." + + ^ self mustBeBooleanIn: thisContext sender!
Item was added: + ----- Method: True>>mustBeBooleanIn: (in category 'converting') ----- + mustBeBooleanIn: aContext + "Overwritten to support conditional jumps (ifTrue:/ifFalse:) based on a proxy to true/false. The jump will first fail, send #mustBeBoolean to the proxy, which will forward hither. Here, we correct the context by replacing the proxy on the stack through the canonical instance of ourselves and reattempting the jump. See ObjectTracer>>mustBeBoolean for an example." + + aContext skipBackBeforeJump. + ^ true!
packages@lists.squeakfoundation.org