[Vm-dev] VM Maker: VMMakerJS-bf.2.mcz

commits at source.squeak.org commits at source.squeak.org
Fri Oct 10 13:45:15 UTC 2014


Bert Freudenberg uploaded a new version of VMMakerJS to project VM Maker:
http://source.squeak.org/VMMaker/VMMakerJS-bf.2.mcz

==================== Summary ====================

Name: VMMakerJS-bf.2
Author: bf
Time: 4 October 2014, 3:03:21.317 pm
UUID: 354e3141-358c-44ff-b3cf-a47b08b40e20
Ancestors: VMMakerJS-bf.1

Fix one-based arrays, div, mod, shift, make JSSmartSyntaxPluginCodeGenerator work.

Can generate MiscPrimitivePlugin now

=============== Diff against VMMakerJS-bf.1 ===============

Item was changed:
  Object subclass: #JSCodeGenerator
+ 	instanceVariableNames: 'translationDict inlineList constants variables variableDeclarations scopeStack methods macros preparedMethodList variablesSetCache headerFiles globalVariableUsage useSymbolicConstants generateDeadCode doNotRemoveMethodList asArgumentTranslationDict receiverDict vmClass currentMethod logger declareMethodsStatic permitMethodPruning pools abstractDeclarations uncheckedAbstractMethods cCodeTranslationDict oneBasedArrays'
- 	instanceVariableNames: 'translationDict inlineList constants variables variableDeclarations scopeStack methods macros preparedMethodList variablesSetCache headerFiles globalVariableUsage useSymbolicConstants generateDeadCode doNotRemoveMethodList asArgumentTranslationDict receiverDict vmClass currentMethod logger declareMethodsStatic permitMethodPruning pools abstractDeclarations uncheckedAbstractMethods cCodeTranslationDict'
  	classVariableNames: 'IsActive UseRightShiftForDivide'
  	poolDictionaries: ''
  	category: 'VMMakerJS-Translation to JS'!
  
+ !JSCodeGenerator commentStamp: 'bf 10/4/2014 13:42' prior: 0!
- !JSCodeGenerator commentStamp: 'bf 10/3/2014 04:17' prior: 0!
  This class is a copy of CCodeGenerator hacked to generate JavaScript instead of C for use with the SqueakJS virtual machine.
  
+ C and JS semantics are pretty close except for pointers, types, and divide/modulo/shift operations. !
- C and JS semantics are pretty close except for pointers, types, and shift operations. !

Item was changed:
  ----- Method: JSCodeGenerator>>addMethodsForPrimitives: (in category 'public') -----
  addMethodsForPrimitives: classAndSelectorList 
  	| sel aClass source verbose meth |
  	classAndSelectorList do:[:classAndSelector | 
  		aClass := Smalltalk at: (classAndSelector at: 1) ifAbsent:[nil].
  		aClass ifNotNil:[
  			self addAllClassVarsFor: aClass.
  			"TPR - should pool vars also be added here?"
  
  			"find the method in either the class or the metaclass"
  			sel := classAndSelector at: 2.
  			(aClass includesSelector: sel)
  				ifTrue: [source := aClass sourceCodeAt: sel ifAbsent:[nil]]
  				ifFalse: [source := aClass class sourceCodeAt: sel ifAbsent:[nil]].
  		].
  		source ifNil:[
  			Transcript cr; show: 'WARNING: Compiled primitive ', classAndSelector first, '>>', classAndSelector last, ' not present'.
  		] ifNotNil:[
  			"compile the method source and convert to a suitable translation 
  			method "
  			meth := (Compiler new
  						parse: source
  						in: aClass
  						notifying: nil)
  						asTranslationMethodOfClass: self translationMethodClass.
  
  			(aClass includesSelector: sel)
  				ifTrue: [meth definingClass: aClass]
  				ifFalse: [meth definingClass: aClass class].
  			meth primitive > 0 ifTrue:[meth preparePrimitiveName].
  			"for old-style array accessing: 
  			meth covertToZeroBasedArrayReferences."
  			meth replaceSizeMessages.
  			self addMethod: meth.
  		].
  	].
  	"method preparation"
  	verbose := false.
  	self prepareMethods.
  	verbose
  		ifTrue: 
  			[self printUnboundCallWarnings.
  			self printUnboundVariableReferenceWarnings.
  			Transcript cr].
  
  	"code generation"
+ 	self doInlining: false.
- 	self doInlining: true.
  
  	methods do:[:m|
  		"if this method is supposed to be a primitive (rather than a helper 
  		routine), add assorted prolog and epilog items"
+ 		m primitive > 0 ifTrue: [m preparePrimitivePrologue].
+ 		"check for one-based array access"	
+ 		m oneBasedArrays ifNotNil: [self oneBasedArrays: true].
+ 	].!
- 		m primitive > 0 ifTrue: [m preparePrimitivePrologue]].!

Item was changed:
  ----- Method: JSCodeGenerator>>generateAt:on:indent: (in category 'JS translation') -----
  generateAt: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
