<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr">Hi Nicolas,<br><div><br></div><div>   on reviewing this code first I really like transformInAssignmentTo: ; it's a much nicer design than my hack.  Now, a specific issue...</div><div><br></div><div>I see </div><div><div>TReturnNode>>transformInAssignmentTo: aTVariableNode</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>"a return shall not be assigned:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">          </span>x := condition ifTrue: [^nil] ifFalse: [2]</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">     </span>shall be transformed into:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">             </span>condition ifTrue: [^nil] ifFalse: [x := 2]"</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>^self</div></div><div><br></div><div>The issue here is that if x is a global variable then a necessary side-effect will be lost.  Hence I think we need to refactor transformInAssignmentTo: to transformInAssignmentTo:codeGen: so that we can query whether the variable is in fact global in the current scope.</div></div></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, Jun 20, 2020 at 11:11 AM <<a href="mailto:commits@source.squeak.org">commits@source.squeak.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"> <br>
Nicolas Cellier uploaded a new version of VMMaker to project VM Maker:<br>
<a href="http://source.squeak.org/VMMaker/VMMaker.oscog-nice.2761.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/VMMaker/VMMaker.oscog-nice.2761.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: VMMaker.oscog-nice.2761<br>
Author: nice<br>
Time: 20 June 2020, 8:11:09.897426 pm<br>
UUID: e8105e2b-a95e-4698-9a5f-939360e251b8<br>
Ancestors: VMMaker.oscog-eem.2760<br>
<br>
1) Revise a bit the tranformation of assignment/returns for C code generation (see below)<br>
<br>
2) Do not try to generate SHA256Plugin, it's obsolete and absent from latest cryptography packages.<br>
<br>
In Smalltalk, every statement is an expression that can be used in other expression, assigned to variables, etc...<br>
<br>
In C, not all statements are valid expressions, or IOW, not all statements have a value.<br>
In C parlance, rvalues are the ones which can be used at the right of an assignment, lvalues the ones which can be used on the left of an assignment.<br>
<br>
While working on FFI, I had invalid code generated looking like:<br>
<br>
        err = switch(atomicType) ...<br>
<br>
This is because there is brittle code for transforming some expressions, which are not generic enough.<br>
<br>
For example, we have similar TSwitchStmtListNode and TCaseStmtNode for handling #dispatchOn:in:, but not exactly same handling of both.<br>
<br>
Before this change, an overview can be given by reviewing senders of:<br>
#isSwitch #isCaseStmt<br>
<br>
This commit is an attempt to enhance/generalize handling of such non-rvalues by distributing the handling of those constructs in TParseNode hierarchy.<br>
<br>
A slight change, is that assigning/returning result of a caseOf: without otherwise clause did create a default branch with an error message and a default value for the returned expression/assigned variable.<br>
See #emitCCodeOn:addToEndOfCases:level:generator:<br>
or #emitCCodeOn:prependToEndOfCases:level:generator:<br>
<br>
The new #transformInAssignmentTo: only create the error message, but does not provide the default value.<br>
This is because type analysis might have been not yet taken place at time of transformation.<br>
Is this really needed? I don't think so.<br>
<br>
Please review!<br>
We might want to further extend the mechanism.<br>
<br>
=============== Diff against VMMaker.oscog-eem.2760 ===============<br>
<br>
Item was changed:<br>
  ----- Method: TAssignmentNode>>emitStatementListExpansion:on:level:generator: (in category 'C code generation') -----<br>
  emitStatementListExpansion: stmtList on: aStream level: level generator: aCodeGen<br>
        stmtList statements last = variable ifTrue:<br>
                [^expression emitCCodeOn: aStream level: level generator: aCodeGen].<br>
+       (stmtList copy transformInAssignmentTo: variable)<br>
-       stmtList copy<br>
-               assignLastExpressionTo: variable;<br>
                emitCCodeOn: aStream level: level generator: aCodeGen!<br>
<br>
Item was changed:<br>
  ----- Method: TAssignmentNode>>emitStatementListExpansionAsExpression:on:level:generator: (in category 'C code generation') -----<br>
  emitStatementListExpansionAsExpression: stmtList on: aStream level: level generator: aCodeGen<br>
        stmtList statements last = variable ifTrue:<br>
                [^expression emitCCodeAsExpressionOn: aStream level: level generator: aCodeGen].<br>
