1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844 |
- Smalltalk current createPackage: 'Compiler' properties: #{}!
- Object subclass: #ChunkParser
- instanceVariableNames: 'stream'
- package:'Compiler'!
- !ChunkParser methodsFor: '*Compiler'!
- stream: aStream
- stream := aStream
- ! !
- !ChunkParser methodsFor: '*Compiler'!
- nextChunk
- "The chunk format (Smalltalk Interchange Format or Fileout format)
- is a trivial format but can be a bit tricky to understand:
- - Uses the exclamation mark as delimiter of chunks.
- - Inside a chunk a normal exclamation mark must be doubled.
- - A non empty chunk must be a valid Smalltalk expression.
- - A chunk on top level with a preceding empty chunk is an instruction chunk:
- - The object created by the expression then takes over reading chunks.
- This metod returns next chunk as a String (trimmed), empty String (all whitespace) or nil."
- | char result chunk |
- result := '' writeStream.
- [char := stream next.
- char notNil] whileTrue: [
- char = '!!' ifTrue: [
- stream peek = '!!'
- ifTrue: [stream next "skipping the escape double"]
- ifFalse: [^result contents trimBoth "chunk end marker found"]].
- result nextPut: char].
- ^nil "a chunk needs to end with !!"
- ! !
- !ChunkParser class methodsFor: '*Compiler'!
- on: aStream
- ^self new stream: aStream
- ! !
- Object subclass: #Exporter
- instanceVariableNames: ''
- package:'Compiler'!
- !Exporter methodsFor: '*Compiler'!
- exportAll
- "Export all packages in the system."
- ^String streamContents: [:stream |
- Smalltalk current packages do: [:pkg |
- stream nextPutAll: (self exportPackage: pkg name)]]
- !
- exportClass: aClass
- "Export a single class. Subclasses override these methods."
- ^String streamContents: [:stream |
- self exportDefinitionOf: aClass on: stream.
- self exportMethodsOf: aClass on: stream.
- self exportMetaDefinitionOf: aClass on: stream.
- self exportMethodsOf: aClass class on: stream]
- !
- exportPackage: packageName
- "Export a given package by name."
- | package |
- ^String streamContents: [:stream |
- package := Smalltalk current packageAt: packageName.
- self exportPackageDefinitionOf: package on: stream.
- "Export classes in dependency order.
- Update (issue #171): Remove duplicates for export"
- package sortedClasses asSet do: [:each |
- stream nextPutAll: (self exportClass: each)].
- self exportPackageExtensionsOf: package on: stream]
- ! !
- !Exporter methodsFor: '*Compiler'!
- classNameFor: aClass
- ^aClass isMetaclass
- ifTrue: [aClass instanceClass name, '.klass']
- ifFalse: [
- aClass isNil
- ifTrue: ['nil']
- ifFalse: [aClass name]]
- !
- exportDefinitionOf: aClass on: aStream
- aStream
- nextPutAll: 'smalltalk.addClass(';
- nextPutAll: '''', (self classNameFor: aClass), ''', ';
- nextPutAll: 'smalltalk.', (self classNameFor: aClass superclass);
- nextPutAll: ', ['.
- aClass instanceVariableNames
- do: [:each | aStream nextPutAll: '''', each, '''']
- separatedBy: [aStream nextPutAll: ', '].
- aStream
- nextPutAll: '], ''';
- nextPutAll: aClass category, '''';
- nextPutAll: ');'.
- aClass comment notEmpty ifTrue: [
- aStream
- lf;
- nextPutAll: 'smalltalk.';
- nextPutAll: (self classNameFor: aClass);
- nextPutAll: '.comment=';
- nextPutAll: aClass comment asJavascript].
- aStream lf
- !
- exportMetaDefinitionOf: aClass on: aStream
- aClass class instanceVariableNames isEmpty ifFalse: [
- aStream
- nextPutAll: 'smalltalk.', (self classNameFor: aClass class);
- nextPutAll: '.iVarNames = ['.
- aClass class instanceVariableNames
- do: [:each | aStream nextPutAll: '''', each, '''']
- separatedBy: [aStream nextPutAll: ','].
- aStream nextPutAll: '];', String lf]
- !
- exportMethod: aMethod of: aClass on: aStream
- aStream
- nextPutAll: 'smalltalk.addMethod(';lf;
- nextPutAll: aMethod selector asSelector asJavascript, ',';lf;
- nextPutAll: 'smalltalk.method({';lf;
- nextPutAll: 'selector: ', aMethod selector asJavascript, ',';lf;
- nextPutAll: 'category: ''', aMethod category, ''',';lf;
- nextPutAll: 'fn: ', aMethod fn compiledSource, ',';lf;
- nextPutAll: 'args: ', aMethod arguments asJavascript, ','; lf;
- nextPutAll: 'source: ', aMethod source asJavascript, ',';lf;
- nextPutAll: 'messageSends: ', aMethod messageSends asJavascript, ',';lf;
- nextPutAll: 'referencedClasses: ', aMethod referencedClasses asJavascript.
- aStream
- lf;
- nextPutAll: '}),';lf;
- nextPutAll: 'smalltalk.', (self classNameFor: aClass);
- nextPutAll: ');';lf;lf
- !
- exportMethodsOf: aClass on: aStream
- "Issue #143: sort methods alphabetically"
- ((aClass methodDictionary values) sorted: [:a :b | a selector <= b selector]) do: [:each |
- (each category match: '^\*') ifFalse: [
- self exportMethod: each of: aClass on: aStream]].
- aStream lf
- !
- exportPackageDefinitionOf: package on: aStream
- aStream
- nextPutAll: 'smalltalk.addPackage(';
- nextPutAll: '''', package name, ''', ', package propertiesAsJSON , ');'.
- aStream lf
- !
- exportPackageExtensionsOf: package on: aStream
- "Issue #143: sort classes and methods alphabetically"
- | name |
- name := package name.
- (Package sortedClasses: Smalltalk current classes) do: [:each |
- {each. each class} do: [:aClass |
- ((aClass methodDictionary values) sorted: [:a :b | a selector <= b selector]) do: [:method |
- (method category match: '^\*', name) ifTrue: [
- self exportMethod: method of: aClass on: aStream ]]]]
- ! !
- Exporter subclass: #ChunkExporter
- instanceVariableNames: ''
- package:'Compiler'!
- !ChunkExporter methodsFor: '*Compiler'!
- chunkEscape: aString
- "Replace all occurrences of !! with !!!! and trim at both ends."
- ^(aString replace: '!!' with: '!!!!') trimBoth
- !
- classNameFor: aClass
- ^aClass isMetaclass
- ifTrue: [aClass instanceClass name, ' class']
- ifFalse: [
- aClass isNil
- ifTrue: ['nil']
- ifFalse: [aClass name]]
- !
- exportDefinitionOf: aClass on: aStream
- "Chunk format."
- aStream
- nextPutAll: (self classNameFor: aClass superclass);
- nextPutAll: ' subclass: #', (self classNameFor: aClass); lf;
- nextPutAll: ' instanceVariableNames: '''.
- aClass instanceVariableNames
- do: [:each | aStream nextPutAll: each]
- separatedBy: [aStream nextPutAll: ' '].
- aStream
- nextPutAll: ''''; lf;
- nextPutAll: ' package: ''', aClass category, '''!!'; lf.
- aClass comment notEmpty ifTrue: [
- aStream
- nextPutAll: '!!', (self classNameFor: aClass), ' commentStamp!!';lf;
- nextPutAll: (self chunkEscape: aClass comment), '!!';lf].
- aStream lf
- !
- exportMetaDefinitionOf: aClass on: aStream
- aClass class instanceVariableNames isEmpty ifFalse: [
- aStream
- nextPutAll: (self classNameFor: aClass class);
- nextPutAll: ' instanceVariableNames: '''.
- aClass class instanceVariableNames
- do: [:each | aStream nextPutAll: each]
- separatedBy: [aStream nextPutAll: ' '].
- aStream
- nextPutAll: '''!!'; lf; lf]
- !
- exportMethod: aMethod of: aClass on: aStream
- aStream
- lf; lf; nextPutAll: (self chunkEscape: aMethod source); lf;
- nextPutAll: '!!'
- !
- exportMethods: methods category: category of: aClass on: aStream
- "Issue #143: sort methods alphabetically"
- aStream
- nextPutAll: '!!', (self classNameFor: aClass);
- nextPutAll: ' methodsFor: ''', category, '''!!'.
- (methods sorted: [:a :b | a selector <= b selector]) do: [:each |
- self exportMethod: each of: aClass on: aStream].
- aStream nextPutAll: ' !!'; lf; lf
- !
- exportMethodsOf: aClass on: aStream
- "Issue #143: sort protocol alphabetically"
- | map |
- map := Dictionary new.
- aClass protocolsDo: [:category :methods |
- (category match: '^\*') ifFalse: [ map at: category put: methods ]].
- (map keys sorted: [:a :b | a <= b ]) do: [:category | | methods |
- methods := map at: category.
- self
- exportMethods: methods
- category: category
- of: aClass
- on: aStream ]
- !
- exportPackageDefinitionOf: package on: aStream
- "Chunk format."
- aStream
- nextPutAll: 'Smalltalk current createPackage: ''', package name,
- ''' properties: ', package properties storeString, '!!'; lf.
- !
- exportPackageExtensionsOf: package on: aStream
- "We need to override this one too since we need to group
- all methods in a given protocol under a leading methodsFor: chunk
- for that class."
- "Issue #143: sort protocol alphabetically"
- | name map |
- name := package name.
- (Package sortedClasses: Smalltalk current classes) do: [:each |
- {each. each class} do: [:aClass |
- map := Dictionary new.
- aClass protocolsDo: [:category :methods |
- (category match: '^\*', name) ifTrue: [ map at: category put: methods ]].
- (map keys sorted: [:a :b | a <= b ]) do: [:category | | methods |
- methods := map at: category.
- self exportMethods: methods category: category of: aClass on: aStream ]]]
- ! !
- Exporter subclass: #StrippedExporter
- instanceVariableNames: ''
- package:'Compiler'!
- !StrippedExporter methodsFor: '*Compiler'!
- exportDefinitionOf: aClass on: aStream
- aStream
- nextPutAll: 'smalltalk.addClass(';
- nextPutAll: '''', (self classNameFor: aClass), ''', ';
- nextPutAll: 'smalltalk.', (self classNameFor: aClass superclass);
- nextPutAll: ', ['.
- aClass instanceVariableNames
- do: [:each | aStream nextPutAll: '''', each, '''']
- separatedBy: [aStream nextPutAll: ', '].
- aStream
- nextPutAll: '], ''';
- nextPutAll: aClass category, '''';
- nextPutAll: ');'.
- aStream lf
- !
- exportMethod: aMethod of: aClass on: aStream
- aStream
- nextPutAll: 'smalltalk.addMethod(';lf;
- nextPutAll: aMethod selector asSelector asJavascript, ',';lf;
- nextPutAll: 'smalltalk.method({';lf;
- nextPutAll: 'selector: ', aMethod selector asJavascript, ',';lf;
- nextPutAll: 'fn: ', aMethod fn compiledSource;lf;
- nextPutAll: '}),';lf;
- nextPutAll: 'smalltalk.', (self classNameFor: aClass);
- nextPutAll: ');';lf;lf
- ! !
- Object subclass: #Importer
- instanceVariableNames: ''
- package:'Compiler'!
- !Importer methodsFor: '*Compiler'!
- import: aStream
- | chunk result parser lastEmpty |
- parser := ChunkParser on: aStream.
- lastEmpty := false.
- [chunk := parser nextChunk.
- chunk isNil] whileFalse: [
- chunk isEmpty
- ifTrue: [lastEmpty := true]
- ifFalse: [
- result := Compiler new evaluateExpression: chunk.
- lastEmpty
- ifTrue: [
- lastEmpty := false.
- result scanFrom: parser]]]
- ! !
- Object subclass: #PackageLoader
- instanceVariableNames: ''
- package:'Compiler'!
- !PackageLoader methodsFor: '*Compiler'!
- initializePackageNamed: packageName prefix: aString
- (Package named: packageName)
- setupClasses;
- commitPathJs: '/', aString, '/js';
- commitPathSt: '/', aString, '/st'
- !
- loadPackage: packageName prefix: aString
- | url |
- url := '/', aString, '/js/', packageName, '.js'.
- jQuery
- ajax: url
- options: #{
- 'type' -> 'GET'.
- 'dataType' -> 'script'.
- 'complete' -> [ :jqXHR :textStatus |
- jqXHR readyState = 4
- ifTrue: [ self initializePackageNamed: packageName prefix: aString ] ].
- 'error' -> [ window alert: 'Could not load package at: ', url ]
- }
- !
- loadPackages: aCollection prefix: aString
- aCollection do: [ :each |
- self loadPackage: each prefix: aString ]
- ! !
- !PackageLoader class methodsFor: '*Compiler'!
- loadPackages: aCollection prefix: aString
- ^ self new loadPackages: aCollection prefix: aString
- ! !
- Error subclass: #CompilerError
- instanceVariableNames: ''
- package:'Compiler'!
- !CompilerError commentStamp!
- I am the common superclass of all compiling errors.!
- CompilerError subclass: #ParseError
- instanceVariableNames: ''
- package:'Compiler'!
- !ParseError commentStamp!
- Instance of ParseError are signaled on any parsing error.
- See `Smalltalk >> #parse:`!
- CompilerError subclass: #SemanticError
- instanceVariableNames: ''
- package:'Compiler'!
- !SemanticError commentStamp!
- I represent an abstract semantic error thrown by the SemanticAnalyzer.
- Semantic errors can be unknown variable errors, etc.
- See my subclasses for concrete errors.
- The IDE should catch instances of Semantic error to deal with them when compiling!
- SemanticError subclass: #InliningError
- instanceVariableNames: ''
- package:'Compiler'!
- !InliningError commentStamp!
- Instances of InliningError are signaled when using an `InliningCodeGenerator`in a `Compiler`.!
- SemanticError subclass: #InvalidAssignmentError
- instanceVariableNames: 'variableName'
- package:'Compiler'!
- !InvalidAssignmentError commentStamp!
- I get signaled when a pseudo variable gets assigned.!
- !InvalidAssignmentError methodsFor: '*Compiler'!
- messageText
- ^ ' Invalid assignment to variable: ', self variableName
- !
- variableName
- ^ variableName
- !
- variableName: aString
- variableName := aString
- ! !
- SemanticError subclass: #ShadowingVariableError
- instanceVariableNames: 'variableName'
- package:'Compiler'!
- !ShadowingVariableError commentStamp!
- I get signaled when a variable in a block or method scope shadows a variable of the same name in an outer scope.!
- !ShadowingVariableError methodsFor: '*Compiler'!
- messageText
- ^ 'Variable shadowing error: ', self variableName, ' is already defined'
- !
- variableName
- ^ variableName
- !
- variableName: aString
- variableName := aString
- ! !
- SemanticError subclass: #UnknownVariableError
- instanceVariableNames: 'variableName'
- package:'Compiler'!
- !UnknownVariableError commentStamp!
- I get signaled when a variable is not defined.
- The default behavior is to allow it, as this is how Amber currently is able to seamlessly send messages to JavaScript objects.!
- !UnknownVariableError methodsFor: '*Compiler'!
- variableName
- ^ variableName
- !
- variableName: aString
- variableName := aString
- ! !
- Object subclass: #Compiler
- instanceVariableNames: 'currentClass source unknownVariables codeGeneratorClass'
- package:'Compiler'!
- !Compiler commentStamp!
- I provide the public interface for compiling Amber source code into JavaScript.
- The code generator used to produce JavaScript can be plugged with `#codeGeneratorClass`.
- The default code generator is an instance of `InlinedCodeGenerator`!
- !Compiler methodsFor: '*Compiler'!
- codeGeneratorClass
- ^codeGeneratorClass ifNil: [InliningCodeGenerator]
- !
- codeGeneratorClass: aClass
- codeGeneratorClass := aClass
- !
- currentClass
- ^currentClass
- !
- currentClass: aClass
- currentClass := aClass
- !
- source
- ^source ifNil: ['']
- !
- source: aString
- source := aString
- !
- unknownVariables
- ^unknownVariables
- !
- unknownVariables: aCollection
- unknownVariables := aCollection
- ! !
- !Compiler methodsFor: '*Compiler'!
- compile: aString
- ^self compileNode: (self parse: aString)
- !
- compile: aString forClass: aClass
- self currentClass: aClass.
- self source: aString.
- ^self compile: aString
- !
- compileExpression: aString
- self currentClass: DoIt.
- self source: 'doIt ^[', aString, '] value'.
- ^self compileNode: (self parse: self source)
- !
- compileNode: aNode
- | generator result |
- generator := self codeGeneratorClass new.
- generator
- source: self source;
- currentClass: self currentClass.
- result := generator compileNode: aNode.
- self unknownVariables: #().
- ^result
- !
- eval: aString
- <return eval(aString)>
- !
- evaluateExpression: aString
- "Unlike #eval: evaluate a Smalltalk expression and answer the returned object"
- | result |
- DoIt addCompiledMethod: (self eval: (self compileExpression: aString)).
- result := DoIt new doIt.
- DoIt removeCompiledMethod: (DoIt methodDictionary at: 'doIt').
- ^result
- !
- install: aString forClass: aBehavior category: anotherString
- | compiled |
- compiled := self eval: (self compile: aString forClass: aBehavior).
- compiled category: anotherString.
- aBehavior addCompiledMethod: compiled.
- self setupClass: aBehavior.
- ^compiled
- !
- parse: aString
- ^Smalltalk current parse: aString
- !
- parseExpression: aString
- ^self parse: 'doIt ^[', aString, '] value'
- !
- recompile: aClass
- aClass methodDictionary do: [:each |
- console log: aClass name, ' >> ', each selector.
- self install: each source forClass: aClass category: each category].
- self setupClass: aClass.
- aClass isMetaclass ifFalse: [self recompile: aClass class]
- !
- recompileAll
- Smalltalk current classes do: [:each |
- Transcript show: each; cr.
- [self recompile: each] valueWithTimeout: 100]
- !
- setupClass: aClass
- <smalltalk.init(aClass)>
- ! !
- !Compiler class methodsFor: '*Compiler'!
- recompile: aClass
- self new recompile: aClass
- !
- recompileAll
- Smalltalk current classes do: [:each |
- self recompile: each]
- ! !
- Object subclass: #DoIt
- instanceVariableNames: ''
- package:'Compiler'!
- !DoIt commentStamp!
- `DoIt` is the class used to compile and evaluate expressions. See `Compiler >> evaluateExpression:`.!
- Object subclass: #NodeVisitor
- instanceVariableNames: ''
- package:'Compiler'!
- !NodeVisitor commentStamp!
- I am the abstract super class of all AST node visitors.!
- !NodeVisitor methodsFor: '*Compiler'!
- visit: aNode
- ^ aNode accept: self
- !
- visitAll: aCollection
- ^ aCollection do: [ :each | self visit: each ]
- !
- visitAssignmentNode: aNode
- ^ self visitNode: aNode
- !
- visitBlockNode: aNode
- ^ self visitNode: aNode
- !
- visitBlockSequenceNode: aNode
- ^ self visitSequenceNode: aNode
- !
- visitCascadeNode: aNode
- ^ self visitNode: aNode
- !
- visitClassReferenceNode: aNode
- ^ self visitVariableNode: aNode
- !
- visitDynamicArrayNode: aNode
- ^ self visitNode: aNode
- !
- visitDynamicDictionaryNode: aNode
- ^ self visitNode: aNode
- !
- visitJSStatementNode: aNode
- ^ self visitNode: aNode
- !
- visitMethodNode: aNode
- ^ self visitNode: aNode
- !
- visitNode: aNode
- ^ self visitAll: aNode nodes
- !
- visitReturnNode: aNode
- ^ self visitNode: aNode
- !
- visitSendNode: aNode
- ^ self visitNode: aNode
- !
- visitSequenceNode: aNode
- ^ self visitNode: aNode
- !
- visitValueNode: aNode
- ^ self visitNode: aNode
- !
- visitVariableNode: aNode
- ^ self visitNode: aNode
- ! !
- NodeVisitor subclass: #AbstractCodeGenerator
- instanceVariableNames: 'currentClass source'
- package:'Compiler'!
- !AbstractCodeGenerator commentStamp!
- I am the abstract super class of all code generators and provide their common API.!
- !AbstractCodeGenerator methodsFor: '*Compiler'!
- classNameFor: aClass
- ^aClass isMetaclass
- ifTrue: [aClass instanceClass name, '.klass']
- ifFalse: [
- aClass isNil
- ifTrue: ['nil']
- ifFalse: [aClass name]]
- !
- currentClass
- ^currentClass
- !
- currentClass: aClass
- currentClass := aClass
- !
- pseudoVariables
- ^#('self' 'super' 'true' 'false' 'nil' 'thisContext')
- !
- safeVariableNameFor: aString
- ^(Smalltalk current reservedWords includes: aString)
- ifTrue: [aString, '_']
- ifFalse: [aString]
- !
- source
- ^source ifNil: ['']
- !
- source: aString
- source := aString
- ! !
- !AbstractCodeGenerator methodsFor: '*Compiler'!
- compileNode: aNode
- self subclassResponsibility
- ! !
- AbstractCodeGenerator subclass: #CodeGenerator
- instanceVariableNames: ''
- package:'Compiler'!
- !CodeGenerator commentStamp!
- I am a basic code generator. I generate a valid JavaScript output, but no not perform any inlining.
- See `InliningCodeGenerator` for an optimized JavaScript code generation.!
- !CodeGenerator methodsFor: '*Compiler'!
- compileNode: aNode
- | ir stream |
- self semanticAnalyzer visit: aNode.
- ir := self translator visit: aNode.
- ^ self irTranslator
- visit: ir;
- contents
- !
- irTranslator
- ^ IRJSTranslator new
- !
- semanticAnalyzer
- ^ SemanticAnalyzer on: self currentClass
- !
- translator
- ^ IRASTTranslator new
- source: self source;
- theClass: self currentClass;
- yourself
- ! !
- Object subclass: #Node
- instanceVariableNames: 'position nodes shouldBeInlined shouldBeAliased'
- package:'Compiler'!
- !Node commentStamp!
- I am the abstract root class of the abstract syntax tree.
- position: holds a point containing lline- and column number of the symbol location in the original source file!
- !Node methodsFor: '*Compiler'!
- addNode: aNode
- self nodes add: aNode
- !
- nodes
- ^nodes ifNil: [nodes := Array new]
- !
- position
- ^position ifNil: [position := 0@0]
- !
- shouldBeAliased
- ^ shouldBeAliased ifNil: [ false ]
- !
- shouldBeAliased: aBoolean
- shouldBeAliased := aBoolean
- !
- shouldBeInlined
- ^ shouldBeInlined ifNil: [ false ]
- !
- shouldBeInlined: aBoolean
- shouldBeInlined := aBoolean
- ! !
- !Node methodsFor: '*Compiler'!
- nodes: aCollection
- nodes := aCollection
- !
- position: aPosition
- position := aPosition
- ! !
- !Node methodsFor: '*Compiler'!
- isAssignmentNode
- ^ false
- !
- isBlockNode
- ^false
- !
- isBlockSequenceNode
- ^false
- !
- isImmutable
- ^false
- !
- isReturnNode
- ^false
- !
- isSendNode
- ^false
- !
- isValueNode
- ^false
- !
- subtreeNeedsAliasing
- ^(self shouldBeAliased or: [ self shouldBeInlined ]) or: [
- (self nodes detect: [ :each | each subtreeNeedsAliasing ] ifNone: [ false ]) ~= false ]
- ! !
- !Node methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitNode: self
- ! !
- Node subclass: #AssignmentNode
- instanceVariableNames: 'left right'
- package:'Compiler'!
- !AssignmentNode methodsFor: '*Compiler'!
- left
- ^left
- !
- left: aNode
- left := aNode
- !
- nodes
- ^ Array with: self left with: self right
- !
- right
- ^right
- !
- right: aNode
- right := aNode
- ! !
- !AssignmentNode methodsFor: '*Compiler'!
- isAssignmentNode
- ^ true
- ! !
- !AssignmentNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitAssignmentNode: self
- ! !
- Node subclass: #BlockNode
- instanceVariableNames: 'parameters scope'
- package:'Compiler'!
- !BlockNode methodsFor: '*Compiler'!
- parameters
- ^parameters ifNil: [parameters := Array new]
- !
- parameters: aCollection
- parameters := aCollection
- !
- scope
- ^ scope
- !
- scope: aLexicalScope
- scope := aLexicalScope
- ! !
- !BlockNode methodsFor: '*Compiler'!
- isBlockNode
- ^true
- ! !
- !BlockNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitBlockNode: self
- ! !
- Node subclass: #CascadeNode
- instanceVariableNames: 'receiver'
- package:'Compiler'!
- !CascadeNode methodsFor: '*Compiler'!
- receiver
- ^receiver
- !
- receiver: aNode
- receiver := aNode
- ! !
- !CascadeNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitCascadeNode: self
- ! !
- Node subclass: #DynamicArrayNode
- instanceVariableNames: ''
- package:'Compiler'!
- !DynamicArrayNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitDynamicArrayNode: self
- ! !
- Node subclass: #DynamicDictionaryNode
- instanceVariableNames: ''
- package:'Compiler'!
- !DynamicDictionaryNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitDynamicDictionaryNode: self
- ! !
- Node subclass: #JSStatementNode
- instanceVariableNames: 'source'
- package:'Compiler'!
- !JSStatementNode methodsFor: '*Compiler'!
- source
- ^source ifNil: ['']
- !
- source: aString
- source := aString
- ! !
- !JSStatementNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitJSStatementNode: self
- ! !
- Node subclass: #MethodNode
- instanceVariableNames: 'selector arguments source scope classReferences messageSends superSends'
- package:'Compiler'!
- !MethodNode methodsFor: '*Compiler'!
- arguments
- ^arguments ifNil: [#()]
- !
- arguments: aCollection
- arguments := aCollection
- !
- classReferences
- ^ classReferences
- !
- classReferences: aCollection
- classReferences := aCollection
- !
- messageSends
- ^ messageSends
- !
- messageSends: aCollection
- messageSends := aCollection
- !
- scope
- ^ scope
- !
- scope: aMethodScope
- scope := aMethodScope
- !
- selector
- ^selector
- !
- selector: aString
- selector := aString
- !
- source
- ^source
- !
- source: aString
- source := aString
- !
- superSends
- ^ superSends
- !
- superSends: aCollection
- superSends := aCollection
- ! !
- !MethodNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitMethodNode: self
- ! !
- Node subclass: #ReturnNode
- instanceVariableNames: 'scope'
- package:'Compiler'!
- !ReturnNode methodsFor: '*Compiler'!
- scope
- ^ scope
- !
- scope: aLexicalScope
- scope := aLexicalScope
- ! !
- !ReturnNode methodsFor: '*Compiler'!
- isReturnNode
- ^ true
- !
- nonLocalReturn
- ^ self scope isMethodScope not
- ! !
- !ReturnNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitReturnNode: self
- ! !
- Node subclass: #SendNode
- instanceVariableNames: 'selector arguments receiver superSend index'
- package:'Compiler'!
- !SendNode methodsFor: '*Compiler'!
- arguments
- ^arguments ifNil: [arguments := #()]
- !
- arguments: aCollection
- arguments := aCollection
- !
- cascadeNodeWithMessages: aCollection
- | first |
- first := SendNode new
- selector: self selector;
- arguments: self arguments;
- yourself.
- ^CascadeNode new
- receiver: self receiver;
- nodes: (Array with: first), aCollection;
- yourself
- !
- index
- ^ index
- !
- index: anInteger
- index := anInteger
- !
- nodes
- ^ (Array withAll: self arguments)
- add: self receiver;
- yourself
- !
- receiver
- ^receiver
- !
- receiver: aNode
- receiver := aNode
- !
- selector
- ^selector
- !
- selector: aString
- selector := aString
- !
- superSend
- ^ superSend ifNil: [ false ]
- !
- superSend: aBoolean
- superSend := aBoolean
- !
- valueForReceiver: anObject
- ^SendNode new
- receiver: (self receiver
- ifNil: [anObject]
- ifNotNil: [self receiver valueForReceiver: anObject]);
- selector: self selector;
- arguments: self arguments;
- yourself
- ! !
- !SendNode methodsFor: '*Compiler'!
- isSendNode
- ^ true
- ! !
- !SendNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitSendNode: self
- ! !
- Node subclass: #SequenceNode
- instanceVariableNames: 'temps scope'
- package:'Compiler'!
- !SequenceNode methodsFor: '*Compiler'!
- scope
- ^ scope
- !
- scope: aLexicalScope
- scope := aLexicalScope
- !
- temps
- ^temps ifNil: [#()]
- !
- temps: aCollection
- temps := aCollection
- ! !
- !SequenceNode methodsFor: '*Compiler'!
- asBlockSequenceNode
- ^BlockSequenceNode new
- nodes: self nodes;
- temps: self temps;
- yourself
- ! !
- !SequenceNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitSequenceNode: self
- ! !
- SequenceNode subclass: #BlockSequenceNode
- instanceVariableNames: ''
- package:'Compiler'!
- !BlockSequenceNode methodsFor: '*Compiler'!
- isBlockSequenceNode
- ^true
- ! !
- !BlockSequenceNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitBlockSequenceNode: self
- ! !
- Node subclass: #ValueNode
- instanceVariableNames: 'value'
- package:'Compiler'!
- !ValueNode methodsFor: '*Compiler'!
- value
- ^value
- !
- value: anObject
- value := anObject
- ! !
- !ValueNode methodsFor: '*Compiler'!
- isImmutable
- ^true
- !
- isValueNode
- ^true
- ! !
- !ValueNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitValueNode: self
- ! !
- ValueNode subclass: #VariableNode
- instanceVariableNames: 'assigned binding'
- package:'Compiler'!
- !VariableNode methodsFor: '*Compiler'!
- alias
- ^ self binding alias
- !
- assigned
- ^assigned ifNil: [false]
- !
- assigned: aBoolean
- assigned := aBoolean
- !
- beAssigned
- self binding validateAssignment.
- assigned := true
- !
- binding
- ^ binding
- !
- binding: aScopeVar
- binding := aScopeVar
- ! !
- !VariableNode methodsFor: '*Compiler'!
- isImmutable
- ^false
- ! !
- !VariableNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitVariableNode: self
- ! !
- VariableNode subclass: #ClassReferenceNode
- instanceVariableNames: ''
- package:'Compiler'!
- !ClassReferenceNode methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitClassReferenceNode: self
- ! !
- Object subclass: #LexicalScope
- instanceVariableNames: 'node instruction temps args outerScope'
- package:'Compiler'!
- !LexicalScope commentStamp!
- I represent a lexical scope where variable names are associated with ScopeVars
- Instances are used for block scopes. Method scopes are instances of MethodLexicalScope.
- I am attached to a ScopeVar and method/block nodes.
- Each context (method/closure) get a fresh scope that inherits from its outer scope.!
- !LexicalScope methodsFor: '*Compiler'!
- alias
- ^ '$ctx', self scopeLevel asString
- !
- allVariableNames
- ^ self args keys, self temps keys
- !
- args
- ^ args ifNil: [ args := Dictionary new ]
- !
- bindingFor: aStringOrNode
- ^ self pseudoVars at: aStringOrNode value ifAbsent: [
- self args at: aStringOrNode value ifAbsent: [
- self temps at: aStringOrNode value ifAbsent: [ nil ]]]
- !
- instruction
- ^ instruction
- !
- instruction: anIRInstruction
- instruction := anIRInstruction
- !
- lookupVariable: aNode
- | lookup |
- lookup := (self bindingFor: aNode).
- lookup ifNil: [
- lookup := self outerScope ifNotNil: [
- (self outerScope lookupVariable: aNode) ]].
- ^ lookup
- !
- methodScope
- ^ self outerScope ifNotNil: [
- self outerScope methodScope ]
- !
- node
- "Answer the node in which I am defined"
-
- ^ node
- !
- node: aNode
- node := aNode
- !
- outerScope
- ^ outerScope
- !
- outerScope: aLexicalScope
- outerScope := aLexicalScope
- !
- pseudoVars
- ^ self methodScope pseudoVars
- !
- scopeLevel
- self outerScope ifNil: [ ^ 1 ].
- self isInlined ifTrue: [ ^ self outerScope scopeLevel ].
-
- ^ self outerScope scopeLevel + 1
- !
- temps
- ^ temps ifNil: [ temps := Dictionary new ]
- ! !
- !LexicalScope methodsFor: '*Compiler'!
- addArg: aString
- self args at: aString put: (ArgVar on: aString).
- (self args at: aString) scope: self
- !
- addTemp: aString
- self temps at: aString put: (TempVar on: aString).
- (self temps at: aString) scope: self
- ! !
- !LexicalScope methodsFor: '*Compiler'!
- canInlineNonLocalReturns
- ^ self isInlined and: [ self outerScope canInlineNonLocalReturns ]
- !
- isBlockScope
- ^ self isMethodScope not
- !
- isInlined
- ^ self instruction notNil and: [
- self instruction isInlined ]
- !
- isMethodScope
- ^ false
- ! !
- LexicalScope subclass: #MethodLexicalScope
- instanceVariableNames: 'iVars pseudoVars unknownVariables localReturn nonLocalReturns'
- package:'Compiler'!
- !MethodLexicalScope commentStamp!
- I represent a method scope.!
- !MethodLexicalScope methodsFor: '*Compiler'!
- allVariableNames
- ^ super allVariableNames, self iVars keys
- !
- bindingFor: aNode
- ^ (super bindingFor: aNode) ifNil: [
- self iVars at: aNode value ifAbsent: [ nil ]]
- !
- iVars
- ^ iVars ifNil: [ iVars := Dictionary new ]
- !
- localReturn
- ^ localReturn ifNil: [ false ]
- !
- localReturn: aBoolean
- localReturn := aBoolean
- !
- methodScope
- ^ self
- !
- nonLocalReturns
- ^ nonLocalReturns ifNil: [ nonLocalReturns := OrderedCollection new ]
- !
- pseudoVars
- pseudoVars ifNil: [
- pseudoVars := Dictionary new.
- Smalltalk current pseudoVariableNames do: [ :each |
- pseudoVars at: each put: ((PseudoVar on: each)
- scope: self methodScope;
- yourself) ]].
- ^ pseudoVars
- !
- unknownVariables
- ^ unknownVariables ifNil: [ unknownVariables := OrderedCollection new ]
- ! !
- !MethodLexicalScope methodsFor: '*Compiler'!
- addIVar: aString
- self iVars at: aString put: (InstanceVar on: aString).
- (self iVars at: aString) scope: self
- !
- addNonLocalReturn: aScope
- self nonLocalReturns add: aScope
- !
- removeNonLocalReturn: aScope
- self nonLocalReturns remove: aScope ifAbsent: []
- ! !
- !MethodLexicalScope methodsFor: '*Compiler'!
- canInlineNonLocalReturns
- ^ true
- !
- hasLocalReturn
- ^ self localReturn
- !
- hasNonLocalReturn
- ^ self nonLocalReturns notEmpty
- !
- isMethodScope
- ^ true
- ! !
- Object subclass: #ScopeVar
- instanceVariableNames: 'scope name'
- package:'Compiler'!
- !ScopeVar commentStamp!
- I am an entry in a LexicalScope that gets associated with variable nodes of the same name.
- There are 4 different subclasses of vars: temp vars, local vars, args, and unknown/global vars.!
- !ScopeVar methodsFor: '*Compiler'!
- alias
- ^ self name asVariableName
- !
- name
- ^ name
- !
- name: aString
- name := aString
- !
- scope
- ^ scope
- !
- scope: aScope
- scope := aScope
- ! !
- !ScopeVar methodsFor: '*Compiler'!
- isArgVar
- ^ false
- !
- isClassRefVar
- ^ false
- !
- isInstanceVar
- ^ false
- !
- isPseudoVar
- ^ false
- !
- isTempVar
- ^ false
- !
- isUnknownVar
- ^ false
- !
- validateAssignment
- (self isArgVar or: [ self isPseudoVar ]) ifTrue: [
- InvalidAssignmentError new
- variableName: self name;
- signal]
- ! !
- !ScopeVar class methodsFor: '*Compiler'!
- on: aString
- ^ self new
- name: aString;
- yourself
- ! !
- ScopeVar subclass: #AliasVar
- instanceVariableNames: 'node'
- package:'Compiler'!
- !AliasVar commentStamp!
- I am an internally defined variable by the compiler!
- !AliasVar methodsFor: '*Compiler'!
- node
- ^ node
- !
- node: aNode
- node := aNode
- ! !
- ScopeVar subclass: #ArgVar
- instanceVariableNames: ''
- package:'Compiler'!
- !ArgVar commentStamp!
- I am an argument of a method or block.!
- !ArgVar methodsFor: '*Compiler'!
- isArgVar
- ^ true
- ! !
- ScopeVar subclass: #ClassRefVar
- instanceVariableNames: ''
- package:'Compiler'!
- !ClassRefVar commentStamp!
- I am an class reference variable!
- !ClassRefVar methodsFor: '*Compiler'!
- alias
- ^ '(smalltalk.', self name, ' || ', self name, ')'
- ! !
- !ClassRefVar methodsFor: '*Compiler'!
- isClassRefVar
- ^ true
- ! !
- ScopeVar subclass: #InstanceVar
- instanceVariableNames: ''
- package:'Compiler'!
- !InstanceVar commentStamp!
- I am an instance variable of a method or block.!
- !InstanceVar methodsFor: '*Compiler'!
- alias
- ^ 'self["@', self name, '"]'
- !
- isInstanceVar
- ^ true
- ! !
- ScopeVar subclass: #PseudoVar
- instanceVariableNames: ''
- package:'Compiler'!
- !PseudoVar commentStamp!
- I am an pseudo variable.
- The five Smalltalk pseudo variables are: 'self', 'super', 'nil', 'true' and 'false'!
- !PseudoVar methodsFor: '*Compiler'!
- alias
- ^ self name
- ! !
- !PseudoVar methodsFor: '*Compiler'!
- isPseudoVar
- ^ true
- ! !
- ScopeVar subclass: #TempVar
- instanceVariableNames: ''
- package:'Compiler'!
- !TempVar commentStamp!
- I am an temporary variable of a method or block.!
- !TempVar methodsFor: '*Compiler'!
- alias
- ^ self scope alias, '.locals.', super alias
- ! !
- !TempVar methodsFor: '*Compiler'!
- isTempVar
- ^ true
- ! !
- ScopeVar subclass: #UnknownVar
- instanceVariableNames: ''
- package:'Compiler'!
- !UnknownVar commentStamp!
- I am an unknown variable. Amber uses unknown variables as JavaScript globals!
- !UnknownVar methodsFor: '*Compiler'!
- isUnknownVar
- ^ true
- ! !
- NodeVisitor subclass: #SemanticAnalyzer
- instanceVariableNames: 'currentScope theClass classReferences messageSends superSends'
- package:'Compiler'!
- !SemanticAnalyzer commentStamp!
- I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
- !SemanticAnalyzer methodsFor: '*Compiler'!
- classReferences
- ^ classReferences ifNil: [ classReferences := Set new ]
- !
- messageSends
- ^ messageSends ifNil: [ messageSends := Dictionary new ]
- !
- superSends
- ^ superSends ifNil: [ superSends := Dictionary new ]
- !
- theClass
- ^ theClass
- !
- theClass: aClass
- theClass := aClass
- ! !
- !SemanticAnalyzer methodsFor: '*Compiler'!
- errorShadowingVariable: aString
- ShadowingVariableError new
- variableName: aString;
- signal
- !
- errorUnknownVariable: aNode
- "Throw an error if the variable is undeclared in the global JS scope (i.e. window)"
- | identifier |
- identifier := aNode value.
- ((#('jQuery' 'window' 'process' 'global') includes: identifier) not and: [ self isVariableGloballyUndefined: identifier ]) ifTrue: [
- UnknownVariableError new
- variableName: aNode value;
- signal ]
- ifFalse: [
- currentScope methodScope unknownVariables add: aNode value. ]
- ! !
- !SemanticAnalyzer methodsFor: '*Compiler'!
- newBlockScope
- ^ self newScopeOfClass: LexicalScope
- !
- newMethodScope
- ^ self newScopeOfClass: MethodLexicalScope
- !
- newScopeOfClass: aLexicalScopeClass
- ^ aLexicalScopeClass new
- outerScope: currentScope;
- yourself
- ! !
- !SemanticAnalyzer methodsFor: '*Compiler'!
- popScope
- currentScope ifNotNil: [
- currentScope := currentScope outerScope ]
- !
- pushScope: aScope
- aScope outerScope: currentScope.
- currentScope := aScope
- !
- validateVariableScope: aString
- "Validate the variable scope in by doing a recursive lookup, up to the method scope"
- (currentScope lookupVariable: aString) ifNotNil: [
- self errorShadowingVariable: aString ]
- ! !
- !SemanticAnalyzer methodsFor: '*Compiler'!
- isVariableGloballyUndefined: aString
- <return eval('typeof ' + aString + ' == "undefined"')>
- ! !
- !SemanticAnalyzer methodsFor: '*Compiler'!
- visitAssignmentNode: aNode
- super visitAssignmentNode: aNode.
- aNode left beAssigned
- !
- visitBlockNode: aNode
- self pushScope: self newBlockScope.
- aNode scope: currentScope.
- currentScope node: aNode.
-
- aNode parameters do: [ :each |
- self validateVariableScope: each.
- currentScope addArg: each ].
- super visitBlockNode: aNode.
- self popScope
- !
- visitCascadeNode: aNode
- "Populate the receiver into all children"
- aNode nodes do: [ :each |
- each receiver: aNode receiver ].
- super visitCascadeNode: aNode.
- aNode nodes first superSend ifTrue: [
- aNode nodes do: [ :each | each superSend: true ]]
- !
- visitClassReferenceNode: aNode
- self classReferences add: aNode value.
- aNode binding: (ClassRefVar new name: aNode value; yourself)
- !
- visitMethodNode: aNode
- self pushScope: self newMethodScope.
- aNode scope: currentScope.
- currentScope node: aNode.
- self theClass allInstanceVariableNames do: [:each |
- currentScope addIVar: each ].
- aNode arguments do: [ :each |
- self validateVariableScope: each.
- currentScope addArg: each ].
- super visitMethodNode: aNode.
- aNode
- classReferences: self classReferences;
- messageSends: self messageSends keys;
- superSends: self superSends keys.
- self popScope
- !
- visitReturnNode: aNode
- aNode scope: currentScope.
- currentScope isMethodScope
- ifTrue: [ currentScope localReturn: true ]
- ifFalse: [ currentScope methodScope addNonLocalReturn: currentScope ].
- super visitReturnNode: aNode
- !
- visitSendNode: aNode
- aNode receiver value = 'super'
- ifTrue: [
- aNode superSend: true.
- aNode receiver value: 'self'.
- self superSends at: aNode selector ifAbsentPut: [ Set new ].
- (self superSends at: aNode selector) add: aNode ]
-
- ifFalse: [ (IRSendInliner inlinedSelectors includes: aNode selector) ifTrue: [
- aNode shouldBeInlined: true.
- aNode receiver shouldBeAliased: true ] ].
- self messageSends at: aNode selector ifAbsentPut: [ Set new ].
- (self messageSends at: aNode selector) add: aNode.
- aNode index: (self messageSends at: aNode selector) size.
- super visitSendNode: aNode
- !
- visitSequenceNode: aNode
- aNode temps do: [ :each |
- self validateVariableScope: each.
- currentScope addTemp: each ].
- super visitSequenceNode: aNode
- !
- visitVariableNode: aNode
- "Bind a ScopeVar to aNode by doing a lookup in the current scope.
- If no ScopeVar is found, bind a UnknowVar and throw an error"
- aNode binding: ((currentScope lookupVariable: aNode) ifNil: [
- self errorUnknownVariable: aNode.
- UnknownVar new name: aNode value; yourself ])
- ! !
- !SemanticAnalyzer class methodsFor: '*Compiler'!
- on: aClass
- ^ self new
- theClass: aClass;
- yourself
- ! !
- NodeVisitor subclass: #IRASTTranslator
- instanceVariableNames: 'source theClass method sequence nextAlias'
- package:'Compiler'!
- !IRASTTranslator commentStamp!
- I am the AST (abstract syntax tree) visitor responsible for building the intermediate representation graph.
- I rely on a builder object, instance of IRBuilder.!
- !IRASTTranslator methodsFor: '*Compiler'!
- method
- ^ method
- !
- method: anIRMethod
- method := anIRMethod
- !
- nextAlias
- nextAlias ifNil: [ nextAlias := 0 ].
- nextAlias := nextAlias + 1.
- ^ nextAlias asString
- !
- sequence
- ^ sequence
- !
- sequence: anIRSequence
- sequence := anIRSequence
- !
- source
- ^ source
- !
- source: aString
- source := aString
- !
- theClass
- ^ theClass
- !
- theClass: aClass
- theClass := aClass
- !
- withSequence: aSequence do: aBlock
- | outerSequence |
- outerSequence := self sequence.
- self sequence: aSequence.
- aBlock value.
- self sequence: outerSequence.
- ^ aSequence
- ! !
- !IRASTTranslator methodsFor: '*Compiler'!
- alias: aNode
- | variable |
- aNode isImmutable ifTrue: [ ^ self visit: aNode ].
- variable := IRVariable new
- variable: (AliasVar new name: '$', self nextAlias);
- yourself.
- self sequence add: (IRAssignment new
- add: variable;
- add: (self visit: aNode);
- yourself).
- self method internalVariables add: variable.
- ^ variable
- !
- aliasTemporally: aCollection
- "https://github.com/NicolasPetton/amber/issues/296
-
- If a node is aliased, all preceding ones are aliased as well.
- The tree is iterated twice. First we get the aliasing dependency,
- then the aliasing itself is done"
- | threshold result |
- threshold := 0.
-
- aCollection withIndexDo: [ :each :i |
- each subtreeNeedsAliasing
- ifTrue: [ threshold := i ]].
- result := OrderedCollection new.
- aCollection withIndexDo: [ :each :i |
- result add: (i <= threshold
- ifTrue: [ self alias: each ]
- ifFalse: [ self visit: each ])].
- ^result
- !
- visitAssignmentNode: aNode
- | left right assignment |
- right := self visit: aNode right.
- left := self visit: aNode left.
- self sequence add: (IRAssignment new
- add: left;
- add: right;
- yourself).
- ^ left
- !
- visitBlockNode: aNode
- | closure |
- closure := IRClosure new
- arguments: aNode parameters;
- scope: aNode scope;
- yourself.
- aNode scope temps do: [ :each |
- closure add: (IRTempDeclaration new
- name: each name;
- scope: aNode scope;
- yourself) ].
- aNode nodes do: [ :each | closure add: (self visit: each) ].
- ^ closure
- !
- visitBlockSequenceNode: aNode
- ^ self
- withSequence: IRBlockSequence new
- do: [
- aNode nodes ifNotEmpty: [
- aNode nodes allButLast do: [ :each |
- self sequence add: (self visit: each) ].
- aNode nodes last isReturnNode
- ifFalse: [ self sequence add: (IRBlockReturn new add: (self visit: aNode nodes last); yourself) ]
- ifTrue: [ self sequence add: (self visit: aNode nodes last) ]]]
- !
- visitCascadeNode: aNode
- | alias |
- aNode receiver isImmutable ifFalse: [
- alias := self alias: aNode receiver.
- aNode nodes do: [ :each |
- each receiver: (VariableNode new binding: alias variable) ]].
- aNode nodes allButLast do: [ :each |
- self sequence add: (self visit: each) ].
- ^ self alias: aNode nodes last
- !
- visitDynamicArrayNode: aNode
- | array |
- array := IRDynamicArray new.
- (self aliasTemporally: aNode nodes) do: [:each | array add: each].
- ^ array
- !
- visitDynamicDictionaryNode: aNode
- | dictionary |
- dictionary := IRDynamicDictionary new.
- (self aliasTemporally: aNode nodes) do: [:each | dictionary add: each].
- ^ dictionary
- !
- visitJSStatementNode: aNode
- ^ IRVerbatim new
- source: aNode source;
- yourself
- !
- visitMethodNode: aNode
- self method: (IRMethod new
- source: self source;
- theClass: self theClass;
- arguments: aNode arguments;
- selector: aNode selector;
- messageSends: aNode messageSends;
- superSends: aNode superSends;
- classReferences: aNode classReferences;
- scope: aNode scope;
- yourself).
- aNode scope temps do: [ :each |
- self method add: (IRTempDeclaration new
- name: each name;
- scope: aNode scope;
- yourself) ].
- aNode nodes do: [ :each | self method add: (self visit: each) ].
- aNode scope hasLocalReturn ifFalse: [
- (self method add: IRReturn new) add: (IRVariable new
- variable: (aNode scope pseudoVars at: 'self');
- yourself) ].
- ^ self method
- !
- visitReturnNode: aNode
- | return |
- return := aNode nonLocalReturn
- ifTrue: [ IRNonLocalReturn new ]
- ifFalse: [ IRReturn new ].
- return scope: aNode scope.
- aNode nodes do: [ :each |
- return add: (self alias: each) ].
- ^ return
- !
- visitSendNode: aNode
- | send all receiver arguments |
- send := IRSend new.
- send
- selector: aNode selector;
- index: aNode index.
- aNode superSend ifTrue: [ send classSend: self theClass superclass ].
-
- all := self aliasTemporally: { aNode receiver }, aNode arguments.
- receiver := all first.
- arguments := all allButFirst.
- send add: receiver.
- arguments do: [ :each | send add: each ].
- ^ send
- !
- visitSequenceNode: aNode
- ^ self
- withSequence: IRSequence new
- do: [
- aNode nodes do: [ :each | | instruction |
- instruction := self visit: each.
- instruction isVariable ifFalse: [
- self sequence add: instruction ]]]
- !
- visitValueNode: aNode
- ^ IRValue new
- value: aNode value;
- yourself
- !
- visitVariableNode: aNode
- ^ IRVariable new
- variable: aNode binding;
- yourself
- ! !
- Object subclass: #IRInstruction
- instanceVariableNames: 'parent instructions'
- package:'Compiler'!
- !IRInstruction commentStamp!
- I am the abstract root class of the IR (intermediate representation) instructions class hierarchy.
- The IR graph is used to emit JavaScript code using a JSStream.!
- !IRInstruction methodsFor: '*Compiler'!
- instructions
- ^ instructions ifNil: [ instructions := OrderedCollection new ]
- !
- parent
- ^ parent
- !
- parent: anIRInstruction
- parent := anIRInstruction
- ! !
- !IRInstruction methodsFor: '*Compiler'!
- add: anObject
- anObject parent: self.
- ^ self instructions add: anObject
- !
- remove
- self parent remove: self
- !
- remove: anIRInstruction
- self instructions remove: anIRInstruction
- !
- replace: anIRInstruction with: anotherIRInstruction
- anotherIRInstruction parent: self.
- self instructions
- at: (self instructions indexOf: anIRInstruction)
- put: anotherIRInstruction
- !
- replaceWith: anIRInstruction
- self parent replace: self with: anIRInstruction
- ! !
- !IRInstruction methodsFor: '*Compiler'!
- canBeAssigned
- ^ true
- !
- isClosure
- ^ false
- !
- isInlined
- ^ false
- !
- isLocalReturn
- ^ false
- !
- isReturn
- ^ false
- !
- isSend
- ^ false
- !
- isSequence
- ^ false
- !
- isTempDeclaration
- ^ false
- !
- isVariable
- ^ false
- ! !
- !IRInstruction methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRInstruction: self
- ! !
- !IRInstruction class methodsFor: '*Compiler'!
- on: aBuilder
- ^ self new
- builder: aBuilder;
- yourself
- ! !
- IRInstruction subclass: #IRAssignment
- instanceVariableNames: ''
- package:'Compiler'!
- !IRAssignment methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRAssignment: self
- ! !
- IRInstruction subclass: #IRDynamicArray
- instanceVariableNames: ''
- package:'Compiler'!
- !IRDynamicArray methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRDynamicArray: self
- ! !
- IRInstruction subclass: #IRDynamicDictionary
- instanceVariableNames: ''
- package:'Compiler'!
- !IRDynamicDictionary methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRDynamicDictionary: self
- ! !
- IRInstruction subclass: #IRScopedInstruction
- instanceVariableNames: 'scope'
- package:'Compiler'!
- !IRScopedInstruction methodsFor: '*Compiler'!
- scope
- ^ scope
- !
- scope: aScope
- scope := aScope
- ! !
- IRScopedInstruction subclass: #IRClosure
- instanceVariableNames: 'arguments'
- package:'Compiler'!
- !IRClosure methodsFor: '*Compiler'!
- arguments
- ^ arguments ifNil: [ #() ]
- !
- arguments: aCollection
- arguments := aCollection
- !
- scope: aScope
- super scope: aScope.
- aScope instruction: self
- !
- sequence
- ^ self instructions last
- ! !
- !IRClosure methodsFor: '*Compiler'!
- isClosure
- ^ true
- ! !
- !IRClosure methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRClosure: self
- ! !
- IRScopedInstruction subclass: #IRMethod
- instanceVariableNames: 'theClass source selector classReferences messageSends superSends arguments internalVariables'
- package:'Compiler'!
- !IRMethod commentStamp!
- I am a method instruction!
- !IRMethod methodsFor: '*Compiler'!
- arguments
- ^ arguments
- !
- arguments: aCollection
- arguments := aCollection
- !
- classReferences
- ^ classReferences
- !
- classReferences: aCollection
- classReferences := aCollection
- !
- internalVariables
- ^ internalVariables ifNil: [ internalVariables := Set new ]
- !
- messageSends
- ^ messageSends
- !
- messageSends: aCollection
- messageSends := aCollection
- !
- scope: aScope
- super scope: aScope.
- aScope instruction: self
- !
- selector
- ^ selector
- !
- selector: aString
- selector := aString
- !
- source
- ^ source
- !
- source: aString
- source := aString
- !
- superSends
- ^ superSends
- !
- superSends: aCollection
- superSends := aCollection
- !
- theClass
- ^ theClass
- !
- theClass: aClass
- theClass := aClass
- ! !
- !IRMethod methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRMethod: self
- ! !
- IRScopedInstruction subclass: #IRReturn
- instanceVariableNames: ''
- package:'Compiler'!
- !IRReturn commentStamp!
- I am a local return instruction.!
- !IRReturn methodsFor: '*Compiler'!
- canBeAssigned
- ^ false
- !
- isBlockReturn
- ^ false
- !
- isLocalReturn
- ^ true
- !
- isNonLocalReturn
- ^ self isLocalReturn not
- !
- isReturn
- ^ true
- ! !
- !IRReturn methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRReturn: self
- ! !
- IRReturn subclass: #IRBlockReturn
- instanceVariableNames: ''
- package:'Compiler'!
- !IRBlockReturn commentStamp!
- Smalltalk blocks return their last statement. I am a implicit block return instruction.!
- !IRBlockReturn methodsFor: '*Compiler'!
- isBlockReturn
- ^ true
- ! !
- !IRBlockReturn methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRBlockReturn: self
- ! !
- IRReturn subclass: #IRNonLocalReturn
- instanceVariableNames: ''
- package:'Compiler'!
- !IRNonLocalReturn commentStamp!
- I am a non local return instruction.
- Non local returns are handled using a try/catch JS statement.
- See IRNonLocalReturnHandling class!
- !IRNonLocalReturn methodsFor: '*Compiler'!
- isLocalReturn
- ^ false
- ! !
- !IRNonLocalReturn methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRNonLocalReturn: self
- ! !
- IRScopedInstruction subclass: #IRTempDeclaration
- instanceVariableNames: 'name'
- package:'Compiler'!
- !IRTempDeclaration methodsFor: '*Compiler'!
- name
- ^ name
- !
- name: aString
- name := aString
- ! !
- !IRTempDeclaration methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRTempDeclaration: self
- ! !
- IRInstruction subclass: #IRSend
- instanceVariableNames: 'selector classSend index'
- package:'Compiler'!
- !IRSend commentStamp!
- I am a message send instruction.!
- !IRSend methodsFor: '*Compiler'!
- classSend
- ^ classSend
- !
- classSend: aClass
- classSend := aClass
- !
- index
- ^ index
- !
- index: anInteger
- index := anInteger
- !
- javascriptSelector
- ^ self classSend
- ifNil: [ self selector asSelector ]
- ifNotNil: [ self selector asSuperSelector ]
- !
- selector
- ^ selector
- !
- selector: aString
- selector := aString
- ! !
- !IRSend methodsFor: '*Compiler'!
- isSend
- ^ true
- ! !
- !IRSend methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRSend: self
- ! !
- IRInstruction subclass: #IRSequence
- instanceVariableNames: ''
- package:'Compiler'!
- !IRSequence methodsFor: '*Compiler'!
- isSequence
- ^ true
- ! !
- !IRSequence methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRSequence: self
- ! !
- IRSequence subclass: #IRBlockSequence
- instanceVariableNames: ''
- package:'Compiler'!
- !IRBlockSequence methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRBlockSequence: self
- ! !
- IRInstruction subclass: #IRValue
- instanceVariableNames: 'value'
- package:'Compiler'!
- !IRValue commentStamp!
- I am the simplest possible instruction. I represent a value.!
- !IRValue methodsFor: '*Compiler'!
- value
- ^value
- !
- value: aString
- value := aString
- ! !
- !IRValue methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRValue: self
- ! !
- IRInstruction subclass: #IRVariable
- instanceVariableNames: 'variable'
- package:'Compiler'!
- !IRVariable commentStamp!
- I am a variable instruction.!
- !IRVariable methodsFor: '*Compiler'!
- variable
- ^ variable
- !
- variable: aScopeVariable
- variable := aScopeVariable
- ! !
- !IRVariable methodsFor: '*Compiler'!
- isVariable
- ^ true
- ! !
- !IRVariable methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRVariable: self
- ! !
- IRInstruction subclass: #IRVerbatim
- instanceVariableNames: 'source'
- package:'Compiler'!
- !IRVerbatim methodsFor: '*Compiler'!
- source
- ^ source
- !
- source: aString
- source := aString
- ! !
- !IRVerbatim methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRVerbatim: self
- ! !
- Object subclass: #IRVisitor
- instanceVariableNames: ''
- package:'Compiler'!
- !IRVisitor methodsFor: '*Compiler'!
- visit: anIRInstruction
- ^ anIRInstruction accept: self
- !
- visitIRAssignment: anIRAssignment
- ^ self visitIRInstruction: anIRAssignment
- !
- visitIRBlockReturn: anIRBlockReturn
- ^ self visitIRReturn: anIRBlockReturn
- !
- visitIRBlockSequence: anIRBlockSequence
- ^ self visitIRSequence: anIRBlockSequence
- !
- visitIRClosure: anIRClosure
- ^ self visitIRInstruction: anIRClosure
- !
- visitIRDynamicArray: anIRDynamicArray
- ^ self visitIRInstruction: anIRDynamicArray
- !
- visitIRDynamicDictionary: anIRDynamicDictionary
- ^ self visitIRInstruction: anIRDynamicDictionary
- !
- visitIRInlinedClosure: anIRInlinedClosure
- ^ self visitIRClosure: anIRInlinedClosure
- !
- visitIRInlinedSequence: anIRInlinedSequence
- ^ self visitIRSequence: anIRInlinedSequence
- !
- visitIRInstruction: anIRInstruction
- anIRInstruction instructions do: [ :each | self visit: each ].
- ^ anIRInstruction
- !
- visitIRMethod: anIRMethod
- ^ self visitIRInstruction: anIRMethod
- !
- visitIRNonLocalReturn: anIRNonLocalReturn
- ^ self visitIRInstruction: anIRNonLocalReturn
- !
- visitIRNonLocalReturnHandling: anIRNonLocalReturnHandling
- ^ self visitIRInstruction: anIRNonLocalReturnHandling
- !
- visitIRReturn: anIRReturn
- ^ self visitIRInstruction: anIRReturn
- !
- visitIRSend: anIRSend
- ^ self visitIRInstruction: anIRSend
- !
- visitIRSequence: anIRSequence
- ^ self visitIRInstruction: anIRSequence
- !
- visitIRTempDeclaration: anIRTempDeclaration
- ^ self visitIRInstruction: anIRTempDeclaration
- !
- visitIRValue: anIRValue
- ^ self visitIRInstruction: anIRValue
- !
- visitIRVariable: anIRVariable
- ^ self visitIRInstruction: anIRVariable
- !
- visitIRVerbatim: anIRVerbatim
- ^ self visitIRInstruction: anIRVerbatim
- ! !
- IRVisitor subclass: #IRJSTranslator
- instanceVariableNames: 'stream'
- package:'Compiler'!
- !IRJSTranslator methodsFor: '*Compiler'!
- contents
- ^ self stream contents
- !
- stream
- ^ stream
- !
- stream: aStream
- stream := aStream
- ! !
- !IRJSTranslator methodsFor: '*Compiler'!
- initialize
- super initialize.
- stream := JSStream new.
- ! !
- !IRJSTranslator methodsFor: '*Compiler'!
- visitIRAssignment: anIRAssignment
- self visit: anIRAssignment instructions first.
- self stream nextPutAssignment.
- self visit: anIRAssignment instructions last.
- !
- visitIRClosure: anIRClosure
- self stream
- nextPutClosureWith: [
- self stream
- nextPutBlockContextFor: anIRClosure
- during: [ super visitIRClosure: anIRClosure ] ]
- arguments: anIRClosure arguments
- !
- visitIRDynamicArray: anIRDynamicArray
- self stream nextPutAll: '['.
- anIRDynamicArray instructions
- do: [ :each | self visit: each ]
- separatedBy: [ self stream nextPutAll: ',' ].
- stream nextPutAll: ']'
- !
- visitIRDynamicDictionary: anIRDynamicDictionary
- self stream nextPutAll: 'smalltalk.HashedCollection._fromPairs_(['.
- anIRDynamicDictionary instructions
- do: [ :each | self visit: each ]
- separatedBy: [self stream nextPutAll: ',' ].
- self stream nextPutAll: '])'
- !
- visitIRMethod: anIRMethod
- self stream
- nextPutMethodDeclaration: anIRMethod
- with: [ self stream
- nextPutFunctionWith: [
- self stream nextPutContextFor: anIRMethod during: [
- anIRMethod internalVariables notEmpty ifTrue: [
- self stream nextPutVars: (anIRMethod internalVariables asArray collect: [ :each |
- each variable alias ]) ].
- anIRMethod scope hasNonLocalReturn
- ifTrue: [
- self stream nextPutNonLocalReturnHandlingWith: [
- super visitIRMethod: anIRMethod ]]
- ifFalse: [ super visitIRMethod: anIRMethod ]]]
- arguments: anIRMethod arguments ]
- !
- visitIRNonLocalReturn: anIRNonLocalReturn
- self stream nextPutNonLocalReturnWith: [
- super visitIRNonLocalReturn: anIRNonLocalReturn ]
- !
- visitIRReturn: anIRReturn
- self stream nextPutReturnWith: [
- super visitIRReturn: anIRReturn ]
- !
- visitIRSend: anIRSend
- anIRSend classSend
- ifNil: [
- self stream nextPutAll: '_st('.
- self visit: anIRSend instructions first.
- self stream nextPutAll: ').', anIRSend selector asSelector, '('.
- anIRSend instructions allButFirst
- do: [ :each | self visit: each ]
- separatedBy: [ self stream nextPutAll: ',' ].
- self stream nextPutAll: ')' ]
- ifNotNil: [
- self stream
- nextPutAll: anIRSend classSend asJavascript, '.fn.prototype.';
- nextPutAll: anIRSend selector asSelector, '.apply(';
- nextPutAll: '_st('.
- self visit: anIRSend instructions first.
- self stream nextPutAll: '), ['.
- anIRSend instructions allButFirst
- do: [ :each | self visit: each ]
- separatedBy: [ self stream nextPutAll: ',' ].
- self stream nextPutAll: '])' ]
- !
- visitIRSequence: anIRSequence
- self stream nextPutSequenceWith: [
- anIRSequence instructions do: [ :each |
- self stream nextPutStatementWith: (self visit: each) ]]
- !
- visitIRTempDeclaration: anIRTempDeclaration
- self stream
- nextPutAll: anIRTempDeclaration scope alias, '.locals.', anIRTempDeclaration name, '=nil;';
- lf
- !
- visitIRValue: anIRValue
- self stream nextPutAll: anIRValue value asJavascript
- !
- visitIRVariable: anIRVariable
- anIRVariable variable name = 'thisContext'
- ifTrue: [ self stream nextPutAll: 'smalltalk.getThisContext()' ]
- ifFalse: [ self stream nextPutAll: anIRVariable variable alias ]
- !
- visitIRVerbatim: anIRVerbatim
- self stream nextPutStatementWith: [
- self stream nextPutAll: anIRVerbatim source ]
- ! !
- Object subclass: #JSStream
- instanceVariableNames: 'stream'
- package:'Compiler'!
- !JSStream methodsFor: '*Compiler'!
- contents
- ^ stream contents
- ! !
- !JSStream methodsFor: '*Compiler'!
- initialize
- super initialize.
- stream := '' writeStream.
- ! !
- !JSStream methodsFor: '*Compiler'!
- lf
- stream lf
- !
- nextPut: aString
- stream nextPut: aString
- !
- nextPutAll: aString
- stream nextPutAll: aString
- !
- nextPutAssignment
- stream nextPutAll: '='
- !
- nextPutBlockContextFor: anIRClosure during: aBlock
- self
- nextPutAll: 'return smalltalk.withContext(function(', anIRClosure scope alias, ') { ';
- nextPutAll: String cr.
- aBlock value.
- self nextPutAll: '})'
- !
- nextPutClosureWith: aBlock arguments: anArray
- stream nextPutAll: '(function('.
- anArray
- do: [ :each | stream nextPutAll: each asVariableName ]
- separatedBy: [ stream nextPut: ',' ].
- stream nextPutAll: '){'; lf.
- aBlock value.
- stream nextPutAll: '})'
- !
- nextPutContextFor: aMethod during: aBlock
- self
- nextPutAll: 'return smalltalk.withContext(function(', aMethod scope alias, ') { ';
- nextPutAll: String cr.
- aBlock value.
- self
- nextPutAll: '}, self, ';
- nextPutAll: aMethod selector asJavascript, ', ['.
- aMethod arguments
- do: [ :each | self nextPutAll: each asVariableName ]
- separatedBy: [ self nextPutAll: ',' ].
- self nextPutAll: '], ';
- nextPutAll: aMethod theClass asJavascript;
- nextPutAll: ')'
- !
- nextPutFunctionWith: aBlock arguments: anArray
- stream nextPutAll: 'fn: function('.
- anArray
- do: [ :each | stream nextPutAll: each asVariableName ]
- separatedBy: [ stream nextPut: ',' ].
- stream nextPutAll: '){'; lf.
- stream nextPutAll: 'var self=this;'; lf.
- aBlock value.
- stream nextPutAll: '}'
- !
- nextPutIf: aBlock with: anotherBlock
- stream nextPutAll: 'if('.
- aBlock value.
- stream nextPutAll: '){'; lf.
- anotherBlock value.
- stream nextPutAll: '}'
- !
- nextPutIfElse: aBlock with: ifBlock with: elseBlock
- stream nextPutAll: 'if('.
- aBlock value.
- stream nextPutAll: '){'; lf.
- ifBlock value.
- stream nextPutAll: '} else {'; lf.
- elseBlock value.
- stream nextPutAll: '}'
- !
- nextPutMethodDeclaration: aMethod with: aBlock
- stream
- nextPutAll: 'smalltalk.method({'; lf;
- nextPutAll: 'selector: "', aMethod selector, '",'; lf;
- nextPutAll: 'source: ', aMethod source asJavascript, ',';lf.
- aBlock value.
- stream
- nextPutAll: ',', String lf, 'messageSends: ';
- nextPutAll: aMethod messageSends asArray asJavascript, ','; lf;
- nextPutAll: 'args: ', (aMethod arguments collect: [ :each | each value ]) asArray asJavascript, ','; lf;
- nextPutAll: 'referencedClasses: ['.
- aMethod classReferences
- do: [:each | stream nextPutAll: each asJavascript]
- separatedBy: [stream nextPutAll: ','].
- stream
- nextPutAll: ']';
- nextPutAll: '})'
- !
- nextPutNonLocalReturnHandlingWith: aBlock
- stream
- nextPutAll: 'var $early={};'; lf;
- nextPutAll: 'try {'; lf.
- aBlock value.
- stream
- nextPutAll: '}'; lf;
- nextPutAll: 'catch(e) {if(e===$early)return e[0]; throw e}'; lf
- !
- nextPutNonLocalReturnWith: aBlock
- stream nextPutAll: 'throw $early=['.
- aBlock value.
- stream nextPutAll: ']'
- !
- nextPutReturn
- stream nextPutAll: 'return '
- !
- nextPutReturnWith: aBlock
- self nextPutReturn.
- aBlock value
- !
- nextPutSequenceWith: aBlock
- "stream
- nextPutAll: 'switch(smalltalk.thisContext.pc){'; lf."
- aBlock value.
- "stream
- nextPutAll: '};'; lf"
- !
- nextPutStatement: anInteger with: aBlock
- stream nextPutAll: 'case ', anInteger asString, ':'; lf.
- self nextPutStatementWith: aBlock.
- stream nextPutAll: 'smalltalk.thisContext.pc=', (anInteger + 1) asString, ';'; lf
- !
- nextPutStatementWith: aBlock
- aBlock value.
- stream nextPutAll: ';'; lf
- !
- nextPutVar: aString
- stream nextPutAll: 'var ', aString, ';'; lf
- !
- nextPutVars: aCollection
- stream nextPutAll: 'var '.
- aCollection
- do: [ :each | stream nextPutAll: each ]
- separatedBy: [ stream nextPutAll: ',' ].
- stream nextPutAll: ';'; lf
- ! !
- !BlockClosure methodsFor: '*Compiler'!
- appendToInstruction: anIRInstruction
- anIRInstruction appendBlock: self
- ! !
- !String methodsFor: '*Compiler'!
- asVariableName
- ^ (Smalltalk current reservedWords includes: self)
- ifTrue: [ self, '_' ]
- ifFalse: [ self ]
- ! !
- IRAssignment subclass: #IRInlinedAssignment
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedAssignment commentStamp!
- I represent an inlined assignment instruction.!
- !IRInlinedAssignment methodsFor: '*Compiler'!
- isInlined
- ^ true
- ! !
- !IRInlinedAssignment methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRInlinedAssignment: self
- ! !
- IRClosure subclass: #IRInlinedClosure
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedClosure commentStamp!
- I represent an inlined closure instruction.!
- !IRInlinedClosure methodsFor: '*Compiler'!
- isInlined
- ^ true
- ! !
- !IRInlinedClosure methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitIRInlinedClosure: self
- ! !
- IRReturn subclass: #IRInlinedReturn
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedReturn commentStamp!
- I represent an inlined local return instruction.!
- !IRInlinedReturn methodsFor: '*Compiler'!
- isInlined
- ^ true
- ! !
- !IRInlinedReturn methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRInlinedReturn: self
- ! !
- IRInlinedReturn subclass: #IRInlinedNonLocalReturn
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedNonLocalReturn commentStamp!
- I represent an inlined non local return instruction.!
- !IRInlinedNonLocalReturn methodsFor: '*Compiler'!
- isInlined
- ^ true
- ! !
- !IRInlinedNonLocalReturn methodsFor: '*Compiler'!
- accept: aVisitor
- ^ aVisitor visitIRInlinedNonLocalReturn: self
- ! !
- IRSend subclass: #IRInlinedSend
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedSend commentStamp!
- I am the abstract super class of inlined message send instructions.!
- !IRInlinedSend methodsFor: '*Compiler'!
- isInlined
- ^ true
- ! !
- !IRInlinedSend methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitInlinedSend: self
- ! !
- IRInlinedSend subclass: #IRInlinedIfFalse
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedIfFalse methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitIRInlinedIfFalse: self
- ! !
- IRInlinedSend subclass: #IRInlinedIfNilIfNotNil
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedIfNilIfNotNil methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitIRInlinedIfNilIfNotNil: self
- ! !
- IRInlinedSend subclass: #IRInlinedIfTrue
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedIfTrue methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitIRInlinedIfTrue: self
- ! !
- IRInlinedSend subclass: #IRInlinedIfTrueIfFalse
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedIfTrueIfFalse methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitIRInlinedIfTrueIfFalse: self
- ! !
- IRBlockSequence subclass: #IRInlinedSequence
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInlinedSequence commentStamp!
- I represent a (block) sequence inside an inlined closure instruction (instance of `IRInlinedClosure`).!
- !IRInlinedSequence methodsFor: '*Compiler'!
- isInlined
- ^ true
- ! !
- !IRInlinedSequence methodsFor: '*Compiler'!
- accept: aVisitor
- aVisitor visitIRInlinedSequence: self
- ! !
- IRVisitor subclass: #IRInliner
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInliner commentStamp!
- I visit an IR tree, inlining message sends and block closures.
- Message selectors that can be inlined are answered by `IRSendInliner >> #inlinedSelectors`!
- !IRInliner methodsFor: '*Compiler'!
- assignmentInliner
- ^ IRAssignmentInliner new
- translator: self;
- yourself
- !
- nonLocalReturnInliner
- ^ IRNonLocalReturnInliner new
- translator: self;
- yourself
- !
- returnInliner
- ^ IRReturnInliner new
- translator: self;
- yourself
- !
- sendInliner
- ^ IRSendInliner new
- translator: self;
- yourself
- ! !
- !IRInliner methodsFor: '*Compiler'!
- shouldInlineAssignment: anIRAssignment
- ^ anIRAssignment isInlined not and: [
- anIRAssignment instructions last isSend and: [
- self shouldInlineSend: (anIRAssignment instructions last) ]]
- !
- shouldInlineReturn: anIRReturn
- ^ anIRReturn isInlined not and: [
- anIRReturn instructions first isSend and: [
- self shouldInlineSend: (anIRReturn instructions first) ]]
- !
- shouldInlineSend: anIRSend
- ^ anIRSend isInlined not and: [
- IRSendInliner shouldInline: anIRSend ]
- ! !
- !IRInliner methodsFor: '*Compiler'!
- transformNonLocalReturn: anIRNonLocalReturn
- "Replace a non local return into a local return"
- | localReturn |
- anIRNonLocalReturn scope canInlineNonLocalReturns ifTrue: [
- anIRNonLocalReturn scope methodScope removeNonLocalReturn: anIRNonLocalReturn scope.
- localReturn := IRReturn new
- scope: anIRNonLocalReturn scope;
- yourself.
- anIRNonLocalReturn instructions do: [ :each |
- localReturn add: each ].
- anIRNonLocalReturn replaceWith: localReturn.
- ^ localReturn ].
- ^ super visitIRNonLocalReturn: anIRNonLocalReturn
- !
- visitIRAssignment: anIRAssignment
- ^ (self shouldInlineAssignment: anIRAssignment)
- ifTrue: [ self assignmentInliner inlineAssignment: anIRAssignment ]
- ifFalse: [ super visitIRAssignment: anIRAssignment ]
- !
- visitIRNonLocalReturn: anIRNonLocalReturn
- ^ (self shouldInlineReturn: anIRNonLocalReturn)
- ifTrue: [ self nonLocalReturnInliner inlineReturn: anIRNonLocalReturn ]
- ifFalse: [ self transformNonLocalReturn: anIRNonLocalReturn ]
- !
- visitIRReturn: anIRReturn
- ^ (self shouldInlineReturn: anIRReturn)
- ifTrue: [ self returnInliner inlineReturn: anIRReturn ]
- ifFalse: [ super visitIRReturn: anIRReturn ]
- !
- visitIRSend: anIRSend
- ^ (self shouldInlineSend: anIRSend)
- ifTrue: [ self sendInliner inlineSend: anIRSend ]
- ifFalse: [ super visitIRSend: anIRSend ]
- ! !
- IRJSTranslator subclass: #IRInliningJSTranslator
- instanceVariableNames: ''
- package:'Compiler'!
- !IRInliningJSTranslator commentStamp!
- I am a specialized JavaScript translator able to write inlined IR instructions to JavaScript stream (`JSStream` instance).!
- !IRInliningJSTranslator methodsFor: '*Compiler'!
- visitIRInlinedAssignment: anIRInlinedAssignment
- self visit: anIRInlinedAssignment instructions last
- !
- visitIRInlinedClosure: anIRInlinedClosure
- anIRInlinedClosure instructions do: [ :each |
- self visit: each ]
- !
- visitIRInlinedIfFalse: anIRInlinedIfFalse
- self stream nextPutIf: [
- self stream nextPutAll: '!! smalltalk.assert('.
- self visit: anIRInlinedIfFalse instructions first.
- self stream nextPutAll: ')' ]
- with: [ self visit: anIRInlinedIfFalse instructions last ]
- !
- visitIRInlinedIfNil: anIRInlinedIfNil
- self stream nextPutIf: [
- self stream nextPutAll: '($receiver = '.
- self visit: anIRInlinedIfNil instructions first.
- self stream nextPutAll: ') == nil || $receiver == undefined' ]
- with: [ self visit: anIRInlinedIfNil instructions last ]
- !
- visitIRInlinedIfNilIfNotNil: anIRInlinedIfNilIfNotNil
- self stream
- nextPutIfElse: [
- self stream nextPutAll: '($receiver = '.
- self visit: anIRInlinedIfNilIfNotNil instructions first.
- self stream nextPutAll: ') == nil || $receiver == undefined' ]
- with: [ self visit: anIRInlinedIfNilIfNotNil instructions second ]
- with: [ self visit: anIRInlinedIfNilIfNotNil instructions third ]
- !
- visitIRInlinedIfTrue: anIRInlinedIfTrue
- self stream nextPutIf: [
- self stream nextPutAll: 'smalltalk.assert('.
- self visit: anIRInlinedIfTrue instructions first.
- self stream nextPutAll: ')' ]
- with: [ self visit: anIRInlinedIfTrue instructions last ]
- !
- visitIRInlinedIfTrueIfFalse: anIRInlinedIfTrueIfFalse
- self stream
- nextPutIfElse: [
- self stream nextPutAll: 'smalltalk.assert('.
- self visit: anIRInlinedIfTrueIfFalse instructions first.
- self stream nextPutAll: ')' ]
- with: [ self visit: anIRInlinedIfTrueIfFalse instructions second ]
- with: [ self visit: anIRInlinedIfTrueIfFalse instructions third ]
- !
- visitIRInlinedNonLocalReturn: anIRInlinedReturn
- self stream nextPutStatementWith: [
- self visit: anIRInlinedReturn instructions last ].
- self stream nextPutNonLocalReturnWith: [ ]
- !
- visitIRInlinedReturn: anIRInlinedReturn
- self visit: anIRInlinedReturn instructions last
- !
- visitIRInlinedSequence: anIRInlinedSequence
- anIRInlinedSequence instructions do: [ :each |
- self stream nextPutStatementWith: [ self visit: each ]]
- ! !
- Object subclass: #IRSendInliner
- instanceVariableNames: 'send translator'
- package:'Compiler'!
- !IRSendInliner commentStamp!
- I inline some message sends and block closure arguments. I heavily rely on #perform: to dispatch inlining methods.!
- !IRSendInliner methodsFor: '*Compiler'!
- send
- ^ send
- !
- send: anIRSend
- send := anIRSend
- !
- translator
- ^ translator
- !
- translator: anASTTranslator
- translator := anASTTranslator
- ! !
- !IRSendInliner methodsFor: '*Compiler'!
- inliningError: aString
- InliningError signal: aString
- ! !
- !IRSendInliner methodsFor: '*Compiler'!
- inlinedClosure
- ^ IRInlinedClosure new
- !
- inlinedSequence
- ^ IRInlinedSequence new
- ! !
- !IRSendInliner methodsFor: '*Compiler'!
- ifFalse: anIRInstruction
- ^ self inlinedSend: IRInlinedIfFalse new with: anIRInstruction
- !
- ifFalse: anIRInstruction ifTrue: anotherIRInstruction
- ^ self perform: #ifTrue:ifFalse: withArguments: { anotherIRInstruction. anIRInstruction }
- !
- ifNil: anIRInstruction
- ^ self
- inlinedSend: IRInlinedIfNilIfNotNil new
- with: anIRInstruction
- with: (IRClosure new
- scope: anIRInstruction scope copy;
- add: (IRBlockSequence new
- add: self send instructions first;
- yourself);
- yourself)
- !
- ifNil: anIRInstruction ifNotNil: anotherIRInstruction
- ^ self inlinedSend: IRInlinedIfNilIfNotNil new with: anIRInstruction with: anotherIRInstruction
- !
- ifNotNil: anIRInstruction
- ^ self
- inlinedSend: IRInlinedIfNilIfNotNil new
- with: (IRClosure new
- scope: anIRInstruction scope copy;
- add: (IRBlockSequence new
- add: self send instructions first;
- yourself);
- yourself)
- with: anIRInstruction
- !
- ifNotNil: anIRInstruction ifNil: anotherIRInstruction
- ^ self inlinedSend: IRInlinedIfNilIfNotNil new with: anotherIRInstruction with: anIRInstruction
- !
- ifTrue: anIRInstruction
- ^ self inlinedSend: IRInlinedIfTrue new with: anIRInstruction
- !
- ifTrue: anIRInstruction ifFalse: anotherIRInstruction
- ^ self inlinedSend: IRInlinedIfTrueIfFalse new with: anIRInstruction with: anotherIRInstruction
- !
- inlineClosure: anIRClosure
- | inlinedClosure sequence statements |
- inlinedClosure := self inlinedClosure.
- inlinedClosure scope: anIRClosure scope.
- "Add the possible temp declarations"
- anIRClosure instructions do: [ :each |
- each isSequence ifFalse: [
- inlinedClosure add: each ]].
- "Add a block sequence"
- sequence := self inlinedSequence.
- inlinedClosure add: sequence.
- "Get all the statements"
- statements := anIRClosure instructions last instructions.
-
- statements ifNotEmpty: [
- statements allButLast do: [ :each | sequence add: each ].
- "Inlined closures don't have implicit local returns"
- (statements last isReturn and: [ statements last isBlockReturn ])
- ifTrue: [ sequence add: statements last instructions first ]
- ifFalse: [ sequence add: statements last ] ].
- ^ inlinedClosure
- !
- inlineSend: anIRSend
- self send: anIRSend.
- ^ self
- perform: self send selector
- withArguments: self send instructions allButFirst
- !
- inlinedSend: inlinedSend with: anIRInstruction
- | inlinedClosure |
- anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
- anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
- inlinedClosure := self translator visit: (self inlineClosure: anIRInstruction).
- inlinedSend
- add: self send instructions first;
- add: inlinedClosure.
- self send replaceWith: inlinedSend.
- ^ inlinedSend
- !
- inlinedSend: inlinedSend with: anIRInstruction with: anotherIRInstruction
- | inlinedClosure1 inlinedClosure2 |
- anIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
- anIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
- anotherIRInstruction isClosure ifFalse: [ self inliningError: 'Message argument should be a block' ].
- anotherIRInstruction arguments size = 0 ifFalse: [ self inliningError: 'Inlined block should have zero argument' ].
- inlinedClosure1 := self translator visit: (self inlineClosure: anIRInstruction).
- inlinedClosure2 := self translator visit: (self inlineClosure: anotherIRInstruction).
- inlinedSend
- add: self send instructions first;
- add: inlinedClosure1;
- add: inlinedClosure2.
- self send replaceWith: inlinedSend.
- ^ inlinedSend
- ! !
- !IRSendInliner class methodsFor: '*Compiler'!
- inlinedSelectors
- ^ #('ifTrue:' 'ifFalse:' 'ifTrue:ifFalse:' 'ifFalse:ifTrue:' 'ifNil:' 'ifNotNil:' 'ifNil:ifNotNil:' 'ifNotNil:ifNil')
- !
- shouldInline: anIRInstruction
- (self inlinedSelectors includes: anIRInstruction selector) ifFalse: [ ^ false ].
- anIRInstruction instructions allButFirst do: [ :each |
- each isClosure ifFalse: [ ^ false ]].
- ^ true
- ! !
- IRSendInliner subclass: #IRAssignmentInliner
- instanceVariableNames: 'assignment'
- package:'Compiler'!
- !IRAssignmentInliner commentStamp!
- I inline message sends together with assignments by moving them around into the inline closure instructions.
- ##Example
- foo
- | a |
- a := true ifTrue: [ 1 ]
- Will produce:
- if(smalltalk.assert(true) {
- a = 1;
- };!
- !IRAssignmentInliner methodsFor: '*Compiler'!
- assignment
- ^ assignment
- !
- assignment: aNode
- assignment := aNode
- ! !
- !IRAssignmentInliner methodsFor: '*Compiler'!
- inlineAssignment: anIRAssignment
- | inlinedAssignment |
- self assignment: anIRAssignment.
- inlinedAssignment := IRInlinedAssignment new.
- anIRAssignment instructions do: [ :each |
- inlinedAssignment add: each ].
- anIRAssignment replaceWith: inlinedAssignment.
- self inlineSend: inlinedAssignment instructions last.
- ^ inlinedAssignment
- !
- inlineClosure: anIRClosure
- | inlinedClosure statements |
- inlinedClosure := super inlineClosure: anIRClosure.
- statements := inlinedClosure instructions last instructions.
-
- statements ifNotEmpty: [
- statements last canBeAssigned ifTrue: [
- statements last replaceWith: (IRAssignment new
- add: self assignment instructions first;
- add: statements last copy;
- yourself) ] ].
- ^ inlinedClosure
- ! !
- IRSendInliner subclass: #IRNonLocalReturnInliner
- instanceVariableNames: ''
- package:'Compiler'!
- !IRNonLocalReturnInliner methodsFor: '*Compiler'!
- inlinedReturn
- ^ IRInlinedNonLocalReturn new
- ! !
- !IRNonLocalReturnInliner methodsFor: '*Compiler'!
- inlineClosure: anIRClosure
- "| inlinedClosure statements |
- inlinedClosure := super inlineClosure: anIRClosure.
- statements := inlinedClosure instructions last instructions.
-
- statements ifNotEmpty: [
- statements last replaceWith: (IRNonLocalReturn new
- add: statements last copy;
- yourself) ].
- ^ inlinedClosure"
- ^ super inlineCLosure: anIRClosure
- ! !
- IRSendInliner subclass: #IRReturnInliner
- instanceVariableNames: ''
- package:'Compiler'!
- !IRReturnInliner commentStamp!
- I inline message sends with inlined closure together with a return instruction.!
- !IRReturnInliner methodsFor: '*Compiler'!
- inlinedReturn
- ^ IRInlinedReturn new
- ! !
- !IRReturnInliner methodsFor: '*Compiler'!
- inlineClosure: anIRClosure
- | closure statements |
- closure := super inlineClosure: anIRClosure.
- statements := closure instructions last instructions.
-
- statements ifNotEmpty: [
- statements last isReturn
- ifFalse: [ statements last replaceWith: (IRReturn new
- add: statements last copy;
- yourself)] ].
- ^ closure
- !
- inlineReturn: anIRReturn
- | return |
- return := self inlinedReturn.
- anIRReturn instructions do: [ :each |
- return add: each ].
- anIRReturn replaceWith: return.
- self inlineSend: return instructions last.
- ^ return
- ! !
- CodeGenerator subclass: #InliningCodeGenerator
- instanceVariableNames: ''
- package:'Compiler'!
- !InliningCodeGenerator commentStamp!
- I am a specialized code generator that uses inlining to produce more optimized JavaScript output!
- !InliningCodeGenerator methodsFor: '*Compiler'!
- compileNode: aNode
- | ir stream |
- self semanticAnalyzer visit: aNode.
- ir := self translator visit: aNode.
- self inliner visit: ir.
- ^ self irTranslator
- visit: ir;
- contents
- !
- inliner
- ^ IRInliner new
- !
- irTranslator
- ^ IRInliningJSTranslator new
- ! !
- NodeVisitor subclass: #AIContext
- instanceVariableNames: 'outerContext pc locals receiver selector'
- package:'Compiler'!
- !AIContext methodsFor: '*Compiler'!
- initializeFromMethodContext: aMethodContext
- self pc: aMethodContext pc.
- self receiver: aMethodContext receiver.
- self selector: aMethodContext selector.
- aMethodContext outerContext ifNotNil: [
- self outerContext: (self class fromMethodContext: aMethodContext outerContext) ].
- aMethodContext locals keysAndValuesDo: [ :key :value |
- self locals at: key put: value ]
- !
- locals
- ^ locals ifNil: [ locals := Dictionary new ]
- !
- outerContext
- ^ outerContext
- !
- outerContext: anAIContext
- outerContext := anAIContext
- !
- pc
- ^ pc ifNil: [ pc := 0 ]
- !
- pc: anInteger
- pc := anInteger
- !
- receiver
- ^ receiver
- !
- receiver: anObject
- receiver := anObject
- !
- selector
- ^ selector
- !
- selector: aString
- selector := aString
- ! !
- !AIContext class methodsFor: '*Compiler'!
- fromMethodContext: aMethodContext
- ^ self new
- initializeFromMethodContext: aMethodContext;
- yourself
- ! !
- NodeVisitor subclass: #ASTInterpreter
- instanceVariableNames: 'currentNode context shouldReturn'
- package:'Compiler'!
- !ASTInterpreter methodsFor: '*Compiler'!
- context
- ^ context
- !
- context: anAIContext
- context := anAIContext
- ! !
- !ASTInterpreter methodsFor: '*Compiler'!
- initialize
- super initialize.
- shouldReturn := false
- ! !
- !ASTInterpreter methodsFor: '*Compiler'!
- interpret: aNode
- shouldReturn := false.
- ^ self interpretNode: aNode
- !
- interpretNode: aNode
- currentNode := aNode.
- ^ self visit: aNode
- !
- messageFromSendNode: aSendNode
- ^ Message new
- selector: aSendNode selector;
- arguments: (aSendNode arguments collect: [ :each |
- self interpretNode: each ]);
- yourself
- ! !
- !ASTInterpreter methodsFor: '*Compiler'!
- visitBlockNode: aNode
- ^ [ self interpretNode: aNode nodes first ]
- !
- visitCascadeNode: aNode
- "TODO: Handle super sends"
- | receiver |
-
- receiver := self interpretNode: aNode receiver.
- aNode nodes allButLast
- do: [ :each |
- (self messageFromSendNode: each)
- sendTo: receiver ].
- ^ (self messageFromSendNode: aNode nodes last)
- sendTo: receiver
- !
- visitClassReferenceNode: aNode
- ^ Smalltalk current at: aNode value
- !
- visitJSStatementNode: aNode
- self halt
- !
- visitReturnNode: aNode
- shouldReturn := true.
- ^ self interpretNode: aNode nodes first
- !
- visitSendNode: aNode
- "TODO: Handle super sends"
-
- ^ (self messageFromSendNode: aNode)
- sendTo: (self interpretNode: aNode receiver)
- !
- visitSequenceNode: aNode
- aNode nodes allButLast do: [ :each | | value |
- value := self interpretNode: each.
- shouldReturn ifTrue: [ ^ value ] ].
- ^ self interpretNode: aNode nodes last
- !
- visitValueNode: aNode
- ^ aNode value
- ! !
|