+ 	| arrayNode indexNode subtractOne complex |
+ 	arrayNode := msgNode receiver.
+ 	indexNode := msgNode args first.
+ 	subtractOne := self isOneBasedArray: arrayNode name in: currentMethod.
+ 	(subtractOne and: [indexNode isPlusOne]) ifTrue: [
+ 		subtractOne := false.
+ 		indexNode := indexNode receiver.	"remove + 1"
+ 	].
+ 	complex := subtractOne and: [indexNode isLeaf not].
+ 	self emitJSExpression: arrayNode on: aStream.
- 
- 	self emitJSExpression: msgNode receiver on: aStream.
  	aStream nextPut: $[.
+ 	complex ifTrue: [aStream nextPut: $(].
+ 	indexNode emitJSCodeAsExpressionOn: aStream level: level + 1 generator: self.
+ 	complex ifTrue: [aStream nextPut: $)].
+ 	subtractOne ifTrue: [aStream nextPutAll: ' - 1'].
+ 	aStream nextPut: $].!
- 	msgNode args first emitJSCodeAsExpressionOn: aStream level: level + 1 generator: self.
- 	aStream nextPut: $]!

Item was changed:
  ----- Method: JSCodeGenerator>>generateAtPut:on:indent: (in category 'JS translation') -----
  generateAtPut: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
+ 	self generateAt: msgNode on: aStream indent: level.
+ 	aStream nextPutAll: ' = '.
- 
- 	self emitJSExpression: msgNode receiver on: aStream.
- 	aStream nextPut: $[.
- 	msgNode args first emitJSCodeAsExpressionOn: aStream level: level + 1 generator: self.
- 	aStream nextPutAll: '] = '.
  	self emitJSExpression: msgNode args last on: aStream!

Item was changed:
  ----- Method: JSCodeGenerator>>generateBitShift:on:indent: (in category 'JS translation') -----
  generateBitShift: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
  
  	| arg rcvr |
  	arg := msgNode args first.
  	rcvr := msgNode receiver.
  	arg isConstant ifTrue: [
  		"bit shift amount is a constant"
- 		aStream nextPutAll: '('.
  		self emitJSExpression: rcvr on: aStream.
  		arg value < 0 ifTrue: [
  			aStream nextPutAll: ' >>> ', arg value negated printString.
  		] ifFalse: [
  			aStream nextPutAll: ' << ', arg value printString.
  		].
- 		aStream nextPutAll: ')'.
  	] ifFalse: [
  		"bit shift amount is an expression"
+ 		self error: 'need to add BITSHIFT to emitJSHeaderOn:'.
+ 		aStream nextPutAll: 'BITSHIFT('.
- 		aStream nextPutAll: '('.
- 		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ' < 0 ? '.
  		self emitJSExpression: rcvr on: aStream.
+ 		aStream nextPutAll: ', '.
- 		aStream nextPutAll: ' >>> (0 - '.
  		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ') : '.
- 		self emitJSExpression: rcvr on: aStream.
- 		aStream nextPutAll: ' << '.
- 		self emitJSExpression: arg on: aStream.
  		aStream nextPutAll: ')'.
  	].!

Item was changed:
  ----- Method: JSCodeGenerator>>generateCDigitCopy:on:indent: (in category 'JS hacks') -----
  generateCDigitCopy: msgNode on: aStream indent: level
  	"LargeIntegerPlugin>>cDigitReplace:from:to:with:startingAt: uses pointer arithmetic. Replace it here"
  	msgNode args first selector = #+ ifFalse: [
  		^msgNode emitJSCodeAsFunctionCallOn: aStream level: level generator: self].
  	msgNode asString = 'self cDigitCopyFrom: pFrom + repStart to: pTo + start len: stop - start + 1'
  		ifFalse: [self halt: 'not handled: ', msgNode asString].
  	aStream nextPutAll: 'function() {
  		// inlining ', msgNode asString, '
- 		debugger;
  		var len = stop - start + 1;
  		for (var i = 0; i < len; i++) {
  			pTo[i + start] = pFrom[i + repStart];
  		}
  		return 0;
  	}();
  '!

Item was changed:
  ----- Method: JSCodeGenerator>>generateDivide:on:indent: (in category 'JS translation') -----
  generateDivide: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
  
  	| rcvr arg divisor |
  	rcvr := msgNode receiver.
  	arg := msgNode args first.
  	(arg isConstant and:
  	 [UseRightShiftForDivide and:
  	 [(divisor := arg value) isInteger and:
  	 [divisor isPowerOfTwo and:
  	 [divisor > 0 and:
  	 [divisor <= (1 bitShift: 31)]]]]])
  	ifTrue: [
  		"use signed (arithmetic) right shift instead of divide"
- 		aStream nextPutAll: '('.
  		self emitJSExpression: rcvr on: aStream.
  		aStream nextPutAll: ' >> ', (divisor log: 2) asInteger printString.
- 		aStream nextPutAll: ')'.
  	] ifFalse: [
  		"use float divide and coerce to integer"
+ 		aStream nextPutAll: 'DIV('.
- 		aStream nextPutAll: '('.
  		self emitJSExpression: rcvr on: aStream.
+ 		aStream nextPutAll: ', '.
- 		aStream nextPutAll: ' / '.
  		self emitJSExpression: arg on: aStream.
+ 		aStream nextPutAll: ')'.
- 		aStream nextPutAll: '|0)'.
  	].
  !

Item was changed:
  ----- Method: JSCodeGenerator>>generateFirstIndexableField:on:indent: (in category 'JS hacks') -----
  generateFirstIndexableField: msgNode on: aStream indent: level
  	| parent cType accessor |