+       (stmtList copy transformInAssignmentTo: variable)<br>
-       stmtList copy<br>
-               assignLastExpressionTo: variable;<br>
                emitCCodeAsExpressionOn: aStream level: level generator: aCodeGen!<br>
<br>
Item was changed:<br>
  ----- Method: TAssignmentNode>>emitValueExpansionOn:level:generator: (in category 'C code generation') -----<br>
  emitValueExpansionOn: aStream level: level generator: aCodeGen<br>
        | stmtList lastStmt copiedStatements |<br>
        self assert: (expression isSend and: [expression isValueExpansion]).<br>
        stmtList := expression receiver.<br>
        lastStmt := stmtList statements last.<br>
        (lastStmt = variable or: [lastStmt isReturn]) ifTrue:<br>
                [^expression emitCCodeOn: aStream level: level generator: aCodeGen].<br>
+       copiedStatements := stmtList copy transformInAssignmentTo: variable.<br>
-       copiedStatements := stmtList copy.<br>
-       copiedStatements statements<br>
-               at: stmtList statements size<br>
-               put: (TAssignmentNode new<br>
-                               setVariable: variable<br>
-                               expression: lastStmt).<br>
        expression copy<br>
                receiver: copiedStatements;<br>
                emitCCodeOn: aStream level: level generator: aCodeGen!<br>
<br>
Item was added:<br>
+ ----- Method: TAssignmentNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "Avoid transforming:<br>
+               x := expression<br>
+       into:<br>
+               x := x := expression"<br>
+               <br>
+       aTVariableNode = variable ifTrue: [^self].<br>
+       ^super transformInAssignmentTo: aTVariableNode!<br>
<br>
Item was changed:<br>
  TParseNode subclass: #TCaseStmtNode<br>
+       instanceVariableNames: 'expression firsts lasts cases otherwiseOrNil'<br>
-       instanceVariableNames: 'expression firsts lasts cases'<br>
        classVariableNames: ''<br>
        poolDictionaries: ''<br>
        category: 'VMMaker-Translation to C'!<br>
<br>
  !TCaseStmtNode commentStamp: '<historical>' prior: 0!<br>
  I implement the main dispatch case statements for bytecode and primitive dispatch.  See TMethod classPool associationAt: #CaseStatements!<br>
<br>
Item was changed:<br>
  ----- Method: TCaseStmtNode>>emitCCodeOn:level:generator: (in category 'C code generation') -----<br>
  emitCCodeOn: aStream level: level generator: aCodeGen<br>
        | printMod expansions duplicates |<br>
        printMod := false.<br>
        (expression isVariable<br>
         and: [expression name = 'currentBytecode']) ifTrue:<br>
                [printMod := true.<br>
                 aStream nextPutAll: 'bytecodeDispatchDebugHook();'; cr; crtab: level.<br>
                 aCodeGen outputAsmLabel: 'bytecodeDispatch' on: aStream.<br>
                 aStream crtab: level].<br>
        aStream nextPutAll: 'switch ('.<br>
        expression emitCCodeOn: aStream level: level generator: aCodeGen.<br>
        aStream nextPutAll: ') {'; cr.<br>
        expansions := aCodeGen suppressAsmLabelsWhile:<br>
                                                [cases collect:<br>
                                                        [:case|<br>
                                                        self filterCommentsFrom:<br>
                                                                (String streamContents:<br>
                                                                        [:s|<br>
                                                                        case emitCCodeOn: s level: 0 generator: aCodeGen])]].<br>
        duplicates := Set new.<br>
        1 to: cases size do:<br>
                [:i|<br>
                (duplicates includes: i) ifFalse:<br>
                        [(duplicates addAll: ((i to: cases size) select: [:j| (expansions at: i) = (expansions at: j)])) do:<br>
                                [:k|<br>
                                (firsts at: k) to: (lasts at: k) do:<br>
                                        [:caseIndex|<br>
                                        aStream tab: level; nextPutAll: 'case '; print: caseIndex; nextPut: $:.<br>
                                        (caseIndex > 255 and: [printMod]) ifTrue:<br>
                                                [aStream nextPutAll: ' /*'; print: (caseIndex bitAnd: 255); nextPutAll: '*/'].<br>
                                        aStream cr]].<br>
                        (cases at: i) emitCCodeOn: aStream level: level + 1 generator: aCodeGen.<br>
                        aStream tab: level + 1; nextPutAll: 'break;'; cr]].<br>
