Spaces:
Running
Running
; | |
/* | |
* Copyright (c) 2013-2025 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.InstructionPrinter', | |
'initialization', { | |
initialize: function(method, vm) { | |
this.method = method; | |
this.vm = vm; | |
}, | |
}, | |
'printing', { | |
printInstructions: function(indent, highlight, highlightPC) { | |
// all args are optional | |
this.indent = indent; // prepend to every line except if highlighted | |
this.highlight = highlight; // prepend to highlighted line | |
this.highlightPC = highlightPC; // PC of highlighted line | |
this.innerIndents = {}; | |
this.result = ''; | |
this.scanner = this.method.methodSignFlag() | |
? new Squeak.InstructionStreamSista(this.method, this.vm) | |
: new Squeak.InstructionStream(this.method, this.vm); | |
this.oldPC = this.scanner.pc; | |
this.endPC = 0; // adjusted while scanning | |
this.done = false; | |
try { | |
while (!this.done) | |
this.scanner.interpretNextInstructionFor(this); | |
} catch(ex) { | |
this.print("!!! " + ex.message); | |
} | |
return this.result; | |
}, | |
print: function(instruction) { | |
if (this.oldPC === this.highlightPC) { | |
if (this.highlight) this.result += this.highlight; | |
} else { | |
if (this.indent) this.result += this.indent; | |
} | |
this.result += this.oldPC; | |
for (var i = 0; i < this.innerIndents[this.oldPC] || 0; i++) | |
this.result += " "; | |
this.result += " <"; | |
for (var i = this.oldPC; i < this.scanner.pc; i++) { | |
if (i > this.oldPC) this.result += " "; | |
this.result += (this.method.bytes[i]+0x100).toString(16).substr(-2).toUpperCase(); // padded hex | |
} | |
this.result += "> " + instruction + "\n"; | |
this.oldPC = this.scanner.pc; | |
} | |
}, | |
'decoding', { | |
blockReturnConstant: function(obj) { | |
this.print('blockReturn: ' + obj.toString()); | |
this.done = this.scanner.pc > this.endPC; // full block | |
}, | |
blockReturnTop: function() { | |
this.print('blockReturn'); | |
this.done = this.scanner.pc > this.endPC; // full block | |
}, | |
doDup: function() { | |
this.print('dup'); | |
}, | |
doPop: function() { | |
this.print('pop'); | |
}, | |
jump: function(offset) { | |
this.print('jumpTo: ' + (this.scanner.pc + offset)); | |
if (this.scanner.pc + offset > this.endPC) this.endPC = this.scanner.pc + offset; | |
}, | |
jumpIf: function(condition, offset) { | |
this.print((condition ? 'jumpIfTrue: ' : 'jumpIfFalse: ') + (this.scanner.pc + offset)); | |
if (this.scanner.pc + offset > this.endPC) this.endPC = this.scanner.pc + offset; | |
}, | |
methodReturnReceiver: function() { | |
this.print('return: receiver'); | |
this.done = this.scanner.pc > this.endPC; | |
}, | |
methodReturnTop: function() { | |
this.print('return: topOfStack'); | |
this.done = this.scanner.pc > this.endPC; | |
}, | |
methodReturnConstant: function(obj) { | |
this.print('returnConst: ' + obj.toString()); | |
this.done = this.scanner.pc > this.endPC; | |
}, | |
nop: function() { | |
this.print('nop'); | |
}, | |
popIntoLiteralVariable: function(anAssociation) { | |
this.print('popIntoBinding: ' + anAssociation.assnKeyAsString()); | |
}, | |
popIntoReceiverVariable: function(offset) { | |
this.print('popIntoInstVar: ' + offset); | |
}, | |
popIntoTemporaryVariable: function(offset) { | |
this.print('popIntoTemp: ' + offset); | |
}, | |
pushActiveContext: function() { | |
this.print('push: thisContext'); | |
}, | |
pushConstant: function(obj) { | |
var value = obj.sqInstName ? obj.sqInstName() : obj.toString(); | |
this.print('pushConst: ' + value); | |
}, | |
pushLiteralVariable: function(anAssociation) { | |
this.print('pushBinding: ' + anAssociation.assnKeyAsString()); | |
}, | |
pushReceiver: function() { | |
this.print('push: self'); | |
}, | |
pushReceiverVariable: function(offset) { | |
this.print('pushInstVar: ' + offset); | |
}, | |
pushTemporaryVariable: function(offset) { | |
this.print('pushTemp: ' + offset); | |
}, | |
send: function(selector, numberArguments, supered) { | |
this.print( (supered ? 'superSend: #' : 'send: #') + (selector.bytesAsString ? selector.bytesAsString() : selector)); | |
}, | |
sendSuperDirected: function(selector) { | |
this.print('directedSuperSend: #' + (selector.bytesAsString ? selector.bytesAsString() : selector)); | |
}, | |
storeIntoLiteralVariable: function(anAssociation) { | |
this.print('storeIntoBinding: ' + anAssociation.assnKeyAsString()); | |
}, | |
storeIntoReceiverVariable: function(offset) { | |
this.print('storeIntoInstVar: ' + offset); | |
}, | |
storeIntoTemporaryVariable: function(offset) { | |
this.print('storeIntoTemp: ' + offset); | |
}, | |
pushNewArray: function(size) { | |
this.print('push: (Array new: ' + size + ')'); | |
}, | |
popIntoNewArray: function(numElements) { | |
this.print('pop: ' + numElements + ' into: (Array new: ' + numElements + ')'); | |
}, | |
pushRemoteTemp: function(offset , arrayOffset) { | |
this.print('push: ' + offset + ' ofTemp: ' + arrayOffset); | |
}, | |
storeIntoRemoteTemp: function(offset , arrayOffset) { | |
this.print('storeInto: ' + offset + ' ofTemp: ' + arrayOffset); | |
}, | |
popIntoRemoteTemp: function(offset , arrayOffset) { | |
this.print('popInto: ' + offset + ' ofTemp: ' + arrayOffset); | |
}, | |
pushClosureCopy: function(numCopied, numArgs, blockSize) { | |
var from = this.scanner.pc, | |
to = from + blockSize; | |
this.print('closure(' + from + '-' + (to-1) + '): ' + numCopied + ' copied, ' + numArgs + ' args'); | |
for (var i = from; i < to; i++) | |
this.innerIndents[i] = (this.innerIndents[i] || 0) + 1; | |
if (to > this.endPC) this.endPC = to; | |
}, | |
pushFullClosure: function(literalIndex, numCopied, numArgs) { | |
this.print('pushFullClosure: (self literalAt: ' + (literalIndex + 1) + ') numCopied: ' + numCopied + ' numArgs: ' + numArgs); | |
}, | |
callPrimitive: function(primitiveIndex) { | |
this.print('primitive: ' + primitiveIndex); | |
}, | |
}); | |