+ 	self oneBasedArrays: false.
  	"HACK: detect cType from parent node"
  	parent := thisContext sender sender sender.
  	cType := parent method == (TAssignmentNode>>#emitJSCodeOn:level:generator:) 
  			ifTrue: [self typeOfVariable: parent receiver variable name] ifFalse: [
  		parent method == (TSendNode>>#emitJSCodeAsFunctionCallOn:level:generator:)
  			ifTrue: [self typeOfArgument: (parent receiver args indexOf: msgNode) in: parent receiver selector] ifFalse: [
  		self halt]].
  	cType ifNotNil: [
  		accessor := (cType beginsWith: 'unsigned char *') ifTrue: ['.bytes']
  			ifFalse: [(cType beginsWith: 'char *') ifTrue: ['.bytes']
  			ifFalse: [self halt: 'need to handle ', cType]].
  		accessor ifNotNil: [msgNode args first emitJSCodeOn: aStream level: level generator: self.
  			^aStream nextPutAll: accessor]].
  	"generic code below, not needed ever hopefully"
  	aStream nextPutAll: 'interpreterProxy.'.
  	^ msgNode emitJSCodeAsFunctionCallOn: aStream level: level generator: self!

Item was added:
+ ----- Method: JSCodeGenerator>>generateIdentityUnary:on:indent: (in category 'JS hacks') -----
+ generateIdentityUnary: msgNode on: aStream indent: level
+ 	"ignore this send, just use its value"
+ 	msgNode receiver emitJSCodeOn: aStream level: level generator: self.
+ !

Item was added:
+ ----- Method: JSCodeGenerator>>generateInterpreterProxyCall:on:indent: (in category 'JS hacks') -----
+ generateInterpreterProxyCall: msgNode on: aStream indent: level
+ 	aStream nextPutAll: 'interpreterProxy.'.
+ 	^ msgNode emitJSCodeAsFunctionCallOn: aStream level: level generator: self!

Item was changed:
  ----- Method: JSCodeGenerator>>generateModulo:on:indent: (in category 'JS translation') -----
  generateModulo: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
  
+ 	aStream nextPutAll: 'MOD('.
  	self emitJSExpression: msgNode receiver on: aStream.
+ 	aStream nextPutAll: ', '.
+ 	self emitJSExpression: msgNode args first on: aStream.
+ 	aStream nextPutAll: ')'.
+ !
- 	aStream nextPutAll: ' % '.
- 	self emitJSExpression: msgNode args first on: aStream.!

Item was changed:
  ----- Method: JSCodeGenerator>>generateShiftLeft:on:indent: (in category 'JS translation') -----
  generateShiftLeft: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
  	| arg rcvr |
  	rcvr := msgNode receiver.
  	arg := msgNode args first.
  	arg isConstant ifTrue: [
  		"bit shift amount is a constant"
  		arg value < 31 ifTrue: [
- 			aStream nextPutAll: '('.
  			self emitJSExpression: rcvr on: aStream.
  				aStream nextPutAll: ' << ', arg value printString.
- 			aStream nextPutAll: ')'.
  		] ifFalse: [
  			self error: 'cannot shift by more than 31'
  		].
  	] ifFalse: [
  		"bit shift amount is an expression"
+ 		aStream nextPutAll: 'SHL('.
- 		aStream nextPutAll: '('.
- 		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ' > 31 ? 0 : '.
  		self emitJSExpression: rcvr on: aStream.
+ 		aStream nextPutAll: ', '.
- 		aStream nextPutAll: ' << '.
  		self emitJSExpression: arg on: aStream.
  		aStream nextPutAll: ')'.
  	].!

Item was changed:
  ----- Method: JSCodeGenerator>>generateShiftRight:on:indent: (in category 'JS translation') -----
  generateShiftRight: msgNode on: aStream indent: level
  	"Generate the JS code for unsigned right-shift onto the given stream."
  	| rcvr arg |
  
  	rcvr := msgNode receiver.
  	arg := msgNode args first.
  	arg isConstant ifTrue: [
  		"bit shift amount is a constant"
  		arg value < 31 ifTrue: [
- 			aStream nextPutAll: '('.
  			self emitJSExpression: rcvr on: aStream.
  				aStream nextPutAll: ' >>> ', arg value printString.
- 			aStream nextPutAll: ')'.
  		] ifFalse: [
  			self error: 'cannot shift by more than 31'
  		].
  	] ifFalse: [
  		"bit shift amount is an expression"
+ 		aStream nextPutAll: 'SHR('.
- 		aStream nextPutAll: '('.
- 		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ' > 31 ? 0 : '.
  		self emitJSExpression: rcvr on: aStream.
+ 		aStream nextPutAll: ', '.
- 		aStream nextPutAll: ' >>> '.
  		self emitJSExpression: arg on: aStream.
  		aStream nextPutAll: ')'.
  	].!

Item was changed:
  ----- Method: JSCodeGenerator>>generateSignedBitShift:on:indent: (in category 'JS translation') -----
  generateSignedBitShift: msgNode on: aStream indent: level
+ 	"Generate the JS code for signedBitShift: onto the given stream."
- 	"Generate the JS code for this message onto the given stream."
  
  	| arg rcvr |
  	arg := msgNode args first.
  	rcvr := msgNode receiver.
  	arg isConstant ifTrue: [
  		"bit shift amount is a constant"
- 		aStream nextPutAll: '('.
  		self emitJSExpression: rcvr on: aStream.
  		arg value < 0 ifTrue: [
  			aStream nextPutAll: ' >> ', arg value negated printString.
  		] ifFalse: [
  			aStream nextPutAll: ' << ', arg value printString.
  		].
- 		aStream nextPutAll: ')'.
  	] ifFalse: [
+ 		self error: 'non-constant signed shift not implemented yet'
- 		"bit shift amount is an expression"
- 		aStream nextPutAll: '('.
- 		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ' < 0 ? '.
- 		self emitJSExpression: rcvr on: aStream.
- 		aStream nextPutAll: ' >> (0 - '.
- 		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ') : '.
- 		self emitJSExpression: rcvr on: aStream.
- 		aStream nextPutAll: ' << '.
- 		self emitJSExpression: arg on: aStream.
- 		aStream nextPutAll: ')'.
  	].!

Item was added:
+ ----- Method: JSCodeGenerator>>generateSizeOfSTArrayFromCPrimitive:on:indent: (in category 'JS hacks') -----
+ generateSizeOfSTArrayFromCPrimitive: msgNode on: aStream indent: level
+ 	| cType sizer |
+ 	cType := self typeOfVariable: msgNode args first name.
+ 	cType ifNotNil: [
+ 		sizer := (cType includesSubString: 'char *') ifTrue: ['.length'] ifFalse: [
+ 			(cType beginsWith: 'int *') ifTrue: ['.length'] 
+ 			ifFalse: [self halt: 'need to handle ', cType]]].
+ 		sizer ifNotNil: [msgNode args first emitJSCodeOn: aStream level: level generator: self.
+ 			^aStream nextPutAll: sizer].
+ 	self halt.
+ 	"generic code below, not needed ever hopefully"
+ 	aStream nextPutAll: 'interpreterProxy.'.
+ 	^ msgNode emitJSCodeAsFunctionCallOn: aStream level: level generator: self!

Item was changed:
  ----- Method: JSCodeGenerator>>generateToByDo:on:indent: (in category 'JS translation') -----
  generateToByDo: msgNode on: aStream indent: level
  	"Generate the JS code for this message onto the given stream."
  	"N.B. MessageNode>>asTranslatorNodeIn: adds the limit var as a hidden fourth argument."
  	| blockExpr iterationVar limitExpr mayHaveSideEffects limitVar step negative |
  	blockExpr := msgNode args third.
  	blockExpr args size = 1 ifFalse:
  		[self error: 'wrong number of block arguments'].
  	iterationVar := blockExpr args first.
  	limitExpr := msgNode args first.
  	aStream nextPutAll: 'for (', iterationVar, ' = '.
  	self emitJSExpression: msgNode receiver on: aStream.
  	mayHaveSideEffects := msgNode args size = 4. "See TMethod>>prepareMethodIn:"
  	mayHaveSideEffects ifTrue:
  		[limitVar := msgNode args last.
  		 aStream nextPutAll: ', ', limitVar name, ' = '.
  		 self emitJSExpression: limitExpr on: aStream.
  		 limitExpr := limitVar].
  	aStream nextPutAll: '; ', iterationVar.
  	negative := ((step := msgNode args at: 2) isConstant and: [step value < 0])
  				or: [step isSend and: [step selector == #negated
  					and: [step receiver isConstant and: [step receiver value >= 0]]]].
  	aStream nextPutAll: (negative ifTrue: [' >= '] ifFalse: [' <= ']).
  	self emitJSExpression: limitExpr on: aStream.
+ 	(step isConstant and: step value = 1)
+ 		ifTrue: [aStream nextPutAll: '; ', iterationVar, '++']
+ 		ifFalse: [aStream nextPutAll: '; ', iterationVar, ' += '.
+ 			self emitJSExpression: step on: aStream].
- 	aStream nextPutAll: '; ', iterationVar, ' += '.
- 	self emitJSExpression: step on: aStream.
  	aStream nextPutAll: ') {'; cr.
  	blockExpr emitJSCodeOn: aStream level: level + 1 generator: self.
  	aStream tab: level.
  	aStream nextPut: $}!

Item was changed:
  ----- Method: JSCodeGenerator>>initializeCTranslationDictionary (in category 'JS translation') -----
  initializeCTranslationDictionary 
  	"Initialize the dictionary mapping message names to actions for C code generation."
  
  	| pairs |
  	translationDict := Dictionary new: 200.
  	pairs := #(
  	#&				#generateAnd:on:indent:
  	#|				#generateOr:on:indent:
  	#and:			#generateSequentialAnd:on:indent:
  	#or:			#generateSequentialOr:on:indent:
  	#not			#generateNot:on:indent:
  
  	#+				#generatePlus:on:indent:
  	#-				#generateMinus:on:indent:
  	#negated		#generateNegated:on:indent:
  	#*				#generateTimes:on:indent:
  	#/				#generateDivide:on:indent:
  	#//				#generateDivide:on:indent:
  	#\\				#generateModulo:on:indent:
  	#<<			#generateShiftLeft:on:indent:
  	#>>			#generateShiftRight:on:indent:
  	#min:			#generateMin:on:indent:
  	#max:			#generateMax:on:indent:
  	#between:and:	#generateBetweenAnd:on:indent:
  
  	#bitAnd:		#generateBitAnd:on:indent:
  	#bitOr:			#generateBitOr:on:indent:
  	#bitXor:		#generateBitXor:on:indent:
  	#bitShift:		#generateBitShift:on:indent:
  	#signedBitShift:	#generateSignedBitShift:on:indent:
  	#bitInvert32		#generateBitInvert32:on:indent:
  	#bitClear:			#generateBitClear:on:indent:
  
  	#<				#generateLessThan:on:indent:
  	#<=			#generateLessThanOrEqual:on:indent:
  	#=				#generateEqual:on:indent:
  	#>				#generateGreaterThan:on:indent:
  	#>=			#generateGreaterThanOrEqual:on:indent:
  	#~=			#generateNotEqual:on:indent:
  	#==			#generateEqual:on:indent:
  	#~~			#generateNotEqual:on:indent:
  	#isNil			#generateIsNil:on:indent:
  	#notNil			#generateNotNil:on:indent:
  
  	#whileTrue: 	#generateWhileTrue:on:indent:
  	#whileFalse:	#generateWhileFalse:on:indent:
  	#whileTrue 		#generateDoWhileTrue:on:indent:
  	#whileFalse		#generateDoWhileFalse:on:indent:
  	#to:do:			#generateToDo:on:indent:
  	#to:by:do:		#generateToByDo:on:indent:
  	#repeat 		#generateRepeat:on:indent:
  
  	#ifTrue:			#generateIfTrue:on:indent:
  	#ifFalse:		#generateIfFalse:on:indent:
  	#ifTrue:ifFalse:	#generateIfTrueIfFalse:on:indent:
  	#ifFalse:ifTrue:	#generateIfFalseIfTrue:on:indent:
  
  	#at:			#generateAt:on:indent:
  	#at:put:			#generateAtPut:on:indent:
  	#basicAt:		#generateAt:on:indent:
  	#basicAt:put:	#generateAtPut:on:indent:
  
  	#integerValueOf:			#generateIntegerValueOf:on:indent:
  	#integerObjectOf:			#generateIntegerObjectOf:on:indent:
  	#isIntegerObject: 			#generateIsIntegerObject:on:indent:
  	#cCode:					#generateInlineCCode:on:indent:
  	#cCode:inSmalltalk:			#generateInlineCCode:on:indent:
  	#cPreprocessorDirective:	#generateInlineCPreprocessorDirective:on:indent:
  	#preprocessorExpression:	#generateInlineCppDirective:on:indent:
  	#isDefined:inSmalltalk:comment:ifTrue:	#generateInlineCppIfDef:on:indent:
  	#isDefined:inSmalltalk:comment:ifTrue:ifFalse:	#generateInlineCppIfDefElse:on:indent:
  	#isDefinedTrueExpression:inSmalltalk:comment:ifTrue:ifFalse:	#generateInlineCppIfElse:on:indent:
  	#cCoerce:to:				#generateCCoercion:on:indent:
  	#cCoerceSimple:to:			#generateCCoercion:on:indent:
  	#addressOf:				#generateAddressOf:on:indent:
  	#signedIntFromLong			#generateSignedIntFromLong:on:indent:
  	#signedIntToLong			#generateSignedIntToLong:on:indent:
  	#signedIntFromShort		#generateSignedIntFromShort:on:indent:
  	#signedIntToShort			#generateSignedIntToShort:on:indent:
  	#preIncrement				#generatePreIncrement:on:indent:
  	#preDecrement				#generatePreDecrement:on:indent:
  	#inline:						#generateInlineDirective:on:indent:
  	#asFloat					#generateAsFloat:on:indent:
  	#asInteger					#generateAsInteger:on:indent:
  	#asUnsignedInteger			#generateAsUnsignedInteger:on:indent:
  	#asSymbol					#generateAsSymbol:on:indent:
  	#anyMask:					#generateBitAnd:on:indent:
  	#raisedTo:					#generateRaisedTo:on:indent:
  	#touch:						#generateTouch:on:indent:
  	#bytesPerWord		#generateBytesPerWord:on:indent:
  	#baseHeaderSize		#generateBaseHeaderSize:on:indent:
  
  	#sharedCodeNamed:inCase:		#generateSharedCodeDirective:on:indent:
  
  	#perform:							#generatePerform:on:indent:
  	#perform:with:						#generatePerform:on:indent:
  	#perform:with:with:					#generatePerform:on:indent:
  	#perform:with:with:with:				#generatePerform:on:indent:
  	#perform:with:with:with:with:		#generatePerform:on:indent:
  	#perform:with:with:with:with:with:	#generatePerform:on:indent:
  
  	#shouldNotImplement				#generateSmalltalkMetaError:on:indent:
  	#shouldBeImplemented				#generateSmalltalkMetaError:on:indent:
  
  	"optimized interpreterProxy calls"
  	#firstIndexableField:				#generateFirstIndexableField:on:indent:
  	#slotSizeOf:						#generateSlotSizeOf:on:indent:
  	#byteSizeOfBytes:					#generateByteSizeOfBytes:on:indent:
  	#fetchClassOf:						#generateFetchClassOf:on:indent:
  	#is:KindOf: 							#generateIsKindOf:on:indent:
  	#cDigitCopyFrom:to:len:				#generateCDigitCopy:on:indent:
+ 	#sizeOfSTArrayFromCPrimitive:		#generateSizeOfSTArrayFromCPrimitive:on:indent:
+ 	#asciiValue							#generateIdentityUnary:on:indent:
+ 	#primitiveFail						#generateInterpreterProxyCall:on:indent:
  	).
  
  	1 to: pairs size by: 2 do: [:i |
  		translationDict at: (pairs at: i) put: (pairs at: i + 1)].
  
  	pairs := #(
  	#ifTrue:				#generateIfTrueAsArgument:on:indent:	
  	#ifFalse:			#generateIfFalseAsArgument:on:indent:
  	#ifTrue:ifFalse:		#generateIfTrueIfFalseAsArgument:on:indent:
  	#ifFalse:ifTrue:		#generateIfFalseIfTrueAsArgument:on:indent:
  	#cCode:			#generateInlineCCodeAsArgument:on:indent:
  	#cCode:inSmalltalk:	#generateInlineCCodeAsArgument:on:indent:
  	).
  
  	asArgumentTranslationDict := Dictionary new: 8.
  	1 to: pairs size by: 2 do: [:i |
  		asArgumentTranslationDict at: (pairs at: i) put: (pairs at: i + 1)].
  
  	cCodeTranslationDict := Dictionary new: 8.
  	pairs := #(
  		'fprintf(stderr, "\n%s: %s", moduleName, s)'					'console.log(moduleName + ": " + s)'
  		'interpreterProxy->majorVersion() == VM_PROXY_MAJOR'	'interpreterProxy.majorVersion() == VM_PROXY_MAJOR'
  		'interpreterProxy->minorVersion() >= VM_PROXY_MINOR'	'interpreterProxy.minorVersion() >= VM_PROXY_MINOR'
  	).
  	1 to: pairs size by: 2 do: [:i |
  		cCodeTranslationDict at: (pairs at: i) put: (pairs at: i + 1)].
  !

