The VM Already Knew That LeveragingCompile-TimeKnowledgetoOptimizeGradualTyping GREGORRICHARDS,UniversityofWaterloo,Canada,Canada ELLENARTECA,UniversityofWaterloo,Canada,Canada ALEXITURCOTTE,UniversityofWaterloo,Canada,Canada Programmersindynamiclanguageswishingtoconstrainandunderstandthebehavioroftheirprograms mayturntogradually-typedlanguages,whichallowtypestobespecifiedoptionallyandcheckvaluesatthe boundarybetweendynamicandstaticcode.Unfortunately,theperformancecostoftheserun-timecheckscan besevere,slowingdownexecutionbyatleast10xwhenchecksarepresent.Modernvirtualmachines(VMs) fordynamiclanguagesusespeculativetechniquestoimproveperformance:Ifaparticularvaluewasseenonce, itislikelythatsimilarvalueswillbeseeninthefuture.Theycombineoptimization-relevantpropertiesof valuesintocacheablełshapesž,thenuseasingleshapechecktosubsumechecksforeachproperty.Valueswith thesamememorylayoutorthesamefieldtypeshavethesameshape.Thisgreatlyreducestheamountoftype checkingthatneedstobeperformedatrun-timetoexecutedynamiccode.WhileveryvaluabletotheVM’s optimization,thesechecksdolittletobenefittheprogrammerasidefromimprovingperformance.Wepresent inthispaperadesignforintrinsicobjectcontracts,whichmakestheobligationsofgradually-typedlanguages’ typechecksanintrinsicpartofobjectshapes,andthuscansubsumerun-timetypechecksintoexistingshape checks,eliminatingredundantchecksentirely.WithanimplementationonaVMforJavaScriptusedasa targetforSafeTypeScript’ssoundnessguarantees,wedemonstrateslowdownaveraging7%infully-typed coderelativetouncheckedcode,andnomorethan45%inpessimalconfigurations. CCSConcepts:•Softwareanditsengineering→Just-in-timecompilers;Runtimeenvironments; AdditionalKeyWordsandPhrases:Gradualtyping,run-timetypechecking ACMReferenceFormat: GregorRichards,EllenArteca,andAlexiTurcotte.2017.TheVMAlreadyKnewThat:LeveragingCompile- TimeKnowledgetoOptimizeGradualTyping.Proc.ACMProgram.Lang.1,OOPSLA,Article55(October2017), 27pages.https://doi.org/10.1145/3133879 1 INTRODUCTION Dynamiclanguagesprovideflexibility,andmodernimplementationsofdynamiclanguagesare quiteefficient.However,thedynamicnatureoftheselanguagesmakesitdifficultforaprogrammer tostaticallyconstraintheirbehavior.Forexample,afunctionmaybewritteninanticipationofa numberasanargument,butcanbecalledwithanobject,whichwillresultinaruntimetypeerror. Programmers wishing to understand or constrain run-time behavior in dynamic languages havetheoptiontousegradually-typedprogramminglanguages.Gradually-typedlanguagesare Authors’addresses:GregorRichards,SchoolofComputerScience,UniversityofWaterloo,Canada,200UniversityAvenue West,Waterloo,Ontario,N2L3G1,Canada,[email protected];EllenArteca,SchoolofComputerScience, UniversityofWaterloo,Canada,200UniversityAvenueWest,Waterloo,Ontario,N2L3G1,Canada,[email protected]; AlexiTurcotte,SchoolofComputerScience,UniversityofWaterloo,Canada,200UniversityAvenueWest,Waterloo,Ontario, N2L3G1,Canada,[email protected]. 55 Permissiontomakedigitalorhardcopiesofpartorallofthisworkforpersonalorclassroomuseisgrantedwithoutfee providedthatcopiesarenotmadeordistributedforprofitorcommercialadvantageandthatcopiesbearthisnoticeand thefullcitationonthefirstpage.Copyrightsforthird-partycomponentsofthisworkmustbehonored.Forallotheruses, Tcohnistawctorthkeisolwicneenrs/eaduuthnodre(rs)a.CreativeCommonsAttribution4.0InternationalLicense. ©2017Copyrightheldbytheowner/author(s). 2475-1421/2017/10-ART55 https://doi.org/10.1145/3133879 ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. 55:2 GregorRichards,EllenArteca,andAlexiTurcotte typicallycompatibleextensionsofexistingdynamically-typedlanguages,whichallowtypedand untypedcodetointeractwhilestillprovidingsometypesoundnessguarantees.Typically,run-time checksareusedtoverifytypeswhenvaluescomefromdynamicsources,andsocan’tbetrusted. Implementingthesechecksfacesasemanticmismatch,however:Typesmaybearbitrarilycomplex, andindeedrecursive,andthuscannotbecheckedquickly.Somegradually-typedlanguagesresolve this by restricting the type system in such a way that checks are always fast [Richards et al. 2015][Bloometal.2009],butthisnaturallyrestrictstheexpressibilityoftypes.Othersworkby delayingchecksuntilafirst-order,easilycheckablevalueisobtained. Inatypicalimplementation,first-ordertypecheckingisimposedonhigher-ordervaluesthrough run-timecontracts.Run-timecontractsinterposeonvaluestocheckallaccesses,guaranteeingthat theconstraintsofthespecifiedtypesarenotviolated.Checkinghasprovedtobeamajoroverhead ingraduallytypedlanguages[Takikawaetal.2016];dependingonwherecheckingisnecessary,the additionofcontractscanslowdowntheexecutionofsoftwareby100times.Otherimplementation techniqueshavebeenused,whichcanmovethecostofchecking,butslowdownsof10x[Vitousek etal.2014]to22x[Rastogietal.2015]arestillreportedwhenstaticanddynamiccodeinteracts. TheseslowdownshaveledTakikawaetal.[2016]toask,łissoundgradualtypingdead?žThegoal ofthisworkistodemonstratethatwithsomehelpfromthevirtualmachine,soundgradualtyping withusableperformanceisstillpractical. Modern virtual machines (VMs) for dynamic languages optimize based on speculation: If a particularvaluewasseenonce,itislikelythatsimilarvalueswillbeseeninthefuture.Thisis possible because of just-in-time (JIT) compilation, which blurs the line between compile-time andrun-time,andallowsthecompilertoutilizerun-timeinformation.Codemaybecompiledor recompiledoncecrucialinformationisknown.ThecodegeneratedbyJITschecksthatcertain propertiesofvaluesaresimilartopreviously-seenvalues,andiftheyare,runsbranchesoptimized tothoseassumptions. Inparticular,dynamiclanguageVMsoptimizeaccesstoobjectfieldsbygivingeveryobjecta łshapež,andassuringthatsimilarobjectshavesimilarshapes.Allobjectswiththesameshapehave thesamefields,andarelaidoutidenticallyinmemory,sooptimizingbasedontheshapeallows suchobjectstobeaccessedefficiently. SpeculativeJIToptimizationsandgradualtypechecksareoftencheckingsimilarproperties,but theyareeffectivelyunawareofeachother.Inthebestcase,theVMmaybeabletospeculatebased oncontractsuccess,butnothingmore.Thegoalofthisworkistoimprovetheperformanceofrun- timecontractsforgradually-typedprogramminglanguagesbyleveragingtheexistingspeculative optimizationframeworkinJITs,andtakingadvantageoftheredundanciesbetweenthesesystems. Thispaperillustratesthedesignofacontractsystemsufficientforthesoundnesschecksrequired bySafeTypeScript,agradually-typedlanguagebasedonJavaScript,andaVMwhichimplements thesecontractswithoptimizationstosubstantiallyreducethecostofrun-timetypechecking.We callthisstyleofcontractsintrinsicobjectcontracts,andimplementitintheHiggs[Chevalier-Boisvert and Feeley 2016] [Chevalier-Boisvert and Feeley 2015] JavaScript research virtual machine, as HiggsCheck.WedemonstratetheefficacyofthissystembyreimplementingSafeTypeScript[Rastogi etal.2015]toprovideitsguaranteeswithourcontractsystem,andshowusingSafeTypeScript’s benchmarks that we achieve slowdown of 7% in the common case, and no worse than 45% in intentionallypessimalconfigurations,ascomparedto22xslowdownsintheoriginalimplementation ofSafeTypeScript.ThepresentedimplementationisonaJavaScript-basedlanguageonaJavaScript virtualmachine,butthetechniquedoesnotdependdeeplyonJavaScript’ssemantics. Thecontributionsofthispaperare • thedesignofanintrinsiccontractsystemattheVMlevel, ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. TheVMAlreadyKnewThat 55:3 • atechniqueforreducingtheoverheadofcheckingofsuchcontracts, • theimplementationofthatsystemontheHiggsvirtualmachine, • theimplementationofSafeTypeScript’ssoundnessguaranteesonthesecontracts, • anevaluation,demonstratingthattheoverheadofthiscontractsystemispractical. Thecompletesourcecodeofthissystemisavailableathttp://plg.uwaterloo.ca/~dynjs/higgscheck/. 2 BACKGROUNDANDGOALS Wewilldemonstratethegoalsofthissystemthroughasimplerunningexample.Considerthe followingJavaScriptcode,whichsumsthevaluesinalistofnumbers: function sum(node) { if (node.next) return sum(node.next) + node.item; else return node.item; } Itisevidentfromthiscodethatnodeisexpectedtobeanobject,withfieldsnextasareferenceto asimilarobjectornull,andfielditemasanumber.Calledwiththeobject{next: {next: null, item: 40}, item: 2}, sum will return 42. However, due to the dynamic nature and peculiar semanticsofJavaScript,inpracticethisfunctionwilldosomethingÐalbeitperhapssomething unpredictableÐwithmanyinputobjects.Forinstance,calledwith{next: 42},whichisobviously notanodeinalist,sumwillreturnNaN(NotaNumber),aconsequenceoftheoddbehaviorof accessingandperformingarithmeticwithnonexistentfields. 2.1 OptionalandGradualTyping AprogrammerwishingtomaketheirintentsclearermayinsteaduseTypeScript[Biermanetal. 2014],alanguagewhichextends,andcompilesinto,JavaScript,addingtypeannotations.InType- Script,thisfunctionanditsexpectedtypecanbeexpressedas: interface Node { function sum(node: Node) { next: Node; if (node.next) item: number; return sum(node.next) + node.item; } else return node.item; } Now,thecallsum({next: 42})willfailtocompile,withanappropriatetypeerror:Argument oftype'{ next: number; }'isnotassignabletoparameteroftype'Node'.However,TypeScript isastrictlycompile-timetypechecker,andisdesignedtointeractwithuntypedcode,soit’strivial toworkaroundthistyperestrictionbyeithercallingsumfromuntypedcode,orusingthespecial łanyžtypetobypasschecking.Indeed,theJavaScriptcodeaboveisthecompiledoutputofthis TypeScriptsnippet.Thisstyleoftypingisoftencalledoptionaltyping,whichimpliesthatthetypes are,ofcourse,optional,butalsothattherearenorun-timeguaranteesthattheexpressedtypes arecorrect.TypeScriptisintentionallyunsound,allowinguncheckeddowncastsandcovariant functionoverloading,butithasasoundcore,andfully-typedprogramswritteninthatsoundcore aretypesound[Biermanetal.2014]. Graduallytypedlanguagesregainsomeoftheassuranceofstaticallytypedlanguages,while maintainingtheflexibilityofinteractingwithdynamiccode.Theydefineadistinctconsistency relation,bywhichatypemaybeconsistentevenifitisnotasubtype,andtheconsistencyrelation isusedtoallowdynamicandstaticcodetointeract.Typically,asingledynamictype(inTypeScript, ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. 55:4 GregorRichards,EllenArteca,andAlexiTurcotte any)isconsistentwithalltypes,andstructuralorhigher-ordertypesmustbeconsistentintheir typemembers.Intermsofdynamicsemantics,thetypecorrectnessofthisrelationshipisenforced byinjectingunsoundcasts,andimplementingthosecastsasrun-timechecks.Thestandardcriteria bywhichtheircorrectnessismeasuredisthataddingtypesdoesnotaffectbehaviorintheabsence ofunsoundcasts,andinthepresenceofunsoundcasts,errorsarealwaysthefaultofdynamic code[Sieketal.2015a][WadlerandFindler2009]. Thecaveatishowoneperformsrun-timecheckswithhigher-ordertypes.Languagessuchas StrongScript[Richardsetal.2015]andThorn[Bloometal.2009]accomplishthisbyrestricting thetypesystemtonominal,Java-likeclasses,suchthatalltypecheckscanbeperformedeagerly, orbyallowingtheprogrammertoexplicitlyspecifywhethercheckingisdesired[Wrigstadetal. 2010],avoidingbehavioralchangeswhencheckingisnotperformed.Moregeneralapproaches, suchasTypedRacket[Tobin-HochstadtandFelleisen2008],ReticulatedPython[Vitouseketal. 2014]and,relevanttothiswork,SafeTypeScript[Rastogietal.2015],performtheircheckslazily: Whenafirst-order,primitivevalueisexpected,itischeckedimmediately,butotherchecksare delayed.Thisapproachallowsforhigher-ordertypes,suchasstructuralobjecttypesandfirst-class functions,inawaythatnominaltypescannot,anddoesnottypicallyinterferewiththebehavior oftheunderlyingdynamiclanguageexcepttointroducechecks. Threeprincipletechniqueshaveemergedfordelayedrun-timechecksingradually-typedlan- guages, each with their own benefits and disadvantages. In each case, if a value undergoes an unsoundcasttoafirst-order,primitivetype,itissimplycheckedeagerly,butcastingtoahigher- ordertypeinvokessomeformofrun-timeprotection. TypedRacketandReticulatedPython(inoneconfiguration)usewrappers,orproxies.These wrappersimplementageneralcontract,whichcouldnotionallyverifyanypropertyexpressiblein thehostlanguage,butaretypicallyusedfortypes.Therelationshipbetweencontractsandtypes hasbeenexploredforbothobjects[FindlerandFelleisen2001]andfunctions[FindlerandFelleisen 2002].Inawrapper-basedgradually-typedsystem,everytimeavalueisobtainedwhichmaynot havethecorrecttype,itiswrappedinafunctionorobjectwhichprotectsitandchecksaccesses. Thiswrapperimplementsarun-timecontract,guaranteeingthatthevalue’sbehaviormatchesthe specifiedtype.Forobjects,forinstance,eachfieldisimplementedasanaccessorfunction.Ifthe field’stypeisitselfhigher-order,thisaccessorwrapsit;ifitisfirst-order,theaccessorchecksit. Further,thesewrapperscontainblameinformation,i.e.,alabelofwherethewrapperitselfwas applied.Becausechecksaredelayed,ifacheckfails,theblameinformationstoredinthewrapper willultimatelytelltheprogrammerwheretheincorrectcastwasperformed,ratherthanthepoint wheretheerroroccurred. Usingthewrappingtechniqueputsthecostofbothallocationofwrappersandcheckingon bothstatically-typedanddynamically-typedcode,foranyvaluewhichhasbeendowncast.Thus, fully-typedcodeincursnocost,andfully-untypedcodealsoincursnocost,butmixedcodecan havesevereslowdown:Whenstaticanddynamiccodeismixed,TypedRacketreportsslowdowns upto100x[Takikawaetal.2016]. ReticulatedPythonintroducedtwoothersemantics:transientchecksandmonotonicobjects.The techniqueoftransientcheckssimplyexplicitlychecksprimitivetypeswhenthey’reexpected.That is,nocheckwouldbeperformedtoverifythatanobjectisaNode(exceptperhapsthatitisan object),butiftheitemmemberofaNode-typedvariableisaccessed,itstypeischecked.Transient checkingcannotpreserveblameinformation,ashigher-orderdowncastshavenorun-timebehavior. It’slikelythattransientcheckscouldbemadetoworkwellwiththeVM,butifthey’repurely superficial,theyalsocausesignificantslowdowns.ReticulatedPythonreportsslowdownsof10xin thisconfiguration[Vitouseketal.2014]. ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. TheVMAlreadyKnewThat 55:5 Reticulated Python’s third mode, monotonic objects, assures that values in the heap which undergounsoundcastsmayonlymonotonicallybecomemorepreciselytypedandtheirbehavior moreconstrained.Solongasobjectsmonotonicallybecomemorepreciselytyped,staticcodecan befreefromperformingchecks:Staticcodeaccessesmembersdirectly,assuredthattheywillbe correctlytyped,whiledynamiccodeisredirectedtoperformchecks,thusnotsullyingtheobjects thatstaticcodeexpects.Thus,adirectimplementationofmonotonicobjectsmakesstaticcodevery efficient,butataseverecosttodynamiccode.Themonotonicsemanticshasbeenshowntobe soundandcorrectwithrespecttoblame[Sieketal.2015b].ReticulatedPythonreportsslowdowns of3.4xofmonotonicobjectsovertransientchecks,soapproximately34xofmonotonicobjectsover uncheckedcode. SafeTypeScript’stechnique,thoughitcarriesthetypesinaseparatełtagheapž,hasthesame semantics:Whenobjectspassthroughanunsoundcast,itisshallowlycheckedandtaggedinthe heap,andallunsound(i.e.,dynamic)updateshavetheirvaluescheckedagainstthetag.Thetag maybemademoreprecise,butneverlessprecise.Thetagcorrespondstoaresolvedstructural objecttype,whichinTypeScriptisalistoftypedfields,andalistofacallsignatures,andwethus designoursystemofcontractstobesufficientforthesetypes. SafeTypeScriptreportssmallslowdowns,onaverage6.5%,infully-typedcode,butinmixedcode, thecostofmaintainingtypesoundnessisonaverage22xforonesuiteofbenchmarks. OurworkimplementsthesyntaxofSafeTypeScript(andthusTypeScript)andthesemantics requiredbySafeTypeScript’stagheap,butdoesnotavoidchecksinfully-staticcode,forreasonsof polymorphismwhichwillbediscussedinlatersections.Wehighlyoptimizedynamicsoundness checksbytakingadvantageofexistingchecksintheinfrastructureofthelanguageimplementation, substantiallyreducingthecostondynamiccode.OurimplementationreportsinSection6similar slowdownstotheoriginalimplementationofSafeTypeScript’stagheapforfullytypedcode,6.61% intheaveragecase,butouroptimizationbringstheperformancetoaslowdownofatworstjust 44.8%inintentionallypessimalmixesoftypedanduntypedcode. 2.2 Just-in-TimeCompilation Modernvirtualmachines(VMs)fordynamiclanguagesuseJust-in-Time(JIT)compilation.This allowsforspeculativeoptimizations,whichoptimizecodebasedonvaluesseenatrun-time:The programisallowedtorununtilaparticularfunctionorblockmustbecompiled,andthenitis compiledundertheassumptionthatthevaluesitinteractswithwillbesimilartotheonespreviously seen.Checksareinsertedtovalidatetheseassumptions,andcodeisrecompiledifthosechecksare violated. Inparticular,derivingfromworkinSelf[Chambersetal.1989],modernimplementationsof JavaScriptandsimilardynamiclanguagesrepresentobjectsasashapeandastore.Thestoreis asimplearraycontainingthevaluesofeveryfieldintheobject,butnottheirnames.Theshape (inothercontextscalledłmapžorłhiddenclassž)isatablemappingfieldnamestotheirposition inthestore.Everyobjectwiththesamefieldsandsamelayoutofthosefieldsinthestorerefers tothesameshape.Thisfactisusedtoavoiddynamicallylookingupfieldsinatechniquewhich inJavaScriptiscalledinlinecaching:Theshapeofaseenobjectiscached,andthesurrounding functionorblockiscompiledwithknowledgeofthatseenshape,andthusthelookupconsistsonly ofaccessingaknownoffsetinanarray. Toassurethateveryobjectwiththesamelayouthasthesameshape,shapesarestoredina globaltree.Emptyobjectsallsharethesameemptyshape,andtoaddafieldtoanobject,one followsthetransitioninthattreefromtheobject’scurrentshapelabeledwiththefieldbeingadded. Forinstance,inFigure1,iffielditemisaddedtoanemptyobject(i.e.,anobjectwithshape0), theobject’sshapeisupdatedtoshape1andthevalueisstoredatoffset0ofthestore.Ifnextis ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. 55:6 GregorRichards,EllenArteca,andAlexiTurcotte shape 0 {} {} shape 0 {} item:numberitem:string toString:function item toString shape 1a shape 1b shape 3 shape 1 shape 3 {item→0} {item→0} {toString→0} {item→0} {toString→0} {item→number} {item→string} {toString→function} next next next:object next:object next:object {item→sh0a,p ne e2xt→1} {toStrinsgh→ap0e, 4next→1} {items→ha0p, en 2exat→1} {items→ha0p, en 2exbt→1} {toStrinsgh→ap0e, 4next→1} {item→number, {item→string, {toString→function, next→object} next→object} next→object} Fig.1. Basicshapetree. Fig.2. Higgsshapetree. thenaddedtotheobject,itsshapewillbeupdatedtoshape2.Ifnotransitionexistsforanecessary additiontoanobject,anewshapeiscreatedandaddedtotheshapetreetosatisfytherequest. Thus,inourpreviousexample,Nodeswillhave1shape2. ThisworkbuildsontheHiggsvirtualmachine[Chevalier-BoisvertandFeeley2016][Chevalier- BoisvertandFeeley2015],aresearchJavaScriptvirtualmachinewithcompetitiveperformance. HiggsimplementsmanymodernJavaScriptoptimizations,aswellasafewuniqueoneswhichwe utilize. Higgsimplementsspeculativeoptimizationsthroughlazybasicblockversioning.Inlazybasic blockversioning,eachbasicblockiscompiledspeculativelywithassumptionsaboutthetypesof datathatitaccesses,andthoseassumptionsarecheckedbeforeexecutionisallowedtoproceed intosuchaspeculativelycompiledblock.Singlebasicblocksmayhavemultiplecompiledversions, correspondingtodifferentsetsofassumptions,andexecutionwillchoosewhichblocktoproceed intothroughrun-timechecks.Whenchecksofthoseassumptionsfail,anewversionofthebasic blockandallfollowingbasicblocksisbuilttofitthenewassumptions.Theseassumptionspropagate intofurtherblocks,sorechecksareelided.Thus,thecompiledcodeforafunctionbecomesagraph ofbasicblockversions,eachrepresentingthecompilationofabasicblockunderaparticularsetof assumptions.Toavoidcodeblowup,limitsareplacedonhowlargethisgraphcanget,buttheyare rarelyreachedinpractice. Forinstance,consideroursumfunction.Adirectcompilationofthisfunctiontobasicblocks, withcontrolflowreplacedbygotos,is: (1) if (!node.next) goto 4 (2) tmp1 = sum(node.next); tmp2 = node.item; (3) return tmp1 + tmp2; (4) return node.item; Blocks2and3mustbesplit,becausethecompilationofthe+operatordependsonthetypesof itsoperands,creatingimplicitcontrolflowforthosetypestobechecked. Uponthefirstcompilationofthisfunction,asblock1requirestheshapeofnode,theshapeseen willbecached,resultinginacompiledprocedureasinFigure3.Assumingthefunctioniscalled withanon-nullnode.next,itwillproceedthroughblock2,andthenbecompiledagain,intoa procedureasinFigure4. Higgs, building on work in TruffleJS [Würthinger et al. 2013], extends shapes by encoding membertypesintothemaswell.Objectsbuiltofthesamefieldsbutdistinctprimitivetypesof 1Thisassumesthattheyarebuiltinaparticularorder,asinJavaScriptVMsthestoreisneverrearranged. ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. TheVMAlreadyKnewThat 55:7 node: shape 2? node: shape 2a? yes node: shape 2? yes yes 1 (node:2) node.next==null? 1 (node:2a) node.next==null? 1 (node:2) node.next==null? no yes no yes 2 (node:2) 4 no no yes no 2 (node:2a) 4 no 2 (node:2) 4 tmp1:number&& tmp1:number? tmp2:number? recompile yes no yes no 3 (node:2a, Fig.3. Firstcompilationof tm3p (1n:onduem:2b,er, recompile ttmmpp21::nnuummbbeerr), recompile sum. tmp2:number) Fig.5. Withtypedshapes. Fig.4. Secondcompilation. theirmemberswillhavedistinctshapes,andobjectswithidenticallayoutsandtypeshavethe sameshape.Thisisaccomplishedbyextendingshapeswithamapofnamestoprimitivetypes. Thetransitionsinthisextendedshapetreearelabeledwithbothanameandaprimitivetype.For instance,inFigure2,iffielditemisaddedtoanemptyobjectwithtypenumber,theobject’sshape isupdatedtoshape1a.Ifit’saddedwithtypestring,theobject’sshapeisupdatedtoshape1b. Becausefieldsaretypicallymutable,thismeanstheshapecanalsochangewhenafieldismodified: Ifanobjectwithshape2a’sitemfieldisupdatedtoastring,theobject’sshapemustbeupdatedto 2b.Thatis,theshapeisonlyasnapshotofthecurrentstateoftheobject,notaguaranteeoffuture state. Thebenefitofaddingtypestoshapeistwofold:First,asprimitivetypesareavailableinthe shape,it’sunnecessarytotagorboxvaluesintheheap[Wößetal.2014].Objectscanthusbe laidoutmoreefficiently,avoidingwastingbitsontypeinformationthat’sidenticalbetweenmany objects.Second,speculatingonashapeallowsthecompilernotonlytooptimizefieldaccess,butto eliminatetypechecksovertheaccessedfieldvalues:Thecheckofthecontainingobject’sshape subsumesthecheckoftheaccessedvalue’stype.Forinstance,sumcanbecompiledtotheprocedure inFigure5withtypedshapes,avoidingacheckforthetypeoftmp2,whichisimpliedbytheshape ofnode.Thisoptimizationisimportanttoourgoal:IfataginSafeTypeScript’stagheapobligesthe itemfieldofanobjecttobeanumber,andthatobject’sshapeisshape2a,thatobligationcannot fail,andsodoesnotneedtobechecked. Lazybasicblockversioningisinmanywayssimilartotracing[Galetal.2009]andevenmeta- tracing [Bolz et al. 2009], and all of the optimizations of contracts discussed herein could be appliedequallytothosecontexts.Thatis,ourperformanceimprovementcomesfromcarefuluseof speculation,notfromtheparticularimplementationofspeculationinHiggs. 2.3 EfficientContracts Thecheckspresentindynamicallytypedprogramminglanguages,andthustheguardsandchecks presentinspeculatively-compileddynamiccode,arefundamentallysimilartotherun-timecontracts generatedbygradually-typedlanguages.Theprincipledifferenceissimplythatthepropertiesa JITischeckingarenotrequestedbytheuser,butbyspeculativecompilation,andthusviolating suchaguardisnotanerror,merelyacauseforrecompilation.WeimplementinHiggsCheck,an extensionofHiggs,asystemofintrinsicobjectcontracts,whichintegrateswiththeshapetreeto maketypecontractsasintrinsicapartofanobject’srun-timeimplementationasitsmemorylayout. ShapesinHiggsCheckencodethelayout,types,andcontractualobligationsofobjectmembers, ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. 55:8 GregorRichards,EllenArteca,andAlexiTurcotte andthecompilerisawareoftheseobligations.Thecontractsthemselves,whilenotasgeneralas truepre-andpost-conditioncontracts,correspondtostructuralobjecttypes,anddelaytypechecks offieldsandfunctionreturnsuntiltherelevantfieldaccessorfunctioncallisperformed,similarly towrappers.Wheneverafieldofanobjectwithcontractsisread,acheckisincurred,andifthe contractstipulatesthatthevalueitselfhaveahigher-ordertype,thenacontractisappliedtothe value.Contractsmaybeaddedbutneverremoved,andsovaluesaremonotonic,andmayonly becomemorerestrictiveÐmorepreciselytypedÐduringtheexecutionofaprogramastheyare usedwithcontracts. Intheremainderofthispaperwediscusstheimplementationofcontractsandhowtheybridge thegapbetweentheVMandgradually-typedlanguages.InSection3wediscussthedesignof intrinsicobjectcontractsinisolation.Section4discusseshowthesecontractscanbeimplemented efficiently,andSection5howtheyareusedinthereimplementationofSafeTypeScript. 3 CONTRACTS In this section we discuss the features and basic implementation of the contracts provided by HiggsCheck,independentoftheiroptimizationsandthedirectuseofthesecontractsbygradually- typedlanguages.Wecallthisstyleofcontractsintrinsicobjectcontracts,asthecontractsareintrinsic totheprotectedobjects.Inparticular,wefocusonhowitsdesigndiffersfromotherunderlying systemsofhigher-orderchecks. 3.1 Design ThebehaviorofintrinsicobjectcontractsinHiggsCheckissemanticallysimple:Valuesmaybe testedagainstcontracts,andthosecontractsdescribeanumberofobligationsthevaluesmust adhereto.Theseobligationsareintendedtosupportgradualtyping,andsoaretotype-related propertiesthatareusefulforlanguagesandsupportedbytheVM.Thatis,acontractisasetof typing obligations over a value, and a value may be tested against a contract to assure that it conformstothoseobligations. Wedividethesupportedobligationsintobasicobligations,whichcanbecheckedquicklyandin constanttimeforanyvalue,andhigher-orderobligations,whichcannot.Higher-orderobligations correspondapproximatelytohigher-ordertypes,whilebasicobligationscorrespondtofirst-order, orprimitive,types.Wecallacontractwithonlybasicobligationsabasiccontract,andacontract with at least one higher-order obligation a higher-order contract. The empty contract, i.e. the contractwithnoobligationsatall,isalsoabasiccontract.Whenavalueistestedagainstacontract, conceptuallyitistestedagainstallofthatcontract’sobligations. Thebasicobligationsaretotheprimitiverun-timetypesandnullness.Forinstance,theobliga- tionłnumberžcorrespondsnaturallytotheJavaScripttypenumber.Thesebasicobligationsare trivialtocheckwithnocontractsystematall,asJavaScript’sbuilt-intypeofoperatorissufficient. Higher-orderobligationsarerequirementsoverobjectfieldsandfunctionreturnvalues.We focusfirstonobjectfields.Theseobligationsrequirethatthevaluesyieldedbyaccessingafield compliestoagivencontract,andthusincurtestingofthevalueagainstthecontractwheneveritis accessed.Ahigher-ordercontractwithseveralfieldobligationsisequivalenttoastructuralobject type,witheachobligationreferringtothetypeofaparticularfield.Forinstance,acontractcanbe builtfortheNodetypeasfollows:Contractα’ssoleobligationisłnumberž.Contractβ,thecontract forNode,hasthreeobligations:Thebasicobligationłobjectž,thehigher-orderobligationthatfield itembetestedagainstcontractα,andtheobligationthatfieldnextbetestedagainstcontractβ. Tosupportarrayindices,whichinJavaScriptaresimplyfieldswithnumericnames,acontract mayhaveanobligationoverallnumericfields.So,tosupportarraysofNodes,wemaycreatea ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. TheVMAlreadyKnewThat 55:9 contractγ withtwoobligations:Thebasicobligationłobjectž,andthehigher-orderobligationthat allfieldswithnumericnamebetestedagainstcontractβ. Because these obligations allow the recursive testing of contracts, they cannot be checked eagerly.Eveninthecasethatthey’renotrecursive,theycannotbecheckedquickly,andbecause objectsinJavaScriptaretypicallymutable,acheckofsuchahigher-ordercontractwouldonly beamomentaryguarantee.Assuch,testingavalueagainstsuchacontractchecksonlythebasic obligationsimmediately,anddelaystestingofhigher-orderobligationsuntiltherelevantfieldis accessed.Testinganobjectxagainstcontractβ,ourcontractforNode,forinstance,willimmediately verifythatxisanobject,butwillonlytestthatx.itemisanumberwhen,andif,x.itemisaccessed. Higher-order obligations may additionally restrict function returns. For instance, a contract δ mayhavetheobligationthatreturnvaluescomplytocontractα above.Ifafunctionistested againstδ,itcannotbecheckedeagerly,andsoinsteadthesystemassertsthatthefunction’sreturn willbetestedinallfuturecalls.Thisisasimpleobligationnotsufficientformoresophisticated functionalprogramming,andthereasonsforthislimitationandpossibleenhancementstosupport functionalprogrammingarediscussedinSection4.3. Whenanobjectorfunction(inJavaScript,functionsareobjects)istestedagainstahigher-order contract,assumingallbasicobligationssucceed,thehigher-orderobligationsareimposedonthe objectthroughdelayedchecks.Toassurethatthesedelayedcheckshappen,thecontractisapplied totheobject.Objectsinthissystemmayhaveanynumberofcontractsappliedtothem,andeach objectretainsalistofappliedcontractsforitslifetime.Thiscontractapplicationiswhatassures thatfutureaccesseswillbetestedagainstthehigher-orderobligations:Whenafieldisreadfroman objectwithappliedcontracts,ifanyofitscontractshaveobligationsoverthatfield,thatobligation’s contractistestedagainstthevalueseenatrun-time.Ashigher-orderobligationseachtestavalue against a contract, this contract application may occur recursively. For instance, if an object x hasthecontractforNodeapplied,anaccesstox.nextwillbetestedagainsttheNodecontract, applyingitifapplicable.Ifanobjectistestedagainstacontractwhichhasalreadybeenapplied,it isn’tappliedasecondtime. Contractsaremonotonic:Whileanobjectmayalwayshavemorecontractsapplied,andthus gainmorespecifictypeinformationandamorerestrictiveAPI,itmayneverlosecontracts.Itis possibletoaddincompatiblecontracts,forinstanceNodealongwithacontractthatdemandsthat itembeastring,inwhichcaseaccessingsuchcontradictingfieldswillinevitablycauseacontract violation.TheyareindependentofJavaScript’sparticularsystemofinheritance,andtreatobjects asblackboxes.Thus,ifithappensthatthevalueofafieldisfulfilledbyaccessingaprototype,or byusinganaccessorfunction,itischeckedthesameasasimple,localfield. Built-infunctions,intendedtobeusedbytheimplementationofagradually-typedlanguage, areprovidedtobuildcontracts.Basiccontractsfortheprimitivetypes(objects,functions,strings, numbers and booleans) and the empty contract are built in, and other contracts are built by extending them through further built-in functions which add field, index and function return obligations. 3.2 Blame Whenacontractisappliedtoanobject,anactualviolationassociatedwiththatcontractmayoccur laterintheexecutionoftheprogram,whenaprimitivevalueisobtained.Withwrapper-based systems,thewrapperobjectcarrieswithitblameinformation,suchasthecodelocationofthe castwhichcausedacontracttobeadded,andwithmonotonicobjects,theblameisstoredinthe objectitself.IntrinsiccontractsinHiggsCheckcontainsimilarblameinformation:Acontracttest mayprovideanadditionalargumentwhichrepresentstherelevantblamelabel,typicallyacode location,andthatblamewillbereportedifthecontract’sobligationsareviolatedontheobject. ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017. 55:10 GregorRichards,EllenArteca,andAlexiTurcotte Whentestingtheobligationsofacontractrequirestestinganothervalueagainstanothercontract, theblameinformationfromtheparentcontractispassedontothechild. ThisgivesintrinsicobjectcontractssupportforblameasinFindlerandFelleisen[2002].As contractsareonlyappliedonce,anobjectcannotaccruemultipleblamelabelsforasinglecontract, andso-calledłthreesomesž[SiekandWadler2010]naturallycollapse:Wedonotre-applyacontract ifonlyitsblamehaschanged.Givenafixednumberofhigher-ordertypesinaprogram,thereare thusafixednumberofcontractsthatanobjectmayeveracquire.However,onlythefirstblame labelappliediskept. Thecreationofusefulblameinformationitselfislefttothecontract-utilizingcode.Itisassumed thatcontractswillusuallybeusedbycodegeneratedfromcompilationofgradually-typedpro- gramming languages, and so it is the role of that compiler to provide useful information. The compilerdescribedinSection5simplyusescodelinesasblamelabels,butusinge.g.stacktracesis analternativeÐalbeitslowerÐoption. 3.3 Discussion Thedesignofourcontractsystemwasintendedtosatisfytworequirements:Thatitbeusablefor thecontractsusuallyneededbygradually-typedobject-orientedprogramminglanguages,andthat itbeimplementableinanoptimalway.Relativetowrapper-basedcontracts,ourcontractsareless easilygeneralizabletootherkindsofchecks,butareimplementablewithasignificantlyreduced performancepenalty,asdemonstratedinSection6. Ourcontractsimplementthesemanticsofmonotonicobjects,andsoallreferencestoanobject have the same contractual obligations. In wrapper-based systems, references derived from the onetowhichthewrapperwasappliedwillconform,andotherswillnot.Bothsemanticshave advantages,andbothhavebeendemonstratedtobecorrect,butmonotonicsemanticsdomeanthat objectscannotbełchameleonsž:Theycannotchangetheirbehaviorinnon-backwards-compatible waysoncecontractshavebeenapplied. Unlikewrapper-basedimplementations,thereisnoquestionofobjectidentity,andnopossibility ofdistinctidentitiesforthesameobject,asnostand-inisevercreatedforanobject.Theapplication ofcontractsdoesnotaffectanobject’sidentityor,equivalently,itaffectstheidentityofallreferences totheobjectinthesameway. As intrinsic object contracts are a new language-level feature, backwards compatibility is a problem.ReticulatedPythondemonstrates[Vitouseketal.2014]thatmonotonicobjectsareimple- mentablethroughaccessorfunctions,andthesewouldbesufficienttoimplementintrinsicobject contractsaswellwithnochangestotheVM,butsuchimplementationsareslow.Ascontractsdonot changethebehaviorofprogramsexcepttoraisecontractviolationerrors,thebehaviorofacorrect programisnotalteredbyremovingitscontracts.Thus,asimpleshimlibrarywhichimplements thecontractbuilt-infunctionswithnobehaviorcanbeusedtosupportstandardimplementations (withnochecking),whilestillusingcontractsonsupportingimplementations. 4 IMPLEMENTATION UsingasimilartechniquetoReticulatedPython’smonotonicobjects,itwouldbepossibletocreatea correctimplementationofthecontractspresentedinSection3withpureJavaScript2.Therequisite checkingofallvalues,however,wouldbeuntenablyslow.HiggsCheckimplementscontractsat theVMlevel,andusescompile-timeknowledgetooptimizethem.Inthissectionwedescribethe 2IfimplementedattheVMlevel,contractswillbenaturallyresistanttomalicioustampering,whichmaybeimpossiblein animplementationinpureJavaScript. ProceedingsoftheACMonProgrammingLanguages,Vol.1,No.OOPSLA,Article55.Publicationdate:October2017.
Description: