Table Of ContentThe 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:Authors' addresses: Gregor Richards, School of Computer Science, University of Waterloo, Canada, 200 University Avenue. West, Waterloo, Ontario, N2L 3G1, value is used later in the function, later checks are elided, and so the cost of run-time checking is similarly made redundant with existing