Item was added:
+ ----- Method: JSCodeGenerator>>isOneBasedArray:in: (in category 'JS hacks') -----
+ isOneBasedArray: varName in: method
+ 	^(method isOneBasedArray: varName)
+ 		ifNil: [self oneBasedArrays]
+ 		ifNotNil: [:oneBased | self oneBasedArrays: oneBased]!

Item was added:
+ ----- Method: JSCodeGenerator>>oneBasedArrays (in category 'JS hacks') -----
+ oneBasedArrays
+ 	oneBasedArrays ifNil: [oneBasedArrays := false].
+ 	^oneBasedArrays!

Item was added:
+ ----- Method: JSCodeGenerator>>oneBasedArrays: (in category 'JS hacks') -----
+ oneBasedArrays: aBoolean
+ 	oneBasedArrays ifNil: [^oneBasedArrays := aBoolean].
+ 	oneBasedArrays = aBoolean ifFalse: [
+ 		self halt: 'mixed one-based and zero-based array access'].
+ 	^aBoolean!

Item was changed:
  Object subclass: #JSMethod
+ 	instanceVariableNames: 'selector returnType args locals declarations primitive parseTree labels possibleSideEffectsCache complete export static sharedLabel sharedCase comment definingClass globalStructureBuildMethodHasFoo canAsmLabel mustAsmLabel properties cascadeVariableNumber extraVariableNumber oneBasedArrays'
- 	instanceVariableNames: 'selector returnType args locals declarations primitive parseTree labels possibleSideEffectsCache complete export static sharedLabel sharedCase comment definingClass globalStructureBuildMethodHasFoo canAsmLabel mustAsmLabel properties cascadeVariableNumber extraVariableNumber'
  	classVariableNames: 'CaseStatements'
  	poolDictionaries: ''
  	category: 'VMMakerJS-Translation to JS'!
  
  !JSMethod commentStamp: 'bf 10/3/2014 04:14' prior: 0!
  A JSMethod is a translation method, representing a MethodNode that is to be translated to JavaScript source. It has a parseTree of translation nodes that mirrors the parse tree of the corresponding Smalltalk method.!