+       otherwiseOrNil<br>
+               ifNotNil:<br>
+                       [aStream<br>
+                               crtab: level;<br>
+                               nextPutAll: 'default:';<br>
+                               cr.<br>
+                       otherwiseOrNil emitCCodeOn: aStream level: level + 1 generator: aCodeGen].<br>
        aStream tab: level; nextPut: $}!<br>
<br>
Item was added:<br>
+ ----- Method: TCaseStmtNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen<br>
+       "A switch is not an rvalue"<br>
+       <br>
+       ^false!<br>
<br>
Item was added:<br>
+ ----- Method: TCaseStmtNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "Destructively transform the receiver so that each case is transformed into an assignment."<br>
+       cases := cases collect: [:node | node copy transformInAssignmentTo: aTVariableNode].<br>
+       otherwiseOrNil := otherwiseOrNil isNil<br>
+               ifTrue: [TStmtListNode new setArguments: #() statements:<br>
+                                       {TSendNode new setSelector: #error<br>
+                                               receiver: (TConstantNode new setValue: 'Case not found')<br>
+                                               arguments: #()}]<br>
+               ifFalse: [otherwiseOrNil copy transformInAssignmentTo: aTVariableNode].<br>
+       ^self!<br>
<br>
Item was added:<br>
+ ----- Method: TGoToNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen<br>
+       "A goto is not an rvalue"<br>
+       <br>
+       ^false!<br>
<br>
Item was removed:<br>
- ----- Method: TMethod>>isConditionalToBeTransformedForAssignment:in: (in category 'inlining') -----<br>
- isConditionalToBeTransformedForAssignment: aSend in: aCodeGen<br>
-       "Answer if a send is of the form<br>
-               e1<br>
-                       ifTrue: [e2 ifTrue: [self m1] ifFalse: [self m2]]<br>
-                       ifFalse: [self m3]<br>
-        such that at least one of the sends mN may be inlined.."<br>
- <br>
-       ^(#(ifTrue:ifFalse: ifFalse:ifTrue:) includes: aSend selector)<br>
-          and: [aSend args anySatisfy:<br>
-                       [:arg| | stmt |<br>
-                       self assert: arg isStmtList.<br>
-                       arg statements size > 1<br>
-                       or: [(stmt := arg statements first) isSwitch<br>
-                       or: [stmt isSend<br>
-                               and: [(aCodeGen mayInline: stmt selector)<br>
-                                       or: [self isConditionalToBeTransformedForAssignment: stmt in: aCodeGen]]]]]]!<br>
<br>
Item was changed:<br>
  ----- Method: TMethod>>transformConditionalAssignment:in: (in category 'inlining') -----<br>
  transformConditionalAssignment: node in: aCodeGen<br>
        "If possible answer the transformation of code of the form<br>
                var := e1<br>
                                ifTrue: [e2 ifTrue: [self m1] ifFalse: [self m2]]<br>
                                ifFalse: [self m3]<br>
         into<br>
                e1<br>
                        ifTrue: [e2 ifTrue: [var := self m1] ifFalse: [var := self m2]]<br>
                        ifFalse: [var := self m3]<br>
+        to allow inlining of m1, m2, et al.  Otherwise answer nil.<br>
+       Also apply to various C constructs like switch"<br>
-        to allow inlining of m1, m2, et al.  Otherwise answer nil."<br>
<br>
-       | expr |<br>
        ^(node isAssignment<br>
+          and: [node expression mustTransformWhenAssignedIn: aCodeGen]) ifTrue:<br>
+               [node expression copy transformInAssignmentTo: node variable]!<br>
-          and: [(expr := node expression) isSend<br>
-          and: [(#(ifTrue:ifFalse: ifFalse:ifTrue:) includes: expr selector)<br>
-          and: [self isConditionalToBeTransformedForAssignment: expr  in: aCodeGen]]]) ifTrue:<br>
-               [expr copy<br>
-                       arguments:<br>
-                               (expr args collect:<br>
-                                       [:stmtList| stmtList copy assignLastExpressionTo: node variable]);<br>
-                       yourself]!<br>
<br>
Item was changed:<br>
  ----- Method: TMethod>>transformReturnSubExpression:toAssignmentOf:andGoto:unless:into: (in category 'inlining') -----<br>
  transformReturnSubExpression: node toAssignmentOf: exitVar andGoto: exitLabel unless: eliminateReturnSelfs into: aBinaryBlock<br>
        | expr replacement |<br>
        expr := node isReturn ifTrue: [node expression] ifFalse: [node].<br>
        replacement := (expr isVariable "Eliminate ^self's"<br>
                                           and: [expr name = 'self'<br>
                                           and: [eliminateReturnSelfs]])<br>
                                                ifTrue: [nil]<br>
                                                ifFalse:<br>
                                                        [exitVar<br>
                                                                ifNil: [expr]<br>
+                                                               ifNotNil: [expr transformInAssignmentTo: (TVariableNode new setName: exitVar)]].<br>
-                                                               ifNotNil: [TAssignmentNode new<br>
-                                                                                       setVariable: (TVariableNode new setName: exitVar)<br>
-                                                                                       expression: expr]].<br>
         node == parseTree statements last<br>
                ifTrue:<br>
                        [aBinaryBlock value: replacement value: false]<br>
                ifFalse:<br>
                        [replacement := replacement<br>
                                                                ifNil: [TGoToNode new setLabel: exitLabel; yourself]<br>
                                                                ifNotNil:<br>
                                                                        [TStmtListNode new<br>
                                                                                setArguments: #()<br>
                                                                                statements: {replacement.<br>
                                                                                                          TGoToNode new setLabel: exitLabel; yourself};<br>
                                                                                yourself].<br>
                         aBinaryBlock value: replacement value: true]!<br>
<br>
Item was changed:<br>
  ----- Method: TMethod>>transformReturns (in category 'type inference') -----<br>
  transformReturns<br>
+       "Once the return type has been found or inferred, returns may need to be modified.<br>
-       "Once the return type has been found or inferred, returns may bneed to be modified.<br>
         If the return type is #void, any occurrences of ^expr must be replaced with expr. ^self.<br>
         If the type is #sqInt any any occurrences of ^self are replaced with ^0."<br>
        (returnType == #void or: [returnType == #sqInt]) ifFalse:<br>
                [^self].<br>
        parseTree nodesWithParentsDo:<br>
                [:node :parent|<br>
                node isReturn ifTrue:<br>
                        [(node expression isVariable and: [node expression name = 'self'])<br>
                                ifTrue:<br>
                                        [returnType = #sqInt ifTrue:<br>
                                                [node setExpression: (TConstantNode new setValue: 0)]]<br>
                                ifFalse:<br>
                                        [returnType = #void ifTrue:<br>
                                                [parent<br>
                                                        replaceChild: node<br>
                                                        with: (TStmtListNode new<br>
                                                                        setArguments: #()<br>
                                                                        statements: {node expression.<br>
                                                                                                  TReturnNode new <br>
                                                                                                        setExpression: (TVariableNode new setName: 'self')<br>
                                                                                                        yourself})]]]]!<br>
<br>
Item was added:<br>
+ ----- Method: TParseNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen<br>
+       "Answer false if this node is potentially not a rvalue.<br>
+       a rvalue is an expression that can occur to the right of an assignement.<br>
+       In C code, not all expression can be a rvalue.<br>
+       For example if() {} else {} are not rvalues, unless they can be transformed into a ()?: construct.<br>
+       Likewise, while and for loops, switch statements are not possible rvalues.<br>
+       This method has to take inlining into account, because a simple message send would be transformed into a function call which is an rvalue.<br>
+       But if the method is inlined rather than called, this may not be the case.<br>
+       Default behavior is to answer true, only notorious non-rvalues have to refine this"<br>
+       <br>
+       ^true<br>
+       !<br>
<br>
Item was added:<br>
+ ----- Method: TParseNode>>mustTransformWhenAssignedIn: (in category 'testing') -----<br>
+ mustTransformWhenAssignedIn: aCodeGen<br>
+       "Answer whether this node must be transformed when assigned to a variable<br>
+       var := expr.<br>
+       Not all statements can be used at the right of assignment in C (rvalues)."<br>
+       <br>
+       ^(self isAnRValueIn: aCodeGen) not<br>
+       !<br>
<br>
Item was added:<br>
+ ----- Method: TParseNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "Default behavior is to transform an expression into an assignement<br>
+       var := expression.<br>
+       This message has to be redefined in subclasses which are not rvalues."<br>
+       ^TAssignmentNode new<br>
+                               setVariable: aTVariableNode<br>
+                               expression: self!<br>
<br>
Item was added:<br>
+ ----- Method: TReturnNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen<br>
+       "This is not an rvalue, we cannot write:<br>
+               x = return y"<br>
+       <br>
+       ^false!<br>
<br>
Item was added:<br>
+ ----- Method: TReturnNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "a return shall not be assigned:<br>
+               x := condition ifTrue: [^nil] ifFalse: [2]<br>
+       shall be transformed into:<br>
+               condition ifTrue: [^nil] ifFalse: [x := 2]"<br>
+               <br>
+       ^self!<br>
<br>
Item was added:<br>
+ ----- Method: TSendNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen <br>
+       self isConditionalSend<br>
+               ifTrue: ["If all expressions of a conditional are rvalue, then the<br>
+                       conditional can be transformed into a ()?: construct and is<br>
+                       thus an rvalue"<br>
+                       ^ (receiver isAnRValueIn: aCodeGen)<br>
+                               and: [self args<br>
+                                               allSatisfy: [:arg | arg isAnRValueIn: aCodeGen]]].<br>
+       "inlined message sends are potentially not rvalues"<br>
+       ^ (aCodeGen mayInline: self selector) not!<br>
<br>
Item was added:<br>
+ ----- Method: TSendNode>>mustTransformWhenAssignedIn: (in category 'transformations') -----<br>
+ mustTransformWhenAssignedIn: aCodeGen<br>
+       "Answer whether this node must be transformed.<br>
+       Avoid infinite transformation loops caused by unchanged nodes."<br>
+       <br>
+       (aCodeGen mayInline: self selector) ifTrue: [^false].<br>
+       ^super mustTransformWhenAssignedIn: aCodeGen!<br>
<br>
Item was added:<br>
+ ----- Method: TSendNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "transform a conditional:<br>
+               condition ifTrue: [stmt1. stmt2] ifFalse: [stmt3. stmt4].<br>
+       into:<br>
+               condition ifTrue: [stmt1. var := stmt2] ifFalse: [stmt3. var := stmt4].<br>
+       If the last expression is itself not an rvalue, it will be transformed recursively"<br>
+       <br>
+       self isConditionalSend ifTrue: [^self copy<br>
+                       arguments:<br>
+                               (self args collect:<br>
+                                       [:stmtList| stmtList copy transformInAssignmentTo: aTVariableNode]);<br>
+                       yourself].<br>
+       "don't attempt to assign aTVariableNode with the error condition (like default switch missing)"<br>
+       selector = #error ifTrue: [^self].<br>
+       ^super transformInAssignmentTo: aTVariableNode!<br>
<br>
Item was removed:<br>
- ----- Method: TStmtListNode>>assignLastExpressionTo: (in category 'transformations') -----<br>
- assignLastExpressionTo: variableNode<br>
-       "Destructively transform the receiver so that its last expression is assigned to the argument."<br>
-       | index |<br>
-       index := statements findLast: [:expr| (expr isGoTo or: [expr isLabel]) not].<br>
-       statements<br>
-               at: index<br>
-               put: (TAssignmentNode new<br>
-                               setVariable: variableNode<br>
-                               expression: (statements at: index))!<br>
<br>
Item was added:<br>
+ ----- Method: TStmtListNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen<br>
+       "A list of statements is not an rvalue...<br>
+       Well, in simple cases we could use comma operator (,), but don't bother here."<br>
+       <br>
+       ^statements size = 1 and: [statements first isAnRValueIn: aCodeGen]!<br>
<br>
Item was added:<br>
+ ----- Method: TStmtListNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "Destructively transform the receiver so that its last expression is assigned to the argument."<br>
+       | index |<br>
+       index := statements findLast: [:expr| (expr isGoTo or: [expr isLabel]) not].<br>
+       statements<br>
+               at: index<br>
+               put: ((statements at: index) copy transformInAssignmentTo: aTVariableNode).<br>
+       ^self!<br>
<br>
Item was added:<br>
+ ----- Method: TSwitchStmtNode>>isAnRValueIn: (in category 'testing') -----<br>
+ isAnRValueIn: aCodeGen<br>
+       "A switch is not an rvalue"<br>
+       <br>
+       ^false!<br>
<br>
Item was added:<br>
+ ----- Method: TSwitchStmtNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "Destructively transform the receiver so that each case is transformed into an assignment."<br>
+       cases := cases collect: [:pair | {pair first. pair last copy transformInAssignmentTo: aTVariableNode}].<br>
+       otherwiseOrNil := otherwiseOrNil isNil<br>
+               ifTrue: [TStmtListNode new setArguments: #() statements:<br>
+                                       {TSendNode new setSelector: #error<br>
+                                               receiver: (TConstantNode new setValue: 'Case not found and no otherwise clause')<br>
+                                               arguments: #()}]<br>
+               ifFalse: [otherwiseOrNil copy transformInAssignmentTo: aTVariableNode].<br>
+       ^self!<br>
<br>
Item was added:<br>
+ ----- Method: TVariableNode>>transformInAssignmentTo: (in category 'transformations') -----<br>
+ transformInAssignmentTo: aTVariableNode<br>
+       "Avoid transforming:<br>
+               x<br>
+       into:<br>
+               x := x"<br>
+               <br>
+       aTVariableNode = self ifTrue: [^self].<br>
+       ^super transformInAssignmentTo: aTVariableNode!<br>
<br>
Item was changed:<br>
  ----- Method: VMMaker class>>generateVMPlugins (in category 'configurations') -----<br>
  generateVMPlugins<br>
        ^VMMaker<br>
                generatePluginsTo: self sourceTree, '/src'<br>
                options: #()<br>
                platformDir: self sourceTree, '/platforms'<br>
                including:#(ADPCMCodecPlugin AsynchFilePlugin<br>
                                        BalloonEnginePlugin B3DAcceleratorPlugin B3DEnginePlugin BMPReadWriterPlugin BitBltSimulation<br>
                                        BochsIA32Plugin BochsX64Plugin GdbARMv6Plugin GdbARMv8Plugin<br>
                                        CameraPlugin CroquetPlugin DeflatePlugin DropPlugin<br>
+                                       "Cryptography Plugins:" DESPlugin DSAPlugin MD5Plugin<br>
-                                       "Cryptography Plugins:" DESPlugin DSAPlugin MD5Plugin SHA256Plugin<br>
                                        "FT2Plugin" FFTPlugin FileCopyPlugin FilePlugin FileAttributesPlugin Float64ArrayPlugin FloatArrayPlugin FloatMathPlugin<br>
                                        GeniePlugin HostWindowPlugin IA32ABIPlugin ImmX11Plugin InternetConfigPlugin<br>
                                        JPEGReadWriter2Plugin JPEGReaderPlugin JoystickTabletPlugin KlattSynthesizerPlugin<br>
                                        LargeIntegersPlugin LocalePlugin MIDIPlugin MacMenubarPlugin Matrix2x3Plugin<br>
                                        MiscPrimitivePlugin Mpeg3Plugin QuicktimePlugin RePlugin<br>
                                        ScratchPlugin SecurityPlugin SerialPlugin SocketPlugin<br>
                                        SoundCodecPlugin SoundGenerationPlugin SoundPlugin SqueakSSLPlugin StarSqueakPlugin<br>
                                        ThreadedFFIPlugin ThreadedARM32FFIPlugin ThreadedARM64FFIPlugin ThreadedIA32FFIPlugin<br>
                                        ThreadedX64SysVFFIPlugin ThreadedX64Win64FFIPlugin<br>
                                        UnicodePlugin UnixAioPlugin UUIDPlugin UnixOSProcessPlugin<br>
                                        Win32OSProcessPlugin VMProfileLinuxSupportPlugin VMProfileMacSupportPlugin WeDoPlugin<br>
                                        XDisplayControlPlugin)!<br>
<br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>