'From Croquet1.0beta of 11 April 2006 [latest update: #1] on 12 November 2008 at 2:55:31 pm'! "Change Set: Closure Compiler Date: 3 June 2008 Author: Eliot Miranda A compiler that produces code that uses closures (BlockClosure) in place of BlueBook blocks."! Object variableSubclass: #BlockClosure instanceVariableNames: 'outerContext startpc numArgs' classVariableNames: '' poolDictionaries: '' category: 'Kernel-Methods'! !BlockClosure commentStamp: '' prior: 0! I am a block closure for Eliot's closure implementation. Not to be confused with the old BlockClosure.! ]style[(103)i! TestCase subclass: #ClosureCompilerTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Tests'! ByteArray variableByteSubclass: #CompiledMethod instanceVariableNames: '' classVariableNames: 'BlockNodeCache LargeFrame SmallFrame SpecialConstants ' poolDictionaries: '' category: 'Kernel-Methods'! InstructionClient subclass: #BlockLocalTempCounter instanceVariableNames: 'stackPointer scanner blockEnd joinOffsets' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Support'! !BlockLocalTempCounter commentStamp: '' prior: 0! I am a support class for the decompiler that is used to find the number of local temps in a block by finding out what the stack offset is at the end of a block.! ]style[(160)i! InstructionClient subclass: #BlockStartLocator instanceVariableNames: 'nextJumpIsAroundBlock' classVariableNames: '' poolDictionaries: '' category: 'Kernel-Methods'! InstructionStream subclass: #Decompiler instanceVariableNames: 'constructor method instVars tempVars constTable stack statements lastPc exit caseExits lastJumpPc lastReturnPc limit hasValue blockStackBase numLocalTemps ' classVariableNames: 'ArgumentFlag CascadeFlag CaseFlag IfNilFlag ' poolDictionaries: '' category: 'Compiler-Kernel'! !Decompiler commentStamp: '' prior: 0! I decompile a method in three phases: Reverser: postfix byte codes -> prefix symbolic codes (nodes and atoms) Parser: prefix symbolic codes -> node tree (same as the compiler) Printer: node tree -> text (done by the nodes) instance vars: constructor method instVars tempVars constTable stack statements lastPc exit caseExits - stack of exit addresses that have been seen in the branches of caseOf:'s lastJumpPc lastReturnPc limit hasValue blockStackBase numLocaltemps - number of temps local to a block; also a flag indicating decompiling a block! !MethodContext commentStamp: '' prior: 0! My instances hold all the dynamic state associated with the execution of either a method activation resulting from a message send or a block activation resulting from a block evaluation. In addition to their inherited state, this includes the receiver (self), the closure for a BlockClosure activation (which is nil for a method activation), a CompiledMethod, and space in the variable part of the context for arguments and temporary variables. MethodContexts, though normal in their variable size, are actually only used in two sizes, small and large, which are determined by the temporary space required by the method being executed. MethodContexts must only be created using the method newForMethod:. Note that it is impossible to determine the real object size of a MethodContext except by asking for the frameSize of its method. Any fields above the stack pointer (stackp) are truly invisible -- even (and especially!!) to the garbage collector. Any store into stackp other than by the primitive method stackp: is potentially fatal.! ParseNode subclass: #BlockNode instanceVariableNames: 'arguments statements returns nArgsNode size remoteCopyNode temporaries remoteTempNode optimized copiedValues closureCreationNode blockExtent startOfLastStatement ' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! ParseNode subclass: #DecompilerConstructor instanceVariableNames: 'method instVars nArgs literalValues tempVars' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Support'! DecompilerConstructor subclass: #DecompilerConstructorForClosures instanceVariableNames: 'tempNameCounter' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Support'! ParseNode subclass: #Encoder instanceVariableNames: 'scopeTable nTemps supered requestor class selector literalStream selectorSet litIndSet litSet sourceRanges globalSourceRanges' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Kernel'! Encoder subclass: #BytecodeEncoder instanceVariableNames: 'stream rootNode position blockExtentsToLocals ' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Kernel'! EncoderForLongFormV3 subclass: #EncoderForLongFormV3PlusClosures instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Kernel'! !EncoderForLongFormV3PlusClosures commentStamp: '' prior: 0! An encoder for the long-form subset of the V3 bytecode set augmented with 138 10001010 jkkkkkkk Push (Array new: kkkkkkk)/Pop kkkkkkk into: (Array new: kkkkkkk) 139 10001011 jjjjkkkk Push Temp At kkkk In Temp Vector At: jjjj 140 10001100 kkkkkkkk jjjjjjjj Push Temp At kkkkkkkk In Temp Vector At: jjjjjjjj 141 10001101 kkkkkkkk jjjjjjjj Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj 142 10001110 kkkkkkkk jjjjjjjj Pop and Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj 143 10001111 kkkkkkkk jjjjjjjj iiiiiiii Push Closure Num Args kkkkkkkk BlockSize jjjjjjjjiiiiiiii This is an exact duplicate of EncoderForV3PlusClosures. Could be a trait (or in Newspeak, a Mixin). For now we impose upon you to synchronise any and all changes between these two classes.! ]style[(73 559 158)cblack;,f4cblack;,cblack;! EncoderForV3 subclass: #EncoderForV3PlusClosures instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Kernel'! !EncoderForV3PlusClosures commentStamp: '' prior: 0! An encoder for the V3 bytecode set augmented with 138 10001010 jkkkkkkk Push (Array new: kkkkkkk)/Pop kkkkkkk into: (Array new: kkkkkkk) 139 10001011 jjjjkkkk Push Temp At kkkk In Temp Vector At: jjjj 140 10001100 kkkkkkkk jjjjjjjj Push Temp At kkkkkkkk In Temp Vector At: jjjjjjjj 141 10001101 kkkkkkkk jjjjjjjj Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj 142 10001110 kkkkkkkk jjjjjjjj Pop and Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj 143 10001111 kkkkkkkk jjjjjjjj iiiiiiii Push Closure Num Args kkkkkkkk BlockSize jjjjjjjjiiiiiiii This is an exact duplicate of EncoderForLongFormV3PlusClosures. Could be a trait (or in Newspeak, a Mixin). For now we impose upon you to synchronise any and all changes between these two classes.! ]style[(49 559 166)cblack;,f4cblack;,cblack;! MethodNode subclass: #BytecodeAgnosticMethodNode instanceVariableNames: 'localsPool locationCounter ' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! ParseNode subclass: #NewArrayNode instanceVariableNames: 'numElements' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! !NewArrayNode commentStamp: '' prior: 0! I represent a node for the genPushNewArray: opcode.! ]style[(51)i! ParseNodeVisitor subclass: #RegenerationPreparingVisitor instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Support'! !RegenerationPreparingVisitor commentStamp: '' prior: 0! I arrange that BlockNodes clean out any remote temps prior to being regenerated.! ]style[(80)i! Error subclass: #SyntaxErrorNotification instanceVariableNames: 'inClass code category doitFlag errorMessage location' classVariableNames: '' poolDictionaries: '' category: 'Exceptions-Extensions'! ParseNodeVisitor subclass: #TempNumberNormalizingVisitor instanceVariableNames: 'count temps' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Support'! VariableNode subclass: #TempVariableNode instanceVariableNames: 'isAnArg hasRefs hasDefs scope definingScope remoteNode readingScopes argType writingScopes ' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! TempVariableNode subclass: #RemoteTempVectorNode instanceVariableNames: 'remoteTemps readNode writeNode' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! !RemoteTempVectorNode commentStamp: '' prior: 0! I am a node for a vector of remote temps, created to share temps between closures when those temps are written to in closures other than their defining ones.! ]style[(157)i! !BlockClosure methodsFor: 'initialize-release' stamp: 'eem 9/3/2008 14:08'! outerContext: aContext startpc: aStartpc numArgs: argCount copiedValues: anArrayOrNil outerContext := aContext. startpc := aStartpc. numArgs := argCount. 1 to: self numCopiedValues do: [:i| self at: i put: (anArrayOrNil at: i)]! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 9/3/2008 13:57'! copiedValueAt: i ^self basicAt: i! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 7/28/2008 13:58'! home ^outerContext home! ! !BlockClosure methodsFor: 'accessing' stamp: 'ajh 1/21/2003 13:16'! isBlock ^ true! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 5/29/2008 12:18'! method ^outerContext method! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 5/28/2008 16:02'! numArgs "Answer the number of arguments that must be used to evaluate this block" ^numArgs! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 9/3/2008 14:07'! numCopiedValues "Answer the number of copied values of the receiver. Since these are stored in the receiver's indexable fields this is the receiver's basic size. Primitive. Answer the number of indexable variables in the receiver. This value is the same as the largest legal subscript." ^self basicSize! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 5/24/2008 11:21'! outerContext ^outerContext! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 6/26/2008 09:17'! receiver ^outerContext receiver! ! !BlockClosure methodsFor: 'accessing' stamp: 'eem 6/1/2008 09:39'! startpc ^startpc! ! !BlockClosure methodsFor: 'evaluating' stamp: 'cmm 2/16/2003 16:08'! bench "See how many times I can value in 5 seconds. I'll answer a meaningful description." | startTime endTime count | count := 0. endTime := Time millisecondClockValue + 5000. startTime := Time millisecondClockValue. [ Time millisecondClockValue > endTime ] whileFalse: [ self value. count := count + 1 ]. endTime := Time millisecondClockValue. ^count = 1 ifTrue: [ ((endTime - startTime) // 1000) printString, ' seconds.' ] ifFalse: [ ((count * 1000) / (endTime - startTime)) asFloat printString, ' per second.' ]! ! !BlockClosure methodsFor: 'evaluating' stamp: 'brp 9/25/2003 13:49'! durationToRun "Answer the duration taken to execute this block." ^ Duration milliSeconds: self timeToRun ! ! !BlockClosure methodsFor: 'evaluating' stamp: 'ajh 1/13/2002 13:36'! ifError: errorHandlerBlock "Evaluate the block represented by the receiver, and normally return it's value. If an error occurs, the errorHandlerBlock is evaluated, and it's value is instead returned. The errorHandlerBlock must accept zero, one, or two parameters (the error message and the receiver)." "Examples: [1 whatsUpDoc] ifError: [:err :rcvr | 'huh?']. [1 / 0] ifError: [:err :rcvr | 'ZeroDivide' = err ifTrue: [Float infinity] ifFalse: [self error: err]] " ^ self on: Error do: [:ex | errorHandlerBlock valueWithPossibleArgs: {ex description. ex receiver}]! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:04'! simulateValueWithArguments: anArray caller: aContext | newContext sz | numArgs ~= anArray size ifTrue: [self numArgsError: anArray size]. newContext := (MethodContext newForMethod: outerContext method) setSender: aContext receiver: outerContext receiver method: outerContext method closure: self startpc: startpc. sz := self basicSize. newContext stackp: sz + numArgs. 1 to: numArgs do: [:i| newContext at: i put: (anArray at: i)]. 1 to: sz do: [:i| newContext at: i + numArgs put: (self at: i)]. ^newContext! ! !BlockClosure methodsFor: 'evaluating' stamp: 'jm 6/3/1998 14:25'! timeToRun "Answer the number of milliseconds taken to execute this block." ^ Time millisecondsToRun: self ! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:09'! value "Activate the receiver, creating a closure activation (MethodContext) whose closure is the receiver and whose caller is the sender of this message. Supply the copied values to the activation as its arguments and copied temps. Primitive. Optional (but you're going to want this for performance)." | newContext ncv | numArgs ~= 0 ifTrue: [self numArgsError: 0]. newContext := self asContextWithSender: thisContext sender. (ncv := self numCopiedValues) > 0 ifTrue: [newContext stackp: ncv. 1 to: ncv do: "nil basicSize = 0" [:i| newContext at: i put: (self at: i)]]. thisContext privSender: newContext! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:08'! valueWithArguments: anArray "Activate the receiver, creating a closure activation (MethodContext) whose closure is the receiver and whose caller is the sender of this message. Supply the arguments in an anArray and copied values to the activation as its arguments and copied temps. Primitive. Optional (but you're going to want this for performance)." | newContext ncv | numArgs ~= anArray size ifTrue: [self numArgsError: anArray size]. newContext := self asContextWithSender: thisContext sender. ncv := self numCopiedValues. newContext stackp: ncv + numArgs. 1 to: numArgs do: [:i| newContext at: i put: (anArray at: i)]. 1 to: ncv do: [:i| newContext at: i + numArgs put: (self at: i)]. thisContext privSender: newContext! ! !BlockClosure methodsFor: 'evaluating' stamp: 'ar 8/17/2007 13:15'! valueWithin: aDuration onTimeout: timeoutBlock "Evaluate the receiver. If the evaluation does not complete in less than aDuration evaluate the timeoutBlock instead" | theProcess delay watchdog | aDuration <= Duration zero ifTrue: [^ timeoutBlock value ]. "the block will be executed in the current process" theProcess := Processor activeProcess. delay := aDuration asDelay. "make a watchdog process" watchdog := [ delay wait. "wait for timeout or completion" theProcess ifNotNil:[ theProcess signalException: TimedOut ] ] newProcess. "Watchdog needs to run at high priority to do its job (but not at timing priority)" watchdog priority: Processor timingPriority-1. "catch the timeout signal" ^ [ watchdog resume. "start up the watchdog" self ensure:[ "evaluate the receiver" theProcess := nil. "it has completed, so ..." delay delaySemaphore signal. "arrange for the watchdog to exit" ]] on: TimedOut do: [ :e | timeoutBlock value ]. ! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 5/28/2008 15:03'! valueWithPossibleArgs: anArray ^numArgs = 0 ifTrue: [self value] ifFalse: [self valueWithArguments: (numArgs = anArray size ifTrue: [anArray] ifFalse: [numArgs > anArray size ifTrue: [anArray, (Array new: numArgs - anArray size)] ifFalse: [anArray copyFrom: 1 to: numArgs]])]! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 5/25/2008 14:47'! valueWithPossibleArgument: anArg "Evaluate the block represented by the receiver. If the block requires one argument, use anArg, if it requires more than one, fill up the rest with nils." | a | numArgs = 0 ifTrue: [^self value]. numArgs = 1 ifTrue: [^self value: anArg]. a := Array new: numArgs. a at: 1 put: anArg. ^self valueWithArguments: a! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:09'! value: anArg "Activate the receiver, creating a closure activation (MethodContext) whose closure is the receiver and whose caller is the sender of this message. Supply the argument and copied values to the activation as its arguments and copied temps. Primitive. Optional (but you're going to want this for performance)." | newContext ncv | numArgs ~= 1 ifTrue: [self numArgsError: 1]. newContext := self asContextWithSender: thisContext sender. ncv := self numCopiedValues. newContext stackp: ncv + 1. newContext at: 1 put: anArg. 1 to: ncv do: [:i| newContext at: i + 1 put: (self at: i)]. thisContext privSender: newContext! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:10'! value: firstArg value: secondArg "Activate the receiver, creating a closure activation (MethodContext) whose closure is the receiver and whose caller is the sender of this message. Supply the arguments and copied values to the activation as its arguments and copied temps. Primitive. Optional (but you're going to want this for performance)." | newContext ncv | numArgs ~= 2 ifTrue: [self numArgsError: 2]. newContext := self asContextWithSender: thisContext sender. ncv := self numCopiedValues. newContext stackp: ncv + 2. newContext at: 1 put: firstArg. newContext at: 2 put: secondArg. 1 to: ncv do: [:i| newContext at: i + 2 put: (self at: i)]. thisContext privSender: newContext! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:11'! value: firstArg value: secondArg value: thirdArg "Activate the receiver, creating a closure activation (MethodContext) whose closure is the receiver and whose caller is the sender of this message. Supply the arguments and copied values to the activation as its arguments and copied temps. Primitive. Optional (but you're going to want this for performance)." | newContext ncv | numArgs ~= 3 ifTrue: [self numArgsError: 3]. newContext := self asContextWithSender: thisContext sender. ncv := self numCopiedValues. newContext stackp: ncv + 3. newContext at: 1 put: firstArg. newContext at: 2 put: secondArg. newContext at: 3 put: thirdArg. 1 to: ncv do: [:i| newContext at: i + 3 put: (self at: i)]. thisContext privSender: newContext! ! !BlockClosure methodsFor: 'evaluating' stamp: 'eem 9/3/2008 14:11'! value: firstArg value: secondArg value: thirdArg value: fourthArg "Activate the receiver, creating a closure activation (MethodContext) whose closure is the receiver and whose caller is the sender of this message. Supply the arguments and copied values to the activation as its arguments and copied temps. Primitive. Optional (but you're going to want this for performance)." | newContext ncv | numArgs ~= 4 ifTrue: [self numArgsError: 4]. newContext := self asContextWithSender: thisContext sender. ncv := self numCopiedValues. newContext stackp: ncv + 4. newContext at: 1 put: firstArg. newContext at: 2 put: secondArg. newContext at: 3 put: thirdArg. newContext at: 4 put: fourthArg. 1 to: ncv do: [:i| newContext at: i + 4 put: (self at: i)]. thisContext privSender: newContext! ! !BlockClosure methodsFor: 'error handing' stamp: 'eem 5/21/2008 15:23'! numArgsError: numArgsForInvocation | printNArgs | printNArgs := [:n| n printString, ' argument', (n = 1 ifTrue: [''] ifFalse:['s'])]. self error: 'This block accepts ', (printNArgs value: numArgs), ', but was called with ', (printNArgs value: numArgsForInvocation printString), '.'! ! !BlockClosure methodsFor: 'testing' stamp: 'eem 5/29/2008 12:20'! hasMethodReturn "Answer whether the receiver has a method-return ('^') in its code." | myMethod scanner preceedingBytecodeMessage end | "Determine end of block from the instruction preceding it. Find the instruction by using an MNU handler to capture the instruction message sent by the scanner." myMethod := outerContext method. scanner := InstructionStream new method: myMethod pc: myMethod initialPC. [scanner pc < startpc] whileTrue: [[scanner interpretNextInstructionFor: nil] on: MessageNotUnderstood do: [:ex| preceedingBytecodeMessage := ex message]]. end := preceedingBytecodeMessage arguments last + startpc - 1. scanner method: myMethod pc: startpc. scanner scanFor: [:byte | (byte between: 120 and: 124) or: [scanner pc > end]]. ^scanner pc <= end! ! !BlockClosure methodsFor: 'testing' stamp: 'eem 5/23/2008 13:48'! isClosure ^true! ! !BlockClosure methodsFor: 'printing' stamp: 'eem 7/28/2008 14:06'! decompile ^Decompiler new decompileBlock: self! ! !BlockClosure methodsFor: 'printing' stamp: 'eem 7/28/2008 14:09'! fullPrintOn: aStream aStream print: self; cr. (self decompile ifNil: ['--source missing--']) printOn: aStream indent: 0! ! !BlockClosure methodsFor: 'printing' stamp: 'eem 5/24/2008 11:23'! printOn: aStream aStream nextPutAll: '[closure] in '. outerContext printOn: aStream! ! !BlockClosure methodsFor: 'private' stamp: 'eem 6/11/2008 11:38'! asContextWithSender: aContext "Inner private support method for evaluation. Do not use unless you know what you're doing." ^(MethodContext newForMethod: outerContext method) setSender: aContext receiver: outerContext receiver method: outerContext method closure: self startpc: startpc! ! !BlockClosure methodsFor: 'private' stamp: 'eem 5/28/2008 14:50'! copyForSaving "Answer a copy of the receiver suitable for serialization. Notionally, if the receiver's outerContext has been returned from then nothing needs to be done and we can use the receiver. But there's a race condition determining if the receiver has been returned from (it could be executing in a different process). So answer a copy anyway." ^self shallowCopy postCopy! ! !BlockClosure methodsFor: 'private' stamp: 'eem 9/3/2008 14:01'! fixTemps "Fix the values of the temporary variables used in the block that are ordinarily shared with the method in which the block is defined. We need to copy the copiedValues, copying anything looking like a remote temp vector. if we accidentally copy an Array that isn't actually an indirect temp vector we may break things, so this is a real hack." 1 to: self numCopiedValues do: [:i| | each | (each := self at: i) isArray ifTrue: [self at: i put: each shallowCopy]]! ! !BlockClosure methodsFor: 'private' stamp: 'eem 5/28/2008 14:56'! reentrant "Answer a version of the recever that can be reentered. Closures are reentrant (unlike BlockContect) so simply answer self." ^self! ! !BlockClosure methodsFor: 'private' stamp: 'ar 3/2/2001 01:16'! valueUnpreemptively "Evaluate the receiver (block), without the possibility of preemption by higher priority processes. Use this facility VERY sparingly!!" "Think about using Block>>valueUninterruptably first, and think about using Semaphore>>critical: before that, and think about redesigning your application even before that!! After you've done all that thinking, go right ahead and use it..." | activeProcess oldPriority result | activeProcess := Processor activeProcess. oldPriority := activeProcess priority. activeProcess priority: Processor highestPriority. result := self ensure: [activeProcess priority: oldPriority]. "Yield after restoring priority to give the preempted processes a chance to run" Processor yield. ^result! ! !BlockClosure methodsFor: 'controlling' stamp: 'jf 9/3/2003 16:45'! doWhileFalse: conditionBlock "Evaluate the receiver once, then again as long the value of conditionBlock is false." | result | [result := self value. conditionBlock value] whileFalse. ^ result! ! !BlockClosure methodsFor: 'controlling' stamp: 'jf 9/3/2003 16:39'! doWhileTrue: conditionBlock "Evaluate the receiver once, then again as long the value of conditionBlock is true." | result | [result := self value. conditionBlock value] whileTrue. ^ result! ! !BlockClosure methodsFor: 'controlling' stamp: 'sma 5/12/2000 13:22'! repeat "Evaluate the receiver repeatedly, ending only if the block explicitly returns." [self value. true] whileTrue! ! !BlockClosure methodsFor: 'controlling' stamp: 'ls 9/24/1999 09:45'! repeatWithGCIf: testBlock | ans | "run the receiver, and if testBlock returns true, garbage collect and run the receiver again" ans := self value. (testBlock value: ans) ifTrue: [ Smalltalk garbageCollect. ans := self value ]. ^ans! ! !BlockClosure methodsFor: 'controlling'! whileFalse "Ordinarily compiled in-line, and therefore not overridable. This is in case the message is sent to other than a literal block. Evaluate the receiver, as long as its value is false." ^ [self value] whileFalse: []! ! !BlockClosure methodsFor: 'controlling'! whileFalse: aBlock "Ordinarily compiled in-line, and therefore not overridable. This is in case the message is sent to other than a literal block. Evaluate the argument, aBlock, as long as the value of the receiver is false." ^ [self value] whileFalse: [aBlock value]! ! !BlockClosure methodsFor: 'controlling' stamp: 'jcg 7/8/2007 18:25'! whileNil: aBlock "Unlike #whileTrue/False: this is not compiled inline." ^ [self value isNil] whileTrue: [aBlock value] ! ! !BlockClosure methodsFor: 'controlling' stamp: 'jcg 7/8/2007 18:25'! whileNotNil: aBlock "Unlike #whileTrue/False: this is not compiled inline." ^ [self value notNil] whileTrue: [aBlock value] ! ! !BlockClosure methodsFor: 'controlling'! whileTrue "Ordinarily compiled in-line, and therefore not overridable. This is in case the message is sent to other than a literal block. Evaluate the receiver, as long as its value is true." ^ [self value] whileTrue: []! ! !BlockClosure methodsFor: 'controlling'! whileTrue: aBlock "Ordinarily compiled in-line, and therefore not overridable. This is in case the message is sent to other than a literal block. Evaluate the argument, aBlock, as long as the value of the receiver is true." ^ [self value] whileTrue: [aBlock value]! ! !BlockClosure methodsFor: 'exceptions' stamp: 'sma 5/11/2000 19:38'! assert self assert: self! ! !BlockClosure methodsFor: 'exceptions' stamp: 'eem 8/22/2008 14:22'! ensure: aBlock "Evaluate a termination block after evaluating the receiver, regardless of whether the receiver's evaluation completes." | returnValue b | returnValue := self valueNoContextSwitch. "aBlock wasn't nil when execution of this method began; it is nil'd out by the unwind machinery, and that's how we know it's already been evaluated ... otherwise, obviously, it needs to be evaluated" aBlock == nil ifFalse: [ "nil out aBlock temp before evaluating aBlock so it is not executed again if aBlock remote returns" b := aBlock. thisContext tempAt: 1 put: nil. "Could be aBlock := nil, but arguments cannot be modified" b value. ]. ^ returnValue! ! !BlockClosure methodsFor: 'exceptions' stamp: 'eem 8/22/2008 14:29'! ifCurtailed: aBlock "Evaluate the receiver with an abnormal termination action. Evaluate aBlock only if execution is unwound during execution of the receiver. If execution of the receiver finishes normally do not evaluate aBlock." ^self valueNoContextSwitch! ! !BlockClosure methodsFor: 'exceptions' stamp: 'ajh 10/9/2001 16:51'! onDNU: selector do: handleBlock "Catch MessageNotUnderstood exceptions but only those of the given selector (DNU stands for doesNotUnderstand:)" ^ self on: MessageNotUnderstood do: [:exception | exception message selector = selector ifTrue: [handleBlock valueWithPossibleArgs: {exception}] ifFalse: [exception pass] ]! ! !BlockClosure methodsFor: 'exceptions' stamp: 'ajh 2/1/2003 00:30'! on: exception do: handlerAction "Evaluate the receiver in the scope of an exception handler." | handlerActive | "just a marker, fail and execute the following" handlerActive := true. ^ self value! ! !BlockClosure methodsFor: 'exceptions' stamp: 'ajh 7/26/2002 11:49'! valueUninterruptably "Prevent remote returns from escaping the sender. Even attempts to terminate (unwind) this process will be halted and the process will resume here. A terminate message is needed for every one of these in the sender chain to get the entire process unwound." ^ self ifCurtailed: [^ self]! ! !BlockClosure methodsFor: 'scheduling' stamp: 'eem 5/28/2008 16:16'! asContext "Create a MethodContext that is ready to execute self. Assumes self takes no args (if it does the args will be nil)" ^self asContextWithSender: nil! ! !BlockClosure methodsFor: 'scheduling' stamp: 'ajh 7/15/2001 16:03'! fork "Create and schedule a Process running the code in the receiver." ^ self newProcess resume! ! !BlockClosure methodsFor: 'scheduling' stamp: 'ajh 10/16/2002 11:14'! forkAndWait "Suspend current process and execute self in new process, when it completes resume current process" | semaphore | semaphore := Semaphore new. [self ensure: [semaphore signal]] fork. semaphore wait. ! ! !BlockClosure methodsFor: 'scheduling' stamp: 'ajh 9/29/2001 21:00'! forkAt: priority "Create and schedule a Process running the code in the receiver at the given priority. Answer the newly created process." ^ self newProcess priority: priority; resume! ! !BlockClosure methodsFor: 'scheduling' stamp: 'svp 6/23/2003 10:59'! forkAt: priority named: name "Create and schedule a Process running the code in the receiver at the given priority and having the given name. Answer the newly created process." | forkedProcess | forkedProcess := self newProcess. forkedProcess priority: priority. forkedProcess name: name. ^ forkedProcess resume! ! !BlockClosure methodsFor: 'scheduling' stamp: 'svp 6/23/2003 10:59'! forkNamed: aString "Create and schedule a Process running the code in the receiver and having the given name." ^ self newProcess name: aString; resume! ! !BlockClosure methodsFor: 'scheduling' stamp: 'ajh 2/10/2003 14:25'! newProcess "Answer a Process running the code in the receiver. The process is not scheduled." "Simulation guard" ^Process forContext: [self value. Processor terminateActive] asContext priority: Processor activePriority! ! !BlockClosure methodsFor: '*sunit-preload' stamp: 'rw 1/23/2002 00:27'! sunitEnsure: aBlock ^self ensure: aBlock! ! !BlockClosure methodsFor: '*sunit-preload' stamp: 'rw 1/23/2002 00:28'! sunitOn: anException do: aHandlerBlock ^self on: anException do: aHandlerBlock! ! !BlockClosure methodsFor: 'copying' stamp: 'eem 5/28/2008 14:53'! postCopy "To render a copy safe we need to provide a new outerContext that cannot be returned from and a copy of any remoteTemp vectors. When a block is active it makes no reference to state in its nested contexts (this is the whole point of the indirect temps scheme; any indirect state is either copied or in indirect temp vectors. So we need to substitute a dummy outerContext and copy the copiedValues, copying anything looking like a remote temp vector. if we accidentally copy an Array that isn't actually an indirect temp vector we do extra work but don't break anything." outerContext := MethodContext sender: nil receiver: outerContext receiver method: outerContext method arguments: #(). self fixTemps! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 16:58'! supportTestSourceRangeAccessForDecompiledInjectInto: method source: source "Test debugger source range selection for inject:into:" ^self supportTestSourceRangeAccessForInjectInto: method source: source selectionSequence: #( ':= t1' 'do: [:t4 | t3 := t2 value: t3 value: t4]' 'value: t3 value: t4' ':= t2 value: t3 value: t4' ']' 'value: t3 value: t4' ':= t2 value: t3 value: t4' ']' '^t3')! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 19:44'! supportTestSourceRangeAccessForDecompiledNoBytecodeInjectInto: method source: source "Test debugger source range selection for inject:into:" ^self supportTestSourceRangeAccessForInjectInto: method source: source selectionSequence: #( 'at: 1 put: t1' 'do: [:t4 | t3 at: 1 put: (t2 value: (t3 at: 1) value: t4)]' 'value: (t3 at: 1) value: t4' 'at: 1 put: (t2 value: (t3 at: 1) value: t4)' ']' 'value: (t3 at: 1) value: t4' 'at: 1 put: (t2 value: (t3 at: 1) value: t4)' ']' '^t3 at: 1')! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 16:56'! supportTestSourceRangeAccessForInjectInto: method source: source "Test debugger source range selection for inject:into:" ^self supportTestSourceRangeAccessForInjectInto: method source: source selectionSequence: #( ':= thisValue' 'do: [:each | nextValue := binaryBlock value: nextValue value: each]' 'value: nextValue value: each' ':= binaryBlock value: nextValue value: each' ']' 'value: nextValue value: each' ':= binaryBlock value: nextValue value: each' ']' '^nextValue')! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 7/29/2008 17:16'! supportTestSourceRangeAccessForInjectInto: method source: source selectionSequence: selections "Test debugger source range selection for inject:into:" | evaluationCount sourceMap debugTokenSequence debugCount | DebuggerMethodMap voidMapCache. evaluationCount := 0. sourceMap := method debuggerMap abstractSourceMap. debugTokenSequence := selections collect: [:string| Scanner new scanTokens: string]. debugCount := 0. thisContext runSimulated: [(1 to: 2) withArgs: { 0. [:sum :each| evaluationCount := evaluationCount + 1. sum + each]} executeMethod: method] contextAtEachStep: [:ctxt| | range debugTokens | (ctxt method == method and: ["Exclude the send of #blockCopy: or #closureCopy:copiedValues: and braceWith:with: to create the block, and the #new: and #at:'s for the indirect temp vector. This for compilation without closure bytecodes. (Note that at:put:'s correspond to stores)" (ctxt willSend and: [(#(closureCopy:copiedValues: blockCopy: new: at: braceWith:with:) includes: ctxt selectorToSendOrSelf) not]) "Exclude the store of the argument into the home context (for BlueBook blocks) and the store of an indirection vector into an initial temp" or: [(ctxt willStore and: [(ctxt isBlock and: [ctxt pc = ctxt startpc]) not and: [(ctxt isBlock not and: [(method usesClosureBytecodes and: [ctxt abstractPC = 2])]) not]]) or: [ctxt willReturn]]]) ifTrue: [debugTokens := debugTokenSequence at: (debugCount := debugCount + 1) ifAbsent: [#(bogusToken)]. self assert: (sourceMap includesKey: ctxt abstractPC). range := sourceMap at: ctxt abstractPC ifAbsent: [(1 to: 0)]. self assert: (Scanner new scanTokens: (source copyFrom: range first to: range last)) = debugTokens]]. self assert: evaluationCount = 2! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/3/2008 12:34'! testBlockNumbering "Test that the compiler and CompiledMethod agree on the block numbering of a substantial doit." "self new testBlockNumbering" | methodNode method tempRefs | methodNode := Parser new encoderClass: EncoderForV3PlusClosures; parse: 'foo | numCopiedValuesCounts | numCopiedValuesCounts := Dictionary new. 0 to: 32 do: [:i| numCopiedValuesCounts at: i put: 0]. Transcript clear. Smalltalk allClasses remove: GeniePlugin; do: [:c| {c. c class} do: [:b| Transcript nextPut: b name first; endEntry. b selectorsAndMethodsDo: [:s :m| | pn | m isQuick not ifTrue: [pn := b parserClass new encoderClass: EncoderForV3PlusClosures; parse: (b sourceCodeAt: s) class: b. pn generate: #(0 0 0 0). [pn accept: nil] on: MessageNotUnderstood do: [:ex| | msg numCopied | msg := ex message. (msg selector == #visitBlockNode: and: [(msg argument instVarNamed: ''optimized'') not]) ifTrue: [numCopied := (msg argument computeCopiedValues: pn) size. numCopiedValuesCounts at: numCopied put: (numCopiedValuesCounts at: numCopied) + 1]. msg setSelector: #==. ex resume: nil]]]]]. numCopiedValuesCounts' class: Object. method := methodNode generate: #(0 0 0 0). tempRefs := methodNode encoder blockExtentsToTempRefs. self assert: tempRefs keys = method startpcsToBlockExtents values asSet! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/3/2008 13:03'! testBlockNumberingForInjectInto "Test that the compiler and CompiledMethod agree on the block numbering of Collection>>inject:into: and that temp names for inject:into: are recorded." "self new testBlockNumberingForInjectInto" | methodNode method tempRefs | methodNode := Parser new encoderClass: EncoderForV3PlusClosures; parse: (Collection sourceCodeAt: #inject:into:) class: Collection. method := methodNode generate: #(0 0 0 0). tempRefs := methodNode encoder blockExtentsToTempRefs. self assert: tempRefs keys = method startpcsToBlockExtents values asSet. self assert: ((tempRefs includesKey: (0 to: 6)) and: [(tempRefs at: (0 to: 6)) hasEqualElements: {'thisValue'. 'binaryBlock'. OrderedCollection with: 'nextValue'}]). self assert: ((tempRefs includesKey: (2 to: 4)) and: [(tempRefs at: (2 to: 4)) hasEqualElements: {'each'. 'binaryBlock'. OrderedCollection with: 'nextValue'}])! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 16:57'! testInjectIntoDecompilations "Test various compilations decompile to the same code for a method sufficiently simple that this is possible and sufficiently complex that the code generated varies between the compilations." "self new testInjectIntoDecompilations" | source | source := (Collection sourceCodeAt: #inject:into:) asString. { Encoder. EncoderForV3. EncoderForLongFormV3. EncoderForV3PlusClosures. EncoderForLongFormV3PlusClosures } do: [:encoderClass| | method | method := (Parser new encoderClass: encoderClass; parse: source class: Collection) generate: #(0 0 0 0). self assert: (Scanner new scanTokens: method decompileString) = #(inject: t1 into: t2 | t3 | t3 ':=' t1 . self do: [ ':t4' | t3 ':=' t2 value: t3 value: t4 ] . ^ t3)]! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 7/29/2008 17:17'! testInjectIntoDecompiledDebugs "Test various debugs of the decompiled form debug correctly." "self new testInjectIntoDecompiledDebugs" | source | source := (Collection sourceCodeAt: #inject:into:) asString. { Encoder. EncoderForV3PlusClosures. EncoderForLongFormV3PlusClosures } do: [:encoderClass| | method | method := (Parser new encoderClass: encoderClass; parse: source class: Collection) generate: #(0 0 0 0). self supportTestSourceRangeAccessForDecompiledInjectInto: method source: method decompileString]! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 15:20'! testSourceRangeAccessForBlueBookInjectInto "Test debugger source range selection for inject:into: for a version compiled with closures" "self new testSourceRangeAccessForBlueBookInjectInto" | source method | source := (Collection sourceCodeAt: #inject:into:) asString. method := (Parser new encoderClass: EncoderForV3; parse: source class: Collection) generate: (Collection compiledMethodAt: #inject:into:) trailer. self supportTestSourceRangeAccessForInjectInto: method source: source! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 15:20'! testSourceRangeAccessForBlueBookLongFormInjectInto "Test debugger source range selection for inject:into: for a version compiled with closures" "self new testSourceRangeAccessForBlueBookLongFormInjectInto" | source method | source := (Collection sourceCodeAt: #inject:into:) asString. method := (Parser new encoderClass: EncoderForLongFormV3; parse: source class: Collection) generate: (Collection compiledMethodAt: #inject:into:) trailer. self supportTestSourceRangeAccessForInjectInto: method source: source! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 15:20'! testSourceRangeAccessForClosureBytecodeInjectInto "Test debugger source range selection for inject:into: for a version compiled with closures" "self new testSourceRangeAccessForClosureBytecodeInjectInto" | source method | source := (Collection sourceCodeAt: #inject:into:) asString. method := (Parser new encoderClass: EncoderForV3PlusClosures; parse: source class: Collection) generate: (Collection compiledMethodAt: #inject:into:) trailer. self supportTestSourceRangeAccessForInjectInto: method source: source! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 15:20'! testSourceRangeAccessForClosureLongFormBytecodeInjectInto "Test debugger source range selection for inject:into: for a version compiled with closures" "self new testSourceRangeAccessForClosureLongFormBytecodeInjectInto" | source method | source := (Collection sourceCodeAt: #inject:into:) asString. method := (Parser new encoderClass: EncoderForLongFormV3PlusClosures; parse: source class: Collection) generate: (Collection compiledMethodAt: #inject:into:) trailer. self supportTestSourceRangeAccessForInjectInto: method source: source! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/4/2008 11:40'! testSourceRangeAccessForInjectInto "Test debugger source range selection for inject:into: for the current version of the method" "self new testSourceRangeAccessForInjectInto" self supportTestSourceRangeAccessForInjectInto: (Collection compiledMethodAt: #inject:into:) source: (Collection sourceCodeAt: #inject:into:) asString! ! !ClosureCompilerTest methodsFor: 'tests' stamp: 'eem 6/15/2008 11:26'! testTempNameAccessForInjectInto "self new testTempNameAccessForInjectInto" | methodNode method evaluationCount block debuggerMap | methodNode := Parser new encoderClass: EncoderForV3PlusClosures; parse: (Collection sourceCodeAt: #inject:into:) class: Collection. method := methodNode generate: #(0 0 0 0). debuggerMap := DebuggerMethodMap forMethod: method methodNode: methodNode. evaluationCount := 0. block := [:prev :each| | theContext tempNames | evaluationCount := evaluationCount + 1. theContext := thisContext sender. tempNames := debuggerMap tempNamesForContext: theContext. self assert: (tempNames hasEqualElements: tempNames). #('thisValue' 'each' 'binaryBlock' 'nextValue') with: { 0. each. block. prev} do: [:tempName :value| self assert: (debuggerMap namedTempAt: (tempNames indexOf: tempName) in: theContext) == value. tempName ~= 'each' ifTrue: [self assert: (debuggerMap namedTempAt: (tempNames indexOf: tempName) in: theContext home) == value]]]. (1 to: 10) withArgs: { 0. block } executeMethod: method. self assert: evaluationCount = 10! ! !BlockLocalTempCounter methodsFor: 'initialize-release' stamp: 'eem 9/26/2008 13:40'! tempCountForBlockAt: pc in: method "Compute the number of local temporaries in a block. If the block begins with a sequence of push: nil bytecodes then some of These could be initializing local temps. We can only reliably disambuguate them from other uses of nil by parsing the stack and seeing what the offset of the stack pointer is at the end of the block. There are short-cuts. The ones we take here are - if there is no sequence of push nils there can be no local temps - we follow forward jumps to shorten the amount of scanning" stackPointer := 0. scanner := InstructionStream new method: method pc: pc. scanner interpretNextInstructionFor: self. blockEnd isNil ifTrue: [self error: 'pc is not that of a block']. scanner nextByte = Encoder pushNilCode ifTrue: [joinOffsets := Dictionary new. [scanner pc < blockEnd] whileTrue: [scanner interpretNextInstructionFor: self]]. ^stackPointer! ! !BlockLocalTempCounter methodsFor: 'initialize-release' stamp: 'eem 9/26/2008 13:41'! testTempCountForBlockAt: startPc in: method "Compute the number of local temporaries in a block. If the block begins with a sequence of push: nil bytecodes then some of These could be initializing local temps. We can only reliably disambuguate them from other uses of nil by parsing the stack and seeing what the offset of the stack pointer is at the end of the block.There are short-cuts. The only one we take here is - if there is no sequence of push nils there can be no local temps" | symbolicLines line prior thePc | symbolicLines := Dictionary new. method symbolicLinesDo: [:pc :lineForPC| symbolicLines at: pc put: lineForPC]. stackPointer := 0. scanner := InstructionStream new method: method pc: startPc. scanner interpretNextInstructionFor: self. blockEnd isNil ifTrue: [self error: 'pc is not that of a block']. scanner nextByte = Encoder pushNilCode ifTrue: [joinOffsets := Dictionary new. [scanner pc < blockEnd] whileTrue: [line := symbolicLines at: scanner pc. prior := stackPointer. thePc := scanner pc. scanner interpretNextInstructionFor: self. Transcript cr; print: prior; nextPutAll: '->'; print: stackPointer; tab; print: thePc; tab; nextPutAll: line; flush]]. ^stackPointer! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 11:36'! blockReturnTop "Return Top Of Stack bytecode." stackPointer := stackPointer - 1. scanner pc < blockEnd ifTrue: [self doJoin]! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:13'! doDup "Duplicate Top Of Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:17'! doPop "Remove Top Of Stack bytecode." stackPointer := stackPointer - 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 13:40'! jump: offset "Unconditional Jump bytecode." offset > 0 ifTrue: [joinOffsets at: scanner pc + offset put: stackPointer. self doJoin]! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 13:40'! jump: offset if: condition "Conditional Jump bytecode." stackPointer := stackPointer - 1. offset > 0 ifTrue: [joinOffsets at: scanner pc + offset put: stackPointer]! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 11:36'! methodReturnConstant: value "Return Constant bytecode." self doJoin! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 11:36'! methodReturnReceiver "Return Self bytecode." self doJoin! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 11:36'! methodReturnTop "Return Top Of Stack bytecode." stackPointer := stackPointer - 1. self doJoin! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:19'! popIntoLiteralVariable: anAssociation "Remove Top Of Stack And Store Into Literal Variable bytecode." stackPointer := stackPointer - 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:19'! popIntoReceiverVariable: offset "Remove Top Of Stack And Store Into Instance Variable bytecode." stackPointer := stackPointer - 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:19'! popIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Remove Top Of Stack And Store Into Offset of Temp Vector bytecode." stackPointer := stackPointer - 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:20'! popIntoTemporaryVariable: offset "Remove Top Of Stack And Store Into Temporary Variable bytecode." stackPointer := stackPointer - 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:20'! pushActiveContext "Push Active Context On Top Of Its Own Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:16'! pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize "Push Closure bytecode. Either compute the end of the block if this is the block we're analysing, or skip it, adjusting the stack as appropriate." blockEnd ifNil: [blockEnd := scanner pc + blockSize] ifNotNil: [stackPointer := stackPointer - numCopied + 1. scanner pc: scanner pc + blockSize]! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:21'! pushConsArrayWithElements: numElements "Push Cons Array of size numElements popping numElements items from the stack into the array bytecode." stackPointer := stackPointer - numElements + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:21'! pushConstant: value "Push Constant, value, on Top Of Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:22'! pushLiteralVariable: anAssociation "Push Contents Of anAssociation On Top Of Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:22'! pushNewArrayOfSize: numElements "Push New Array of size numElements bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:22'! pushReceiver "Push Active Context's Receiver on Top Of Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:23'! pushReceiverVariable: offset "Push Contents Of the Receiver's Instance Variable Whose Index is the argument, offset, On Top Of Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:23'! pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Push Contents at Offset in Temp Vector bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:23'! pushTemporaryVariable: offset "Push Contents Of Temporary Variable Whose Index Is the argument, offset, On Top Of Stack bytecode." stackPointer := stackPointer + 1! ! !BlockLocalTempCounter methodsFor: 'instruction decoding' stamp: 'eem 9/23/2008 16:24'! send: selector super: supered numArgs: numberArguments "Send Message With Selector, selector, bytecode. The argument, supered, indicates whether the receiver of the message is specified with 'super' in the source method. The arguments of the message are found in the top numArguments locations on the stack and the receiver just below them." stackPointer := stackPointer - numberArguments! ! !BlockLocalTempCounter methodsFor: 'private' stamp: 'eem 9/26/2008 13:40'! doJoin scanner pc < blockEnd ifTrue: [stackPointer := joinOffsets at: scanner pc]! ! !BlockStartLocator methodsFor: 'initialize-release' stamp: 'eem 5/31/2008 13:43'! initialize nextJumpIsAroundBlock := false! ! !BlockStartLocator methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:48'! jump: offset "If this jump is around a block answer the size of that block." nextJumpIsAroundBlock ifTrue: [nextJumpIsAroundBlock := false. ^offset]! ! !BlockStartLocator methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:54'! pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize "Answer the size of the block" ^blockSize! ! !BlockStartLocator methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 14:16'! send: selector super: supered numArgs: numberArguments nextJumpIsAroundBlock := #closureCopy:copiedValues: == selector "Don't use nextJumpIsAroundBlock := #(blockCopy: closureCopy:copiedValues:) includes: selector since BlueBook BlockContexts do not have their own temps."! ! !DecompilerConstructorForClosures methodsFor: 'constructor' stamp: 'eem 9/25/2008 10:34'! codeMethod: selector block: block tempVars: vars primitive: primitive class: class | node visibleTemps invisibleTemps arguments temporaries | node := self codeSelector: selector code: nil. tempVars := vars. visibleTemps := OrderedCollection new. invisibleTemps := OrderedCollection new. tempVars do: [:t| ((t isIndirectTempVector or: [t scope >= 0]) ifTrue: [visibleTemps] ifFalse: [invisibleTemps]) addLast: t]. arguments := visibleTemps copyFrom: 1 to: nArgs. temporaries := visibleTemps copyFrom: nArgs + 1 to: visibleTemps size. block arguments: arguments; temporaries: temporaries. ^BytecodeAgnosticMethodNode new selector: node arguments: arguments precedence: selector precedence temporaries: temporaries block: block encoder: (EncoderForV3PlusClosures new initScopeAndLiteralTables temps: visibleTemps, invisibleTemps literals: literalValues class: class) primitive: primitive properties: method properties! ! !DecompilerConstructorForClosures methodsFor: 'constructor' stamp: 'eem 10/20/2008 13:01'! codeRemoteTemp: index remoteTemps: tempVector ^(RemoteTempVectorNode new name: '_r', index printString index: index type: LdTempType scope: 0) remoteTemps: tempVector; yourself! ! !DecompilerConstructorForClosures methodsFor: 'testing' stamp: 'eem 6/4/2008 14:41'! isForClosures ^true! ! !EncoderForLongFormV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:10'! genPushClosureCopyNumCopiedValues: numCopied numArgs: numArgs jumpSize: jumpSize "143 10001111 llllkkkk jjjjjjjj iiiiiiii Push Closure Num Copied llll Num Args kkkk BlockSize jjjjjjjjiiiiiiii" (jumpSize < 0 or: [jumpSize > 65535]) ifTrue: [^self outOfRangeError: 'block size' index: jumpSize range: 0 to: 65535]. (numCopied < 0 or: [numCopied > 15]) ifTrue: [^self outOfRangeError: 'num copied' index: numCopied range: 0 to: 15]. (numArgs < 0 or: [numArgs > 15]) ifTrue: [^self outOfRangeError: 'num args' index: numArgs range: 0 to: 15]. stream nextPut: 143; nextPut: numArgs + (numCopied bitShift: 4); nextPut: (jumpSize bitShift: -8); nextPut: (jumpSize bitAnd: 16rFF)! ! !EncoderForLongFormV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:06'! genPushConsArray: size (size < 0 or: [size > 127]) ifTrue: [^self outOfRangeError: 'numElements' index: size range: 0 to: 127]. "138 10001010 1kkkkkkk Pop kkkkkkk into: (Array new: kkkkkkk)" stream nextPut: 138; nextPut: size + 128! ! !EncoderForLongFormV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:05'! genPushNewArray: size (size < 0 or: [size > 127]) ifTrue: [^self outOfRangeError: 'size' index: size range: 0 to: 127]. "138 10001010 0kkkkkkk Push (Array new: kkkkkkk)" stream nextPut: 138; nextPut: size! ! !EncoderForLongFormV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 6/16/2008 09:45'! genPushRemoteTemp: tempIndex inVectorAt: tempVectorIndex (tempIndex >= 0 and: [tempIndex < 256 and: [tempVectorIndex >= 0 and: [tempVectorIndex < 256]]]) ifTrue: ["140 10001100 kkkkkkkk jjjjjjjj Push Temp At kkkkkkkk In Temp Vector At: jjjjjjjj" stream nextPut: 140; nextPut: tempIndex; nextPut: tempVectorIndex. ^self]. tempIndex >= 256 ifTrue: [^self outOfRangeError: 'remoteTempIndex' index: tempIndex range: 0 to: 255]. tempVectorIndex >= 256 ifTrue: [^self outOfRangeError: 'tempVectorIndex' index: tempVectorIndex range: 0 to: 255]! ! !EncoderForLongFormV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:04'! genStorePopRemoteTemp: tempIndex inVectorAt: tempVectorIndex "142 10001110 kkkkkkkk jjjjjjjj Pop and Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj" (tempIndex >= 0 and: [tempIndex < 256 and: [tempVectorIndex >= 0 and: [tempVectorIndex < 256]]]) ifTrue: [stream nextPut: 142; nextPut: tempIndex; nextPut: tempVectorIndex. ^self]. tempIndex >= 256 ifTrue: [^self outOfRangeError: 'remoteTempIndex' index: tempIndex range: 0 to: 255]. tempVectorIndex >= 256 ifTrue: [^self outOfRangeError: 'tempVectorIndex' index: tempVectorIndex range: 0 to: 255]! ! !EncoderForLongFormV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:04'! genStoreRemoteTemp: tempIndex inVectorAt: tempVectorIndex "141 10001101 kkkkkkkk jjjjjjjj Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj" (tempIndex >= 0 and: [tempIndex < 256 and: [tempVectorIndex >= 0 and: [tempVectorIndex < 256]]]) ifTrue: [stream nextPut: 141; nextPut: tempIndex; nextPut: tempVectorIndex. ^self]. tempIndex >= 256 ifTrue: [^self outOfRangeError: 'remoteTempIndex' index: tempIndex range: 0 to: 255]. tempVectorIndex >= 256 ifTrue: [^self outOfRangeError: 'tempVectorIndex' index: tempVectorIndex range: 0 to: 255]! ! !EncoderForLongFormV3PlusClosures methodsFor: 'testing' stamp: 'eem 5/24/2008 18:12'! supportsClosureOpcodes ^true! ! !EncoderForV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:11'! genPushClosureCopyNumCopiedValues: numCopied numArgs: numArgs jumpSize: jumpSize "143 10001111 llllkkkk jjjjjjjj iiiiiiii Push Closure Num Copied llll Num Args kkkk BlockSize jjjjjjjjiiiiiiii" (jumpSize < 0 or: [jumpSize > 65535]) ifTrue: [^self outOfRangeError: 'block size' index: jumpSize range: 0 to: 65535]. (numCopied < 0 or: [numCopied > 15]) ifTrue: [^self outOfRangeError: 'num copied' index: numCopied range: 0 to: 15]. (numArgs < 0 or: [numArgs > 15]) ifTrue: [^self outOfRangeError: 'num args' index: numArgs range: 0 to: 15]. stream nextPut: 143; nextPut: numArgs + (numCopied bitShift: 4); nextPut: (jumpSize bitShift: -8); nextPut: (jumpSize bitAnd: 16rFF)! ! !EncoderForV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:03'! genPushConsArray: size (size < 0 or: [size > 127]) ifTrue: [^self outOfRangeError: 'numElements' index: size range: 0 to: 127]. "138 10001010 1kkkkkkk Push (Array new: kkkkkkk)" stream nextPut: 138; nextPut: size + 128! ! !EncoderForV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:06'! genPushNewArray: size (size < 0 or: [size > 127]) ifTrue: [^self outOfRangeError: 'numElements' index: size range: 0 to: 127]. "138 10001010 0kkkkkkk Pop kkkkkkk into: (Array new: kkkkkkk)" stream nextPut: 138; nextPut: size! ! !EncoderForV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 6/16/2008 09:45'! genPushRemoteTemp: tempIndex inVectorAt: tempVectorIndex (tempIndex >= 0 and: [tempIndex < 256 and: [tempVectorIndex >= 0 and: [tempVectorIndex < 256]]]) ifTrue: ["140 10001100 kkkkkkkk jjjjjjjj Push Temp At kkkkkkkk In Temp Vector At: jjjjjjjj" stream nextPut: 140; nextPut: tempIndex; nextPut: tempVectorIndex. ^self]. tempIndex >= 256 ifTrue: [^self outOfRangeError: 'remoteTempIndex' index: tempIndex range: 0 to: 255]. tempVectorIndex >= 256 ifTrue: [^self outOfRangeError: 'tempVectorIndex' index: tempVectorIndex range: 0 to: 255]! ! !EncoderForV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:02'! genStorePopRemoteTemp: tempIndex inVectorAt: tempVectorIndex "142 10001110 kkkkkkkk jjjjjjjj Pop and Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj" (tempIndex >= 0 and: [tempIndex < 256 and: [tempVectorIndex >= 0 and: [tempVectorIndex < 256]]]) ifTrue: [stream nextPut: 142; nextPut: tempIndex; nextPut: tempVectorIndex. ^self]. tempIndex >= 256 ifTrue: [^self outOfRangeError: 'remoteTempIndex' index: tempIndex range: 0 to: 255]. tempVectorIndex >= 256 ifTrue: [^self outOfRangeError: 'tempVectorIndex' index: tempVectorIndex range: 0 to: 255]! ! !EncoderForV3PlusClosures methodsFor: 'bytecode generation' stamp: 'eem 5/30/2008 17:02'! genStoreRemoteTemp: tempIndex inVectorAt: tempVectorIndex "141 10001101 kkkkkkkk jjjjjjjj Store Temp At kkkkkkkk In Temp Vector At: jjjjjjjj" (tempIndex >= 0 and: [tempIndex < 256 and: [tempVectorIndex >= 0 and: [tempVectorIndex < 256]]]) ifTrue: [stream nextPut: 141; nextPut: tempIndex; nextPut: tempVectorIndex. ^self]. tempIndex >= 256 ifTrue: [^self outOfRangeError: 'remoteTempIndex' index: tempIndex range: 0 to: 255]. tempVectorIndex >= 256 ifTrue: [^self outOfRangeError: 'tempVectorIndex' index: tempVectorIndex range: 0 to: 255]! ! !EncoderForV3PlusClosures methodsFor: 'testing' stamp: 'eem 5/24/2008 18:12'! supportsClosureOpcodes ^true! ! !NewArrayNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/25/2008 14:58'! emitCodeForValue: stack encoder: encoder encoder genPushNewArray: numElements. stack push: 1! ! !NewArrayNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/25/2008 14:58'! sizeCodeForValue: encoder ^encoder sizePushNewArray: numElements! ! !NewArrayNode methodsFor: 'accessing' stamp: 'eem 5/25/2008 14:58'! numElements ^numElements! ! !NewArrayNode methodsFor: 'accessing' stamp: 'eem 5/25/2008 14:59'! numElements: n numElements := n! ! !NewArrayNode methodsFor: 'code generation (closures)' stamp: 'eem 6/16/2008 09:31'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" "This is a no-op except in TempVariableNode" ^self! ! !RegenerationPreparingVisitor methodsFor: 'visiting' stamp: 'eem 9/10/2008 15:13'! visitBlockNode: aBlockNode aBlockNode cleanUpForRegeneration. super visitBlockNode: aBlockNode! ! !RegenerationPreparingVisitor methodsFor: 'visiting' stamp: 'eem 7/2/2008 12:18'! visitTempVariableNode: aTempNode aTempNode cleanUpForRegeneration! ! !TempNumberNormalizingVisitor methodsFor: 'initialize-release' stamp: 'eem 9/23/2008 21:42'! initialize count := 0. temps := IdentitySet new! ! !TempNumberNormalizingVisitor methodsFor: 'visiting' stamp: 'eem 9/25/2008 11:52'! visitBlockNode: aBlockNode aBlockNode arguments do: [:tempNode| self renumberTemp: tempNode]. aBlockNode temporaries do: [:tempNode| (tempNode scope >= 0 or: [tempNode isIndirectTempVector]) ifTrue: [self renumberTemp: tempNode]]. super visitBlockNode: aBlockNode! ! !TempNumberNormalizingVisitor methodsFor: 'visiting' stamp: 'eem 9/23/2008 21:48'! visitTempVariableNode: aTempVariableNode self renumberTemp: aTempVariableNode! ! !TempNumberNormalizingVisitor methodsFor: 'private' stamp: 'eem 9/25/2008 09:50'! renumberTemp: tempNode | name newName | tempNode isIndirectTempVector ifTrue: [tempNode remoteTemps do: [:remoteTempNode| self renumberTemp: remoteTempNode]. ^self]. (temps includes: tempNode) ifTrue: [^self]. name := tempNode key. name size >= 2 ifFalse: [^self]. name first = $t ifFalse: [^self]. 2 to: name size do: [:i| (name at: i) isDigit ifFalse: [^self]]. newName := 't', (count := count + 1) printString. tempNode name: newName key: newName code: tempNode code. temps add: tempNode ! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/29/2008 16:19'! addRemoteTemp: aTempVariableNode encoder: encoder remoteTemps isNil ifTrue: [remoteTemps := OrderedCollection new]. remoteTemps addLast: aTempVariableNode. aTempVariableNode referenceScopesAndIndicesDo: [:scopeBlock "" :location ""| self addReadWithin: scopeBlock at: location]. encoder supportsClosureOpcodes ifFalse: [encoder encodeLiteral: remoteTemps size. readNode := encoder encodeSelector: #at:. writeNode := encoder encodeSelector: #at:put:]! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:20'! emitCodeForIndexOf: aTempVariableNode stack: stack encoder: encoder self assert: encoder supportsClosureOpcodes not. (encoder encodeLiteral: (remoteTemps indexOf: aTempVariableNode)) emitCodeForValue: stack encoder: encoder! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:19'! emitCodeForLoadFor: aTempVariableNode stack: stack encoder: encoder encoder supportsClosureOpcodes ifTrue: [^self]. "Need to generate the first half of tempVector at: index put: expr i.e. the push of tempVector and index." super emitCodeForValue: stack encoder: encoder. self emitCodeForIndexOf: aTempVariableNode stack: stack encoder: encoder! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 23:02'! emitCodeForStoreInto: aTempVariableNode stack: stack encoder: encoder encoder supportsClosureOpcodes ifTrue: [encoder genStoreRemoteTemp: (remoteTemps indexOf: aTempVariableNode) - 1 inVectorAt: index] ifFalse: [writeNode emitCode: stack args: 2 encoder: encoder super: false]! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 23:02'! emitCodeForStorePopInto: aTempVariableNode stack: stack encoder: encoder encoder supportsClosureOpcodes ifTrue: [encoder genStorePopRemoteTemp: (remoteTemps indexOf: aTempVariableNode) - 1 inVectorAt: index] ifFalse: [self emitCodeForStoreInto: aTempVariableNode stack: stack encoder: encoder. encoder genPop]. stack pop: 1! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 23:20'! emitCodeForValueOf: aTempVariableNode stack: stack encoder: encoder encoder supportsClosureOpcodes ifTrue: [encoder genPushRemoteTemp: (remoteTemps indexOf: aTempVariableNode) - 1 inVectorAt: index. stack push: 1] ifFalse: [self emitCodeForLoadFor: aTempVariableNode stack: stack encoder: encoder. readNode emitCode: stack args: 1 encoder: encoder super: false]! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 6/2/2008 16:50'! isIndirectTempVector ^true! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 9/25/2008 17:16'! nodeToInitialize: encoder ^AssignmentNode new variable: self value: (encoder supportsClosureOpcodes ifTrue: [NewArrayNode new numElements: remoteTemps size] ifFalse: [MessageNode new receiver: (encoder encodeVariable: 'Array') selector: #new: arguments: (Array with: (encoder encodeLiteral: remoteTemps size)) precedence: 3 from: encoder])! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 17:57'! referenceScopesAndIndicesDo: aBinaryBlock self shouldNotImplement! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 9/8/2008 10:47'! scope "Answer scope of temporary variables. Currently only the following distinctions are made: 0 outer level: args and user-declared temps 1 block args and doLimiT temps -1 a block temp that is no longer active -2 a block temp that held limit of to:do: -3 an indirect temp vector" ^-3! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:20'! sizeCodeForIndexOf: aTempVariableNode encoder: encoder self assert: encoder supportsClosureOpcodes not. ^(encoder encodeLiteral: (remoteTemps indexOf: aTempVariableNode)) sizeCodeForValue: encoder! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:19'! sizeCodeForLoadFor: aTempVariableNode encoder: encoder encoder supportsClosureOpcodes ifTrue: [^0]. "Need to size the first half of tempVector at: index put: expr i.e. the push of tempVector and index." ^(super sizeCodeForValue: encoder) + (self sizeCodeForIndexOf: aTempVariableNode encoder: encoder)! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:24'! sizeCodeForStoreInto: aTempVariableNode encoder: encoder encoder supportsClosureOpcodes ifTrue: [^encoder sizeStoreRemoteTemp: (remoteTemps indexOf: aTempVariableNode) - 1 inVectorAt: index]. ^writeNode sizeCode: encoder args: 2 super: false! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:23'! sizeCodeForStorePopInto: aTempVariableNode encoder: encoder encoder supportsClosureOpcodes ifTrue: [^encoder sizeStorePopRemoteTemp: (remoteTemps indexOf: aTempVariableNode) - 1 inVectorAt: index]. ^(self sizeCodeForStoreInto: aTempVariableNode encoder: encoder) + encoder sizePop! ! !RemoteTempVectorNode methodsFor: 'code generation (closures)' stamp: 'eem 5/24/2008 18:23'! sizeCodeForValueOf: aTempVariableNode encoder: encoder encoder supportsClosureOpcodes ifTrue: [^encoder sizePushRemoteTemp: (remoteTemps indexOf: aTempVariableNode) - 1 inVectorAt: index]. ^(self sizeCodeForValue: encoder) + (self sizeCodeForIndexOf: aTempVariableNode encoder: encoder) + (readNode sizeCode: encoder args: 1 super: false)! ! !RemoteTempVectorNode methodsFor: 'accessing' stamp: 'eem 6/2/2008 16:47'! remoteTemps ^remoteTemps! ! !RemoteTempVectorNode methodsFor: 'debugger access' stamp: 'eem 6/21/2008 13:34'! cleanUpForRegeneration super cleanUpForRegeneration. remoteTemps := nil! ! !RemoteTempVectorNode methodsFor: 'printing' stamp: 'eem 7/23/2008 21:21'! printDefinitionForClosureAnalysisOn: aStream | refs | aStream nextPut: ${; nextPutAll: key. definingScope ifNotNil: [definingScope blockExtent ifNotNil: [:be| aStream nextPutAll: ' d@'; print: be first]]. readingScopes notNil ifTrue: [refs := Set new. readingScopes do: [:elems| refs addAll: elems]. refs asSortedCollection do: [:read| aStream nextPutAll: ' r@'; print: read]]. remoteTemps do: [:rt| rt printDefinitionForClosureAnalysisOn: aStream] separatedBy: [aStream nextPut: $,; space]. aStream nextPut: $}! ! !RemoteTempVectorNode methodsFor: 'decompiler' stamp: 'eem 9/25/2008 09:46'! remoteTemps: anArray remoteTemps := anArray. anArray do: [:tempNode| tempNode remoteNode: self]! ! !Object methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream "Append to the argument, aStream, a sequence of characters that identifies the receiver." | title | title := self class name. aStream nextPutAll: (title first isVowel ifTrue: ['an '] ifFalse: ['a ']); nextPutAll: title! ! !Object methodsFor: 'testing' stamp: 'eem 5/23/2008 13:47'! isClosure ^false! ! !Behavior methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream "Refer to the comment in Object|printOn:." aStream nextPutAll: 'a descendent of '. superclass printWithClosureAnalysisOn: aStream! ! !ClassDescription methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream aStream nextPutAll: self name! ! !BlockClosure class methodsFor: 'instance creation' stamp: 'eem 9/3/2008 14:02'! outerContext: aContext startpc: aStartpc numArgs: argCount copiedValues: anArrayOrNil ^(self new: anArrayOrNil basicSize) outerContext: aContext startpc: aStartpc numArgs: argCount copiedValues: anArrayOrNil! ! !ClosureCompilerTest class methodsFor: 'code examples' stamp: 'eem 5/19/2008 14:24'! methodWithOptimizedBlocks | s c | s := self isNil ifTrue: [| a | a := 'isNil'. a] ifFalse: [| b | b := 'notNil'. b]. c := String new: s size. 1 to: s size do: [:i| c at: i put: (s at: i)]. ^c "Parser new parse: (self class sourceCodeAt: #methodWithOptimizedBlocks) class: self class"! ! !ClosureCompilerTest class methodsFor: 'code examples' stamp: 'eem 5/19/2008 14:24'! methodWithOptimizedBlocksA | s c | s := self isNil ifTrue: [| a | a := 'isNil'. a] ifFalse: [| a | a := 'notNil'. a]. c := String new: s size. 1 to: s size do: [:i| c at: i put: (s at: i)]. ^c "Parser new parse: (self class sourceCodeAt: #methodWithOptimizedBlocksA) class: self class"! ! !ClosureCompilerTest class methodsFor: 'code examples' stamp: 'eem 5/19/2008 14:12'! methodWithVariousTemps | classes total totalLength | classes := self withAllSuperclasses. total := totalLength := 0. classes do: [:class| | className | className := class name. total := total + 1. totalLength := totalLength + className size]. ^total -> totalLength "Parser new parse: (self class sourceCodeAt: #methodWithVariousTemps) class: self class"! ! !CompiledMethod methodsFor: 'testing' stamp: 'eem 7/29/2008 16:51'! isBlueBookCompiled "Answer whether the receiver was compiled using the closure compiler. This is used to help DebuggerMethodMap choose which mechanisms to use to inspect activations of the receiver. This method answers false negatives in that it only identifies methods that create old BlockClosures or use the new BlockClosure bytecodes. It cannot tell if a method which uses neither the old nor the new block bytecodes is compiled with the blue-book compiler or the new compiler. But since methods that don't create blocks have essentially the same code when compiled with either compiler this makes little difference." ^((InstructionStream on: self) scanFor: [:instr | (instr >= 138 and: [instr <= 143]) ifTrue: [^false]. instr = 200]) or: [(self hasLiteral: #blockCopy:) and: [self messages includes: #blockCopy:]]! ! !CompiledMethod methodsFor: 'testing' stamp: 'eem 6/4/2008 16:19'! usesClosureBytecodes "Answer whether the receiver was compiled using the closure compiler. This is used to help DebuggerMethodMap choose which mechanisms to use to inspect activations of the receiver. This method answers false negatives in that it only identifies methods that use the new BlockClosure bytecodes. But since methods that don't create blocks have essentially the same code when compiled with either compiler this makes little difference." ^(InstructionStream on: self) scanFor: [:instr | instr >= 138 and: [instr <= 143]]! ! !InstructionClient methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:51'! popIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Remove Top Of Stack And Store Into Offset of Temp Vector bytecode."! ! !InstructionClient methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:49'! pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize "Push Closure bytecode." ! ! !InstructionClient methodsFor: 'instruction decoding' stamp: 'eem 6/16/2008 14:26'! pushConsArrayWithElements: numElements "Push Cons Array of size numElements popping numElements items from the stack into the array bytecode." ! ! !InstructionClient methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:50'! pushNewArrayOfSize: numElements "Push New Array of size numElements bytecode." ! ! !InstructionClient methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:54'! pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Push Contents at Offset in Temp Vector bytecode."! ! !InstructionClient methodsFor: 'instruction decoding' stamp: 'eem 5/31/2008 13:52'! storeIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Store Top Of Stack And Store Into Offset of Temp Vector bytecode."! ! !BlockLocalTempCounter class methodsFor: 'instance creation' stamp: 'eem 9/23/2008 16:07'! tempCountForBlockAt: pc in: method ^self new tempCountForBlockAt: pc in: method! ! !InstructionPrinter methodsFor: 'instruction decoding' stamp: 'eem 5/25/2008 14:06'! popIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex self print: 'popIntoTemp: ', remoteTempIndex printString, ' inVectorAt: ', tempVectorIndex printString! ! !InstructionPrinter methodsFor: 'instruction decoding' stamp: 'eem 5/25/2008 14:06'! storeIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex self print: 'storeIntoTemp: ', remoteTempIndex printString, ' inVectorAt: ', tempVectorIndex printString! ! !InstructionStream methodsFor: 'scanning' stamp: 'eem 6/4/2008 10:57'! selectorToSendOrSelf "If this instruction is a send, answer the selector, otherwise answer self." | byte byte2 | byte := self method at: pc. byte < 131 ifTrue: [^self]. byte >= 176 ifTrue: ["special byte or short send" byte >= 208 ifTrue: [^self method literalAt: (byte bitAnd: 15) + 1] ifFalse: [^Smalltalk specialSelectorAt: byte - 176 + 1]] ifFalse: [byte <= 134 ifTrue: [byte2 := self method at: pc + 1. byte = 131 ifTrue: [^self method literalAt: byte2 \\ 32 + 1]. byte = 132 ifTrue: [byte2 < 64 ifTrue: [^self method literalAt: (self method at: pc + 2) + 1]]. byte = 133 ifTrue: [^self method literalAt: byte2 \\ 32 + 1]. byte = 134 ifTrue: [^self method literalAt: byte2 \\ 64 + 1]]]! ! !InstructionStream methodsFor: 'debugger access' stamp: 'eem 6/5/2008 10:28'! abstractPC ^self method abstractPCForConcretePC: pc! ! !ContextPart methodsFor: 'accessing' stamp: 'eem 6/15/2008 11:31'! contextForLocalVariables "Answer the context in which local variables (temporaries) are stored." self subclassResponsibility! ! !ContextPart methodsFor: 'accessing' stamp: 'eem 6/15/2008 11:27'! methodReturnContext "Answer the context from which an ^-return should return from." self subclassResponsibility! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 8/29/2008 06:28'! pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize "Simulate the action of a 'closure copy' bytecode whose result is the new BlockClosure for the following code" | copiedValues | numCopied > 0 ifTrue: [copiedValues := Array new: numCopied. numCopied to: 1 by: -1 do: [:i| copiedValues at: i put: self pop]] ifFalse: [copiedValues := nil]. self push: (BlockClosure outerContext: self startpc: pc numArgs: numArgs copiedValues: copiedValues). self jump: blockSize! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 5/27/2008 11:53'! storeIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Simulate the action of bytecode that stores the top of the stack at an offset in one of my local variables being used as a remote temp vector." (self at: tempVectorIndex + 1) at: remoteTempIndex + 1 put: self top! ! !BlockContext methodsFor: 'accessing' stamp: 'eem 5/28/2008 10:43'! activeHome "Search senders for the home context. If the home context is not found on the sender chain answer nil." ^self caller findContextSuchThat: [:ctxt | ctxt = home]! ! !BlockContext methodsFor: 'accessing' stamp: 'eem 5/24/2008 11:20'! closureHome "Answer the context from which an ^-return should return from." ^self home! ! !BlockContext methodsFor: 'accessing' stamp: 'eem 6/15/2008 11:32'! contextForLocalVariables "Answer the context in which local variables (temporaries) are stored." ^home! ! !Decompiler methodsFor: 'private' stamp: 'eem 7/29/2008 17:41'! sawBlueBookBlock constructor isForClosures ifTrue: [constructor primitiveChangeClassTo: DecompilerConstructor new]! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 6/15/2008 11:28'! activeHome "If executing closure, search senders for the activation of the original (outermost) method that (indirectly) created my closure (the closureHome). If the closureHome is not found on the sender chain answer nil." | methodReturnContext | self isExecutingBlock ifFalse: [^self]. self sender ifNil: [^nil]. methodReturnContext := self methodReturnContext. ^self sender findContextSuchThat: [:ctxt | ctxt = methodReturnContext]! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 6/15/2008 11:31'! contextForLocalVariables "Answer the context in which local variables (temporaries) are stored." ^self! ! !ParseNode methodsFor: 'testing' stamp: 'eem 6/16/2008 09:37'! isAssignmentNode ^false! ! !ParseNode methodsFor: 'code generation' stamp: 'eem 8/4/2008 13:57'! pc: anInteger "Used by encoder source mapping." pc := anInteger! ! !ParseNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:51'! printWithClosureAnalysis ^String streamContents: [:str| self printWithClosureAnalysisOn: str]! ! !ParseNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream "Refer to the comment in Object|printOn:." aStream nextPut: ${. self printWithClosureAnalysisOn: aStream indent: 0. aStream nextPut: $}.! ! !ParseNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: anInteger "If control gets here, avoid recursion loop." super printWithClosureAnalysisOn: aStream! ! !ParseNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level precedence: p self printWithClosureAnalysisOn: aStream indent: level! ! !ParseNode methodsFor: 'code generation (closures)' stamp: 'eem 5/19/2008 16:54'! ifOptimizedBlockHoistTempsInto: scopeBlock "" "This is a No-op for all nodes except non-optimized BlockNodes." ^self! ! !AssignmentNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level variable printWithClosureAnalysisOn: aStream indent: level. aStream nextPutAll: ' := '. value printWithClosureAnalysisOn: aStream indent: level + 2! ! !AssignmentNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level precedence: p aStream nextPut: $(. self printWithClosureAnalysisOn: aStream indent: level. aStream nextPut: $)! ! !AssignmentNode methodsFor: 'testing' stamp: 'eem 6/16/2008 09:37'! isAssignmentNode ^true! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 5/30/2008 12:12'! nArgsSlot "Private for the Encoder to use in bindArg" ^nArgsNode! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 5/30/2008 12:12'! nArgsSlot: anInteger "Private for the Encoder to use in bindArg" nArgsNode := anInteger! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 7/24/2008 12:37'! optimized ^optimized! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 8/4/2008 10:48'! startOfLastStatement ^startOfLastStatement! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 8/4/2008 10:50'! startOfLastStatement: anInteger "Note the source index of the start of the last full statement. The last full statement is the value answered by a block and hence the expression the debugger should display as the value of the block." startOfLastStatement := anInteger! ! !BlockNode methodsFor: 'printing' stamp: 'eem 9/23/2008 15:05'! printStatementsOn: aStream indent: levelOrZero | len shown thisStatement level | level := 1 max: levelOrZero. comment == nil ifFalse: [self printCommentOn: aStream indent: level. aStream crtab: level]. len := shown := statements size. (levelOrZero = 0 "top level" and: [statements last isReturnSelf]) ifTrue: [shown := 1 max: shown - 1] ifFalse: ["should a trailing nil be printed or not? Not if it is an implicit result." (arguments size = 0 and: [len >= 1 and: [(statements at: len) == NodeNil and: [len = 1 or: [len > 1 and: [(statements at: len - 1) isMessageNode and: [(statements at: len - 1) isNilIf]]]]]]) ifTrue: [shown := shown - 1]]. 1 to: shown do: [:i | thisStatement := statements at: i. thisStatement printOn: aStream indent: level. i < shown ifTrue: [aStream nextPut: $.; crtab: level]. (thisStatement comment ~~ nil and: [thisStatement comment size > 0]) ifTrue: [i = shown ifTrue: [aStream crtab: level]. thisStatement printCommentOn: aStream indent: level. i < shown ifTrue: [aStream crtab: level]]]! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 09:25'! computeCopiedValues: rootNode | referencedValues | referencedValues := rootNode referencedValuesWithinBlockExtent: blockExtent. ^((referencedValues reject: [:temp| temp isDefinedWithinBlockExtent: blockExtent]) asSortedCollection: [:t1 :t2 | t1 index < t2 index]) asArray! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/19/2008 17:12'! noteOptimized optimized := true! ! !BraceNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level aStream nextPut: ${. 1 to: elements size do: [:i | (elements at: i) printWithClosureAnalysisOn: aStream indent: level. i < elements size ifTrue: [aStream nextPutAll: '. ']]. aStream nextPut: $}! ! !CascadeNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level self printWithClosureAnalysisOn: aStream indent: level precedence: 0! ! !CascadeNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level precedence: p p > 0 ifTrue: [aStream nextPut: $(]. messages first printWithClosureAnalysisReceiver: receiver on: aStream indent: level. 1 to: messages size do: [:i | (messages at: i) printWithClosureAnalysisOn: aStream indent: level. i < messages size ifTrue: [aStream nextPut: $;. messages first precedence >= 2 ifTrue: [aStream crtab: level + 1]]]. p > 0 ifTrue: [aStream nextPut: $)]! ! !DecompilerConstructor methodsFor: 'constructor' stamp: 'eem 9/23/2008 22:06'! codeMethod: selector block: block tempVars: vars primitive: primitive class: class | node methodTemps arguments temporaries | node := self codeSelector: selector code: nil. tempVars := vars. methodTemps := tempVars select: [:t | t scope >= 0]. arguments := methodTemps copyFrom: 1 to: nArgs. temporaries := methodTemps copyFrom: nArgs + 1 to: methodTemps size. block arguments: arguments; temporaries: temporaries. ^MethodNode new selector: node arguments: arguments precedence: selector precedence temporaries: temporaries block: block encoder: (Encoder new initScopeAndLiteralTables temps: tempVars literals: literalValues class: class) primitive: primitive! ! !DecompilerConstructor methodsFor: 'testing' stamp: 'eem 6/4/2008 14:41'! isForClosures ^false! ! !Encoder methodsFor: 'encoding' stamp: 'eem 6/24/2008 14:23'! doItInContextName ^'_thisContext'! ! !Encoder methodsFor: 'temps' stamp: 'eem 5/30/2008 12:05'! bindBlockArg: name within: aBlockNode "With standard Smalltalk-80 (BlueBook) blocks it used to be legal to use a method temp as a block argument. This shouldn't be the case with the current compiler, which checks for temp names already being used as block arguments. But it is easily fooled by local block temps in optimized blocks, e.g. false ifTrue: [| temp |] ifFalse:[[:temp|]] Rather than fix this we keep the semantics and fix it in the closure compiler." ^self autoBind: name! ! !Encoder methodsFor: 'temps' stamp: 'eem 5/30/2008 14:14'! bindBlockTemp: name within: aBlockNode "The BlockContext compiler (the Smalltalk-80 BlueBook compiler) does provide support for ANSI block syntax, but not for ANSI block semantics. Here all temps live at the same level, the method level. The approach taken to two block-local temps in different blocks is to merge them into a single temp. e.g. expr ifTrue: [|temp| self statementOne] ifFalse: [|temp| self statementTwo] is effectvely transformed into | temp | expr ifTrue: [self statementOne] ifFalse: [self statementTwo] and expr do: [:each| | temp | ...]. expr do: [:each| | temp | ...]. is also effectively transformed into | temp | expr do: [:each| ...]. expr do: [:each| ...]. The closure compiler treats the former similarly, but not the latter. The indirection through #bindBlockTemp:within: allows the closure encoder to do this." ^self bindBlockTemp: name! ! !BytecodeEncoder methodsFor: 'opcode sizing' stamp: 'eem 5/30/2008 16:46'! sizePushClosureCopyNumCopiedValues: numCopied numArgs: numArgs jumpSize: jumpSize ^self sizeOpcodeSelector: #genPushClosureCopyNumCopiedValues:numArgs:jumpSize: withArguments: {numCopied. numArgs. jumpSize}! ! !BytecodeEncoder methodsFor: 'opcode sizing' stamp: 'eem 5/30/2008 16:36'! sizePushConsArray: numElements ^self sizeOpcodeSelector: #genPushConsArray: withArguments: {numElements}! ! !BytecodeEncoder methodsFor: 'opcode sizing' stamp: 'eem 5/24/2008 12:35'! sizePushNewArray: size ^self sizeOpcodeSelector: #genPushNewArray: withArguments: {size}! ! !BytecodeEncoder methodsFor: 'accessing' stamp: 'eem 5/24/2008 11:56'! rootNode "^" ^rootNode! ! !BytecodeEncoder methodsFor: 'accessing' stamp: 'eem 5/24/2008 11:56'! rootNode: node "" rootNode := node! ! !LiteralNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level key isVariableBinding ifTrue: [key key isNil ifTrue: [aStream nextPutAll: '###'; nextPutAll: key value soleInstance name] ifFalse: [aStream nextPutAll: '##'; nextPutAll: key key]] ifFalse: [key storeOn: aStream]! ! !MessageNode methodsFor: 'testing' stamp: 'eem 9/23/2008 14:06'! isNilIf ^(special between: 3 and: 4) and: [(arguments first returns or: [arguments first isJust: NodeNil]) and: [(arguments last returns or: [arguments last isJust: NodeNil])]]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisCaseOn: aStream indent: level "receiver caseOf: {[key]->[value]. ...} otherwise: [otherwise]" | braceNode otherwise extra | braceNode := arguments first. otherwise := arguments last. (arguments size = 1 or: [otherwise isJustCaseError]) ifTrue: [otherwise := nil]. receiver printWithClosureAnalysisOn: aStream indent: level precedence: 3. aStream nextPutAll: ' caseOf: '. braceNode isVariableReference ifTrue: [braceNode printWithClosureAnalysisOn: aStream indent: level] ifFalse: [aStream nextPutAll: '{'; crtab: level + 1. braceNode casesForwardDo: [:keyNode :valueNode :last | keyNode printWithClosureAnalysisOn: aStream indent: level + 1. aStream nextPutAll: ' -> '. valueNode isComplex ifTrue: [aStream crtab: level + 2. extra := 1] ifFalse: [extra := 0]. valueNode printWithClosureAnalysisOn: aStream indent: level + 1 + extra. last ifTrue: [aStream nextPut: $}] ifFalse: [aStream nextPut: $.; crtab: level + 1]]]. otherwise notNil ifTrue: [aStream crtab: level + 1; nextPutAll: ' otherwise: '. extra := otherwise isComplex ifTrue: [aStream crtab: level + 2. 1] ifFalse: [0]. otherwise printWithClosureAnalysisOn: aStream indent: level + 1 + extra]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisIfNilNotNil: aStream indent: level self printWithClosureAnalysisReceiver: receiver ifNilReceiver on: aStream indent: level. (arguments first isJust: NodeNil) ifTrue: [^self printWithClosureAnalysisKeywords: #ifNotNil: arguments: { arguments second } on: aStream indent: level]. (arguments second isJust: NodeNil) ifTrue: [^self printWithClosureAnalysisKeywords: #ifNil: arguments: { arguments first } on: aStream indent: level]. ^self printWithClosureAnalysisKeywords: #ifNil:ifNotNil: arguments: arguments on: aStream indent: level! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisIfNil: aStream indent: level self printWithClosureAnalysisReceiver: receiver on: aStream indent: level. ^self printWithClosureAnalysisKeywords: selector key arguments: (Array with: arguments first) on: aStream indent: level! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisIfOn: aStream indent: level receiver ifNotNil: [receiver printWithClosureAnalysisOn: aStream indent: level + 1 precedence: precedence]. (arguments last isJust: NodeNil) ifTrue: [^self printWithClosureAnalysisKeywords: #ifTrue: arguments: (Array with: arguments first) on: aStream indent: level]. (arguments last isJust: NodeFalse) ifTrue: [^self printWithClosureAnalysisKeywords: #and: arguments: (Array with: arguments first) on: aStream indent: level]. (arguments first isJust: NodeNil) ifTrue: [^self printWithClosureAnalysisKeywords: #ifFalse: arguments: (Array with: arguments last) on: aStream indent: level]. (arguments first isJust: NodeTrue) ifTrue: [^self printWithClosureAnalysisKeywords: #or: arguments: (Array with: arguments last) on: aStream indent: level]. self printWithClosureAnalysisKeywords: #ifTrue:ifFalse: arguments: arguments on: aStream indent: level! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level "may not need this check anymore - may be fixed by the #receiver: change" special ifNil: [^aStream nextPutAll: '** MessageNode with nil special **']. special > 0 ifTrue: [^self perform: self macroPrinter with: aStream with: level]. self printWithClosureAnalysisReceiver: receiver on: aStream indent: level. self printWithClosureAnalysisKeywords: selector key arguments: arguments on: aStream indent: level! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: strm indent: level precedence: outerPrecedence | parenthesize | parenthesize := precedence > outerPrecedence or: [outerPrecedence = 3 and: [precedence = 3 "both keywords"]]. parenthesize ifTrue: [strm nextPutAll: '('. self printWithClosureAnalysisOn: strm indent: level. strm nextPutAll: ')'] ifFalse: [self printWithClosureAnalysisOn: strm indent: level]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisReceiver: rcvr on: aStream indent: level rcvr ifNil: [^self]. "Force parens around keyword receiver of kwd message" rcvr printWithClosureAnalysisOn: aStream indent: level precedence: precedence! ! !MessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisWhileOn: aStream indent: level self printWithClosureAnalysisReceiver: receiver on: aStream indent: level. (arguments isEmpty not and: [arguments first isJust: NodeNil]) ifTrue: [selector := SelectorNode new key: (selector key == #whileTrue: ifTrue: [#whileTrue] ifFalse: [#whileFalse]) code: #macro. arguments := Array new]. self printWithClosureAnalysisKeywords: selector key arguments: arguments on: aStream indent: level! ! !MethodNode methodsFor: 'code generation' stamp: 'eem 9/25/2008 15:20'! selectorNode "Answer a SelectorNode for the message selector of the method represented by the receiver." ^(selectorOrFalse isMemberOf: SelectorNode) ifTrue: [selectorOrFalse] ifFalse: [SelectorNode new key: selectorOrFalse]! ! !MethodNode methodsFor: 'converting' stamp: 'eem 6/20/2008 14:50'! prepareForRegeneration "Nothing to do for vanilla nodes."! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 5/29/2008 15:27'! addLocalsToPool: locals "" localsPool isNil ifTrue: [localsPool := IdentitySet new]. localsPool addAll: locals! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 5/29/2008 16:07'! referencedValuesWithinBlockExtent: anInterval ^(localsPool select: [:temp| temp isReferencedWithinBlockExtent: anInterval]) collect: [:temp| temp isRemote ifTrue: [temp remoteNode] ifFalse: [temp]]! ! !BytecodeAgnosticMethodNode methodsFor: 'printing' stamp: 'eem 7/24/2008 10:07'! printWithClosureAnalysisOn: aStream self ensureClosureAnalysisDone. super printWithClosureAnalysisOn: aStream! ! !Parser methodsFor: 'expression types' stamp: 'eem 5/30/2008 14:16'! temporaryBlockVariablesFor: aBlockNode "Scan and answer temporary block variables." | variables | (self match: #verticalBar) ifFalse: "There are't any temporary variables." [^#()]. variables := OrderedCollection new. [hereType == #word] whileTrue: [variables addLast: (encoder bindBlockTemp: self advance within: aBlockNode)]. ^(self match: #verticalBar) ifTrue: [variables] ifFalse: [self expected: 'Vertical bar']! ! !ParseStack methodsFor: 'accessing' stamp: 'eem 9/12/2008 10:31'! position: n (position := n) > length ifTrue: [length := position]! ! !ReturnNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:49'! printWithClosureAnalysisOn: aStream indent: level aStream nextPutAll: '^ '. "make this a preference??" expr printWithClosureAnalysisOn: aStream indent: level. expr printCommentOn: aStream indent: level! ! !ReturnNode methodsFor: 'code generation (closures)' stamp: 'eem 5/29/2008 16:14'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" "Note we could do this: scopeBlock ~~ rootNode block ifTrue: [scopeBlock noteNonLocalReturn]. and pass up the flag in >>analyseTempsWithin:rootNode: which may be fast but may also screw up the debugger. For now we consider clean blocks a premature optimization." self flag: 'consider clean blocks'. expr analyseTempsWithin: scopeBlock rootNode: rootNode! ! !SelectorNode methodsFor: 'printing' stamp: 'eem 9/25/2008 14:56'! key: aSelector "This is for printing of FFI selectors." key := aSelector! ! !SelectorNode methodsFor: 'printing' stamp: 'eem 9/25/2008 15:01'! printAsFFICallWithArguments: aSequence on: aStream indent: level aStream nextPutAll: (key copyUpTo: $)). aSequence do: [:arg| arg printOn: aStream indent: level] separatedBy: [aStream nextPutAll: ', ']. aStream nextPut: $)! ! !SelectorNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level aStream nextPutAll: (key == nil ifTrue: [''] ifFalse: [key])! ! !SelectorNode methodsFor: 'testing' stamp: 'eem 9/25/2008 15:18'! isForFFICall ^key includesSubString: '()/'! ! !SyntaxErrorNotification methodsFor: 'accessing' stamp: 'eem 9/23/2008 14:23'! errorMessage ^errorMessage! ! !SyntaxErrorNotification methodsFor: 'accessing' stamp: 'eem 9/23/2008 14:23'! location ^location! ! !SyntaxErrorNotification methodsFor: 'accessing' stamp: 'eem 9/23/2008 14:20'! setClass: aClass category: aCategory code: codeString doitFlag: aBoolean errorMessage: errorString location: anInteger inClass := aClass. category := aCategory. code := codeString. doitFlag := aBoolean. errorMessage := errorString. location := anInteger! ! !SyntaxErrorNotification class methodsFor: 'exceptionInstantiator' stamp: 'eem 9/23/2008 14:19'! inClass: aClass category: aCategory withCode: codeString doitFlag: doitFlag errorMessage: errorString location: location ^ (self new setClass: aClass category: aCategory code: codeString doitFlag: doitFlag errorMessage: errorString location: location) signal! ! !TileMessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisIfOn: aStream indent: level "Just copied the old MessageNode one down here." (arguments last isJust: NodeNil) ifTrue: [^self printWithClosureAnalysisKeywords: #ifTrue: arguments: (Array with: arguments first) on: aStream indent: level]. (arguments last isJust: NodeFalse) ifTrue: [^self printWithClosureAnalysisKeywords: #and: arguments: (Array with: arguments first) on: aStream indent: level]. (arguments first isJust: NodeNil) ifTrue: [^self printWithClosureAnalysisKeywords: #ifFalse: arguments: (Array with: arguments last) on: aStream indent: level]. (arguments first isJust: NodeTrue) ifTrue: [^self printWithClosureAnalysisKeywords: #or: arguments: (Array with: arguments last) on: aStream indent: level]. self printWithClosureAnalysisKeywords: #ifTrue:ifFalse: arguments: arguments on: aStream indent: level! ! !TileMessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisReceiver: rcvr on: aMorph indent: level "I don't think we need this to do anything since we already printed the receiver ourself"! ! !TileMessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisToDoOn: aMorph indent: level | limitNode | limitNode := (arguments last isNil or: [(arguments last isMemberOf: AssignmentNode) not]) ifTrue: [arguments first] ifFalse: [arguments last value]. (selector key = #to:by:do: and: [arguments second isConstantNumber and: [arguments second key == 1]]) ifTrue: [self printWithClosureAnalysisKeywords: #to:do: arguments: (Array with: limitNode with: (arguments third)) on: aMorph indent: level] ifFalse: [self printWithClosureAnalysisKeywords: selector key arguments: (Array with: limitNode) , arguments allButFirst on: aMorph indent: level]! ! !TileMessageNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisWhileOn: aMorph indent: level (arguments first isJust: NodeNil) ifTrue: [ selector := SelectorNode new key: (selector key == #whileTrue: ifTrue: [#whileTrue] ifFalse: [#whileFalse]) code: #macro. arguments := Array new ]. self printWithClosureAnalysisKeywords: selector key arguments: arguments on: aMorph indent: level! ! !VariableNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level aStream nextPutAll: name! ! !VariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/21/2008 16:15'! beingAssignedToAnalyseTempsWithin: scopeBlock "" rootNode: rootNode "" "No-op overridden by TempVariableNode"! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 9/8/2008 18:22'! beBlockArg argType := #block! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 9/8/2008 18:21'! beMethodArg argType := #method! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 9/8/2008 18:20'! isBlockArg ^#block == argType! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 5/29/2008 15:51'! isRemote ^remoteNode notNil! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 5/29/2008 15:51'! remoteNode ^remoteNode! ! !TempVariableNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:47'! printWithClosureAnalysisOn: aStream indent: level aStream nextPutAll: name! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 11:17'! definingScope: scopeBlock "" definingScope := scopeBlock! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/29/2008 16:09'! isDefinedWithinBlockExtent: anInterval ^anInterval rangeIncludes: definingScope blockExtent first! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 6/2/2008 16:50'! isIndirectTempVector ^false! ! !TempVariableNode methodsFor: 'debugger access' stamp: 'eem 6/21/2008 13:32'! cleanUpForRegeneration remoteNode := nil. definingScope := writingScopes := readingScopes := nil! ! !TempVariableNode methodsFor: 'decompiler' stamp: 'eem 9/25/2008 09:45'! remoteNode: aRemoteTempVectorNode remoteNode := aRemoteTempVectorNode! ! !ClosureCompilerTest class methodsFor: 'code examples' stamp: 'eem 5/20/2008 09:40'! methodWithCopiedAndAssignedTemps | blk "0w" a "0w" b "0w" c "0w" t "0w" r1 "0w" r2 "0w" | a := 1. "1w" b := 2. "1w" c := 4. "1w" t := 0. "1w" blk "5w" := ["2" t "3w" := t "3r" + a "3r" + b "3r" + c "3r" ] "4". r1 "5w" := blk "5r" value. b "5w" := -100. r2 "5w" := blk "5r" value. ^r1 "5r" -> r2 "5r" -> t "5r" "a: main(read(),write(0,1)), block(read(3),write()) => copy; no writes follow read b: main(read(),write(0,1,5)), block(read(3),write()) => remote; write follows contained read blk: main(read(5),write(0,5)), block(read(),write()) => no copy in blocks < 5 c: main(read(),write(0,1)), block(read(3),write()) => copy; no writes follow read r1: main(read(5),write(0,5)), block(read(),write()) => no copy in blocks < 5 r2: main(read(5),write(0,5)), block(read(),write()) => no copy in blocks < 5 t: main(read(5),write(0,1)), block(read(3),write(3)) => remote; read follows contained write" "(Parser new encoderClass: EncoderForV3; parse: (self class sourceCodeAt: #methodWithCopiedAndAssignedTemps) class: self class) generateUsingClosures: #(0 0 0 0)"! ! !ClosureCompilerTest class methodsFor: 'code examples' stamp: 'eem 5/19/2008 20:45'! methodWithCopiedAndPostClosedOverAssignedTemps | blk a b c r1 r2 | a := 1. b := 2. c := 4. blk := [a + b + c]. r1 := blk value. b := nil. r2 := blk value. r1 -> r2 "(Parser new encoderClass: EncoderForV3; parse: (self class sourceCodeAt: #methodWithCopiedAndPostClosedOverAssignedTemps) class: self class) generateUsingClosures: #(0 0 0 0)"! ! !ClosureCompilerTest class methodsFor: 'code examples' stamp: 'eem 5/19/2008 20:10'! methodWithCopiedTemps | a b c r | a := 1. b := 2. c := 4. r := [a + b + c] value. b := nil. r "Parser new parse: (self class sourceCodeAt: #methodWithCopiedTemps) class: self class" "(Parser new encoderClass: EncoderForV3; parse: (self class sourceCodeAt: #methodWithCopiedTemps) class: self class) generateUsingClosures: #(0 0 0 0)"! ! !CompiledMethod methodsFor: 'testing' stamp: 'eem 6/3/2008 13:30'! isClosureCompiled "Answer whether the receiver was compiled using the closure compiler. This is used to help DebuggerMethodMap choose which mechanisms to use to inspect activations of the receiver. This method answers false negatives in that it only identifies methods that create new BlockClosures or use the new BlockClosure bytecodes. But since methods that don't create blocks have essentially the same code when compiled with either compiler this makes little difference." ^((InstructionStream on: self) scanFor: [:instr | instr >= 138 and: [instr <= 143]]) or: [(self hasLiteral: #closureCopy:copiedValues:) and: [self messages includes: #closureCopy:copiedValues:]]! ! !CompiledMethod methodsFor: 'literals' stamp: 'eem 7/29/2008 17:23'! headerDescription "Answer a description containing the information about the form of the receiver and the form of the context needed to run the receiver." | s | s := '' writeStream. self header printOn: s. s cr; nextPutAll: '"primitive: '. self primitive printOn: s. s cr; nextPutAll: ' numArgs: '. self numArgs printOn: s. s cr; nextPutAll: ' numTemps: '. self numTemps printOn: s. s cr; nextPutAll: ' numLiterals: '. self numLiterals printOn: s. s cr; nextPutAll: ' frameSize: '. self frameSize printOn: s. s cr; nextPutAll: ' isClosureCompiled: '. self isBlueBookCompiled not printOn: s. s nextPut: $"; cr. ^ s contents! ! !CompiledMethod methodsFor: 'source code management' stamp: 'eem 6/24/2008 14:27'! qCompress: string firstTry: firstTry "A very simple text compression routine designed for method temp names. Most common 12 chars get values 0-11 packed in one 4-bit nibble; others get values 12-15 (2 bits) * 16 plus next nibble. Last char of str must be a space so it may be dropped without consequence if output ends on odd nibble. Normal call is with firstTry == true." | charTable odd ix oddNibble names shorterStr maybe str temps | str := string isOctetString ifTrue: [string] ifFalse: [temps := string findTokens: ' '. String streamContents: [:stream | 1 to: temps size do: [:index | stream nextPut: $t. stream nextPutAll: index asString. stream space]]]. charTable := "Character encoding table must match qDecompress:" ' eatrnoislcm_bdfghjkpquvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'. ^ ByteArray streamContents: [:strm | odd := true. "Flag for odd or even nibble out" oddNibble := nil. str do: [:char | ix := (charTable indexOf: char) - 1. (ix <= 12 ifTrue: [Array with: ix] ifFalse: [Array with: ix//16+12 with: ix\\16]) do: [:nibble | (odd := odd not) ifTrue: [strm nextPut: oddNibble*16 + nibble] ifFalse: [oddNibble := nibble]]]. strm position > 251 ifTrue: ["Only values 1...251 are available for the flag byte that signals compressed temps. See the logic in endPC." "Before giving up completely, we attempt to encode most of the temps, but with the last few shortened to tNN-style names." firstTry ifFalse: [^ nil "already tried --give up now"]. names := str findTokens: ' '. names size < 8 ifTrue: [^ nil "weird case -- give up now"]. 4 to: names size//2 by: 4 do: [:i | shorterStr := String streamContents: [:s | 1 to: names size - i do: [:j | s nextPutAll: (names at: j); space]. 1 to: i do: [:j | s nextPutAll: 't' , j printString; space]]. (maybe := self qCompress: shorterStr firstTry: false) ifNotNil: [^ maybe]]. ^ nil]. strm nextPut: strm position] " | m s | m := CompiledMethod new. s := 'charTable odd ix oddNibble '. ^ Array with: s size with: (m qCompress: s) size with: (m qDecompress: (m qCompress: s)) " ! ! !CompiledMethod methodsFor: 'source code management' stamp: 'eem 6/24/2008 14:30'! qDecompress: byteArray "Decompress strings compressed by qCompress:. Most common 12 chars get values 0-11 packed in one 4-bit nibble; others get values 12-15 (2 bits) * 16 plus next nibble" | charTable extended ext | charTable := "Character encoding table must match qCompress:" ' eatrnoislcm_bdfghjkpquvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'. ^ String streamContents: [:strm | extended := false. "Flag for 2-nibble characters" byteArray do: [:byte | (Array with: byte//16 with: byte\\16) do: [:nibble | extended ifTrue: [strm nextPut: (charTable at: ext*16+nibble + 1). extended := false] ifFalse: [nibble < 12 ifTrue: [strm nextPut: (charTable at: nibble + 1)] ifFalse: [ext := nibble-12. extended := true]]]]]! ! !CompiledMethod methodsFor: 'decompiling' stamp: 'eem 7/29/2008 17:15'! methodNode "Return the parse tree that represents self" | aClass source | aClass := self methodClass. ^ (source := self getSourceFromFile) ifNil: [self decompile] ifNotNil: [aClass parserClass new encoderClass: (self isBlueBookCompiled ifTrue: [EncoderForV3] ifFalse: [EncoderForV3PlusClosures]); parse: source class: aClass]! ! !CompiledMethod methodsFor: 'debugger support' stamp: 'eem 6/3/2008 16:15'! blockExtentsInto: aDictionary from: initialPC to: endPC scanner: scanner numberer: numbererBlock "Support routine for startpcsToBlockExtents" | extentStart blockSizeOrLocator | self flag: 'belongs in DebuggerMethodMap'. extentStart := numbererBlock value. [scanner pc <= endPC] whileTrue: [blockSizeOrLocator := scanner interpretNextInstructionFor: BlockStartLocator new. blockSizeOrLocator isInteger ifTrue: [self blockExtentsInto: aDictionary from: scanner pc to: scanner pc + blockSizeOrLocator - 1 scanner: scanner numberer: numbererBlock]]. aDictionary at: initialPC put: (extentStart to: numbererBlock value). ^aDictionary! ! !CompiledMethod methodsFor: 'debugger support' stamp: 'eem 6/3/2008 16:15'! startpcsToBlockExtents "Answer a Dictionary of startpc to Interval of blockExtent, using the identical numbering scheme described in and orchestrated by BlockNode>>analyseArguments:temporaries:rootNode:. This is used in part to find the temp names for any block in a method, as needed by the debugger. The other half is to recompile the method, obtainign the temp names for each block extent. By indirecting through the blockExtent instead of using the startpc directly we decouple the debugger's access to temp names form the exact bytecode; insulating debugging from minor changes in the compiler (e.g. changes in literal pooling, adding prefix bytecodes, adding inst vars to CompiledMethod in literals towards the end of the literal frame, etc). If the recompilation doesn't produce exactly the same bytecode at exactly the same offset no matter; the blockExtents will be the same." | index | self flag: 'belongs in DebuggerMethodMap'. index := 0. ^self blockExtentsInto: Dictionary new from: self initialPC to: self endPC scanner: (InstructionStream on: self) numberer: [| value | value := index. index := index + 2. value]! ! !CompiledMethod class methodsFor: 'class initialization' stamp: 'eem 6/5/2008 09:05'! initialize "CompiledMethod initialize" "Initialize class variables specifying the size of the temporary frame needed to run instances of me." SmallFrame := 16. "Context range for temps+stack" LargeFrame := 56! ! !Compiler methodsFor: 'error handling' stamp: 'eem 9/25/2008 12:41'! notify: aString at: location "Refer to the comment in Object|notify:." ^requestor == nil ifTrue: [SyntaxErrorNotification inClass: class category: category withCode: (sourceStream contents copyReplaceFrom: location to: location - 1 with: aString) doitFlag: false errorMessage: aString location: location] ifFalse: [requestor notify: aString at: location in: sourceStream]! ! !Debugger methodsFor: 'context stack (message list)' stamp: 'eem 9/5/2008 13:57'! selectedMessageName "Answer the message selector of the currently selected context. If the method is unbound we can still usefully answer its old selector." | selector | selector := self selectedContext methodSelector. ^(selector ~~ self selectedContext method selector and: [selector beginsWith: 'DoIt']) ifTrue: [self selectedContext method selector] ifFalse: [selector]! ! !InstructionPrinter methodsFor: 'instruction decoding' stamp: 'eem 6/16/2008 14:04'! pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize self print: 'closureNumCopied: ', numCopied printString , ' numArgs: ', numArgs printString , ' bytes ', scanner pc printString , ' to ', (scanner pc + blockSize - 1) printString. innerIndents atAll: (scanner pc to: scanner pc + blockSize - 1) put: (innerIndents at: scanner pc - 1) + 1! ! !InstructionPrinter methodsFor: 'instruction decoding' stamp: 'eem 5/30/2008 17:42'! pushConsArrayWithElements: numElements self print: 'pop ', numElements printString, ' into (Array new: ', numElements printString, ')'! ! !InstructionPrinter methodsFor: 'instruction decoding' stamp: 'eem 5/25/2008 15:02'! pushNewArrayOfSize: numElements self print: 'push: (Array new: ', numElements printString, ')'! ! !InstructionPrinter methodsFor: 'instruction decoding' stamp: 'eem 5/25/2008 00:00'! pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex self print: 'pushTemp: ', remoteTempIndex printString, ' inVectorAt: ', tempVectorIndex printString! ! !InstructionStream methodsFor: 'testing' stamp: 'eem 6/4/2008 15:58'! willStore "Answer whether the next bytecode is a store or store-pop" | byte | byte := self method at: pc. ^(byte between: 96 and: 142) and: [byte <= 111 "96 103 storeAndPopReceiverVariableBytecode" "104 111 storeAndPopTemporaryVariableBytecode" or: [byte >= 129 "129 extendedStoreBytecode" and: [byte <= 130 "130 extendedStoreAndPopBytecode" or: [(byte = 132 "132 doubleExtendedDoAnythingBytecode" and: [(self method at: pc+1) >= 160]) or: [byte = 141 "141 storeRemoteTempLongBytecode" or: [byte = 142 "142 storeAndPopRemoteTempLongBytecode"]]]]]]! ! !InstructionStream methodsFor: 'testing' stamp: 'eem 6/4/2008 15:56'! willStorePop "Answer whether the next bytecode is a store-pop." | byte | byte := self method at: pc. ^byte = 130 "130 extendedStoreAndPopBytecode" or: [byte = 142 "142 storeAndPopRemoteTempLongBytecode" or: [byte between: 96 and: 111 "96 103 storeAndPopReceiverVariableBytecode" "104 111 storeAndPopTemporaryVariableBytecode"]]! ! !InstructionStream methodsFor: 'decoding' stamp: 'eem 9/29/2008 11:59'! interpretJumpIfCond | byte | byte := self method at: pc. (byte between: 152 and: 159) ifTrue: [pc := pc + 1. ^byte - 151]. (byte between: 168 and: 175) ifTrue: [pc := pc + 2. ^(byte bitAnd: 3) * 256 + (self method at: pc - 1)]. ^nil! ! !InstructionStream methodsFor: 'scanning' stamp: 'eem 6/4/2008 10:58'! addSelectorTo: set "If this instruction is a send, add its selector to set." | selectorOrSelf | (selectorOrSelf := self selectorToSendOrSelf) == self ifFalse: [set add: selectorOrSelf]! ! !InstructionStream methodsFor: 'scanning' stamp: 'eem 6/16/2008 09:51'! scanFor: scanBlock "Answer the index of the first bytecode for which scanBlock answer true when supplied with that bytecode." | method end byte type | method := self method. end := method endPC. [pc <= end] whileTrue: [(scanBlock value: (byte := method at: pc)) ifTrue: [^true]. type := byte // 16. pc := type = 8 "extensions" ifTrue: [pc + (#(2 2 2 2 3 2 2 1 1 1 2 1 3 3 3 4) at: byte \\ 16 + 1)] ifFalse: [type = 10 "long jumps" ifTrue: [pc + 2] ifFalse: [pc + 1]]]. ^false! ! !InstructionStream methodsFor: 'private' stamp: 'eem 6/16/2008 09:49'! interpretExtension: offset in: method for: client | type offset2 byte2 byte3 byte4 | offset <= 6 ifTrue: ["Extended op codes 128-134" byte2 := method at: pc. pc := pc + 1. offset <= 2 ifTrue: ["128-130: extended pushes and pops" type := byte2 // 64. offset2 := byte2 \\ 64. offset = 0 ifTrue: [type = 0 ifTrue: [^client pushReceiverVariable: offset2]. type = 1 ifTrue: [^client pushTemporaryVariable: offset2]. type = 2 ifTrue: [^client pushConstant: (method literalAt: offset2 + 1)]. type = 3 ifTrue: [^client pushLiteralVariable: (method literalAt: offset2 + 1)]]. offset = 1 ifTrue: [type = 0 ifTrue: [^client storeIntoReceiverVariable: offset2]. type = 1 ifTrue: [^client storeIntoTemporaryVariable: offset2]. type = 2 ifTrue: [self error: 'illegalStore']. type = 3 ifTrue: [^client storeIntoLiteralVariable: (method literalAt: offset2 + 1)]]. offset = 2 ifTrue: [type = 0 ifTrue: [^client popIntoReceiverVariable: offset2]. type = 1 ifTrue: [^client popIntoTemporaryVariable: offset2]. type = 2 ifTrue: [self error: 'illegalStore']. type = 3 ifTrue: [^client popIntoLiteralVariable: (method literalAt: offset2 + 1)]]]. "131-134: extended sends" offset = 3 ifTrue: "Single extended send" [^client send: (method literalAt: byte2 \\ 32 + 1) super: false numArgs: byte2 // 32]. offset = 4 ifTrue: "Double extended do-anything" [byte3 := method at: pc. pc := pc + 1. type := byte2 // 32. type = 0 ifTrue: [^client send: (method literalAt: byte3 + 1) super: false numArgs: byte2 \\ 32]. type = 1 ifTrue: [^client send: (method literalAt: byte3 + 1) super: true numArgs: byte2 \\ 32]. type = 2 ifTrue: [^client pushReceiverVariable: byte3]. type = 3 ifTrue: [^client pushConstant: (method literalAt: byte3 + 1)]. type = 4 ifTrue: [^client pushLiteralVariable: (method literalAt: byte3 + 1)]. type = 5 ifTrue: [^client storeIntoReceiverVariable: byte3]. type = 6 ifTrue: [^client popIntoReceiverVariable: byte3]. type = 7 ifTrue: [^client storeIntoLiteralVariable: (method literalAt: byte3 + 1)]]. offset = 5 ifTrue: "Single extended send to super" [^client send: (method literalAt: byte2 \\ 32 + 1) super: true numArgs: byte2 // 32]. offset = 6 ifTrue: "Second extended send" [^client send: (method literalAt: byte2 \\ 64 + 1) super: false numArgs: byte2 // 64]]. offset = 7 ifTrue: [^client doPop]. offset = 8 ifTrue: [^client doDup]. offset = 9 ifTrue: [^client pushActiveContext]. byte2 := method at: pc. pc := pc + 1. offset = 10 ifTrue: [^byte2 < 128 ifTrue: [client pushNewArrayOfSize: byte2] ifFalse: [client pushConsArrayWithElements: byte2 - 128]]. offset = 11 ifTrue: [^self error: 'unusedBytecode']. byte3 := method at: pc. pc := pc + 1. offset = 12 ifTrue: [^client pushRemoteTemp: byte2 inVectorAt: byte3]. offset = 13 ifTrue: [^client storeIntoRemoteTemp: byte2 inVectorAt: byte3]. offset = 14 ifTrue: [^client popIntoRemoteTemp: byte2 inVectorAt: byte3]. "offset = 15" byte4 := method at: pc. pc := pc + 1. ^client pushClosureCopyNumCopiedValues: (byte2 bitShift: -4) numArgs: (byte2 bitAnd: 16rF) blockSize: (byte3 * 256) + byte4! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 6/15/2008 11:27'! methodReturnConstant: value "Simulate the action of a 'return constant' bytecode whose value is the argument, value. This corresponds to a source expression like '^0'." ^self return: value from: self methodReturnContext! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 6/15/2008 11:27'! methodReturnReceiver "Simulate the action of a 'return receiver' bytecode. This corresponds to the source expression '^self'." ^self return: self receiver from: self methodReturnContext! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 6/15/2008 11:27'! methodReturnTop "Simulate the action of a 'return top of stack' bytecode. This corresponds to source expressions like '^something'." ^self return: self pop from: self methodReturnContext! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 5/27/2008 11:38'! popIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Simulate the action of bytecode that removes the top of the stack and stores it into an offset in one of my local variables being used as a remote temp vector." (self at: tempVectorIndex + 1) at: remoteTempIndex + 1 put: self pop! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 6/15/2008 11:34'! popIntoTemporaryVariable: offset "Simulate the action of bytecode that removes the top of the stack and stores it into one of my temporary variables." self contextForLocalVariables at: offset + 1 put: self pop! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 5/27/2008 11:32'! pushNewArrayOfSize: arraySize self push: (Array new: arraySize)! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 5/27/2008 11:44'! pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex "Simulate the action of bytecode that pushes the value at remoteTempIndex in one of my local variables being used as a remote temp vector." self push: ((self at: tempVectorIndex + 1) at: remoteTempIndex + 1)! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 6/15/2008 11:34'! pushTemporaryVariable: offset "Simulate the action of bytecode that pushes the contents of the temporary variable whose index is the argument, index, on the top of the stack." self push: (self contextForLocalVariables at: offset + 1)! ! !ContextPart methodsFor: 'instruction decoding' stamp: 'eem 6/15/2008 11:34'! storeIntoTemporaryVariable: offset "Simulate the action of bytecode that stores the top of the stack into one of my temporary variables." self contextForLocalVariables at: offset + 1 put: self top! ! !ContextPart methodsFor: 'debugger access' stamp: 'eem 6/1/2008 09:43'! stackOfSize: limit "Answer an OrderedCollection of the top 'limit' contexts on the receiver's sender chain." | stack ctxt | stack := OrderedCollection new. stack addLast: (ctxt := self). [(ctxt := ctxt sender) ~~ nil and: [stack size < limit]] whileTrue: [stack addLast: ctxt]. ^stack! ! !ContextPart methodsFor: 'debugger access'! swapSender: coroutine "Replace the receiver's sender with coroutine and answer the receiver's previous sender. For use in coroutining." | oldSender | oldSender := sender. sender := coroutine. ^oldSender! ! !ContextPart methodsFor: 'controlling' stamp: 'eem 6/14/2008 19:17'! blockCopy: numArgs "Primitive. Distinguish a block of code from its enclosing method by creating a new BlockContext for that block. The compiler inserts into all methods that contain blocks the bytecodes to send the message blockCopy:. Do not use blockCopy: in code that you write!! Only the compiler can decide to send the message blockCopy:. Fail if numArgs is not a SmallInteger. Optional. No Lookup. See Object documentation whatIsAPrimitive." ^ (BlockContext newForMethod: self method) home: self home startpc: pc + 2 nargs: numArgs! ! !ContextPart methodsFor: 'controlling' stamp: 'eem 8/29/2008 06:27'! closureCopy: numArgs copiedValues: anArray "Distinguish a block of code from its enclosing method by creating a BlockClosure for that block. The compiler inserts into all methods that contain blocks the bytecodes to send the message closureCopy:copiedValues:. Do not use closureCopy:copiedValues: in code that you write!! Only the compiler can decide to send the message closureCopy:copiedValues:. Fail if numArgs is not a SmallInteger. Optional. No Lookup. See Object documentation whatIsAPrimitive." ^BlockClosure outerContext: self startpc: pc + 2 numArgs: numArgs copiedValues: anArray! ! !ContextPart methodsFor: 'controlling' stamp: 'ar 3/6/2001 14:26'! terminateTo: previousContext "Terminate all the Contexts between me and previousContext, if previousContext is on my Context stack. Make previousContext my sender." | currentContext sendingContext | (self hasSender: previousContext) ifTrue: [ currentContext := sender. [currentContext == previousContext] whileFalse: [ sendingContext := currentContext sender. currentContext terminate. currentContext := sendingContext]]. sender := previousContext! ! !ContextPart methodsFor: 'private' stamp: 'eem 9/5/2008 12:29'! 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. Any primitive which provikes execution needs to be intercepted and simulated to avoid execution running away." | value | "Simulation guard" "If successful, push result and return resuming context, else ^ PrimitiveFailToken" (primitiveIndex = 19) ifTrue: [ToolSet debugContext: self label:'Code simulation error' contents: nil]. "ContextPart>>blockCopy:; simulated to get startpc right" (primitiveIndex = 80 and: [receiver isKindOf: ContextPart]) ifTrue: [^self push: ((BlockContext newForMethod: receiver method) home: receiver home startpc: pc + 2 nargs: (arguments at: 1))]. (primitiveIndex = 81 and: [receiver isMemberOf: BlockContext]) "BlockContext>>value[:value:...]" ifTrue: [^receiver pushArgs: arguments from: self]. (primitiveIndex = 82 and: [receiver isMemberOf: BlockContext]) "BlockContext>>valueWithArguments:" ifTrue: [^receiver pushArgs: arguments first from: self]. primitiveIndex = 83 "afr 9/11/1998 19:50" "Object>>perform:[with:...]" ifTrue: [^self send: arguments first to: receiver with: arguments allButFirst super: false]. primitiveIndex = 84 "afr 9/11/1998 19:50" "Object>>perform:withArguments:" ifTrue: [^self send: arguments first to: receiver with: (arguments at: 2) super: false]. primitiveIndex = 188 ifTrue: "eem 5/27/2008 11:10 Object>>withArgs:executeMethod:" [^MethodContext sender: self receiver: receiver method: (arguments at: 2) arguments: (arguments at: 1)]. "Closure primitives" (primitiveIndex = 200 and: [receiver == self]) ifTrue: "ContextPart>>closureCopy:copiedValues:; simulated to get startpc right" [^self push: (BlockClosure outerContext: receiver startpc: pc + 2 numArgs: arguments first copiedValues: arguments last)]. ((primitiveIndex between: 201 and: 205) "BlockClosure>>value[:value:...]" or: [primitiveIndex between: 221 and: 222]) ifTrue: "BlockClosure>>valueNoContextSwitch[:]" [^receiver simulateValueWithArguments: arguments caller: self]. primitiveIndex = 206 ifTrue: "BlockClosure>>valueWithArguments:" [^receiver simulateValueWithArguments: arguments first caller: self]. arguments size > 6 ifTrue: [^PrimitiveFailToken]. value := primitiveIndex = 117 "named primitives" ifTrue:[self tryNamedPrimitiveIn: meth for: receiver withArgs: arguments] ifFalse:[receiver tryPrimitive: primitiveIndex withArgs: arguments]. ^value == PrimitiveFailToken ifTrue: [PrimitiveFailToken] ifFalse: [self push: value]! ! !BlockContext methodsFor: 'accessing' stamp: 'eem 5/29/2008 13:14'! caller ^sender! ! !BlockContext methodsFor: 'accessing' stamp: 'eem 6/15/2008 11:33'! methodReturnContext "Answer the context from which an ^-return should return from." ^home! ! !BlockContext methodsFor: 'printing' stamp: 'eem 7/28/2008 14:10'! fullPrintOn: aStream aStream print: self; cr. (self decompile ifNil: ['--source missing--']) printOn: aStream indent: 0! ! !Decompiler methodsFor: 'control' stamp: 'eem 7/29/2008 17:42'! checkForBlockCopy: receiver "We just saw a blockCopy: message. Check for a following block." | savePc jump args argPos block | receiver == constructor codeThisContext ifFalse: [^false]. savePc := pc. (jump := self interpretJump) ifNil: [pc := savePc. ^false]. self sawBlueBookBlock. "Definitely a block" jump := jump + pc. argPos := statements size. [self willStorePop] whileTrue: [stack addLast: ArgumentFlag. "Flag for doStore:" self interpretNextInstructionFor: self]. args := Array new: statements size - argPos. 1 to: args size do: "Retrieve args" [:i | args at: i put: statements removeLast. (args at: i) scope: -1 "flag args as block temps"]. block := self blockTo: jump. stack addLast: (constructor codeArguments: args block: block). ^true! ! !Decompiler methodsFor: 'control' stamp: 'eem 5/29/2008 13:16'! checkForBlock: receiver selector: selector arguments: arguments selector == #blockCopy: ifTrue: [^self checkForBlockCopy: receiver]. self assert: selector == #closureCopy:copiedValues:. ^self checkForClosureCopy: receiver arguments: arguments! ! !Decompiler methodsFor: 'control' stamp: 'eem 5/29/2008 17:02'! checkForClosureCopy: receiver arguments: arguments "We just saw a closureCopy:copiedValues: message. Check for and construct a following block." | savePc jump | receiver == constructor codeThisContext ifFalse: [^false]. savePc := pc. (jump := self interpretJump) notNil ifFalse: [pc := savePc. ^nil]. "Definitely a block" self doClosureCopyCopiedValues: arguments last "" elements numArgs: arguments first key blockSize: jump. ^true! ! !Decompiler methodsFor: 'control' stamp: 'eem 9/25/2008 09:41'! doClosureCopyCopiedValues: blockCopiedValues numArgs: numArgs blockSize: blockSize | savedTemps savedNumLocalTemps jump blockArgs blockTemps blockTempsOffset block | savedTemps := tempVars. savedNumLocalTemps := numLocalTemps. jump := blockSize + pc. numLocalTemps := BlockLocalTempCounter tempCountForBlockAt: pc - 4 in: method. blockArgs := (1 to: numArgs) collect: [:i| (constructor codeTemp: i - 1) beBlockArg]. blockTempsOffset := numArgs + blockCopiedValues size. blockTemps := (1 to: numLocalTemps) collect: [:i| constructor codeTemp: i + blockTempsOffset - 1]. tempVars := blockArgs, blockCopiedValues, blockTemps. numLocalTemps timesRepeat: [self interpretNextInstructionFor: self. stack removeLast]. block := self blockTo: jump. stack addLast: (constructor codeArguments: (tempVars copyFrom: 1 to: numArgs) temps: (tempVars copyFrom: blockTempsOffset + 1 to: blockTempsOffset + numLocalTemps) block: block). tempVars := savedTemps. numLocalTemps := savedNumLocalTemps! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/29/2008 15:02'! case: dist "statements = keyStmts CascadeFlag keyValueBlock ... keyStmts" | nextCase thenJump stmtStream elements b node cases otherBlock myExits | nextCase := pc + dist. "Now add CascadeFlag & keyValueBlock to statements" statements addLast: stack removeLast. stack addLast: CaseFlag. "set for next pop" statements addLast: (self blockForCaseTo: nextCase). stack last == CaseFlag ifTrue: "Last case" ["ensure jump is within block (in case thenExpr returns wierdly I guess)" stack removeLast. "get rid of CaseFlag" stmtStream := ReadStream on: (self popTo: stack removeLast). elements := OrderedCollection new. b := OrderedCollection new. [stmtStream atEnd] whileFalse: [(node := stmtStream next) == CascadeFlag ifTrue: [elements addLast: (constructor codeMessage: (constructor codeBlock: b returns: false) selector: (constructor codeSelector: #-> code: #macro) arguments: (Array with: stmtStream next)). b := OrderedCollection new] ifFalse: [b addLast: node]]. b size > 0 ifTrue: [self error: 'Bad cases']. cases := constructor codeBrace: elements. "try find the end of the case" myExits := caseExits removeLast: elements size. myExits := myExits reject: [ :e | e isNil or: [ e < 0 or: [ e > method endPC ] ] ]. thenJump := myExits isEmpty ifTrue: [ nextCase ] ifFalse: [ myExits max ]. otherBlock := self blockTo: thenJump. stack addLast: (constructor codeMessage: stack removeLast selector: (constructor codeSelector: #caseOf:otherwise: code: #macro) arguments: (Array with: cases with: otherBlock))].! ! !Decompiler methodsFor: 'instruction decoding'! doStore: stackOrBlock "Only called internally, not from InstructionStream. StackOrBlock is stack for store, statements for storePop." | var expr | var := stack removeLast. expr := stack removeLast. stackOrBlock addLast: (expr == ArgumentFlag ifTrue: [var] ifFalse: [constructor codeAssignTo: var value: expr])! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/29/2008 10:32'! jump: dist if: condition | savePc sign elsePc elseStart end cond ifExpr thenBlock elseBlock thenJump elseJump condHasValue isIfNil saveStack | stack last == CascadeFlag ifTrue: [^ self case: dist]. elsePc := lastPc. elseStart := pc + dist. end := limit. "Check for bfp-jmp to invert condition. Don't be fooled by a loop with a null body." sign := condition. savePc := pc. self interpretJump ifNotNil: [:elseDist| (elseDist >= 0 and: [elseStart = pc]) ifTrue: [sign := sign not. elseStart := pc + elseDist]]. pc := savePc. ifExpr := stack removeLast. (isIfNil := stack size > 0 and: [stack last == IfNilFlag]) ifTrue: [stack removeLast]. saveStack := stack. stack := OrderedCollection new. thenBlock := self blockTo: elseStart. condHasValue := hasValue or: [isIfNil]. "ensure jump is within block (in case thenExpr returns)" thenJump := exit <= end ifTrue: [exit] ifFalse: [elseStart]. "if jump goes back, then it's a loop" thenJump < elseStart ifTrue: ["Must be a while loop... thenJump will jump to the beginning of the while expr." stack := saveStack. statements addLast: (constructor codeMessage: (constructor codeBlock: { ifExpr } returns: false) selector: (constructor codeSelector: (sign ifTrue: [#whileFalse:] ifFalse: [#whileTrue:]) code: #macro) arguments: { thenBlock }). self convertToDoLoop] ifFalse: ["Must be a conditional..." elseBlock := self blockTo: thenJump. elseJump := exit. "if elseJump is backwards, it is not part of the elseExpr" elseJump < elsePc ifTrue: [pc := lastPc]. cond := isIfNil ifTrue: [constructor codeMessage: ifExpr ifNilReceiver selector: (constructor codeSelector: (sign ifTrue: [#ifNotNil:] ifFalse: [#ifNil:]) code: #macro) arguments: (Array with: thenBlock)] ifFalse: [constructor codeMessage: ifExpr selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro) arguments: (sign ifTrue: [{elseBlock. thenBlock}] ifFalse: [{thenBlock. elseBlock}])]. stack := saveStack. condHasValue ifTrue: [stack addLast: cond] ifFalse: [statements addLast: cond]]! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/26/2008 15:43'! methodReturnTop | last | last := stack removeLast "test test" asReturnNode. stack size > blockStackBase "get effect of elided pop before return" ifTrue: [statements addLast: stack removeLast]. exit := pc. lastJumpPc := lastReturnPc := lastPc. statements addLast: last! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 6/4/2008 14:44'! popIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex self sawClosureBytecode. self pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex; doStore: statements! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/25/2008 10:27'! popIntoTemporaryVariable: offset | maybeTVTag tempVector start | maybeTVTag := stack last. ((maybeTVTag isMemberOf: Association) and: [maybeTVTag key == #pushNewArray]) ifTrue: [tempVector := maybeTVTag value. offset + 1 <= tempVars size ifTrue: [start := 2. tempVector at: 1 put: (tempVars at: offset + 1)] ifFalse: [tempVars := (Array new: offset + 1) replaceFrom: 1 to: tempVars size with: tempVars. start := 1]. start to: tempVector size do: [:i| tempVector at: i put: (constructor codeTemp: numLocalTemps + offset + i - 1)]. tempVars at: offset + 1 put: (constructor codeRemoteTemp: offset + 1 remoteTemps: tempVector). stack removeLast. ^self]. self pushTemporaryVariable: offset; doStore: statements! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/5/2008 14:27'! pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize | copiedValues | self sawClosureBytecode. numCopied > 0 ifTrue: [copiedValues := Array new: numCopied. numLocalTemps == #decompileBlock: ifTrue: "Hack fake temps for copied values" [1 to: numCopied do: [:i| stack addLast: (constructor codeTemp: i - 1)]]. numCopied to: 1 by: -1 do: [:i| copiedValues at: i put: stack removeLast]] ifFalse: [copiedValues := #()]. self doClosureCopyCopiedValues: copiedValues numArgs: numArgs blockSize: blockSize! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 6/4/2008 14:44'! pushConsArrayWithElements: numElements | array | self sawClosureBytecode. array := Array new: numElements. numElements to: 1 by: -1 do: [:i| array at: i put: stack removeLast]. stack addLast: (constructor codeBrace: array)! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 6/4/2008 14:45'! pushNewArrayOfSize: size self sawClosureBytecode. stack addLast: #pushNewArray -> (Array new: size)! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/25/2008 09:48'! pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex self sawClosureBytecode. stack addLast: ((tempVars at: tempVectorIndex + 1) remoteTemps at: remoteTempIndex + 1)! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 9/29/2008 19:23'! send: selector super: superFlag numArgs: numArgs | args rcvr selNode msgNode messages | args := Array new: numArgs. (numArgs to: 1 by: -1) do: [:i | args at: i put: stack removeLast]. rcvr := stack removeLast. superFlag ifTrue: [rcvr := constructor codeSuper]. ((#(blockCopy: closureCopy:copiedValues:) includes: selector) and: [self checkForBlock: rcvr selector: selector arguments: args]) ifFalse: [selNode := constructor codeAnySelector: selector. rcvr == CascadeFlag ifTrue: ["May actually be a cascade or an ifNil: for value." self willJumpIfFalse ifTrue: "= generated by a case macro" [selector == #= ifTrue: [" = signals a case statement..." statements addLast: args first. stack addLast: rcvr. "restore CascadeFlag" ^ self]. selector == #== ifTrue: [" == signals an ifNil: for value..." stack removeLast; removeLast. rcvr := stack removeLast. stack addLast: IfNilFlag; addLast: (constructor codeMessage: rcvr selector: selNode arguments: args). ^ self]] ifFalse: [(self willJumpIfTrue and: [selector == #==]) ifTrue: [" == signals an ifNotNil: for value..." stack removeLast; removeLast. rcvr := stack removeLast. stack addLast: IfNilFlag; addLast: (constructor codeMessage: rcvr selector: selNode arguments: args). ^ self]]. msgNode := constructor codeCascadedMessage: selNode arguments: args. stack last == CascadeFlag ifFalse: ["Last message of a cascade" statements addLast: msgNode. messages := self popTo: stack removeLast. "Depth saved by first dup" msgNode := constructor codeCascade: stack removeLast messages: messages]] ifFalse: [msgNode := constructor codeMessage: rcvr selector: selNode arguments: args]. stack addLast: msgNode]! ! !Decompiler methodsFor: 'instruction decoding' stamp: 'eem 6/4/2008 14:45'! storeIntoRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex self sawClosureBytecode. self pushRemoteTemp: remoteTempIndex inVectorAt: tempVectorIndex; doStore: stack! ! !Decompiler methodsFor: 'public access' stamp: 'eem 10/20/2008 14:09'! decompile: aSelector in: aClass method: aMethod "Answer a MethodNode that is the root of the parse tree for the argument, aMethod, which is the CompiledMethod associated with the message, aSelector. Variables are determined with respect to the argument, aClass." ^self decompile: aSelector in: aClass method: aMethod using: (self constructorForMethod: aMethod)! ! !Decompiler methodsFor: 'public access' stamp: 'eem 10/20/2008 14:09'! decompileBlock: aBlock "Decompile aBlock, returning the result as a BlockNode. Show temp names from source if available." "Decompiler new decompileBlock: [3 + 4]" | startpc end homeClass blockNode tempNames home source | (home := aBlock home) ifNil: [^ nil]. method := home method. (homeClass := home who first) == #unknown ifTrue: [^ nil]. constructor := self constructorForMethod: aBlock method. method fileIndex ~~ 0 ifTrue: ["got any source code?" source := [method getSourceFromFile] on: Error do: [:ex | ^ nil]. tempNames := ([homeClass compilerClass new parse: source in: homeClass notifying: nil] on: (Smalltalk classNamed: 'SyntaxErrorNotification') do: [:ex | ^ nil]) tempNames. self withTempNames: tempNames]. self initSymbols: homeClass. startpc := aBlock startpc. end := aBlock isClosure ifTrue: [(method at: startpc - 2) * 256 + (method at: startpc - 1) + startpc - 1] ifFalse: [(method at: startpc - 2) \\ 16 - 4 * 256 + (method at: startpc - 1) + startpc - 1]. stack := OrderedCollection new: method frameSize. caseExits := OrderedCollection new. statements := OrderedCollection new: 20. super method: method pc: (aBlock isClosure ifTrue: [startpc - 4] ifFalse: [startpc - 5]). aBlock isClosure ifTrue: [numLocalTemps := #decompileBlock: "Get pushClosureCopy... to hack fake temps for copied values"]. blockNode := self blockTo: end. stack isEmpty ifFalse: [self error: 'stack not empty']. ^blockNode statements first! ! !Decompiler methodsFor: 'public access' stamp: 'eem 9/23/2008 22:07'! decompile: aSelector in: aClass method: aMethod using: aConstructor | block | constructor := aConstructor. method := aMethod. self initSymbols: aClass. "create symbol tables" method isQuick ifTrue: [block := self quickMethod] ifFalse: [stack := OrderedCollection new: method frameSize. caseExits := OrderedCollection new. statements := OrderedCollection new: 20. numLocalTemps := 0. super method: method pc: method initialPC. block := self blockTo: method endPC + 1. stack isEmpty ifFalse: [self error: 'stack not empty']]. ^(constructor codeMethod: aSelector block: block tempVars: tempVars primitive: method primitive class: aClass) accept: TempNumberNormalizingVisitor new; yourself! ! !Decompiler methodsFor: 'private' stamp: 'eem 9/6/2008 08:45'! blockScopeRefersOnlyOnceToTemp: offset | nRefs byteCode extension scanner scan | scanner := InstructionStream on: method. nRefs := 0. scan := offset <= 15 ifTrue: [byteCode := 16 + offset. [:instr | instr = byteCode ifTrue: [nRefs := nRefs + 1]. nRefs > 1]] ifFalse: [extension := 64 + offset. [:instr | (instr = 128 and: [scanner followingByte = extension]) ifTrue: [nRefs := nRefs + 1]. nRefs > 1]]. self scanBlockScopeFor: pc from: method initialPC to: method endPC with: scan scanner: scanner. ^nRefs = 1! ! !Decompiler methodsFor: 'private' stamp: 'eem 10/20/2008 15:49'! constructorForMethod: aMethod ^(aMethod isBlueBookCompiled ifTrue: [DecompilerConstructor] ifFalse: [DecompilerConstructorForClosures]) new! ! !Decompiler methodsFor: 'private' stamp: 'eem 9/5/2008 18:41'! convertToDoLoop "If statements contains the pattern var := startExpr. [var <= limit] whileTrue: [...statements... var := var + incConst] then replace this by startExpr to: limit by: incConst do: [:var | ...statements...]" | initStmt toDoStmt limitStmt | statements size < 2 ifTrue: [^ self]. initStmt := statements at: statements size-1. (toDoStmt := statements last toDoFromWhileWithInit: initStmt) == nil ifTrue: [^ self]. initStmt variable scope: -1. "Flag arg as block temp" statements removeLast; removeLast; addLast: toDoStmt. "Attempt further conversion of the pattern limitVar := limitExpr. startExpr to: limitVar by: incConst do: [:var | ...statements...] to startExpr to: limitExpr by: incConst do: [:var | ...statements...]" statements size < 2 ifTrue: [^ self]. limitStmt := statements at: statements size-1. ((limitStmt isMemberOf: AssignmentNode) and: [limitStmt variable isTemp and: [limitStmt variable == toDoStmt arguments first and: [self blockScopeRefersOnlyOnceToTemp: limitStmt variable fieldOffset]]]) ifFalse: [^ self]. toDoStmt arguments at: 1 put: limitStmt value. limitStmt variable scope: -2. "Flag limit var so it won't print" statements removeLast; removeLast; addLast: toDoStmt. ! ! !Decompiler methodsFor: 'private' stamp: 'eem 6/4/2008 14:43'! sawClosureBytecode constructor isForClosures ifFalse: [constructor primitiveChangeClassTo: DecompilerConstructorForClosures new]! ! !Decompiler methodsFor: 'private' stamp: 'eem 9/6/2008 09:27'! scanBlockScopeFor: refpc from: startpc to: endpc with: scan scanner: scanner | bsl maybeBlockSize | bsl := BlockStartLocator new. scanner pc: startpc. [scanner pc <= endpc] whileTrue: [refpc = scanner pc ifTrue: [scanner pc: startpc. [scanner pc <= endpc] whileTrue: [(scan value: scanner firstByte) ifTrue: [^endpc]. (maybeBlockSize := scanner interpretNextInstructionFor: bsl) isInteger ifTrue: [scanner pc: scanner pc + maybeBlockSize]]. ^self]. (maybeBlockSize := scanner interpretNextInstructionFor: bsl) isInteger ifTrue: [refpc <= (scanner pc + maybeBlockSize) ifTrue: [^self scanBlockScopeFor: refpc from: scanner pc to: scanner pc + maybeBlockSize with: scan scanner: scanner] ifFalse: [scanner pc: scanner pc + maybeBlockSize]]]! ! !MethodContext methodsFor: 'initialize-release' stamp: 'eem 8/22/2008 09:57'! privRefreshWith: aCompiledMethod "Reinitialize the receiver as though it had been for a different method. Used by a Debugger when one of the methods to which it refers is recompiled." aCompiledMethod isCompiledMethod ifFalse: [self error: 'method can only be set to aCompiledMethod']. method := aCompiledMethod. self assert: closureOrNil == nil. "was: receiverMap := nil." self privRefresh! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 5/28/2008 10:45'! activeOuterContext "If executing closure, search senders for the activation in which the receiver's closure was created (the receiver's outerContext). If the outerContext is not found on the sender chain answer nil." | outerContext | self isExecutingBlock ifFalse: [^self]. self sender ifNil: [^nil]. outerContext := self outerContext. ^self sender findContextSuchThat: [:ctxt | ctxt = outerContext]! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 7/22/2008 11:57'! closure ^closureOrNil! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 7/22/2008 11:57'! home "Answer the context in which the receiver was defined." closureOrNil == nil ifTrue: [^self]. ^closureOrNil outerContext home! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 7/22/2008 11:57'! isExecutingBlock "Is this executing a block versus a method? In the new closure implemetation this is true if closureOrNil is not nil, in which case it should be holding a BlockClosure." ^closureOrNil isClosure! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 7/22/2008 11:58'! methodReturnContext "Answer the context from which an ^-return should return from." closureOrNil == nil ifTrue: [^self]. ^closureOrNil outerContext methodReturnContext! ! !MethodContext methodsFor: 'accessing' stamp: 'eem 7/22/2008 11:58'! outerContext "Answer the context within which the receiver is nested." ^closureOrNil == nil ifFalse: [closureOrNil outerContext]! ! !MethodContext methodsFor: 'private' stamp: 'eem 6/15/2008 11:27'! aboutToReturn: result through: firstUnwindContext "Called from VM when an unwindBlock is found between self and its home. Return to home's sender, executing unwind blocks on the way." self methodReturnContext return: result! ! !MethodContext methodsFor: 'private' stamp: 'eem 7/22/2008 11:59'! setSender: s receiver: r method: m arguments: args "Create the receiver's initial state." sender := s. receiver := r. method := m. closureOrNil := nil. pc := method initialPC. self stackp: method numTemps. 1 to: args size do: [:i | self at: i put: (args at: i)]! ! !MethodContext methodsFor: 'private' stamp: 'eem 7/22/2008 12:00'! setSender: s receiver: r method: m closure: c startpc: startpc "Create the receiver's initial state." sender := s. receiver := r. method := m. closureOrNil := c. pc := startpc. stackp := 0! ! !MethodContext methodsFor: 'private' stamp: 'eem 7/22/2008 12:00'! startpc ^closureOrNil ifNil: [self method initialPC] ifNotNil: [closureOrNil startpc]! ! !MethodContext methodsFor: 'private-exceptions' stamp: 'eem 7/22/2008 11:57'! cannotReturn: result closureOrNil notNil ifTrue: [^self cannotReturn: result to: sender]. ToolSet debugContext: thisContext label: 'computation has been terminated' contents: nil! ! !MethodContext methodsFor: 'printing' stamp: 'eem 5/27/2008 17:23'! printOn: aStream self outerContext ifNil: [super printOn: aStream] ifNotNil: [:outerContext| aStream nextPutAll: '[] in '. outerContext printOn: aStream]! ! !MethodContext methodsFor: 'instruction decoding (closures)' stamp: 'eem 7/22/2008 11:56'! blockReturnTop "Simulate the interpreter's action when a ReturnTopOfStackToCaller bytecode is encountered in the receiver. This should only happen in a closure activation." self assert: closureOrNil isClosure. ^self return: self pop from: self! ! !MethodContext methodsFor: 'instruction decoding (closures)' stamp: 'eem 5/30/2008 18:40'! pushConsArrayWithElements: numElements | array | array := Array new: numElements. numElements to: 1 by: -1 do: [:i| array at: i put: self pop]. self push: array! ! !MethodContext methodsFor: 'system simulation' stamp: 'eem 9/3/2008 13:58'! pushArgs: args "" from: sendr "" "Helps simulate action of the value primitive for closures. This is used by ContextPart>>runSimulated:contextAtEachStep:" stackp ~= 0 ifTrue: [self error: 'stack pointer should be zero!!']. closureOrNil ifNil: [self error: 'context needs a closure!!']. args do: [:arg| self push: arg]. 1 to: closureOrNil numCopiedValues do: [:i| self push: (closureOrNil copiedValueAt: i)]. sender := sendr! ! !AssignmentNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 09:37'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" "N.B. since assigment happens _after_ the value is evaluated the value is sent the message _first_." value analyseTempsWithin: scopeBlock rootNode: rootNode. variable beingAssignedToAnalyseTempsWithin: scopeBlock rootNode: rootNode! ! !BlockNode methodsFor: 'initialize-release' stamp: 'eem 5/20/2008 13:40'! arguments: argNodes statements: statementsCollection returns: returnBool from: encoder "Compile." arguments := argNodes. statements := statementsCollection size > 0 ifTrue: [statementsCollection] ifFalse: [argNodes size > 0 ifTrue: [statementsCollection copyWith: arguments last] ifFalse: [Array with: NodeNil]]. optimized := false. returns := returnBool! ! !BlockNode methodsFor: 'initialize-release' stamp: 'eem 8/4/2008 14:12'! noteSourceRangeStart: start end: end encoder: encoder "Note two source ranges for this node. One is for the debugger and is of the last expression, the result of the block. One is for source analysis and is for the entire block." encoder noteSourceRange: (start to: end) forNode: self closureCreationNode. startOfLastStatement ifNil: [encoder noteSourceRange: (start to: end) forNode: self] ifNotNil: [encoder noteSourceRange: (startOfLastStatement to: end - 1) forNode: self]! ! !BlockNode methodsFor: 'initialize-release' stamp: 'eem 5/20/2008 13:40'! statements: statementsCollection returns: returnBool "Decompile." | returnLast | returnLast := returnBool. returns := false. statements := (statementsCollection size > 1 and: [(statementsCollection at: statementsCollection size - 1) isReturningIf]) ifTrue: [returnLast := false. statementsCollection allButLast] ifFalse: [statementsCollection size = 0 ifTrue: [Array with: NodeNil] ifFalse: [statementsCollection]]. arguments := #(). temporaries := #(). optimized := false. returnLast ifTrue: [self returnLast]! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 6/2/2008 14:00'! addArgument: aTempVariableNode temporaries := temporaries copyWith: aTempVariableNode! ! !BlockNode methodsFor: 'accessing' stamp: 'eem 8/22/2008 10:01'! closureCreationNode closureCreationNode ifNil: [closureCreationNode := LeafNode new key: #closureCreationNode code: nil]. ^closureCreationNode! ! !BlockNode methodsFor: 'testing' stamp: 'eem 7/17/2008 12:20'! generateAsClosure "Answer if we're compiling under the closure regime. If blockExtent has been set by analyseTempsWithin:rootNode: et al then we're compiling under the closure regime." ^blockExtent ~~ nil! ! !BlockNode methodsFor: 'printing' stamp: 'eem 9/25/2008 12:48'! printOn: aStream indent: level "statements size <= 1 ifFalse: [aStream crtab: level]." aStream nextPut: $[. self printArgumentsOn: aStream indent: level. (self printTemporaries: temporaries on: aStream doPrior: []) ifTrue: ["If >0 temps and >1 statement, put all statements on separate lines" statements size > 1 ifTrue: [aStream crtab: level] ifFalse: [aStream space]]. self printStatementsOn: aStream indent: level. aStream nextPut: $]! ! !BlockNode methodsFor: 'printing' stamp: 'eem 11/12/2008 12:31'! printTemporaries: tempSequence on: aStream doPrior: aBlock "Print any in-scope temporaries. If there are any evaluate aBlock prior to printing. Answer whether any temporaries were printed." (tempSequence == nil or: [tempSequence size = 0 or: [tempSequence allSatisfy: [:temp| temp scope < 0 and: [temp isIndirectTempVector not]]]]) ifTrue: [^false]. aBlock value. aStream nextPut: $|. tempSequence do: [:tempNode | tempNode isIndirectTempVector ifTrue: [tempNode remoteTemps do: [:tempVariableNode| tempVariableNode scope >= 0 ifTrue: [aStream space; nextPutAll: tempVariableNode key]]] ifFalse: [tempNode scope >= 0 ifTrue: [aStream space; nextPutAll: tempNode key]]]. aStream space; nextPut: $|. ^true! ! !BlockNode methodsFor: 'printing' stamp: 'eem 6/2/2008 12:06'! printWithClosureAnalysisArgumentsOn: aStream indent: level arguments size = 0 ifTrue: [^self]. arguments do: [:tempNode | aStream space; nextPut: $:. tempNode printDefinitionForClosureAnalysisOn: aStream]. aStream nextPut: $|; space. "If >0 args and >1 statement, put all statements on separate lines" statements size > 1 ifTrue: [aStream crtab: level]! ! !BlockNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:53'! printWithClosureAnalysisOn: aStream indent: level aStream nextPut: $[. blockExtent ifNotNil: [aStream print: blockExtent]. self printWithClosureAnalysisArgumentsOn: aStream indent: level. self printWithClosureAnalysisTemporariesOn: aStream indent: level. self printWithClosureAnalysisStatementsOn: aStream indent: level. aStream nextPut: $]! ! !BlockNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:48'! printWithClosureAnalysisStatementsOn: aStream indent: levelOrZero | len shown thisStatement level | level := 1 max: levelOrZero. comment == nil ifFalse: [self printCommentOn: aStream indent: level. aStream crtab: level]. len := shown := statements size. (levelOrZero = 0 "top level" and: [statements last isReturnSelf]) ifTrue: [shown := 1 max: shown - 1] ifFalse: [(len = 1 and: [((statements at: 1) == NodeNil) & (arguments size = 0)]) ifTrue: [shown := shown - 1]]. 1 to: shown do: [:i | thisStatement := statements at: i. thisStatement printWithClosureAnalysisOn: aStream indent: level. i < shown ifTrue: [aStream nextPut: $.; crtab: level]. (thisStatement comment ~~ nil and: [thisStatement comment size > 0]) ifTrue: [i = shown ifTrue: [aStream crtab: level]. thisStatement printCommentOn: aStream indent: level. i < shown ifTrue: [aStream crtab: level]]]! ! !BlockNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:54'! printWithClosureAnalysisTemporariesOn: aStream indent: level (temporaries == nil or: [temporaries size = 0]) ifFalse: [aStream nextPut: $|. temporaries do: [:tempNode | aStream space. tempNode printDefinitionForClosureAnalysisOn: aStream]. aStream nextPutAll: ' | '. "If >0 args and >1 statement, put all statements on separate lines" statements size > 1 ifTrue: [aStream crtab: level]]! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 11:18'! addHoistedTemps: additionalTemporaries "" additionalTemporaries do: [:temp| temp definingScope: self]. temporaries := (temporaries == nil ifTrue: [#()] ifFalse: [temporaries]), additionalTemporaries! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 11:26'! addRemoteTemp: aTempVariableNode rootNode: rootNode "" temporaries isArray ifTrue: [temporaries := temporaries asOrderedCollection]. remoteTempNode == nil ifTrue: [remoteTempNode := RemoteTempVectorNode new name: '<', blockExtent first printString, '-', blockExtent last printString, '>' index: arguments size + temporaries size type: LdTempType scope: 0. temporaries addLast: remoteTempNode. remoteTempNode definingScope: self]. remoteTempNode addRemoteTemp: aTempVariableNode encoder: rootNode encoder. temporaries remove: aTempVariableNode. ^remoteTempNode! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 6/20/2008 15:11'! analyseArguments: methodArguments temporaries: methodTemporaries rootNode: rootNode "" "^>" "Top level entry-point for analysing temps within the hierarchy of blocks in the receiver's method. Answer the (possibly modified) sequence of temp vars. Need to hoist temps out of macro-optimized blocks into their actual blocks. Need to note reads and writes to temps from blocks other than their actual blocks to determine whether blocks can be local (simple slots within a block/method context) or remote (slots in indirection vectors that are shared between contexts by sharing indirection vectors). The algorithm is based on numbering temporary reads and writes and block extents. The index used for numbering starts at zero and is incremented on every block entry and block exit. So the following | a b blk r1 r2 t | a := 1. b := 2. t := 0. blk := [ | s | s := a + b. t := t + s]. r1 := blk value. b := -100. r2 := blk value. r1 -> r2 -> t is numbered as method block 0 to: 6: | a b blk r1 r2 t | a w@1 := 1. b w@1 := 2. t w@1 := 0. blk w@5 := [entry@2 | s | t w@3 := t r@3 + a r@3 + b r@3 ] exit@4. r1 w@5 := blk r@5 value. b w@5 := nil. r2 w@5 := blk r@5 value. r1 r@5 -> r2 r@5 -> t r@5 So: b and blk cannot be copied because fpr both there exists a write @5 that follows a read @4 within block 2 through 4 t must be remote because there exists a write @3 within block (2 to: 4)" self assert: (arguments isEmpty or: [arguments hasEqualElements: methodArguments]). arguments := methodArguments asArray. "won't change" self assert: (temporaries isNil or: [temporaries isEmpty or: [temporaries hasEqualElements: methodTemporaries]]). temporaries := OrderedCollection withAll: methodTemporaries. self assert: optimized not. "the top-level block should not be optimized." self analyseTempsWithin: self rootNode: rootNode. "The top-level block needs to reindex temporaries since analysis may have rearranged them. This happens during sizing for nested blocks." temporaries withIndexDo: [:temp :offsetPlusOne| temp index: arguments size + offsetPlusOne - 1]. "Answer the (possibly modified) sequence of temps." ^temporaries asArray! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 11:28'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" | blockStart | optimized ifTrue: [self assert: (temporaries isEmpty and: [arguments isNil or: [arguments size <= 1]]). statements do: [:statement| statement analyseTempsWithin: scopeBlock rootNode: rootNode]. ^self]. rootNode noteBlockEntry: [:entryNumber| blockStart := entryNumber. arguments notNil ifTrue: [arguments do: [:temp| temp definingScope: self]]. temporaries notNil ifTrue: [temporaries do: [:temp| temp definingScope: self]]]. statements do: [:statement| statement analyseTempsWithin: self rootNode: rootNode]. rootNode noteBlockExit: [:exitNumber| blockExtent := blockStart to: exitNumber]. self postNumberingProcessTemps: rootNode! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 12:16'! blockExtent "^" ^blockExtent! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 8/4/2008 14:10'! constructClosureCreationNode: encoder copiedValues := self computeCopiedValues: encoder rootNode. encoder supportsClosureOpcodes ifTrue: [^self closureCreationNode]. "Without the bytecode we can still get by." ^MessageNode new receiver: (encoder encodeVariable: 'thisContext') selector: #closureCopy:copiedValues: arguments: (Array with: (encoder encodeLiteral: arguments size) with: (copiedValues isEmpty ifTrue: [NodeNil] ifFalse: [BraceNode new elements: copiedValues])) precedence: 3 from: encoder! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 8/4/2008 14:10'! emitCodeForClosureValue: stack encoder: encoder "if not supportsClosureOpcodes closureCreationSupportNode is the node for thisContext closureCopy: numArgs [ copiedValues: { values } ]" encoder supportsClosureOpcodes ifTrue: [copiedValues do: [:copiedValue| copiedValue emitCodeForValue: stack encoder: encoder]. closureCreationNode pc: encoder methodStreamPosition + 1. encoder genPushClosureCopyNumCopiedValues: copiedValues size numArgs: arguments size jumpSize: size. stack pop: copiedValues size; push: 1] ifFalse: [closureCreationNode emitCodeForValue: stack encoder: encoder. encoder genJumpLong: size]. "Force a two byte jump." "Emit the body of the block" self emitCodeForEvaluatedClosureValue: stack encoder: encoder! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 9/12/2008 10:55'! emitCodeForEvaluatedClosureValue: stack encoder: encoder | position | position := stack position. stack position: arguments size + copiedValues size. temporaries size timesRepeat: [NodeNil emitCodeForValue: stack encoder: encoder]. self reindexingLocalsDo: [self emitCodeForEvaluatedValue: stack encoder: encoder] encoder: encoder. self returns ifFalse: [encoder genReturnTopToCaller. pc := encoder methodStreamPosition]. stack position: position! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 14:00'! ifOptimizedBlockHoistTempsInto: scopeBlock "" "This is a No-op for all nodes except non-optimized BlockNodes." "Let's assume the special > 0 guard in MessageNode>>analyseTempsWithin:forValue:encoder: is correct. Then we can simply hoist our temps up." self assert: (arguments isNil or: [arguments size <= 1]). (arguments notNil and: [arguments notEmpty]) ifTrue: [scopeBlock addHoistedTemps: arguments. arguments := #()]. temporaries notEmpty ifTrue: [scopeBlock addHoistedTemps: temporaries. temporaries := #()]! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 6/16/2008 09:43'! postNumberingProcessTemps: rootNode "" (temporaries isNil or: [temporaries isEmpty]) ifTrue: ["Add all arguments to the pool so that copiedValues can be computed during sizing." rootNode addLocalsToPool: arguments. ^self]. "A temp can be local (and copied if it is not written to after it is captured. A temp cannot be local if it is written to remotely. Need to enumerate a copy of the temporaries because any temps becoming remote will be removed from temporaries in analyseClosure: (and a single remote temp node will get added)" temporaries copy do: [:each| each analyseClosure: rootNode]. "Now we may have added a remoteTempNode. So we need a statement to initialize it." remoteTempNode ~~ nil ifTrue: ["statements isArray ifTrue: [statements := statements asOrderedCollection]." "true for decompiled trees" (statements notEmpty and: [statements first isAssignmentNode and: [statements first variable isTemp and: [statements first variable isIndirectTempVector]]]) ifTrue: "If this is a decompiled tree there already is a temp vector initialization node." [statements first variable become: remoteTempNode] ifFalse: [statements addFirst: (remoteTempNode nodeToInitialize: rootNode encoder)]]. "Now add all arguments and locals to the pool so that copiedValues can be computed during sizing." rootNode addLocalsToPool: arguments; addLocalsToPool: temporaries! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 6/2/2008 16:37'! reindexingLocalsDo: aBlock encoder: encoderOrNil "Evaluate aBlock wih arguments, temporaries and copiedValues reindexed for their positions within the receiver's block, restoring the correct indices afterwards. If encoder is not nil remember the temps for this block's extent." | tempIndices result tempsToReindex | self assert: copiedValues notNil. tempsToReindex := arguments asArray, copiedValues, temporaries. tempIndices := tempsToReindex collect: [:temp| temp index]. tempsToReindex withIndexDo: [:temp :newIndex| temp index: newIndex - 1. self assert: temp index + 1 = newIndex]. encoderOrNil ifNotNil: [encoderOrNil noteBlockExtent: blockExtent hasLocals: tempsToReindex]. result := aBlock ensure: ["Horribly pragmatic hack. The copiedValues will have completely unrelated indices within the closure method and sub-method. Avoiding the effort of rebinding temps in the inner scope simply update the indices to their correct ones during the generation of the closure method and restore the indices immedately there-after." tempsToReindex with: tempIndices do: [:temp :oldIndex| temp index: oldIndex. self assert: temp index = oldIndex]]. ^result! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 8/4/2008 14:11'! sizeCodeForClosureValue: encoder "Compute the size for the creation of the block and its code." "If we have the closure bytecodes constructClosureCreationNode: will note the copied values in the copiedValues inst var and answer #pushCopiedValues." closureCreationNode := self constructClosureCreationNode: encoder. "Remember size of body for emit time so we know the size of the jump around it." size := self sizeCodeForEvaluatedClosureValue: encoder. ^encoder supportsClosureOpcodes ifTrue: [(copiedValues inject: 0 into: [:sum :node| sum + (node sizeCodeForValue: encoder)]) + (encoder sizePushClosureCopyNumCopiedValues: copiedValues size numArgs: arguments size jumpSize: size) + size] ifFalse: ["closureCreationSupportNode is send closureCopy:copiedValues:" (closureCreationNode sizeCodeForValue: encoder) + (encoder sizeJumpLong: size) + size]! ! !BlockNode methodsFor: 'code generation (closures)' stamp: 'eem 5/31/2008 14:31'! sizeCodeForEvaluatedClosureValue: encoder "The closure value primitives push the arguments and the copied values. The compiler guarantees that any copied values come before all local temps. So on closure activation we only need to push nils for the remaining temporaries." ^temporaries size * (NodeNil sizeCodeForValue: encoder) + (self reindexingLocalsDo: [self sizeCodeForEvaluatedValue: encoder] encoder: nil "don't store temps yet") + (self returns ifTrue: [0] ifFalse: [encoder sizeReturnTopToCaller])! ! !BlockNode methodsFor: 'debugger access' stamp: 'eem 7/2/2008 12:13'! cleanUpForRegeneration arguments notNil ifTrue: [arguments do: [:temp| temp cleanUpForRegeneration]]. temporaries notNil ifTrue: [temporaries do: [:temp| temp cleanUpForRegeneration]]. (temporaries notNil and: [temporaries notEmpty and: [temporaries last isIndirectTempVector]]) ifTrue: [temporaries := temporaries allButLast. (statements notEmpty and: [statements first isAssignmentNode and: [statements first variable isTemp and: [statements first variable isIndirectTempVector]]]) ifTrue: [statements removeFirst]]. remoteTempNode := nil! ! !BraceNode methodsFor: 'testing' stamp: 'eem 9/25/2008 14:48'! blockAssociationCheck: encoder "If all elements are MessageNodes of the form [block]->[block], and there is at least one element, answer true. Otherwise, notify encoder of an error." elements size = 0 ifTrue: [^encoder notify: 'At least one case required']. elements with: sourceLocations do: [:x :loc | (x isMessage: #-> receiver: [:rcvr | rcvr isBlockNode and: [rcvr numberOfArguments = 0]] arguments: [:arg | arg isBlockNode and: [arg numberOfArguments = 0]]) ifFalse: [^encoder notify: 'Association between 0-argument blocks required' at: loc]]. ^true! ! !BraceNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/30/2008 17:40'! emitCodeForValue: stack encoder: encoder (encoder supportsClosureOpcodes "Hack; we have no way of knowing how much stack space is available" and: [elements size <= self maxElementsForConsArray]) ifTrue: [elements do: [:node| node emitCodeForValue: stack encoder: encoder]. encoder genPushConsArray: elements size. stack pop: elements size; push: 1. ^self]. ^emitNode emitCodeForValue: stack encoder: encoder! ! !BraceNode methodsFor: 'code generation (closures)' stamp: 'eem 5/19/2008 20:26'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" elements do: [:node| node analyseTempsWithin: scopeBlock rootNode: rootNode]! ! !BraceNode methodsFor: 'code generation (closures)' stamp: 'eem 5/21/2008 10:40'! elements ^elements! ! !BraceNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 17:22'! maxElementsForConsArray "Hack; we have no way of knowing how much stack space is available during sizing" ^8! ! !CascadeNode methodsFor: 'code generation (closures)' stamp: 'eem 5/19/2008 20:26'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" { receiver }, messages do: [:node| node analyseTempsWithin: scopeBlock rootNode: rootNode]! ! !DecompilerConstructor methodsFor: 'constructor' stamp: 'eem 5/21/2008 13:28'! codeArguments: args temps: temps block: block block arguments: args; temporaries: temps. ^block! ! !DecompilerConstructor methodsFor: 'constructor'! codeTemp: index ^ TempVariableNode new name: 't' , (index + 1) printString index: index type: LdTempType scope: 0! ! !DecompilerConstructor methodsFor: 'constructor'! codeTemp: index named: tempName ^ TempVariableNode new name: tempName index: index type: LdTempType scope: 0! ! !Encoder methodsFor: 'initialize-release' stamp: 'eem 6/24/2008 14:24'! init: aClass context: aContext notifying: req requestor := req. class := aClass. nTemps := 0. supered := false. self initScopeAndLiteralTables. class variablesAndOffsetsDo: [:variable "" :offset "" | offset isNil ifTrue: [scopeTable at: variable name put: (FieldNode new fieldDefinition: variable)] ifFalse: [scopeTable at: variable put: (offset >= 0 ifTrue: [InstanceVariableNode new name: variable index: offset] ifFalse: [MaybeContextInstanceVariableNode new name: variable index: offset negated])]]. aContext ~~ nil ifTrue: [| homeNode | homeNode := self bindTemp: self doItInContextName. "0th temp = aContext passed as arg" aContext tempNames withIndexDo: [:variable :index| scopeTable at: variable put: (MessageAsTempNode new receiver: homeNode selector: #namedTempAt: arguments: (Array with: (self encodeLiteral: index)) precedence: 3 from: self)]]. sourceRanges := Dictionary new: 32. globalSourceRanges := OrderedCollection new: 32! ! !Encoder methodsFor: 'accessing' stamp: 'eem 5/29/2008 09:36'! methodNodeClass ^MethodNode! ! !Encoder methodsFor: 'results' stamp: 'eem 5/27/2008 12:07'! tempNodes | tempNodes | tempNodes := SortedCollection sortBlock: [:n1 :n2 | n1 code <= n2 code]. scopeTable associationsDo: [:assn | assn value isArray ifTrue: [assn value do: [:temp| tempNodes add: temp]] ifFalse: [assn value isTemp ifTrue: [tempNodes add: assn value]]]. ^tempNodes! ! !Encoder methodsFor: 'results' stamp: 'eem 9/8/2008 18:27'! tempsAndBlockArgs | tempNodes | tempNodes := OrderedCollection new. scopeTable associationsDo: [:assn | | var | var := assn value. (var isTemp and: [var isMethodArg not and: [var scope = 0 or: [var scope = -1]]]) ifTrue: [tempNodes add: var]]. ^tempNodes! ! !Encoder methodsFor: 'results' stamp: 'eem 6/24/2008 14:24'! unusedTempNames | unused | unused := OrderedCollection new. scopeTable associationsDo: [:assn | | name | (assn value isUnusedTemp) ifTrue: [name := assn value key. name ~= self doItInContextName ifTrue: [unused add: name]]]. ^ unused! ! !Encoder methodsFor: 'private' stamp: 'eem 9/10/2008 14:03'! lookupInPools: varName ifFound: assocBlock ^Symbol hasInterned: varName ifTrue: [:sym| (class bindingOf: sym) ifNil: [^false] ifNotNil: [:assoc| assocBlock value: assoc]]! ! !BytecodeEncoder methodsFor: 'opcode sizing' stamp: 'eem 5/24/2008 22:59'! sizePushRemoteTemp: tempIndex inVectorAt: tempVectorIndex ^self sizeOpcodeSelector: #genPushRemoteTemp:inVectorAt: withArguments: {tempIndex. tempVectorIndex}! ! !BytecodeEncoder methodsFor: 'opcode sizing' stamp: 'eem 5/24/2008 23:02'! sizeStorePopRemoteTemp: tempIndex inVectorAt: tempVectorIndex ^self sizeOpcodeSelector: #genStorePopRemoteTemp:inVectorAt: withArguments: {tempIndex. tempVectorIndex}! ! !BytecodeEncoder methodsFor: 'opcode sizing' stamp: 'eem 5/24/2008 23:02'! sizeStoreRemoteTemp: tempIndex inVectorAt: tempVectorIndex ^self sizeOpcodeSelector: #genStoreRemoteTemp:inVectorAt: withArguments: {tempIndex. tempVectorIndex}! ! !BytecodeEncoder methodsFor: 'accessing' stamp: 'eem 5/29/2008 09:36'! methodNodeClass ^BytecodeAgnosticMethodNode! ! !BytecodeEncoder methodsFor: 'testing' stamp: 'eem 7/17/2008 12:34'! supportsClosureOpcodes "Answer if the receiver supports the genPushNewArray:/genPushConsArray: genPushRemoteTemp:inVectorAt: genStoreRemoteTemp:inVectorAt: genStorePopRemoteTemp:inVectorAt: genPushClosureCopyCopiedValues:numArgs:jumpSize: opcodes" ^false! ! !BytecodeEncoder methodsFor: 'temps' stamp: 'eem 9/8/2008 18:24'! bindBlockArg: name within: aBlockNode "Read the comment in the superclass's method. If we have closures we should check the argument count against the block, not the method. (Note that this isn't entirely adequate either since optimized blocks will slip through the cracks (their arguments (i.e. ifNotNil: [:expr|) are charged against their enclosing block, not themselves))." | nArgs | self supportsClosureOpcodes ifFalse: [^super bindBlockArg: name within: aBlockNode]. (nArgs := aBlockNode nArgsSlot) isNil ifTrue: [aBlockNode nArgsSlot: (nArgs := 0)]. nArgs >= 15 ifTrue: [^self notify: 'Too many arguments']. aBlockNode nArgsSlot: nArgs + 1. ^(self bindTemp: name) beBlockArg; nowHasDef; nowHasRef; yourself! ! !BytecodeEncoder methodsFor: 'temps' stamp: 'eem 5/30/2008 14:35'! bindBlockTemp: name within: aBlockNode "Read the comment in the superclass's bindBlockArg:within: method. If we have closures we should check the argument count against the block, not the method. (Note that this isn't entirely adequate either since optimized blocks will slip through the cracks (their arguments (i.e. ifNotNil: [:expr|) are charged against their enclosing block, not themselves))." | nArgs | self supportsClosureOpcodes ifFalse: [^super bindBlockTemp: name within: aBlockNode]. (nArgs := aBlockNode nArgsSlot) isNil ifTrue: [aBlockNode nArgsSlot: (nArgs := 0)]. nArgs >= (CompiledMethod fullFrameSize - 1) ifTrue: [^self notify: 'Too many temporaries']. aBlockNode nArgsSlot: nArgs + 1. ^self bindTemp: name! ! !BytecodeEncoder methodsFor: 'temps' stamp: 'eem 7/18/2008 07:33'! bindTemp: name "Declare a temporary; error not if a field or class variable or out-of-scope temp. Read the comment in Encoder>>bindBlockArg:within: and subclass implementations." self supportsClosureOpcodes ifFalse: [^super bindTemp: name]. scopeTable at: name ifPresent: [:node| "When non-interactive raise the error only if it is a duplicate" node isTemp ifTrue:[node scope >= 0 ifTrue: [^self notify:'Name is already defined']] ifFalse:[self warnAboutShadowed: name]]. ^self reallyBind: name! ! !BytecodeEncoder methodsFor: 'temps' stamp: 'eem 6/14/2008 19:05'! blockExtentsToTempRefs | blockExtentsToTempRefs | blockExtentsToLocals ifNil: [^nil]. blockExtentsToTempRefs := Dictionary new. blockExtentsToLocals keysAndValuesDo: [:blockExtent :locals| blockExtentsToTempRefs at: blockExtent put: (locals collect: [:local| local isIndirectTempVector ifTrue: [local remoteTemps collect: [:remoteLocal| remoteLocal key]] ifFalse: [local key]])]. ^blockExtentsToTempRefs! ! !BytecodeEncoder methodsFor: 'temps' stamp: 'eem 6/3/2008 12:33'! noteBlockExtent: blockExtent hasLocals: tempNodes blockExtentsToLocals ifNil: [blockExtentsToLocals := Dictionary new]. blockExtentsToLocals at: blockExtent put: tempNodes asArray! ! !LeafNode methodsFor: 'code generation (closures)' stamp: 'eem 6/16/2008 09:32'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" "This is a no-op except in TempVariableNode" ^self! ! !MessageNode methodsFor: 'initialize-release' stamp: 'eem 9/25/2008 17:22'! receiver: rcvr selector: selNode arguments: args precedence: p "Decompile." self receiver: rcvr arguments: args precedence: p. selNode code == #macro ifTrue: [self noteSpecialSelector: selNode key] ifFalse: [special := 0]. selector := selNode. "self pvtCheckForPvtSelector: encoder" "We could test code being decompiled, but the compiler should've checked already. And where to send the complaint?"! ! !MessageNode methodsFor: 'testing' stamp: 'eem 9/26/2008 12:39'! isReturningIf ^((special between: 3 and: 4) "ifTrue:ifFalse:/ifFalse:ifTrue:" or: [special between: 17 and: 18]) "ifNil:ifNotNil:/ifNotNil:ifNil:" and: [arguments first returns and: [arguments last returns]]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 9/25/2008 12:48'! toDoFromWhileWithInit: initStmt "Return nil, or a to:do: expression equivalent to this whileTrue:" | variable increment limit toDoBlock body test | (selector key == #whileTrue: and: [initStmt isAssignmentNode and: [initStmt variable isTemp]]) ifFalse: [^nil]. body := arguments last statements. variable := initStmt variable. increment := body last toDoIncrement: variable. (increment == nil or: [receiver statements size ~= 1]) ifTrue: [^nil]. test := receiver statements first. "Note: test chould really be checked that <= or >= comparison jibes with the sign of the (constant) increment" (test isMessageNode and: [(limit := test toDoLimit: variable) notNil]) ifFalse: [^nil]. toDoBlock := BlockNode statements: body allButLast returns: false. toDoBlock arguments: (Array with: variable). variable scope: -1. ^MessageNode new receiver: initStmt value selector: (SelectorNode new key: #to:by:do: code: #macro) arguments: (Array with: limit with: increment with: toDoBlock) precedence: precedence! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 7/27/2008 18:50'! transformCase: encoder | caseNode | caseNode := arguments first. (caseNode isMemberOf: BraceNode) ifTrue: [((caseNode blockAssociationCheck: encoder) and: [arguments size = 1 or: [self checkBlock: arguments last as: 'otherwise arg' from: encoder]]) ifFalse: [^false]. caseNode elements do: [:messageNode| messageNode receiver noteOptimized. messageNode arguments first noteOptimized]. arguments size = 2 ifTrue: [arguments last noteOptimized]. ^true]. (caseNode canBeSpecialArgument and: [(caseNode isMemberOf: BlockNode) not]) ifTrue: [^false]. "caseOf: variable" ^encoder notify: 'caseOf: argument must be a brace construct or a variable'! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/19/2008 17:15'! transformAnd: encoder (self transformBoolean: encoder) ifTrue: [arguments := Array with: (arguments at: 1) noteOptimized with: (BlockNode withJust: NodeFalse) noteOptimized. ^true] ifFalse: [^false]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/19/2008 17:15'! transformIfFalse: encoder (self transformBoolean: encoder) ifTrue: [arguments := Array with: (BlockNode withJust: NodeNil) noteOptimized with: (arguments at: 1) noteOptimized. ^true] ifFalse: [^false]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 7/27/2008 16:19'! transformIfFalseIfTrue: encoder ^(self checkBlock: (arguments at: 1) as: 'False arg' from: encoder) and: [(self checkBlock: (arguments at: 2) as: 'True arg' from: encoder) and: [selector := SelectorNode new key: #ifTrue:ifFalse: code: #macro. arguments swap: 1 with: 2. arguments do: [:arg| arg noteOptimized]. true]]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/23/2008 10:51'! transformIfNil: encoder "vb: Removed the original transformBoolean: which amounds to a test we perform in each of the branches below." (MacroSelectors at: special) = #ifNotNil: ifTrue: [(self checkBlock: arguments first as: 'ifNotNil arg' from: encoder maxArgs: 1) ifFalse: [^false]. "Transform 'ifNotNil: [stuff]' to 'ifNil: [nil] ifNotNil: [stuff]'. Slightly better code and more consistent with decompilation." self noteSpecialSelector: #ifNil:ifNotNil:. selector := SelectorNode new key: (MacroSelectors at: special) code: #macro. arguments := Array with: (BlockNode withJust: NodeNil) noteOptimized with: arguments first noteOptimized. (self transform: encoder) ifFalse: [self error: 'compiler logic error']. ^true]. (self checkBlock: arguments first as: 'ifNil arg' from: encoder) ifFalse: [^false]. arguments first noteOptimized. ^true! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/23/2008 10:56'! transformIfNilIfNotNil: encoder "vb: Changed to support one-argument ifNotNil: branch. In the 1-arg case we transform the receiver to (var := receiver) which is further transformed to (var := receiver) == nil ifTrue: .... ifFalse: ... This does not allow the block variable to shadow an existing temp, but it's no different from how to:do: is done." | ifNotNilArg | ifNotNilArg := arguments at: 2. ((self checkBlock: (arguments at: 1) as: 'Nil arg' from: encoder) and: [self checkBlock: ifNotNilArg as: 'NotNil arg' from: encoder maxArgs: 1]) ifFalse: [^false]. ifNotNilArg numberOfArguments = 1 ifTrue: [receiver := AssignmentNode new variable: ifNotNilArg firstArgument value: receiver]. selector := SelectorNode new key: #ifTrue:ifFalse: code: #macro. receiver := MessageNode new receiver: receiver selector: #== arguments: (Array with: NodeNil) precedence: 2 from: encoder. arguments do: [:arg| arg noteOptimized]. ^true! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/23/2008 11:00'! transformIfNotNilIfNil: encoder "vb: Changed to support one-argument ifNotNil: branch. In the 1-arg case we transform the receiver to (var := receiver) which is further transformed to (var := receiver) == nil ifTrue: .... ifFalse: ... This does not allow the block variable to shadow an existing temp, but it's no different from how to:do: is done." | ifNotNilArg | ifNotNilArg := arguments at: 1. ((self checkBlock: ifNotNilArg as: 'NotNil arg' from: encoder maxArgs: 1) and: [self checkBlock: (arguments at: 2) as: 'Nil arg' from: encoder]) ifFalse: [^false]. ifNotNilArg numberOfArguments = 1 ifTrue: [receiver := AssignmentNode new variable: ifNotNilArg firstArgument value: receiver]. selector := SelectorNode new key: #ifTrue:ifFalse: code: #macro. receiver := MessageNode new receiver: receiver selector: #== arguments: (Array with: NodeNil) precedence: 2 from: encoder. arguments swap: 1 with: 2. arguments do: [:arg| arg noteOptimized]. ^true! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/19/2008 17:17'! transformIfTrue: encoder (self transformBoolean: encoder) ifTrue: [arguments := Array with: (arguments at: 1) noteOptimized with: (BlockNode withJust: NodeNil) noteOptimized. ^true] ifFalse: [^false]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/19/2008 17:17'! transformIfTrueIfFalse: encoder ^(self checkBlock: (arguments at: 1) as: 'True arg' from: encoder) and: [(self checkBlock: (arguments at: 2) as: 'False arg' from: encoder) and: [arguments do: [:arg| arg noteOptimized]. true]]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/19/2008 17:15'! transformOr: encoder (self transformBoolean: encoder) ifTrue: [arguments := Array with: (BlockNode withJust: NodeTrue) noteOptimized with: (arguments at: 1) noteOptimized. ^true] ifFalse: [^false]! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 6/2/2008 14:14'! transformToDo: encoder " var := rcvr. L1: [var <= arg1] Bfp(L2) [block body. var := var + inc] Jmp(L1) L2: " | limit increment block initStmt test incStmt limitInit blockVar myRange blockRange | "First check for valid arguments" ((arguments last isMemberOf: BlockNode) and: [arguments last numberOfArguments = 1 and: [arguments last firstArgument isVariableReference "As with debugger remote vars"]]) ifFalse: [^false]. arguments size = 3 ifTrue: [increment := arguments at: 2. (increment isConstantNumber and: [increment literalValue ~= 0]) ifFalse: [^ false]] ifFalse: [increment := encoder encodeLiteral: 1]. arguments size < 3 ifTrue: "transform to full form" [selector := SelectorNode new key: #to:by:do: code: #macro]. "Now generate auxiliary structures" myRange := encoder rawSourceRanges at: self ifAbsent: [1 to: 0]. block := arguments last. blockRange := encoder rawSourceRanges at: block ifAbsent: [1 to: 0]. blockVar := block firstArgument. initStmt := AssignmentNode new variable: blockVar value: receiver. limit := arguments at: 1. limit isVariableReference | limit isConstantNumber ifTrue: [limitInit := nil] ifFalse: "Need to store limit in a var" [limit := encoder bindBlockArg: blockVar key, 'LimiT' within: block. limit scope: -2. "Already done parsing block; flag so it won't print" block addArgument: limit. limitInit := AssignmentNode new variable: limit value: arguments first]. test := MessageNode new receiver: blockVar selector: (increment key > 0 ifTrue: [#<=] ifFalse: [#>=]) arguments: (Array with: limit) precedence: precedence from: encoder sourceRange: (myRange first to: blockRange first). incStmt := AssignmentNode new variable: blockVar value: (MessageNode new receiver: blockVar selector: #+ arguments: (Array with: increment) precedence: precedence from: encoder) from: encoder sourceRange: (myRange last to: myRange last). arguments := (Array with: limit with: increment with: block), (Array with: initStmt with: test with: incStmt with: limitInit). block noteOptimized. ^true! ! !MessageNode methodsFor: 'macro transformations' stamp: 'eem 5/19/2008 19:17'! transformWhile: encoder (self checkBlock: receiver as: 'receiver' from: encoder) ifFalse: [^false]. arguments size = 0 ifTrue: "transform bodyless form to body form" [selector := SelectorNode new key: (special = 10 ifTrue: [#whileTrue:] ifFalse: [#whileFalse:]) code: #macro. arguments := Array with: (BlockNode withJust: NodeNil) noteOptimized. receiver noteOptimized. ^true]. ^(self transformBoolean: encoder) and: [receiver noteOptimized. arguments first noteOptimized. true]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 9/25/2008 16:12'! printOn: aStream indent: level "may not need this check anymore - may be fixed by the #receiver: change" special ifNil: [^aStream nextPutAll: '** MessageNode with nil special **']. special > 0 ifTrue: [^self perform: self macroPrinter with: aStream with: level]. self printReceiver: receiver on: aStream indent: level. selector isForFFICall ifTrue: [aStream space. selector printAsFFICallWithArguments: arguments on: aStream indent: 0] ifFalse: [self printKeywords: selector key arguments: arguments on: aStream indent: level]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 9/25/2008 14:51'! printParenReceiver: rcvr on: aStream indent: level rcvr isBlockNode ifTrue: [^rcvr printOn: aStream indent: level]. aStream nextPut: $(. rcvr printOn: aStream indent: level. aStream nextPut: $) ! ! !MessageNode methodsFor: 'printing' stamp: 'eem 9/25/2008 14:41'! printToDoOn: aStream indent: level | limitNode | self printReceiver: receiver on: aStream indent: level. (arguments last == nil or: [(arguments last isMemberOf: AssignmentNode) not]) ifTrue: [limitNode := arguments first] ifFalse: [limitNode := arguments last value]. (selector key = #to:by:do: and: [(arguments at: 2) isConstantNumber and: [(arguments at: 2) key == 1]]) ifTrue: [self printKeywords: #to:do: arguments: (Array with: limitNode with: (arguments at: 3)) on: aStream indent: level] ifFalse: [self printKeywords: selector key arguments: (Array with: limitNode) , arguments allButFirst on: aStream indent: level]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 9/25/2008 14:51'! printWithClosureAnalysisKeywords: key arguments: args on: aStream indent: level | keywords indent arg kwd doCrTab | args size = 0 ifTrue: [aStream space; nextPutAll: key. ^self]. keywords := key keywords. doCrTab := args size > 2 or: [{receiver} , args anySatisfy: [:thisArg | thisArg isBlockNode or: [thisArg isMessageNode and: [thisArg precedence >= 3]]]]. 1 to: (args size min: keywords size) do: [:i | arg := args at: i. kwd := keywords at: i. doCrTab ifTrue: [aStream crtab: level+1. indent := 1] "newline after big args" ifFalse: [aStream space. indent := 0]. aStream nextPutAll: kwd; space. arg printWithClosureAnalysisOn: aStream indent: level + 1 + indent precedence: (precedence = 2 ifTrue: [1] ifFalse: [precedence])]! ! !MessageNode methodsFor: 'printing' stamp: 'eem 9/25/2008 14:53'! printWithClosureAnalysisParenReceiver: rcvr on: aStream indent: level rcvr isBlockNode ifTrue: [^rcvr printWithClosureAnalysisOn: aStream indent: level]. aStream nextPut: $(. rcvr printWithClosureAnalysisOn: aStream indent: level. aStream nextPut: $)! ! !MessageNode methodsFor: 'printing' stamp: 'eem 9/25/2008 14:53'! printWithClosureAnalysisToDoOn: aStream indent: level | limitNode | self printWithClosureAnalysisReceiver: receiver on: aStream indent: level. limitNode := (arguments last == nil or: [arguments last isAssignmentNode not]) ifTrue: [arguments first] ifFalse: [arguments last value]. (selector key = #to:by:do: and: [(arguments at: 2) isConstantNumber and: [(arguments at: 2) key == 1]]) ifTrue: [self printWithClosureAnalysisKeywords: #to:do: arguments: (Array with: limitNode with: (arguments at: 3)) on: aStream indent: level] ifFalse: [self printWithClosureAnalysisKeywords: selector key arguments: (Array with: limitNode) , arguments allButFirst on: aStream indent: level]! ! !MessageNode methodsFor: 'equation translation' stamp: 'eem 9/25/2008 14:50'! eval "When everything in me is a constant, I can produce a value. This is only used by the Scripting system (TilePadMorph tilesFrom:in:)" | rec args | receiver isVariableNode ifFalse: [^ #illegal]. rec := receiver key value. args := arguments collect: [:each | each eval]. ^ rec perform: selector key withArguments: args! ! !MessageNode methodsFor: 'code generation (new scheme)' stamp: 'eem 9/29/2008 14:45'! emitCodeForCase: stack encoder: encoder value: forValue | braceNode sizeStream allReturn | forValue ifFalse: [^super emitCodeForEffect: stack encoder: encoder]. braceNode := arguments first. sizeStream := ReadStream on: sizes. receiver emitCodeForValue: stack encoder: encoder. "There must be at least one branch around the otherwise/caseError so the decompiler can identify the end of the otherwise/caseError." allReturn := true. "assume every case ends with a return" braceNode casesForwardDo: [:keyNode :valueNode :last | | thenSize elseSize | thenSize := sizeStream next. elseSize := sizeStream next. last ifFalse: [encoder genDup. stack push: 1]. keyNode emitCodeForEvaluatedValue: stack encoder: encoder. equalNode emitCode: stack args: 1 encoder: encoder. self emitCodeForBranchOn: false dist: thenSize pop: stack encoder: encoder. last ifFalse: [encoder genPop. stack pop: 1]. valueNode emitCodeForEvaluatedValue: stack encoder: encoder. last ifTrue: [stack pop: 1]. valueNode returns ifFalse: [self emitCodeForJump: elseSize encoder: encoder. allReturn := false]. (last and: [allReturn]) ifTrue: [self emitCodeForJump: elseSize encoder: encoder]]. arguments size = 2 ifTrue: [arguments last emitCodeForEvaluatedValue: stack encoder: encoder] "otherwise: [...]" ifFalse: [NodeSelf emitCodeForValue: stack encoder: encoder. caseErrorNode emitCode: stack args: 0 encoder: encoder]! ! !MessageNode methodsFor: 'code generation (new scheme)' stamp: 'eem 9/29/2008 14:43'! sizeCodeForCase: encoder value: forValue | braceNode sizeIndex thenSize elseSize allReturn | forValue not ifTrue: [^super sizeCodeForEffect: encoder]. equalNode := encoder encodeSelector: #=. braceNode := arguments first. sizes := Array new: 2 * braceNode numElements. sizeIndex := sizes size. elseSize := arguments size = 2 ifTrue: [arguments last sizeCodeForEvaluatedValue: encoder] "otherwise: [...]" ifFalse: [caseErrorNode := encoder encodeSelector: #caseError. (NodeSelf sizeCodeForValue: encoder) + (caseErrorNode sizeCode: encoder args: 0 super: false)]. "self caseError" "There must be at least one branch around the otherwise/caseError so the decompiler can identify the end of the otherwise/caseError." allReturn := true. "assume every case ends with a return" braceNode casesForwardDo: [:keyNode :valueNode :last | valueNode returns ifFalse: [allReturn := false]]. braceNode casesReverseDo: [:keyNode :valueNode :last | sizes at: sizeIndex put: elseSize. thenSize := valueNode sizeCodeForEvaluatedValue: encoder. last ifFalse: [thenSize := thenSize + encoder sizePop]. valueNode returns ifFalse: [thenSize := thenSize + (self sizeCode: encoder forJump: elseSize)]. (last and: [allReturn]) ifTrue: [thenSize := thenSize + (self sizeCode: encoder forJump: elseSize)]. sizes at: sizeIndex-1 put: thenSize. last ifFalse: [elseSize := elseSize + encoder sizeDup]. elseSize := elseSize + (keyNode sizeCodeForEvaluatedValue: encoder) + (equalNode sizeCode: encoder args: 1 super: false) + (self sizeCode: encoder forBranchOn: false dist: thenSize) + thenSize. sizeIndex := sizeIndex - 2]. ^(receiver sizeCodeForValue: encoder) + elseSize! ! !MessageNode methodsFor: 'code generation (closures)' stamp: 'eem 7/27/2008 19:10'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" special > 0 ifTrue: [receiver ifOptimizedBlockHoistTempsInto: scopeBlock. "caseOf: is complicated. We need to handle it specially. The cases are within a BraceNode where each case is of the form [guard block] -> [action block]" (selector key beginsWith: 'caseOf:') ifTrue: [arguments first elements do: [:messageNode| messageNode receiver ifOptimizedBlockHoistTempsInto: scopeBlock. messageNode arguments first ifOptimizedBlockHoistTempsInto: scopeBlock]]. arguments do: [:node| node == nil ifFalse: "last argument of optimized to:do: can be nil" [node ifOptimizedBlockHoistTempsInto: scopeBlock]]]. "receiver is nil in cascades" receiver == nil ifFalse: [receiver analyseTempsWithin: scopeBlock rootNode: rootNode]. arguments do: [:node| node == nil ifFalse: "last argument of optimized to:do: can be nil" [node analyseTempsWithin: scopeBlock rootNode: rootNode]]! ! !MethodNode methodsFor: 'printing' stamp: 'eem 9/25/2008 15:22'! printOn: aStream | selectorNode | selectorNode := self selectorNode. precedence = 1 ifTrue: [selectorNode isForFFICall ifTrue: [selectorNode printAsFFICallWithArguments: arguments on: aStream indent: 0] ifFalse: [aStream nextPutAll: selectorNode key]] ifFalse: [selectorNode key keywords with: arguments do: [:kwd :arg | aStream nextPutAll: kwd; space; nextPutAll: arg key; space]]. comment == nil ifFalse: [aStream crtab: 1. self printCommentOn: aStream indent: 1]. block printTemporaries: temporaries on: aStream doPrior: [aStream crtab: 1]. primitive > 0 ifTrue: [(primitive between: 255 and: 519) ifFalse: "Dont decompile quick prims e.g, ^ self or ^instVar" [aStream crtab: 1. self printPrimitiveOn: aStream]]. self printPropertiesOn: aStream. aStream crtab: 1. block printStatementsOn: aStream indent: 0! ! !MethodNode methodsFor: 'printing' stamp: 'eem 6/2/2008 11:56'! printWithClosureAnalysisOn: aStream precedence = 1 ifTrue: [(self selector includesSubString: '()/') ifTrue: [aStream nextPutAll: (self selector copyUpTo: $)). arguments do: [:arg| aStream nextPutAll: arg key] separatedBy: [aStream nextPutAll: ', ']. aStream nextPut: $)] ifFalse: [aStream nextPutAll: self selector]] "no node for method selector" ifFalse: [self selector keywords with: arguments do: [:kwd :arg | aStream nextPutAll: kwd; space. arg printDefinitionForClosureAnalysisOn: aStream. aStream space]]. comment == nil ifFalse: [aStream crtab: 1. self printCommentOn: aStream indent: 1]. temporaries size > 0 ifTrue: [aStream crtab: 1; nextPut: $|. temporaries do: [:temp | aStream space. temp printDefinitionForClosureAnalysisOn: aStream]. aStream space; nextPut: $|]. primitive > 0 ifTrue: [(primitive between: 255 and: 519) ifFalse: "Dont decompile quick prims e.g, ^ self or ^instVar" [aStream crtab: 1. self printPrimitiveOn: aStream]]. self printPropertiesOn: aStream. aStream crtab: 1. block printWithClosureAnalysisStatementsOn: aStream indent: 0! ! !MethodNode methodsFor: 'source mapping' stamp: 'eem 6/4/2008 19:21'! rawSourceRanges ^self rawSourceRangesAndMethodDo: [:rawSourceRanges :method| rawSourceRanges]! ! !MethodNode methodsFor: 'source mapping' stamp: 'eem 6/20/2008 15:09'! rawSourceRangesAndMethodDo: aBinaryBlock "Evaluate aBinaryBlock with the rawSourceRanges and method generated from the receiver." | methNode method | methNode := sourceText ifNil: "No source, use decompile string as source to map from" [self parserClass new encoderClass: encoder class; parse: self decompileString class: self methodClass] ifNotNil: [self prepareForRegeneration. self]. method := methNode generate: #(0 0 0 0). "set bytecodes to map to" ^aBinaryBlock value: methNode encoder rawSourceRanges value: method! ! !MethodNode methodsFor: 'visiting' stamp: 'eem 9/10/2008 15:52'! accept: aVisitor aVisitor visitMethodNode: self. ^aVisitor! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (new scheme)' stamp: 'eem 9/12/2008 11:00'! generate: trailer "The receiver is the root of a parse tree. Answer a CompiledMethod. The argument, trailer, is the reference to the source code that is stored with every CompiledMethod." | blkSize nLits literals stack method | self generate: trailer ifQuick: [:m | m literalAt: 2 put: encoder associationForClass; properties: properties. ^m]. encoder supportsClosureOpcodes ifTrue: [self ensureClosureAnalysisDone. encoder rootNode: self. "this is for BlockNode>>sizeCodeForClosureValue:"]. blkSize := block sizeCodeForEvaluatedValue: encoder. method := CompiledMethod newBytes: blkSize trailerBytes: trailer nArgs: arguments size nTemps: (encoder supportsClosureOpcodes ifTrue: [| locals | locals := arguments, temporaries. encoder noteBlockExtent: block blockExtent hasLocals: locals. locals size] ifFalse: [encoder maxTemp]) nStack: 0 nLits: (nLits := (literals := encoder allLiterals) size) primitive: primitive. nLits > 255 ifTrue: [^self error: 'Too many literals referenced']. 1 to: nLits do: [:lit | method literalAt: lit put: (literals at: lit)]. encoder streamToMethod: method. stack := ParseStack new init. stack position: method numTemps. block emitCodeForEvaluatedValue: stack encoder: encoder. stack position ~= (method numTemps + 1) ifTrue: [^self error: 'Compiler stack discrepancy']. encoder methodStreamPosition ~= (method size - trailer size) ifTrue: [^self error: 'Compiler code size discrepancy']. method needsFrameSize: stack size - method numTemps. method properties: properties. ^method! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 5/29/2008 15:53'! copiedValuesWithinBlockExtent: anInterval ^((localsPool select: [:temp| temp isReferencedWithinBlockExtent: anInterval]) collect: [:temp| temp isRemote ifTrue: [temp remoteNode] ifFalse: [temp]]) asSortedCollection: [:t1 :t2 | t1 index < t2 index]! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 7/24/2008 10:04'! ensureClosureAnalysisDone block blockExtent ifNil: [temporaries := block analyseArguments: arguments temporaries: temporaries rootNode: self]! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 13:43'! locationCounter ^locationCounter! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 5/30/2008 11:27'! noteBlockEntry: aBlock "Evaluate aBlock with the numbering for the block entry." locationCounter isNil ifTrue: [locationCounter := -1]. aBlock value: locationCounter + 1. locationCounter := locationCounter + 2! ! !BytecodeAgnosticMethodNode methodsFor: 'code generation (closures)' stamp: 'eem 6/2/2008 12:12'! noteBlockExit: aBlock "Evaluate aBlock with the numbering for the block exit." aBlock value: locationCounter + 1. locationCounter := locationCounter + 2! ! !BytecodeAgnosticMethodNode methodsFor: 'debugger support' stamp: 'eem 6/21/2008 13:39'! blockExtentsToTempRefs | blockExtentsToTempRefs methNode | blockExtentsToTempRefs := encoder blockExtentsToTempRefs. blockExtentsToTempRefs ifNil: [methNode := sourceText ifNil: "No source, use decompile string as source to map from" [self parserClass new encoderClass: encoder class; parse: self decompileString class: self methodClass] ifNotNil: [self prepareForRegeneration. self]. methNode generate: #(0 0 0 0). "set bytecodes to map to" blockExtentsToTempRefs := methNode encoder blockExtentsToTempRefs]. ^blockExtentsToTempRefs! ! !BytecodeAgnosticMethodNode methodsFor: 'debugger support' stamp: 'eem 6/20/2008 15:13'! prepareForRegeneration "Closure methods need to remove any remote temps prior to being regenerated." (temporaries notEmpty and: [temporaries last isIndirectTempVector]) ifTrue: [temporaries := temporaries allButLast]. self accept: RegenerationPreparingVisitor new! ! !ParseNode class methodsFor: 'accessing' stamp: 'eem 5/21/2008 13:18'! pushNilCode ^LdNil! ! !Parser methodsFor: 'public access' stamp: 'eem 6/19/2008 09:38'! encoder encoder isNil ifTrue: [encoder := Encoder new]. ^encoder! ! !Parser methodsFor: 'public access' stamp: 'eem 9/12/2008 13:34'! parse: sourceStreamOrString class: behavior ^ self parse: sourceStreamOrString readStream class: behavior noPattern: false context: nil notifying: nil ifFail: [^nil]! ! !Parser methodsFor: 'expression types' stamp: 'eem 8/4/2008 13:38'! blockExpression "[ ({:var} |) (| {temps} |) (statements) ] => BlockNode." | blockNode variableNodes temporaryBlockVariables start | blockNode := BlockNode new. variableNodes := OrderedCollection new. start := prevMark + requestorOffset. "Gather parameters." [self match: #colon] whileTrue: [variableNodes addLast: (encoder bindBlockArg: self argumentName within: blockNode)]. (variableNodes size > 0 & (hereType ~~ #rightBracket) and: [(self match: #verticalBar) not]) ifTrue: [^self expected: 'Vertical bar']. temporaryBlockVariables := self temporaryBlockVariablesFor: blockNode. self statements: variableNodes innerBlock: true blockNode: blockNode. parseNode temporaries: temporaryBlockVariables. (self match: #rightBracket) ifFalse: [^self expected: 'Period or right bracket']. blockNode noteSourceRangeStart: start end: self endOfLastToken encoder: encoder. "The scope of the parameters and temporary block variables is no longer active." temporaryBlockVariables do: [:variable | variable scope: -1]. variableNodes do: [:variable | variable scope: -1]! ! !Parser methodsFor: 'expression types' stamp: 'eem 5/29/2008 09:36'! newMethodNode ^self encoder methodNodeClass new! ! !Parser methodsFor: 'expression types' stamp: 'eem 7/16/2008 11:05'! pattern: fromDoit inContext: ctxt " unarySelector | binarySelector arg | keyword arg {keyword arg} => {selector, arguments, precedence}." | args selector | doitFlag := fromDoit. fromDoit ifTrue: [^ctxt == nil ifTrue: [{#DoIt. {}. 1}] ifFalse: [{#DoItIn:. {encoder encodeVariable: encoder doItInContextName}. 3}]]. hereType == #word ifTrue: [^ {self advance asSymbol. {}. 1}]. (hereType == #binary or: [hereType == #verticalBar]) ifTrue: [selector := self advance asSymbol. args := Array with: (encoder bindArg: self argumentName). ^ {selector. args. 2}]. hereType == #keyword ifTrue: [selector := WriteStream on: (String new: 32). args := OrderedCollection new. [hereType == #keyword] whileTrue:[ selector nextPutAll: self advance. args addLast: (encoder bindArg: self argumentName). ]. ^ {selector contents asSymbol. args. 3}]. hereType == #positionalMessage ifTrue:[ args := OrderedCollection new. selector := self advance. hereType == #rightParenthesis ifTrue:[self advance. ^{(selector,'/0') asSymbol. args. 1}]. [ args addLast: (encoder bindArg: self argumentName). hereType == #rightParenthesis ifTrue:[ self advance. selector := (selector,'/', args size printString) asSymbol. ^{selector. args. 1}]. here == #, ifFalse:[self expected: 'comma']. self advance. ] repeat. ]. ^self expected: 'Message pattern'! ! !Parser methodsFor: 'expression types' stamp: 'eem 5/30/2008 11:51'! statements: argNodes innerBlock: inner ^self statements: argNodes innerBlock: inner blockNode: BlockNode new! ! !Parser methodsFor: 'expression types' stamp: 'eem 8/4/2008 10:56'! statements: argNodes innerBlock: inner blockNode: theBlockNode | stmts returns start | "give initial comment to block, since others trail statements" theBlockNode comment: currentComment. stmts := OrderedCollection new. returns := false. hereType ~~ #rightBracket ifTrue: [[theBlockNode startOfLastStatement: (start := self startOfNextToken). (returns := self matchReturn) ifTrue: [self expression ifFalse: [^self expected: 'Expression to return']. self addComment. stmts addLast: (parseNode isReturningIf ifTrue: [parseNode] ifFalse: [ReturnNode new expr: parseNode encoder: encoder sourceRange: (start to: self endOfLastToken)])] ifFalse: [self expression ifTrue: [self addComment. stmts addLast: parseNode] ifFalse: [self addComment. stmts size = 0 ifTrue: [stmts addLast: (encoder encodeVariable: (inner ifTrue: ['nil'] ifFalse: ['self']))]]]. returns ifTrue: [self match: #period. (hereType == #rightBracket or: [hereType == #doIt]) ifFalse: [^self expected: 'End of block']]. returns not and: [self match: #period]] whileTrue]. theBlockNode arguments: argNodes statements: stmts returns: returns from: encoder. parseNode := theBlockNode. ^true! ! !Parser methodsFor: 'error handling' stamp: 'eem 9/25/2008 12:41'! notify: string at: location requestor isNil ifTrue: [(encoder == self or: [encoder isNil]) ifTrue: [^ self fail "failure setting up syntax error"]. SyntaxErrorNotification inClass: encoder classEncoding category: category withCode: (source contents copyReplaceFrom: location to: location - 1 with: string , ' ->') doitFlag: doitFlag errorMessage: string location: location] ifFalse: [requestor notify: string , ' ->' at: location in: source]. ^self fail! ! !Scanner class methodsFor: 'testing' stamp: 'eem 9/29/2008 21:28'! isLiteralSymbol: aSymbol "Test whether a symbol can be stored as # followed by its characters. Symbols created internally with asSymbol may not have this property, e.g. '3' asSymbol." | i ascii type | i := aSymbol size. i = 0 ifTrue: [^ false]. i = 1 ifTrue: [^('$''"()#0123456789_' includes: (aSymbol at: 1)) not]. ascii := (aSymbol at: 1) charCode. "TypeTable should have been origined at 0 rather than 1 ..." ascii = 0 ifTrue: [^ false]. type := TypeTable at: ascii ifAbsent: [#xLetter]. (type == #xColon or: [type == #verticalBar or: [type == #xBinary]]) ifTrue: [^i = 1]. type == #xLetter ifTrue: [[i > 1] whileTrue: [ascii := (aSymbol at: i) charCode. ascii = 0 ifTrue: [^false]. type := TypeTable at: ascii ifAbsent: [#xLetter]. (type == #xLetter or: [type == #xDigit or: [type == #xColon]]) ifFalse: [^false]. i := i - 1]. ^true]. ^false! ! !SystemDictionary methodsFor: 'special objects' stamp: 'eem 7/22/2008 18:37'! recreateSpecialObjectsArray "Smalltalk recreateSpecialObjectsArray" "The Special Objects Array is an array of object pointers used by the Squeak virtual machine. Its contents are critical and unchecked, so don't even think of playing here unless you know what you are doing." | newArray | newArray := Array new: 50. "Nil false and true get used throughout the interpreter" newArray at: 1 put: nil. newArray at: 2 put: false. newArray at: 3 put: true. "This association holds the active process (a ProcessScheduler)" newArray at: 4 put: (self associationAt: #Processor). "Numerous classes below used for type checking and instantiation" newArray at: 5 put: Bitmap. newArray at: 6 put: SmallInteger. newArray at: 7 put: ByteString. newArray at: 8 put: Array. newArray at: 9 put: Smalltalk. newArray at: 10 put: Float. newArray at: 11 put: MethodContext. newArray at: 12 put: BlockContext. newArray at: 13 put: Point. newArray at: 14 put: LargePositiveInteger. newArray at: 15 put: Display. newArray at: 16 put: Message. newArray at: 17 put: CompiledMethod. newArray at: 18 put: (self specialObjectsArray at: 18). "(low space Semaphore)" newArray at: 19 put: Semaphore. newArray at: 20 put: Character. newArray at: 21 put: #doesNotUnderstand:. newArray at: 22 put: #cannotReturn:. newArray at: 23 put: nil. "An array of the 32 selectors that are compiled as special bytecodes, paired alternately with the number of arguments each takes." newArray at: 24 put: #( #+ 1 #- 1 #< 1 #> 1 #<= 1 #>= 1 #= 1 #~= 1 #* 1 #/ 1 #\\ 1 #@ 1 #bitShift: 1 #// 1 #bitAnd: 1 #bitOr: 1 #at: 1 #at:put: 2 #size 0 #next 0 #nextPut: 1 #atEnd 0 #== 1 #class 0 #blockCopy: 1 #value 0 #value: 1 #do: 1 #new 0 #new: 1 #x 0 #y 0 ). "An array of the 255 Characters in ascii order." newArray at: 25 put: ((0 to: 255) collect: [:ascii | Character value: ascii]). newArray at: 26 put: #mustBeBoolean. newArray at: 27 put: ByteArray. newArray at: 28 put: Process. "An array of up to 31 classes whose instances will have compact headers" newArray at: 29 put: self compactClassesArray. newArray at: 30 put: (self specialObjectsArray at: 30). "(delay Semaphore)" newArray at: 31 put: (self specialObjectsArray at: 31). "(user interrupt Semaphore)" "Prototype instances that can be copied for fast initialization" newArray at: 32 put: (Float new: 2). newArray at: 33 put: (LargePositiveInteger new: 4). newArray at: 34 put: Point new. newArray at: 35 put: #cannotInterpret:. "Note: This must be fixed once we start using context prototypes (yeah, right)" "(MethodContext new: CompiledMethod fullFrameSize)." newArray at: 36 put: (self specialObjectsArray at: 36). "Is the prototype MethodContext (unused by the VM)" newArray at: 37 put: BlockClosure. "(BlockContext new: CompiledMethod fullFrameSize)." newArray at: 38 put: (self specialObjectsArray at: 38). "Is the prototype BlockContext (unused by the VM)" newArray at: 39 put: (self specialObjectsArray at: 39). "preserve external semaphores" "array of objects referred to by external code" newArray at: 40 put: PseudoContext. newArray at: 41 put: TranslatedMethod. "finalization Semaphore" newArray at: 42 put: ((self specialObjectsArray at: 42) ifNil: [Semaphore new]). newArray at: 43 put: LargeNegativeInteger. "External objects for callout. Note: Written so that one can actually completely remove the FFI." newArray at: 44 put: (self at: #ExternalAddress ifAbsent: []). newArray at: 45 put: (self at: #ExternalStructure ifAbsent: []). newArray at: 46 put: (self at: #ExternalData ifAbsent: []). newArray at: 47 put: (self at: #ExternalFunction ifAbsent: []). newArray at: 48 put: (self at: #ExternalLibrary ifAbsent: []). newArray at: 49 put: #aboutToReturn:through:. newArray at: 50 put: #run:with:in:. "Now replace the interpreter's reference in one atomic operation" self specialObjectsArray become: newArray! ! !VariableNode methodsFor: 'testing' stamp: 'eem 5/21/2008 11:06'! index "This code attempts to reconstruct the index from its encoding in code." code < 0 ifTrue:[^nil]. code > 256 ifTrue: [self assert: index = (code \\ 256). ^code \\ 256]. code >= (CodeBases at: self type) ifTrue: [self assert: index = (code - (CodeBases at: self type)). ^code - (CodeBases at: self type)]. self assert: index = (code - self type). ^code - self type! ! !VariableNode methodsFor: 'code generation' stamp: 'eem 9/5/2008 18:14'! fieldOffset "Return temp or instVar offset for this variable" ^index ifNil: [code < 256 ifTrue: [code \\ 16] ifFalse: [code \\ 256]]! ! !TempVariableNode methodsFor: 'initialize-release' stamp: 'eem 9/8/2008 18:27'! name: varName index: i type: type scope: level "Only used for initting temporary variables" hasDefs := hasRefs := false. scope := level. ^super name: varName key: varName index: i type: type! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 9/10/2008 10:04'! assignmentCheck: encoder at: location ^((self isBlockArg and: [Preferences allowBlockArgumentAssignment not]) or: [self isMethodArg]) ifTrue: [location] ifFalse: [-1]! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 9/8/2008 18:24'! isArg ^argType notNil! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 9/8/2008 18:20'! isMethodArg ^#method == argType! ! !TempVariableNode methodsFor: 'testing' stamp: 'eem 5/30/2008 12:31'! scope "Answer scope of temporary variables. Currently only the following distinctions are made: 0 outer level: args and user-declared temps 1 block args and doLimiT temps -1 a block temp that is no longer active -2 a block temp that held limit of to:do:" ^scope! ! !TempVariableNode methodsFor: 'printing' stamp: 'eem 7/23/2008 21:21'! printDefinitionForClosureAnalysisOn: aStream | refs | aStream nextPut: ${; nextPutAll: key. definingScope ifNotNil: [definingScope blockExtent ifNotNil: [:be| aStream nextPutAll: ' d@'; print: be first]]. readingScopes notNil ifTrue: [refs := Set new. readingScopes do: [:elems| refs addAll: elems]. refs asSortedCollection do: [:read| aStream nextPutAll: ' r@'; print: read]]. writingScopes notNil ifTrue: [refs := Set new. writingScopes do: [:elems| refs addAll: elems]. refs asSortedCollection do: [:write| aStream nextPutAll: ' w@'; print: write]]. aStream nextPut: $}! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 16:22'! emitCodeForLoad: stack encoder: encoder remoteNode ~~ nil ifTrue: [remoteNode emitCodeForLoadFor: self stack: stack encoder: encoder]! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 14:54'! emitCodeForStorePop: stack encoder: encoder remoteNode ~~ nil ifTrue: [^remoteNode emitCodeForStorePopInto: self stack: stack encoder: encoder]. encoder genStorePopTemp: index. stack pop: 1! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 14:53'! emitCodeForStore: stack encoder: encoder remoteNode ~~ nil ifTrue: [^remoteNode emitCodeForStoreInto: self stack: stack encoder: encoder]. encoder genStoreTemp: index! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 14:53'! emitCodeForValue: stack encoder: encoder remoteNode ~~ nil ifTrue: [^remoteNode emitCodeForValueOf: self stack: stack encoder: encoder]. encoder genPushTemp: index. stack push: 1! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 16:23'! sizeCodeForLoad: encoder ^remoteNode isNil ifTrue: [0] ifFalse: [remoteNode sizeCodeForLoadFor: self encoder: encoder]! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 14:52'! sizeCodeForStorePop: encoder remoteNode ~~ nil ifTrue: [^remoteNode sizeCodeForStorePopInto: self encoder: encoder]. self reserve: encoder. ^encoder sizeStorePopTemp: index! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 14:52'! sizeCodeForStore: encoder remoteNode ~~ nil ifTrue: [^remoteNode sizeCodeForStoreInto: self encoder: encoder]. self reserve: encoder. ^encoder sizeStoreTemp: index! ! !TempVariableNode methodsFor: 'code generation (new scheme)' stamp: 'eem 5/20/2008 14:51'! sizeCodeForValue: encoder remoteNode ~~ nil ifTrue: [^remoteNode sizeCodeForValueOf: self encoder: encoder]. self reserve: encoder. ^encoder sizePushTemp: index! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 10:56'! addReadWithin: scopeBlock "" at: location "" readingScopes ifNil: [readingScopes := Dictionary new]. (readingScopes at: scopeBlock ifAbsentPut: [Set new]) add: location! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 10:55'! addWriteWithin: scopeBlock "" at: location "" writingScopes ifNil: [writingScopes := Dictionary new]. (writingScopes at: scopeBlock ifAbsentPut: [Set new]) add: location! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/21/2008 11:24'! analyseClosure: rootNode "" "A temp cannot be local if it is written to remotely, or if it is written to after it is closed-over." | latestWrite | latestWrite := 0. ((writingScopes notNil and: [writingScopes associations anySatisfy: [:assoc| [:blockScope :refs| refs do: [:write| latestWrite := write max: latestWrite]. "A temp cannot be local if it is written to remotely." blockScope ~~ definingScope] value: assoc key value: assoc value]]) or: [readingScopes notNil and: [readingScopes associations anySatisfy: [:assoc| [:blockScope :refs| "A temp cannot be local if it is written to after it is closed-over." blockScope ~~ definingScope and: [refs anySatisfy: [:read| read < latestWrite]]] value: assoc key value: assoc value]]]) ifTrue: [remoteNode := definingScope addRemoteTemp: self rootNode: rootNode]! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 10:56'! analyseTempsWithin: scopeBlock "" rootNode: rootNode "" self addReadWithin: scopeBlock at: rootNode locationCounter! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 11:42'! beingAssignedToAnalyseTempsWithin: scopeBlock "" rootNode: rootNode "" self addWriteWithin: scopeBlock at: rootNode locationCounter! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 14:39'! index: anInteger "For renumbering temps in the closure compiler. Intended to be used only in BlockNode>>postNumberingProcessTemps:" index := anInteger. code := self code: index type: LdTempType! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 18:07'! isReferencedWithinBlockExtent: anInterval readingScopes ~~ nil ifTrue: [readingScopes do: [:set ">"| set do: [:location| (anInterval rangeIncludes: location) ifTrue: [^true]]]]. writingScopes ~~ nil ifTrue: [writingScopes do: [:set ">"| set do: [:location| (anInterval rangeIncludes: location) ifTrue: [^true]]]]. ^false! ! !TempVariableNode methodsFor: 'code generation (closures)' stamp: 'eem 5/20/2008 18:01'! referenceScopesAndIndicesDo: aBinaryBlock "Evaluate aBinaryBlock with all read or write scopes and locations. This is used to copy the reference information into RemoteTempVectorNodes" readingScopes ~~ nil ifTrue: [readingScopes keysAndValuesDo: [:scopeBlock "" :set ">"| set do: [:location| aBinaryBlock value: scopeBlock value: location]]]. writingScopes ~~ nil ifTrue: [writingScopes keysAndValuesDo: [:scopeBlock "" :set ">"| set do: [:location| aBinaryBlock value: scopeBlock value: location]]]! ! RemoteTempVectorNode removeSelector: #addRemoteTemp:! RemoteTempVectorNode removeSelector: #emitCodeForIndexOf:encoder:! RemoteTempVectorNode removeSelector: #emitCodeForLoadOf:stack:encoder:! RemoteTempVectorNode removeSelector: #emitCodeForStorePop:encoder:! RemoteTempVectorNode removeSelector: #emitCodeForStore:encoder:! RemoteTempVectorNode removeSelector: #postCopyForAssertionCheck! RemoteTempVectorNode removeSelector: #sizeCodeForLoadOf:encoder:! RemoteTempVectorNode removeSelector: #sizeCodeForLoad:! TempVariableNode removeSelector: #analyseClosure! TempVariableNode removeSelector: #analyseTempsWithin:encoder:! TempVariableNode removeSelector: #beingAssignedToAnalyseTempsWithin:encoder:! VariableNode subclass: #TempVariableNode instanceVariableNames: 'argType isAnArg hasRefs hasDefs scope definingScope readingScopes writingScopes remoteNode' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! !TempVariableNode reorganize! ('initialize-release' name:index:type:scope: nowHasDef nowHasRef scope:) ('testing' assignmentCheck:at: beBlockArg beMethodArg isArg isBlockArg isMethodArg isRemote isTemp isUndefTemp isUnusedTemp remoteNode scope) ('printing' printDefinitionForClosureAnalysisOn: printOn:indent: printWithClosureAnalysisOn:indent:) ('tiles' asMorphicSyntaxIn:) ('code generation (new scheme)' emitCodeForLoad:encoder: emitCodeForStorePop:encoder: emitCodeForStore:encoder: emitCodeForValue:encoder: sizeCodeForLoad: sizeCodeForStorePop: sizeCodeForStore: sizeCodeForValue:) ('code generation (closures)' addReadWithin:at: addWriteWithin:at: analyseClosure: analyseTempsWithin:rootNode: beingAssignedToAnalyseTempsWithin:rootNode: definingScope: index: isDefinedWithinBlockExtent: isIndirectTempVector isReferencedWithinBlockExtent: referenceScopesAndIndicesDo:) ('visiting' accept:) ('debugger access' cleanUpForRegeneration) ('decompiler' remoteNode:) ! !VariableNode reorganize! ('initialize-release' asStorableNode: name:index:type: name:key:code: name:key:index:type:) ('testing' assignmentCheck:at: canBeSpecialArgument index isSelfPseudoVariable isVariableNode isVariableReference type) ('code generation' emitForReturn:on: emitForValue:on: emitLoad:on: emitStore:on: emitStorePop:on: fieldOffset sizeForReturn: sizeForStore: sizeForStorePop:) ('printing' printOn:indent: printWithClosureAnalysisOn:indent:) ('tiles' asMorphicSyntaxIn: currentValueIn: variableGetterBlockIn:) ('accessing' name) ('code generation (new scheme)' emitCodeForLoad:encoder: emitCodeForReturn:encoder: emitCodeForStorePop:encoder: emitCodeForStore:encoder: emitCodeForValue:encoder: sizeCodeForReturn: sizeCodeForStorePop: sizeCodeForStore: sizeCodeForValue:) ('code generation (closures)' beingAssignedToAnalyseTempsWithin:rootNode:) ('*VMMaker-C translation' asTranslatorNode) ('visiting' accept:) ! TileMessageNode removeSelector: #printWithClosureAnalysisIfNilNotNil:indent:! TileMessageNode removeSelector: #printWithClosureAnalysisKeywords:arguments:on:indent:! SelectorNode removeSelector: #printAsFFICallWithArguments:on:! !Parser class reorganize! ('class initialization' initialize) ! ReturnNode removeSelector: #analyseTempsWithin:forValue:encoder:! Parser removeSelector: #temporaryBlockVariables! ParseNodeVisitor removeSelector: #visitConsArrayNode:! !ParseNode class reorganize! ('class initialization' initialize) ('accessing' blockReturnCode popCode pushNilCode) ! BytecodeAgnosticMethodNode removeSelector: #generateForClosure! BytecodeAgnosticMethodNode removeSelector: #generateForClosure:! BytecodeAgnosticMethodNode removeSelector: #generateUsingClosures:! BytecodeAgnosticMethodNode removeSelector: #printWithClosureAnalysisOn:indent:! MethodNode subclass: #BytecodeAgnosticMethodNode instanceVariableNames: 'locationCounter localsPool' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! !BytecodeAgnosticMethodNode reorganize! ('code generation (new scheme)' generate:) ('code generation (closures)' addLocalsToPool: copiedValuesWithinBlockExtent: ensureClosureAnalysisDone locationCounter noteBlockEntry: noteBlockExit: referencedValuesWithinBlockExtent:) ('debugger support' blockExtentsToTempRefs prepareForRegeneration) ('printing' printWithClosureAnalysisOn:) ! MethodNode removeSelector: #rawSourceRangesAndMethodGeneratedUsingClosuresDo:! MethodNode removeSelector: #sourceMap! !MethodNode reorganize! ('initialize-release' block selector: selector:arguments:precedence:temporaries:block:encoder:primitive: selector:arguments:precedence:temporaries:block:encoder:primitive:properties: sourceText:) ('code generation' encoder generate: generate:ifQuick: parserClass selector selectorNode) ('converting' asColorizedSmalltalk80Text decompileString decompileText prepareForRegeneration) ('printing' methodClass printOn: printPrimitiveOn: printPropertiesOn: printWithClosureAnalysisOn: sourceText tempNames) ('source mapping' rawSourceRanges rawSourceRangesAndMethodDo:) ('tiles' asMorphicSyntaxIn: asMorphicSyntaxUsing:) ('*VMMaker-C translation' asTranslationMethodOfClass:) ('visiting' accept:) ! MessageNode removeSelector: #analyseTempsWithin:forValue:encoder:! MessageNode removeSelector: #printKeywords:arguments:on:indent:prefix:! MessageNode removeSelector: #printWithClosureAnalysisKeywords:arguments:on:indent:prefix:! !MessageNode reorganize! ('initialize-release' receiver:selector:arguments:precedence: receiver:selector:arguments:precedence:from: receiver:selector:arguments:precedence:from:sourceRange: selector:) ('testing' canCascade isComplex isMessageNode isMessage:receiver:arguments: isNilIf isReturningIf toDoIncrement: toDoLimit:) ('cascading' cascadeReceiver) ('macro transformations' noteSpecialSelector: toDoFromWhileWithInit: transformCase: transform: transformAnd: transformBoolean: transformIfFalse: transformIfFalseIfTrue: transformIfNil: transformIfNilIfNotNil: transformIfNotNilIfNil: transformIfTrue: transformIfTrueIfFalse: transformOr: transformToDo: transformWhile:) ('code generation' emitCase:on:value: emitForEffect:on: emitForValue:on: emitIf:on:value: emitIfNil:on:value: emitToDo:on:value: emitWhile:on:value: sizeCase:value: sizeForEffect: sizeForValue: sizeIf:value: sizeIfNil:value: sizeToDo:value: sizeWhile:value:) ('printing' asMorphicCaseOn:indent: macroPrinter precedence printCaseOn:indent: printIfNil:indent: printIfNilNotNil:indent: printIfOn:indent: printKeywords:arguments:on:indent: printOn:indent: printOn:indent:precedence: printParenReceiver:on:indent: printReceiver:on:indent: printToDoOn:indent: printWhileOn:indent: printWithClosureAnalysisCaseOn:indent: printWithClosureAnalysisIfNilNotNil:indent: printWithClosureAnalysisIfNil:indent: printWithClosureAnalysisIfOn:indent: printWithClosureAnalysisKeywords:arguments:on:indent: printWithClosureAnalysisOn:indent: printWithClosureAnalysisOn:indent:precedence: printWithClosureAnalysisParenReceiver:on:indent: printWithClosureAnalysisReceiver:on:indent: printWithClosureAnalysisToDoOn:indent: printWithClosureAnalysisWhileOn:indent: test) ('private' checkBlock:as:from: checkBlock:as:from:maxArgs: ifNilReceiver pvtCheckForPvtSelector: receiver:arguments:precedence:) ('equation translation' arguments arguments: eval receiver receiver: selector) ('tiles' asMorphicSyntaxIn: morphFromKeywords:arguments:on:indent:) ('code generation (new scheme)' emitCodeForCase:encoder:value: emitCodeForEffect:encoder: emitCodeForIfNil:encoder:value: emitCodeForIf:encoder:value: emitCodeForToDo:encoder:value: emitCodeForValue:encoder: emitCodeForWhile:encoder:value: sizeCodeForCase:value: sizeCodeForEffect: sizeCodeForIfNil:value: sizeCodeForIf:value: sizeCodeForToDo:value: sizeCodeForValue: sizeCodeForWhile:value:) ('code generation (closures)' analyseTempsWithin:rootNode:) ('*VMMaker-C translation' asTranslatorNode) ('visiting' accept: argumentsInEvaluationOrder) ! LeafNode removeSelector: #analyseTempsWithin:encoder:! EncoderForV3PlusClosures removeSelector: #emitPushNewArray:! EncoderForV3PlusClosures removeSelector: #emitStorePopRemoteTemp:inVectorAt:! EncoderForV3PlusClosures removeSelector: #emitStoreRemoteTemp:inVectorAt:! EncoderForV3PlusClosures removeSelector: #genPushClosureCopyCopiedValues:jumpSize:! EncoderForLongFormV3PlusClosures removeSelector: #emitPushNewArray:! EncoderForLongFormV3PlusClosures removeSelector: #emitPushRemoteTemp:inVectorAt:! EncoderForLongFormV3PlusClosures removeSelector: #emitStorePopRemoteTemp:inVectorAt:! EncoderForLongFormV3PlusClosures removeSelector: #emitStoreRemoteTemp:inVectorAt:! EncoderForLongFormV3PlusClosures removeSelector: #genPushClosureCopyCopiedValues:jumpSize:! BytecodeEncoder removeSelector: #blockExtentsToTempNames! BytecodeEncoder removeSelector: #cloneForClosure! BytecodeEncoder removeSelector: #noteBlockExtent:hasTempNames:! BytecodeEncoder removeSelector: #sizePushClosureCopyCopiedValues:! Encoder subclass: #BytecodeEncoder instanceVariableNames: 'stream position rootNode blockExtentsToLocals' classVariableNames: '' poolDictionaries: '' category: 'Compiler-Kernel'! !BytecodeEncoder reorganize! ('initialize-release' streamToMethod:) ('opcode sizing' nextPut: sizeBranchPopFalse: sizeBranchPopTrue: sizeDup sizeJumpLong: sizeJump: sizeOpcodeSelector:withArguments: sizePop sizePushClosureCopyNumCopiedValues:numArgs:jumpSize: sizePushConsArray: sizePushInstVarLong: sizePushInstVar: sizePushLiteralVar: sizePushLiteral: sizePushNewArray: sizePushReceiver sizePushRemoteTemp:inVectorAt: sizePushSpecialLiteral: sizePushTemp: sizePushThisContext sizeReturnReceiver sizeReturnSpecialLiteral: sizeReturnTop sizeReturnTopToCaller sizeSendSuper:numArgs: sizeSend:numArgs: sizeStoreInstVarLong: sizeStoreInstVar: sizeStoreLiteralVar: sizeStorePopInstVarLong: sizeStorePopInstVar: sizeStorePopLiteralVar: sizeStorePopRemoteTemp:inVectorAt: sizeStorePopTemp: sizeStoreRemoteTemp:inVectorAt: sizeStoreTemp:) ('bytecode generation' outOfRangeError:index:range:to:) ('accessing' methodNodeClass methodStreamPosition rootNode rootNode:) ('testing' supportsClosureOpcodes) ('special literal encodings' if:isSpecialLiteralForPush: if:isSpecialLiteralForReturn:) ('temps' bindAndJuggle: bindBlockArg:within: bindBlockTemp:within: bindTemp: blockExtentsToTempRefs noteBlockExtent:hasLocals:) ! Encoder removeSelector: #abstractSourceMapGiven:! DecompilerConstructorForClosures removeSelector: #codeTemp:! DecompilerConstructorForClosures removeSelector: #codeTemp:named:! DecompilerConstructorForClosures removeSelector: #incrementTempNameCount! DecompilerConstructor removeSelector: #codeTemp:nameIndex:! DecompilerConstructor removeSelector: #maxTempIndex! !DecompilerConstructor reorganize! ('initialize-release' method:class:literals:) ('constructor' codeAnyLitInd: codeAnyLiteral: codeAnySelector: codeArguments:block: codeArguments:temps:block: codeAssignTo:value: codeBlock:returns: codeBrace: codeCascade:messages: codeCascadedMessage:arguments: codeConstants codeEmptyBlock codeInst: codeMessage:selector:arguments: codeMethod:block:tempVars:primitive:class: codeSelector:code: codeSuper codeTemp: codeTemp:named: codeThisContext decodeIfNilWithReceiver:selector:arguments:) ('accessing') ('visiting' accept:) ('testing' isForClosures) ! CascadeNode removeSelector: #analyseTempsWithin:forValue:encoder:! BraceNode removeSelector: #analyseTempsWithin:forValue:encoder:! BlockNode removeSelector: #addTempReferenceTo:! BlockNode removeSelector: #analyseArguments:temporaries:! BlockNode removeSelector: #analyseTempsEncoder:! BlockNode removeSelector: #analyseTempsEncoder:arguments:temporaries:! BlockNode removeSelector: #analyseTempsWithin:encoder:! BlockNode removeSelector: #analyseTempsWithin:forValue:encoder:! BlockNode removeSelector: #emitCodeForEvaluatedClosureValue:encoder:numCopiedValues:! BlockNode removeSelector: #noteBlockEntry:! BlockNode removeSelector: #noteBlockExit:! BlockNode removeSelector: #noteWriteToArgumentsAndTemporariesAt:! BlockNode removeSelector: #noteWriteTo:within:! BlockNode removeSelector: #postNumberingProcessTemps! BlockNode removeSelector: #reindexingLocalsDo:! BlockNode removeSelector: #sizeCodeForEvaluatedClosureValue:copiedValues:! BlockNode removeSelector: #sizeCodeForEvaluatedClosureValue:numCopiedValues:! ParseNode subclass: #BlockNode instanceVariableNames: 'arguments statements returns nArgsNode size remoteCopyNode temporaries optimized blockExtent remoteTempNode copiedValues closureCreationNode startOfLastStatement' classVariableNames: '' poolDictionaries: '' category: 'Compiler-ParseNodes'! !BlockNode reorganize! ('initialize-release' arguments:statements:returns:from: noteSourceRangeStart:end:encoder: statements:returns:) ('accessing' addArgument: arguments arguments: block closureCreationNode firstArgument nArgsSlot nArgsSlot: numberOfArguments optimized returnLast returnNilIfNoOther returnSelfIfNoOther: startOfLastStatement startOfLastStatement: temporaries temporaries:) ('testing' canBeSpecialArgument generateAsClosure isBlockNode isComplex isJust: isJustCaseError isQuick returns) ('code generation' code emitExceptLast:on: emitForEvaluatedEffect:on: emitForEvaluatedValue:on: emitForValue:on: sizeExceptLast: sizeForEvaluatedEffect: sizeForEvaluatedValue: sizeForValue:) ('printing' printArgumentsOn:indent: printOn:indent: printStatementsOn:indent: printTemporaries:on:doPrior: printWithClosureAnalysisArgumentsOn:indent: printWithClosureAnalysisOn:indent: printWithClosureAnalysisStatementsOn:indent: printWithClosureAnalysisTemporariesOn:indent:) ('equation translation' statements statements:) ('tiles' asMorphicCollectSyntaxIn: asMorphicSyntaxIn:) ('code generation (new scheme)' emitCodeExceptLast:encoder: emitCodeForEvaluatedEffect:encoder: emitCodeForEvaluatedValue:encoder: emitCodeForValue:encoder: sizeCodeExceptLast: sizeCodeForEvaluatedEffect: sizeCodeForEvaluatedValue: sizeCodeForValue:) ('code generation (closures)' addHoistedTemps: addRemoteTemp:rootNode: analyseArguments:temporaries:rootNode: analyseTempsWithin:rootNode: blockExtent computeCopiedValues: constructClosureCreationNode: emitCodeForClosureValue:encoder: emitCodeForEvaluatedClosureValue:encoder: ifOptimizedBlockHoistTempsInto: noteOptimized postNumberingProcessTemps: reindexingLocalsDo:encoder: sizeCodeForClosureValue: sizeCodeForEvaluatedClosureValue:) ('*VMMaker-C translation' asTranslatorNode) ('visiting' accept:) ('debugger access' cleanUpForRegeneration) ! AssignmentNode removeSelector: #analyseTempsWithin:encoder:! MethodContext removeSelector: #blockHome! MethodContext removeSelector: #cachesStack! MethodContext removeSelector: #finalBlockHome! MethodContext removeSelector: #hideFromDebugger! MethodContext removeSelector: #isMethodContext! !MethodContext reorganize! ('initialize-release' privRefresh privRefreshWith:) ('accessing' activeHome activeOuterContext closure contextForLocalVariables hasInstVarRef home isExecutingBlock method methodReturnContext outerContext receiver removeSelf tempAt: tempAt:put:) ('private' aboutToReturn:through: instVarAt:put: setSender:receiver:method:arguments: setSender:receiver:method:closure:startpc: startpc) ('private-exceptions' cannotReturn: isHandlerContext isUnwindContext receiver: restartWithNewReceiver: swapReceiver:) ('controlling' answer:) ('private-debugger' cachedStackTop) ('printing' longPrint:on:limitedTo:indent: printDetails: printOn: printString who) ('closure support' contextTag) ('instruction decoding (closures)' blockReturnTop pushConsArrayWithElements:) ('system simulation' pushArgs:from:) ('debugger access') ! Decompiler removeSelector: #checkForBlock:! Decompiler removeSelector: #checkForBlock:selector:! Decompiler removeSelector: #checkForClosureCopy:selector:arguments:! Decompiler removeSelector: #pushClosureCopyCopiedValuesNumArgs:blockSize:! Decompiler removeSelector: #storePopRemoteTemp:inVectorAt:! InstructionStream subclass: #Decompiler instanceVariableNames: 'constructor method instVars tempVars constTable stack statements lastPc exit caseExits lastJumpPc lastReturnPc limit hasValue blockStackBase numLocalTemps' classVariableNames: 'ArgumentFlag CascadeFlag CaseFlag IfNilFlag' poolDictionaries: '' category: 'Compiler-Kernel'! !Decompiler reorganize! ('initialize-release' initSymbols: withTempNames:) ('control' blockForCaseTo: blockTo: checkForBlockCopy: checkForBlock:selector:arguments: checkForClosureCopy:arguments: doClosureCopyCopiedValues:numArgs:blockSize: statementsForCaseTo: statementsTo:) ('instruction decoding' blockReturnTop case: doDup doPop doStore: jump: jump:if: methodReturnConstant: methodReturnReceiver methodReturnTop popIntoLiteralVariable: popIntoReceiverVariable: popIntoRemoteTemp:inVectorAt: popIntoTemporaryVariable: pushActiveContext pushClosureCopyNumCopiedValues:numArgs:blockSize: pushConsArrayWithElements: pushConstant: pushLiteralVariable: pushNewArrayOfSize: pushReceiver pushReceiverVariable: pushRemoteTemp:inVectorAt: pushTemporaryVariable: send:super:numArgs: storeIntoLiteralVariable: storeIntoReceiverVariable: storeIntoRemoteTemp:inVectorAt: storeIntoTemporaryVariable:) ('public access' decompile:in: decompile:in:method: decompileBlock: decompile:in:method:using: tempAt:) ('private' blockScopeRefersOnlyOnceToTemp: constructorForMethod: convertToDoLoop interpretNextInstructionFor: methodRefersOnlyOnceToTemp: popTo: quickMethod sawBlueBookBlock sawClosureBytecode scanBlockScopeFor:from:to:with:scanner:) ! BlockContext removeSelector: #blockHome! BlockContext removeSelector: #finalBlockHome! BlockContext removeSelector: #hideFromDebugger! BlockContext removeSelector: #ifProperUnwindSupportedElseSignalAboutToReturn! BlockContext removeSelector: #isMethodContext! ContextPart removeSelector: #abstractPC! ContextPart removeSelector: #blockHome! ContextPart removeSelector: #cachesStack! ContextPart removeSelector: #closureCopy:! ContextPart removeSelector: #closureHome! ContextPart removeSelector: #pushClosureCopyCopiedValuesNumArgs:blockSize:! !ContextPart reorganize! ('accessing' at: at:put: basicAt: basicAt:put: basicSize client contextForLocalVariables home method methodNode methodNodeFormattedAndDecorated: methodReturnContext receiver size tempAt: tempAt:put:) ('instruction decoding' doDup doPop jump: jump:if: methodReturnConstant: methodReturnReceiver methodReturnTop popIntoLiteralVariable: popIntoReceiverVariable: popIntoRemoteTemp:inVectorAt: popIntoTemporaryVariable: pushActiveContext pushClosureCopyNumCopiedValues:numArgs:blockSize: pushConstant: pushLiteralVariable: pushNewArrayOfSize: pushReceiver pushReceiverVariable: pushRemoteTemp:inVectorAt: pushTemporaryVariable: return:from: send:super:numArgs: storeIntoLiteralVariable: storeIntoReceiverVariable: storeIntoRemoteTemp:inVectorAt: storeIntoTemporaryVariable:) ('debugger access' contextStack depthBelow: errorReportOn: longStack mclass methodSelector namedTempAt: namedTempAt:put: pc print:limitedTo: print:on: release releaseTo: selector sender shortStack singleRelease sourceCode stack stackOfSize: swapSender: tempNames tempsAndValues tempsAndValuesLimitedTo:indent:) ('controlling' activateMethod:withArgs:receiver:class: blockCopy: closureCopy:copiedValues: hasSender: jump pop push: quickSend:to:with:super: restart resume resume: return return: return:to: runUntilErrorOrReturnFrom: send:to:with:super: terminate terminateTo: top) ('printing' printDetails: showStack: showStack:label:) ('system simulation' completeCallee: quickStep runSimulated:contextAtEachStep: step stepToCallee stepToSendOrReturn) ('private' activateReturn:value: cannotReturn:to: copyTo:blocks: cut: doPrimitive:method:receiver:args: insertSender: privSender: push:fromIndexable: stackPtr stackp: tryNamedPrimitiveIn:for:withArgs: tryPrimitiveFor:receiver:args:) ('private-exceptions' canHandleSignal: findNextHandlerContextStarting findNextUnwindContextUpTo: handleSignal: isHandlerContext isUnwindContext nextHandlerContext unwindTo:) ('objects from disk' storeDataOn:) ('query' bottomContext copyStack copyTo: findContextSuchThat: hasContext: isDead secondFromBottom) ('*Tweak-Hacks-override' printOn:) ('*QwaqExtensions' stackOfSize:ignoringFirst: stackOfSize:ignoringFirst:indentedBy:) ! !InstructionStream reorganize! ('testing' willBlockReturn willJump willJumpIfFalse willJumpIfTrue willJustPop willReallySend willReturn willSend willStore willStorePop) ('decoding' atEnd interpret interpretJump interpretJumpIfCond interpretNextInstructionFor:) ('scanning' addSelectorTo: firstByte followingByte fourthByte method nextByte nextInstruction pc peekInstruction previousPc scanFor: secondByte selectorToSendOrSelf skipBackBeforeJump thirdByte) ('private' interpretExtension:in:for: method:pc: pc:) ('debugger access' abstractPC debuggerMap) ! InstructionPrinter removeSelector: #pushClosureCopyCopiedValuesNumArgs:blockSize:! InstructionPrinter removeSelector: #storePopRemoteTemp:inVectorAt:! InstructionPrinter removeSelector: #storeRemoteTemp:inVectorAt:! BlockLocalTempCounter removeSelector: #doReturn! CompiledMethod initialize! !CompiledMethod class reorganize! ('class initialization' fullFrameSize initialize smallFrameSize) ('instance creation' basicNew: new new: newBytes:trailerBytes:nArgs:nTemps:nStack:nLits:primitive: newBytes:trailerBytes:nArgs:nTemps:nStack:nLits:primitive:flag: newMethod:header: primitive:numArgs:numTemps:stackSize:literals:bytecodes:trailer: toReturnConstant:trailerBytes: toReturnField:trailerBytes: toReturnSelf toReturnSelfTrailerBytes:) ('*Islands' howToPassAsArgument) ! ByteArray variableByteSubclass: #CompiledMethod instanceVariableNames: '' classVariableNames: 'LargeFrame SmallFrame' poolDictionaries: '' category: 'Kernel-Methods'! !CompiledMethod reorganize! ('initialize-release' copyWithTrailerBytes: needsFrameSize:) ('accessing' defaultSelector endPC flag flushCache frameSize initialPC methodClass methodClass: methodReference numArgs numLiterals numTemps primitive properties properties: returnField selector selector: trailer) ('comparing' =) ('testing' hasNewPropertyFormat hasReportableSlip isBlueBookCompiled isClosureCompiled isCompiledMethod isQuick isReturnField isReturnSelf isReturnSpecial usesClosureBytecodes) ('printing' abstractSymbolic dateMethodLastSubmitted decompileString longPrintOn: longPrintOn:indent: longPrintRelativeOn:indent: printOnStream: printOn: printPrimitiveOn: storeLiteralsOn:forClass: storeOn: symbolic symbolicLinesDo: timeStamp who) ('literals' allLiterals hasLiteralSuchThat: hasLiteralThorough: hasLiteral: header headerDescription indexOfLiteral: literalAt: literalAt:put: literalStrings literals literalsDo: objectAt: objectAt:put:) ('scanning' messages readsField: readsRef: scanFor: scanLongLoad: scanLongStore: scanVeryLongLoad:offset: scanVeryLongStore:offset: sendsToSuper writesField: writesRef:) ('source code management' checkOKToAdd:at: copyWithTempNames: fileIndex filePosition getPreambleFrom:at: getSourceFor:in: getSourceFromFile holdsTempNames putSource:fromParseNode:class:category:inFile:priorMethod: putSource:fromParseNode:class:category:withStamp:inFile:priorMethod: putSource:fromParseNode:inFile:withPreamble: qCompress:firstTry: qDecompress: setSourcePointer: setSourcePosition:inFile: sourceClass sourceFileStream sourcePointer sourceSelector tempNames) ('file in/out' readDataFrom:size: storeDataOn: veryDeepCopyWith: zapSourcePointer) ('evaluating' valueWithReceiver:arguments:) ('decompiling' decompile decompileClass:selector: decompilerClass methodNode methodNodeFormattedAndDecorated: methodNodeFormattedDecompileClass:selector:decorate: parserClass primitiveNode) ('debugger support' abstractPCForConcretePC: blockExtentsInto:from:to:scanner:numberer: debuggerMap hasBreakpoint pcPreviousTo: startpcsToBlockExtents) ('*Tools-Inspector' explorerContents inspectorClass) ('*Tweak-Hacks' propertyValueAt: propertyValueAt:ifAbsent: propertyValueAt:put: readsTweakField: refersToField: writesTweakField:) ! !ClosureCompilerTest class reorganize! ('code examples' methodWithCopiedAndAssignedTemps methodWithCopiedAndPostClosedOverAssignedTemps methodWithCopiedTemps methodWithOptimizedBlocks methodWithOptimizedBlocksA methodWithVariousTemps) ! ClosureCompilerTest removeSelector: #testSourceRangeAccessForClosureLongFormNoBytecodeInjectInto! ClosureCompilerTest removeSelector: #testSourceRangeAccessForClosureNoBytecodeInjectInto! Object removeSelector: #isBlockClosure! "Postscript: Remove unused class vars from CompiledMethod since we can't redefine its class definition directly. Add the new BlockClosure to the specialObjectsArray" (#(#BlockNodeCache #MethodProperties #SpecialConstants) intersection: CompiledMethod classPool keys) do: [:classVarName| CompiledMethod removeClassVarName: classVarName]. Smalltalk recreateSpecialObjectsArray!