Item was changed:
  ----- Method: JSMethod>>argConversionExprFor:stackIndex: (in category 'primitive compilation') -----
  argConversionExprFor: varName stackIndex: stackIndex 
  	"Return the parse tree for an expression that fetches and converts the 
  	primitive argument at the given stack offset."
  	| exprList decl stmtList |
+ 	oneBasedArrays ifNil: [oneBasedArrays := Set new]. "only non-nil in a primitive method"
  	exprList := OrderedCollection new.
  	(declarations includesKey: varName) ifTrue:[
  		decl := declarations at: varName.
  		(decl includes: $*) ifTrue:["array"
+ 			(decl includesSubString: 'char') ifTrue:[
+ 				exprList add: varName , ' := ', self vmNameString, ' stackBytes: ',stackIndex printString] ifFalse: [
+ 			(decl beginsWith: 'unsigned short') ifTrue:[
+ 				exprList add: varName , ' := ', self vmNameString, ' stackUint16Array: ',stackIndex printString] ifFalse: [
+ 			(decl beginsWith: 'int') ifTrue:[
+ 				exprList add: varName , ' := ', self vmNameString, ' stackInt32Array: ',stackIndex printString]
+ 			ifFalse: [self halt]]].
+ 			self beOneBasedArray: varName.
- 			(decl includesSubString: 'char') ifTrue:[ | expr |
- 				expr := '(interpreterProxy isBytes: (interpreterProxy stackValue: (stackIndex))) ifFalse:[^interpreterProxy primitiveFail].'.
- 				expr := expr copyReplaceAll: 'interpreterProxy' with: self vmNameString.
- 				expr := expr copyReplaceAll: 'stackIndex' with: stackIndex printString.
- 				exprList add: expr.
- 			].
- 			exprList add: varName , ' := ', self vmNameString, ' arrayValueOf: (', self vmNameString, ' stackValue: (' , stackIndex printString , '))'.
- 			exprList add: varName , ' := ' , varName , ' - 1'.
  		] ifFalse:["must be a double"
  			(decl findString: 'double' startingAt: 1) = 0 ifTrue: [
  				self error: 'unsupported type declaration in a primitive method'
  			].
  			exprList add: varName , ' := ', self vmNameString, ' stackFloatValue: ' , stackIndex printString.
  		]
  	] ifFalse: ["undeclared variables are taken to be integer"
  		exprList add: varName , ' := ', self vmNameString, ' stackIntegerValue: ' , stackIndex printString
  	].
  	stmtList := OrderedCollection new.
  	exprList do: [:e | stmtList addAll: (self statementsFor: e varName: varName)].
  	^ stmtList!

Item was added:
+ ----- Method: JSMethod>>beOneBasedArray: (in category 'JS code generation') -----
+ beOneBasedArray: varName
+ 	oneBasedArrays add: varName.
+ !

Item was changed:
  ----- Method: JSMethod>>checkSuccessExpr (in category 'primitive compilation') -----
  checkSuccessExpr
  	"Return the parse tree for an expression that aborts the primitive if the successFlag is not true."
  
  	| expr |
+ 	expr := 'interpreterProxy failed ifTrue: [^ nil ]'.
- 	expr := 'successFlag ifFalse: [^ nil ]'.
  	^ self statementsFor: expr varName: ''
  !

Item was changed:
  ----- Method: JSMethod>>fixUpReturns:postlog: (in category 'primitive compilation') -----
  fixUpReturns: argCount postlog: postlog
  	"Replace each return statement in this method with (a) the given postlog, (b) code to pop the receiver and the given number of arguments, and (c) code to push the integer result and return."
  
  	| newStmts |
  	parseTree nodesDo: [:node |
  		node isStmtList ifTrue: [
  			newStmts := OrderedCollection new: 100.
  			node statements do: [:stmt |
  				stmt isReturn
  					ifTrue: [
  						(stmt expression isSend and:
  						 ['primitiveFail' = stmt expression selector])
  							ifTrue: [  "failure return"
  								newStmts addLast: stmt expression.
  								newStmts addLast: (TReturnNode new
  									setExpression: (TVariableNode new setName: 'null'))]
  							ifFalse: [  "normal return"
  								newStmts addAll: postlog.
- 								newStmts addAll: (self popArgsExpr: argCount + 1).
  								newStmts addLast: (TSendNode new
+ 									setSelector: #pop:thenPush:
- 									setSelector: #pushInteger:
  									receiver: (TVariableNode new setName: self vmNameString)
+ 									arguments: {TConstantNode new setValue: argCount + 1. stmt expression}).
- 									arguments: (Array with: stmt expression)).
  								newStmts addLast: (TReturnNode new
  									setExpression: (TVariableNode new setName: 'null'))]]
  					ifFalse: [
  						newStmts addLast: stmt]].
  			node setStatements: newStmts asArray]].
  !

Item was added:
+ ----- Method: JSMethod>>isOneBasedArray: (in category 'JS code generation') -----
+ isOneBasedArray: varName
+ 	^oneBasedArrays ifNotNil: [oneBasedArrays includes: varName].
+ !

Item was added:
+ ----- Method: JSMethod>>oneBasedArrays (in category 'JS code generation') -----
+ oneBasedArrays
+ 	^oneBasedArrays!

Item was changed:
  ----- Method: JSMethod>>replaceSizeMessages (in category 'primitive compilation') -----
  replaceSizeMessages
  	"Replace sends of the message 'size' with calls to sizeOfSTArrayFromCPrimitive."
  
- 	| argExpr |
  	parseTree nodesDo: [:n |
  		(n isSend and: [n selector = #size]) ifTrue: [
- 			argExpr := TSendNode new
- 				setSelector: #+
- 				receiver: n receiver
- 				arguments: (Array with: (TConstantNode new setValue: 1)).
  			n
  				setSelector: #sizeOfSTArrayFromCPrimitive:
  				receiver: (TVariableNode new setName: self vmNameString)
+ 				arguments: (Array with: n receiver)]].
- 				arguments: (Array with: argExpr)]].
  !

Item was changed:
  ----- Method: JSMethod>>vmNameString (in category 'primitive compilation') -----
  vmNameString
  	"return the string to use as the vm name in code generated for this method"
+ 	^'interpreterProxy'!
- 	^'self'!

Item was changed:
  ----- Method: JSPluginCodeGenerator>>emitJSHeaderForPrimitivesOn: (in category 'JS code generator') -----
  emitJSHeaderForPrimitivesOn: aStream
+ 	"Write a JS file header for compiled primitives onto the given stream."
- 	"Write a C file header for compiled primitives onto the given stream."
  
  	self emitJSHeaderOn: aStream.
+ !
- 	aStream nextPutAll: '
- /*** Proxy Functions ***/
- #define stackValue(i) (interpreterProxy->stackValue(i))
- #define stackIntegerValue(i) (interpreterProxy->stackIntegerValue(i))
- #define successFlag (!!interpreterProxy->failed())
- #define success(bool) (interpreterProxy->success(bool))
- #define arrayValueOf(oop) (interpreterProxy->arrayValueOf(oop))
- #define checkedIntegerValueOf(oop) (interpreterProxy->checkedIntegerValueOf(oop))
- #define fetchArrayofObject(idx,oop) (interpreterProxy->fetchArrayofObject(idx,oop))
- #define fetchFloatofObject(idx,oop) (interpreterProxy->fetchFloatofObject(idx,oop))
- #define fetchIntegerofObject(idx,oop) (interpreterProxy->fetchIntegerofObject(idx,oop))
- #define floatValueOf(oop) (interpreterProxy->floatValueOf(oop))
- #define pop(n) (interpreterProxy->pop(n))
- #define pushInteger(n) (interpreterProxy->pushInteger(n))
- #define sizeOfSTArrayFromCPrimitive(cPtr) (interpreterProxy->sizeOfSTArrayFromCPrimitive(cPtr))
- #define storeIntegerofObjectwithValue(idx,oop,value) (interpreterProxy->storeIntegerofObjectwithValue(idx,oop,value))
- #define primitiveFail() interpreterProxy->primitiveFail()
- /* allows accessing Strings in both C and Smalltalk */
- #define asciiValue(c) c
- 
- '.
- 	aStream cr.!

Item was changed:
  ----- Method: JSPluginCodeGenerator>>emitJSHeaderOn: (in category 'JS code generator') -----
  emitJSHeaderOn: aStream
  	"Write a JS file header onto the given stream."
  
  	aStream nextPutAll: '/* '.
  	aStream nextPutAll: VMMaker headerNotice.
  	aStream nextPutAll: ' */';cr.
  	aStream nextPutAll: (self fileHeaderVersionStampForSourceClass: vmClass).
  	aStream nextPutAll: '
  module("users.bert.SqueakJS.plugins.', pluginName, '").requires("users.bert.SqueakJS.vm").toRun(function() {
  
  var VM_PROXY_MAJOR = ', InterpreterPrimitives vmProxyMajorVersion, ';
  var VM_PROXY_MINOR = ', InterpreterPrimitives vmProxyMinorVersion, ';
  
+ /*** Functions ***/
+ function DIV(a, b) { return Math.floor(a / b) | 0; }   // integer division
+ function MOD(a, b) { return a - DIV(a, b) * b | 0; }   // signed modulus
+ function SHL(a, b) { return b > 31 ? 0 : a << b ; }    // fix JS shift
+ function SHR(a, b) { return b > 31 ? 0 : a >>> b ; }   // fix JS shift
+ 
  '.!

Item was removed:
- ----- Method: JSSmartSyntaxPluginMethod>>oldReplaceSizeMessages (in category 'private') -----
- oldReplaceSizeMessages
- 	"Replace sends of the message 'size' with calls to sizeOfSTArrayFromCPrimitive."
- 
- 	| argExpr |
- 	parseTree nodesDo: [:n |
- 		(n isSend and: [n selector = #size]) ifTrue: [
- 			argExpr := TSendNode new
- 				setSelector: #+
- 				receiver: n receiver
- 				arguments: (Array with: (TConstantNode new setValue: 1)).
- 			n
- 				setSelector: #sizeOfSTArrayFromCPrimitive:
- 				receiver: (TVariableNode new setName: 'interpreterProxy')
- 				arguments: (Array with: argExpr)]].
- !

Item was changed:
  ----- Method: TAssignmentNode>>emitJSCodeOn:level:generator: (in category '*vmmakerjs') -----
  emitJSCodeOn: aStream level: level generator: aCodeGen
  	expression isSwitch ifTrue:
  		[^expression emitJSCodeOn: aStream addToEndOfCases: self level: level generator: aCodeGen].
- 	variable emitJSCodeOn: aStream level: level generator: aCodeGen.
  	self isVariableUpdatingAssignment
  		ifTrue:
+ 			[(expression args first isConstant and: [expression args first value = 1])
+ 				ifTrue: [aStream nextPutAll: expression selector, expression selector.
+ 					variable emitJSCodeOn: aStream level: level generator: aCodeGen]
+ 				ifFalse: [
+ 					variable emitJSCodeOn: aStream level: level generator: aCodeGen.
+ 					aStream nextPutAll: ' ', expression selector, '= '.
+ 					expression args first emitJSCodeAsArgumentOn: aStream level: level generator: aCodeGen]]
- 			[aStream
- 				space;
- 				nextPutAll: expression selector;	"+ or -"
- 				nextPut: $=;
- 				space.
- 			expression args first emitJSCodeAsArgumentOn: aStream level: level generator: aCodeGen]
  		ifFalse:
+ 			[variable emitJSCodeOn: aStream level: level generator: aCodeGen.
+ 			aStream nextPutAll: ' = '.
+ 			expression emitJSCodeAsArgumentOn: aStream level: level generator: aCodeGen]!
- 			[aStream space; nextPut: $=; space.
- 			 expression emitJSCodeAsArgumentOn: aStream level: level generator: aCodeGen]!

Item was added:
+ ----- Method: TParseNode>>isPlusOne (in category '*vmmakerjs') -----
+ isPlusOne
+ 	^false!

Item was added:
+ ----- Method: TSendNode>>isPlusOne (in category '*vmmakerjs') -----
+ isPlusOne
+ 	^ self selector = #+ and: [self args first isConstant and: [self args first value = 1]]!

Item was added:
+ ----- Method: VMPluginCodeGenerator class>>new (in category '*vmmakerjs') -----
+ new
+ 	JSCodeGenerator isActive ifTrue: [^JSPluginCodeGenerator new].
+ 	^super new!



More information about the Vm-dev mailing list