From 245bcf2f6376dc30f08ccba030b7d47279decafe Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Tue, 25 Aug 2020 09:18:51 +0800 Subject: [PATCH] =?UTF-8?q?import=20changes=20from=20`dev'=20branch=20of?= =?UTF-8?q?=20rmottola/Arctic-Fox:=20-=20Bug=201153148=20-=20Add=20documen?= =?UTF-8?q?tation=20comment=20for=20JSOP=5FSUPERBASE=20and=20JSOP=5FINITHO?= =?UTF-8?q?MEOBJECT.=20r=3Defaust=20(b6ecd21e1)=20-=20Bug=201153057=20-=20?= =?UTF-8?q?Properly=20initialize=20the=20[[HomeObject]]=20of=20methods=20w?= =?UTF-8?q?ith=20computed=20property=20names.=20(r=3Djorendorff)=20(6c2226?= =?UTF-8?q?674)=20-=20Bug=201152304=20-=20Record=20the=20end=20of=20block?= =?UTF-8?q?=20scope=20notes=20to=20be=20after=20emission=20of=20JSOP=5FPOP?= =?UTF-8?q?BLOCKSCOPE.=20(r=3Djimb)=20(295502f81)=20-=20Pointer=20style=20?= =?UTF-8?q?(7f3f22ce3)=20-=20Bug=201155466=20-=20Freshened=20blocks=20shou?= =?UTF-8?q?ld=20pop=20their=20old=20blocks=20in=20debug=20scopes.=20(r=3DW?= =?UTF-8?q?aldo)=20(359fff80b)=20-=20pointer=20style=20(107fb5b95)=20-=20B?= =?UTF-8?q?ug=201156190=20-=20Do=20not=20emit=20DEBUGLEAVESCOPE=20if=20we'?= =?UTF-8?q?re=20also=20going=20to=20emit=20POPBLOCKSCOPE.=20(r=3Djimb)=20(?= =?UTF-8?q?af5fde7ef)=20-=20cleanup=20(292b5baa7)=20-=20do=20not=20inline?= =?UTF-8?q?=20(cc14b6170)=20-=20pointer=20style=20(637cc4e01)=20-=20pointe?= =?UTF-8?q?r=20style=20(e203a150f)=20-=20Bug=201149498=20-=20Watch=20for?= =?UTF-8?q?=20preliminary=20object=20groups=20in=20more=20places,=20r=3Dja?= =?UTF-8?q?ndem.=20(a623a2d06)=20-=20Bug=201155946=20part=201=20-=20Add=20?= =?UTF-8?q?a=20mayResolve=20class=20hook=20to=20optimize=20objects=20with?= =?UTF-8?q?=20resolve=20hooks=20better.=20r=3Dbhackett=20(e45ebda33)=20-?= =?UTF-8?q?=20Bug=201155946=20part=202.=20Add=20mayResolve=20methods=20to?= =?UTF-8?q?=20DOM=20classes=20with=20resolve=20hooks.=20r=3Dpeterv=20(9efb?= =?UTF-8?q?0c070)=20-=20pointer=20style=20(d0c09e4ec)=20-=20Bug=201149119?= =?UTF-8?q?=20-=20Use=20Atoms=20in=20the=20template=20object=20hold=20by?= =?UTF-8?q?=20Baseline.=20r=3Djandem=20(63fe170e9)=20-=20Bug=201149119=20-?= =?UTF-8?q?=20Do=20not=20inline=20bound=20functions=20with=20non-atomized?= =?UTF-8?q?=20arguments.=20r=3Djandem=20(facc9cb06)=20-=20Bug=201155807=20?= =?UTF-8?q?-=20Watch=20for=20converted=20native=20groups=20with=20unknown?= =?UTF-8?q?=20properties,=20r=3Djandem.=20(257e11ff3)=20-=20Bug=201136584?= =?UTF-8?q?=20-=20Fix=20warning-as-errors=20error.=20r=3Dme=20(11768280a)?= =?UTF-8?q?=20-=20pointer=20style=20(f2234b36f)=20-=20Bug=201157809=20-=20?= =?UTF-8?q?Optimizing=20scanning=20of=20unboxed=20plain=20objects=20and=20?= =?UTF-8?q?typed=20objects=20during=20nursery=20collections,=20r=3Dterrenc?= =?UTF-8?q?e.=20(f37855a60)=20-=20Bug=201153266=20-=20Allow=20turning=20on?= =?UTF-8?q?=20unboxed=20objects=20with=20an=20environment=20variable,=20r?= =?UTF-8?q?=3Dh4writer.=20(504984da7)=20-=20Bug=201145426=20-=20Fix=20comp?= =?UTF-8?q?ilation=20of=20unboxed=20object=20construction=20stubs=20on=20A?= =?UTF-8?q?RM=20and=20x86,=20r=3Djandem.=20(be1491b56)=20-=20Bug=201145795?= =?UTF-8?q?=20-=20Remove=20LJSCallInstructionHelper::numStackArgs()=20foot?= =?UTF-8?q?gun.=20(r=3Djandem)=20(c3e952856)=20-=20Bug=201147629=20-=20Use?= =?UTF-8?q?=20getStackPointer()=20and=20helper=20functions.=20r=3Djandem?= =?UTF-8?q?=20(97d6b4054)=20-=20Bug=201147608=20-=20Fixes=20for=20unboxed?= =?UTF-8?q?=20object=20creation=20stub=20compilation=20on=20x86=20and=20AR?= =?UTF-8?q?M,=20r=3Djandem.=20(abc526773)=20-=20Bug=201146363:=20Inline=20?= =?UTF-8?q?SIMD.int32x4.bool;=20r=3Dsunfish=20(d655dc72e)=20-=20Bug=201155?= =?UTF-8?q?793=20part=201.=20Make=20it=20possible=20to=20safely=20change?= =?UTF-8?q?=20the=20number=20of=20bits=20in=20the=20slotIndex=20field=20in?= =?UTF-8?q?=20jitinfo.=20r=3Defaust=20(c6124396d)=20-=20Bug=201155793=20pa?= =?UTF-8?q?rt=202.=20Split=20apart=20the=20concepts=20of=20movability=20an?= =?UTF-8?q?d=20eliminatability=20in=20jitinfo,=20since=20some=20things=20a?= =?UTF-8?q?re=20not=20movable=20but=20are=20eliminatable.=20r=3Defaust=20(?= =?UTF-8?q?848b751b8)=20-=20Bug=201135040:=20Optimize=20SIMD=20shifts=20in?= =?UTF-8?q?=20Ion;=20r=3Dnbp=20(a61b8ae26)=20-=20pointer=20style=20(b9921d?= =?UTF-8?q?90f)=20-=20rearrange=20(38c9a7c33)=20-=20Bug=201142668:=20Fix?= =?UTF-8?q?=20int32x4=20to=20float32x4=20conversions=20in=20the=20JIT;=20r?= =?UTF-8?q?=3Dsunfish=20(6f3ac7a59)=20-=20pointer=20style=20(29df6bf54)=20?= =?UTF-8?q?-=20pointer=20style=20(bebd7f1d6)=20-=20Bug=201134198=20-=20Ref?= =?UTF-8?q?actor=20JS=5FGENERATOR=5FCLOSED=20checking.=20(r=3Djandem)=20(5?= =?UTF-8?q?5c3062dd)=20-=20Bug=201134198=20-=20Rename=20assertNotInFrameMa?= =?UTF-8?q?ps=20to=20inFrameMaps.=20(r=3Dtromey)=20(e8a8ecfff)=20-=20Bug?= =?UTF-8?q?=201134198=20-=20Update=20tests=20to=20reflect=20new=20specced?= =?UTF-8?q?=20behavior=20on=20Debugger.Frame.onPop.=20(r=3Dtromey)=20(8126?= =?UTF-8?q?f8ecf)=20-=20Bug=201134198=20-=20Update=20docs=20for=20new=20De?= =?UTF-8?q?bugger.Frame.onPop=20spec.=20(r=3Dtromey)=20(7520665c1)=20-=20B?= =?UTF-8?q?ug=201134198=20-=20Fix=20up=20tests=20now=20that=20onPop=20and?= =?UTF-8?q?=20onExceptionUnwind=20may=20be=20called=20at=20different=20loc?= =?UTF-8?q?ations=20than=20previously.=20(r=3Dtromey)=20(6651e74b7)=20-=20?= =?UTF-8?q?Bug=201134198=20-=20Don't=20call=20Debugger::slowPathOnLeaveFra?= =?UTF-8?q?me=20on=20frames=20no=20longer=20in=20Debugger=20frame=20maps.?= =?UTF-8?q?=20(r=3Djimb)=20(a8856ece8)=20-=20Bug=201134198=20-=20Call=20De?= =?UTF-8?q?bugger::onPop=20at=20the=20point=20that=20caused=20the=20frame?= =?UTF-8?q?=20to=20pop=20before=20any=20unwinding=20in=20the=20interpreter?= =?UTF-8?q?.=20(r=3Djimb)=20(84a4f3f70)=20-=20pointer=20style=20and=20clea?= =?UTF-8?q?nup=20(5bc4c3114)=20-=20no=20bug=20-=20fix=20windows=20includes?= =?UTF-8?q?=20for=20case=20sensitive=20file=20systems=20(48835fd8b)=20-=20?= =?UTF-8?q?pointer=20style=20(ef70bfbcb)=20-=20pointer=20style=20(f739ba48?= =?UTF-8?q?8)=20-=20pointer=20style=20(1cc9808f8)=20-=20Bug=201146597=20-?= =?UTF-8?q?=20Add=20unboxed=20arrays=20for=20JSOP=5FNEWARRAY=20arrays,=20a?= =?UTF-8?q?nd=20shell=20option=20for=20using=20them,=20r=3Djandem.=20(d7b4?= =?UTF-8?q?24fc3)=20-=20pointer=20style=20(5877eb088)=20-=20Bug=201157703?= =?UTF-8?q?=20-=20Cache=20iterators=20involving=20unboxed=20objects,=20r?= =?UTF-8?q?=3Djandem.=20(f2670a817)=20-=20pointer=20style=20(bfbb868b7)=20?= =?UTF-8?q?-=20Bug=201146597=20-=20Remove=20redundant=20check=20for=20unbo?= =?UTF-8?q?xed=20object/array=20runtime=20option.=20(3b533c202)=20-=20Poin?= =?UTF-8?q?ter=20style=20(1984bb1e8)=20-=20pointer=20style=20(6fbaea70a)?= =?UTF-8?q?=20-=20Bug=201142828=20-=20Refactor=20to=20avoid=20having=20[[S?= =?UTF-8?q?et]]-specific=20weird=20cases=20in=20the=20[[DefineOwnProperty]?= =?UTF-8?q?]=20code.=20r=3DWaldo.=20(4661cdecb)=20-=20Bug=201147660,=20par?= =?UTF-8?q?t=201=20-=20Refactor=20NativeDefineProperty=20to=20put=20Define?= =?UTF-8?q?PropertyOrElement's=20only=20call=20site=20right=20at=20the=20e?= =?UTF-8?q?nd.=20No=20change=20in=20behavior.=20r=3Defaust.=20(824cb5e2b)?= =?UTF-8?q?=20-=20Bug=201147660,=20part=202=20-=20Merge=20DefinePropertyOr?= =?UTF-8?q?Element=20into=20NativeDefineProperty,=20making=20one=20long=20?= =?UTF-8?q?function=20we=20can=20refactor.=20r=3Defaust.=20(89a6bf7e0)=20-?= =?UTF-8?q?=20Bug=201147660,=20part=203=20-=20Rearrange=20NativeDefineProp?= =?UTF-8?q?erty=20so=20that=20special=20cases=20are=20all=20dispensed=20wi?= =?UTF-8?q?th,=20and=20ES6=20checks=20done,=20by=20the=20time=20we=20start?= =?UTF-8?q?=20thinking=20about=20how=20to=20update=20the=20object.=20r=3De?= =?UTF-8?q?faust.=20(b0234d161)=20-=20Bug=201147660,=20part=204=20-=20Chan?= =?UTF-8?q?ge=20NativeDefineProperty=20to=20use=20a=20PropertyDescriptor?= =?UTF-8?q?=20internally=20instead=20of=20a=20bunch=20of=20variables.=20Th?= =?UTF-8?q?is=20is=20a=20little=20ugly=20at=20first=20but=20it'll=20get=20?= =?UTF-8?q?better.=20r=3Defaust.=20(1c1999a57)=20-=20Bug=201147660,=20part?= =?UTF-8?q?=205=20-=20Split=20the=20part=20of=20NativeDefineProperty=20tha?= =?UTF-8?q?t=20updates=20the=20object=20into=20a=20separate=20function=20a?= =?UTF-8?q?gain.=20r=3Defaust.=20(5dc0316b5)=20-=20pointer=20style=20(f664?= =?UTF-8?q?59369)=20-=20Bug=201148652,=20part=201=20-=20Move=20array-speci?= =?UTF-8?q?fic=20special=20cases=20to=20the=20top=20of=20NativeDefinePrope?= =?UTF-8?q?rty;=20update=20ArraySetLength=20to=20be=20able=20to=20cope=20w?= =?UTF-8?q?ith=20incomplete=20attrs.=20r=3Defaust.=20(e2dca6628)=20-=20Bug?= =?UTF-8?q?=201148652,=20part=202=20-=20Use=20mostly-compliant=20code=20fo?= =?UTF-8?q?r=20defining=20TypedArray=20elements,=20already=20used=20from?= =?UTF-8?q?=20StandardDefineProperty,=20in=20NativeDefineProperty=20as=20w?= =?UTF-8?q?ell.=20r=3Defaust.=20(b5e8489ed)=20-=20Bug=201148652,=20part=20?= =?UTF-8?q?3=20-=20Mark=20arguments.length=20as=20overridden=20when=20it?= =?UTF-8?q?=20is=20redefined=20via=20the=20C=20API.=20r=3Defaust.=20(0f449?= =?UTF-8?q?25f1)=20-=20Bug=201148750,=20part=201=20-=20Factor=20out=20the?= =?UTF-8?q?=20lookup=20common=20to=20three=20branches=20at=20the=20top=20o?= =?UTF-8?q?f=20NativeDefineProperty.=20r=3Defaust.=20(b9e4e7254)=20-=20Bug?= =?UTF-8?q?=201148750,=20part=202=20-=20Check=20extensibility=20in=20Nativ?= =?UTF-8?q?eDefineProperty.=20r=3Defaust.=20(202f4e12e)=20-=20Bug=20114875?= =?UTF-8?q?0,=20part=203=20-=20Implement=20ValidateAndApplyPropertyDescrip?= =?UTF-8?q?tor=20step=202.=20r=3Defaust.=20(acb6afa35)=20-=20Bug=201148750?= =?UTF-8?q?,=20part=204=20-=20Strip=20out=20redundant=20if-conditions=20in?= =?UTF-8?q?=20parts=20of=20NativeDefineProperty=20where=20shape=20can't=20?= =?UTF-8?q?be=20null.=20r=3Defaust.=20(15d24b5f4)=20-=20Bug=201148750,=20p?= =?UTF-8?q?art=205=20-=20CompletePropertyDescriptor=20upgrade.=20r=3Defaus?= =?UTF-8?q?t.=20(69bd74ce3)=20-=20Bug=201148750,=20part=206=20-=20Implemen?= =?UTF-8?q?t=20ValidateAndApplyPropertyDescriptor=20up=20to=20step=205.=20?= =?UTF-8?q?r=3Defaust.=20(94aef08b5)=20-=20Bug=201148750,=20part=207=20-?= =?UTF-8?q?=20Fill=20in=20configurable=20and=20enumerable.=20r=3Defaust.?= =?UTF-8?q?=20(698bfa16d)=20-=20Bug=201148750,=20part=208=20-=20Implement?= =?UTF-8?q?=20ValidateAndApplyPropertyDescriptor=20step=206.=20r=3Defaust.?= =?UTF-8?q?=20(7459d31ba)=20-=20Bug=201148750,=20part=209=20-=20Implement?= =?UTF-8?q?=20ValidateAndApplyPropertyDescriptor=20step=207.=20r=3Defaust.?= =?UTF-8?q?=20(9593b3312)=20-=20Bug=201148750,=20part=2010=20-=20js::Nativ?= =?UTF-8?q?eDefineProperty:=20Swap=20the=20order=20of=20the=20cases=20in?= =?UTF-8?q?=20the=20remaining=20old=20code.=20r=3Defaust.=20(90a61b163)=20?= =?UTF-8?q?-=20Bug=201148750,=20part=2011=20-=20Remove=20some=20code=20for?= =?UTF-8?q?=20TypedArray=20cases=20rendered=20unreachable=20by=20part=201?= =?UTF-8?q?=20of=20this=20bug.=20r=3Defaust.=20(d57f75b20)=20-=20Bug=20114?= =?UTF-8?q?8750,=20part=2012=20-=20Reject=20redefinition=20of=20non-writab?= =?UTF-8?q?le=20non-configurable=20data=20property=20as=20writable.=20This?= =?UTF-8?q?=20fixes=20bug=201073808.=20r=3Defaust.=20(d1783a786)=20-=20Bug?= =?UTF-8?q?=201148750,=20part=2013=20-=20Simplify=20code=20to=20fill=20in?= =?UTF-8?q?=20desc.writable,=20if=20not=20present,=20from=20the=20existing?= =?UTF-8?q?=20shape.=20r=3Defaust.=20(aa01780a6)=20-=20Bug=20895223=20-=20?= =?UTF-8?q?Use=20JSNative=20instead=20of=20JSGetterOp=20for=20ctypes=20Fie?= =?UTF-8?q?ldGetter/Setter.=20r=3Djorendorff=20(01372b866)=20-=20Bug=20895?= =?UTF-8?q?223=20-=20Always=20pass=20the=20holder=20to=20JSGetterOps.=20r?= =?UTF-8?q?=3Djorendorff,jandem=20(62bff004b)=20-=20Bug=20895223=20-=20Tes?= =?UTF-8?q?t=20for=20the=20now=20correctly=20behaving=20properties=20(e.g?= =?UTF-8?q?=20array.length).=20r=3Djorendorff=20(5238a0c44)=20-=20Bug=2011?= =?UTF-8?q?53651=20-=20Define=20array.length=20as=20JSPROP=5FSHADOWABLE.?= =?UTF-8?q?=20r=3Djorendorff=20(6645ed732)=20-=20Bug=201149563=20-=20Chang?= =?UTF-8?q?e=20bogus=20ctypes=20code=20that=20modifies=20a=20frozen=20obje?= =?UTF-8?q?ct.=20r=3Defaust.=20(1777ad24d)=20-=20Bug=201148750,=20part=201?= =?UTF-8?q?4=20-=20Reject=20redefining=20a=20non-writable=20non-configurab?= =?UTF-8?q?le=20data=20property=20to=20have=20a=20different=20value.=20r?= =?UTF-8?q?=3Defaust.=20(a2e81df42)=20-=20Bug=201148750,=20part=2015=20-?= =?UTF-8?q?=20Stop=20retaining=20getter=20and=20setter=20ops=20when=20rede?= =?UTF-8?q?fining=20a=20data=20property.=20r=3Defaust.=20(1c6f67821)=20-?= =?UTF-8?q?=20Bug=201148750,=20part=2016=20-=20Implement=20ValidateAndAppl?= =?UTF-8?q?yPropertyDescriptor=20step=209=20(redefining=20an=20existing=20?= =?UTF-8?q?accessor=20property).=20Remove=20CheckAccessorRedefinition.=20r?= =?UTF-8?q?=3Defaust.=20(0affd1437)=20-=20pointer=20style=20(0697799b8)=20?= =?UTF-8?q?-=20Bug=201153475=20-=20Always=20ignore=20desc.object()=20in=20?= =?UTF-8?q?DefineProperty=20functions=20that=20take=20a=20PropertyDescript?= =?UTF-8?q?or=20argument.=20r=3Defaust.=20(d9615554e)=20-=20pointer=20styl?= =?UTF-8?q?e=20(f883227a9)=20-=20Bug=201147005=20-=20Change=20JSAddPropert?= =?UTF-8?q?yOp=20signature.=20r=3Djorendorff,peterv=20(83530a9b4)=20-=20Bu?= =?UTF-8?q?g=201125567=20-=20Remove=20FindClassPrototype/FindClassObject.?= =?UTF-8?q?=20r=3Djorendorff=20(f89fd6b99)=20-=20Bug=201125302=20-=20Remov?= =?UTF-8?q?e=20NativeLookupProperty.=20r=3Dluke=20(b6dc7bac8)=20-=20Bug=20?= =?UTF-8?q?1062473:=20Add=20'const'=20qualifiers=20to=20this=20for=20some?= =?UTF-8?q?=20NativeObject=20methods=20and=20free=20functions.=20r=3Dterre?= =?UTF-8?q?nce=20(7e4cc56cd)=20-=20Bug=201062473:=20Add=20'pointer-byte-si?= =?UTF-8?q?ze'=20to=20getBuildConfiguration=20results.=20r=3Dsfink=20(93ed?= =?UTF-8?q?baca6)=20-=20pointer=20style=20(ff234eb8b)=20-=20Bug=201155197:?= =?UTF-8?q?=20For=20JS::ubi::Node,=20provide=20jsObjectClassName=20directl?= =?UTF-8?q?y=20in=20JSObject=20specialization.=20r=3Dterrence=20(9151ceb5f?= =?UTF-8?q?)=20-=20Bug=201062473:=20Make=20JSObject::allocKindForTenure=20?= =?UTF-8?q?out=20of=20GetObjectAllocKindForCopy.=20r=3Dterrence=20(9575ad9?= =?UTF-8?q?0e)=20-=20Bug=201062473:=20Implement=20JS::ubi::Node::size=20fo?= =?UTF-8?q?r=20JSObjects.=20r=3Dsfink,terrence=20(aeacda1ca)=20-=20Bug=201?= =?UTF-8?q?160986=20-=20Update=20Histograms.json=20for=20RegExp#source=20a?= =?UTF-8?q?ccess=20(followup=20for=20bug=201153963).=20r=3Dgfritzsche=20(9?= =?UTF-8?q?defab499)=20-=20=20Bug=201154296=20-=20Small=20GetOwnPropertyDe?= =?UTF-8?q?scriptor=20cleanup.=20r=3Djorendorff=20(fef8fb35e)=20-=20Bug=20?= =?UTF-8?q?1134865=20-=20Part=201:=20Add=20JSObject::constructorDisplayAto?= =?UTF-8?q?m;=20r=3Ddjvj=20(db97321c4)=20-=20pointer=20style=20(f237fad9c)?= =?UTF-8?q?=20-=20Bug=201154079=20-=20Add=20the=20allocated=20object's=20[?= =?UTF-8?q?[class]]=20name=20to=20the=20allocations=20log.=20r=3Dshu=20(d5?= =?UTF-8?q?bab15fa)=20-=20Bug=201134865=20-=20Part=202:=20Add=20constructo?= =?UTF-8?q?r=20name=20to=20the=20allocations=20log;=20r=3Ddjvj=20(c1112532?= =?UTF-8?q?3)=20-=20Bug=201063257:=20Implement=20JS::ubi::Node::size=20for?= =?UTF-8?q?=20JSString.=20r=3Dsfink=20(491b25b28)=20-=20Bug=201134865=20-?= =?UTF-8?q?=20Part=203:=20Add=20JS::ubi::Node::jsObjectConstructorName;=20?= =?UTF-8?q?r=3Ddjvj=20(dd74ed300)=20-=20Bug=201158463=20-=20Reorder=20prop?= =?UTF-8?q?erty=20creation=20in=20js::FromPropertyDescriptorToObject.=20r?= =?UTF-8?q?=3DWaldo=20(afb9f302d)=20-=20Bug=201161077=20-=20Allocate=20unb?= =?UTF-8?q?oxed=20arrays=20in=20the=20nursery,=20r=3Dterrence.=20(df198da4?= =?UTF-8?q?5)=20-=20Bug=201159806=20-=20Replace=20macro=20assertions=20wit?= =?UTF-8?q?h=20inline=20functions;=20r=3Dsfink=20(c493b7a83)=20-=20Bug=201?= =?UTF-8?q?161353=20-=20Remove=20the=20post-barrier=20verifier;=20r=3Dsfin?= =?UTF-8?q?k=20(3c64f8b44)=20-=20pointer=20style=20(0f1785941)=20-=20Bug?= =?UTF-8?q?=201148921=20-=20Check=20correctness=20of=20hash=20table=20modi?= =?UTF-8?q?fications=20later=20on=20during=20minor=20GCs,=20r=3Djonco.=20(?= =?UTF-8?q?6f8cf20b0)=20-=20Bug=201158569=20-=20Don't=20trigger=20sweeping?= =?UTF-8?q?=20of=20dead=20type=20information=20when=20scanning=20unboxed?= =?UTF-8?q?=20objects=20in=20minor=20GCs,=20r=3Dterrence.=20(7d5c9d20d)=20?= =?UTF-8?q?-=20Bug=201161726=20-=20Use=20a=20custom=20tracer=20for=20tenur?= =?UTF-8?q?ing=20to=20avoid=20the=20indirect=20calls;=20r=3Djonco=20(f2088?= =?UTF-8?q?e2ae)=20-=20Bug=201161353=20-=20Follow-up=20to=20remove=20more?= =?UTF-8?q?=20post-barrier=20verifier=20cruft;=20r=3Dterrence=20(903a81bd5?= =?UTF-8?q?)=20-=20Bug=201160887=20-=20Fix=20various=20unboxed=20object=20?= =?UTF-8?q?bugs,=20r=3Djandem,terrence.=20(a7aca9fc9)=20-=20Bug=201161762?= =?UTF-8?q?=20-=20Fix=20test=20used=20for=20whether=20an=20unboxed=20objec?= =?UTF-8?q?t=20layout=20is=20too=20large,=20r=3Djandem.=20(772157822)=20-?= =?UTF-8?q?=20Bug=201161346=20-=20Add=20missing=20capacity=20for=20unboxed?= =?UTF-8?q?=20arrays,=20r=3Djandem.=20(9810cbf73)=20-=20Bug=201163810=20-?= =?UTF-8?q?=20Use=20type-based=20dispatch=20for=20IncrementalReferenceBarr?= =?UTF-8?q?ier;=20r=3Djonco=20(caf1273bd)=20-=20Bug=201162301=20-=20Move?= =?UTF-8?q?=20tenuring=20implementation=20to=20TenuringTracer;=20r=3Dsfink?= =?UTF-8?q?=20(5e51a3341)=20-=20Bug=201162303=20-=20Simplify=20TenuringTra?= =?UTF-8?q?cer's=20implementation;=20r=3Djonco=20(813446aa1)=20-=20fix=20o?= =?UTF-8?q?rder=20(15279b7ed)=20-=20Bug=201163643=20-=20Fix=20unified=20bu?= =?UTF-8?q?ild=20errors=20following=20recent=20marking=20changes=20r=3Dter?= =?UTF-8?q?rence=20(83fe5b47a)=20-=20Bug=201161664=20-=20Stay=20inline=20w?= =?UTF-8?q?hen=20visiting=20the=20WholeObject=20store=20buffer;=20r=3Dsfin?= =?UTF-8?q?k=20(d5efe7867)=20-=20Bug=201112627:=20Implement=20shift=20coun?= =?UTF-8?q?t=20saturation;=20r=3DWaldo=20(4f82dcabd)=20-=20Bug=201112627:?= =?UTF-8?q?=20Remove=20redundant=20inline=20specifier=20in=20SIMD=20operat?= =?UTF-8?q?ors=20impl;=20r=3DWaldo=20(0e6f1a31b)=20-=20Bug=201063946=20SIM?= =?UTF-8?q?D:=20Group=20tests=20in=20logical=20units=20-=20conversions;=20?= =?UTF-8?q?r=3Dbbouvier=20(35930b52c)=20-=20Bug=201155081=20-=20Part=2012:?= =?UTF-8?q?=20Replace=20ThrowError(JSMSG=5FTYPEDOBJECT=5FARRAYTYPE=5FBAD?= =?UTF-8?q?=5FARGS)=20with=20ThrowTypeError(JSMSG=5FTYPEDOBJECT=5FBAD=5FAR?= =?UTF-8?q?GS)=20in=20TypedObject.js.=20r=3Dtill=20(01152b406)=20-=20Bug?= =?UTF-8?q?=201155081=20-=20Part=201:=20Replace=20ThrowError=20with=20Thro?= =?UTF-8?q?wTypeError=20in=20Array.js.=20r=3Dtill=20(002a1f3b9)=20-=20Bug?= =?UTF-8?q?=201155081=20-=20Part=202:=20Replace=20ThrowError=20with=20Thro?= =?UTF-8?q?wTypeError=20in=20Error.js.=20r=3Dtill=20(4a76cc576)=20-=20Bug?= =?UTF-8?q?=201155081=20-=20Part=203:=20Replace=20ThrowError=20with=20Thro?= =?UTF-8?q?wTypeError=20in=20Generator.js.=20r=3Dtill=20(08f8d6ae1)=20-=20?= =?UTF-8?q?Bug=201155081=20-=20Part=204:=20Replace=20ThrowError=20with=20T?= =?UTF-8?q?hrowTypeError/ThrowRangeError=20in=20Intl.js.=20r=3Dtill=20(978?= =?UTF-8?q?326c2d)=20-=20Bug=201155081=20-=20Part=205:=20Replace=20ThrowEr?= =?UTF-8?q?ror=20with=20ThrowTypeError=20in=20Map.j=E2=80=A6s/Set.js/WeakS?= =?UTF-8?q?et.js.=20r=3Dtill=20(217998d56)=20-=20Bug=201155081=20-=20Part?= =?UTF-8?q?=206:=20Replace=20ThrowError=20with=20ThrowTypeError=20in=20Obj?= =?UTF-8?q?ect.js.=20r=3Dtill=20(1c0e15e13)=20-=20Bug=201155081=20-=20Part?= =?UTF-8?q?=207:=20Replace=20ThrowError=20with=20ThrowTypeError=20in=20Reg?= =?UTF-8?q?Exp.js.=20r=3Dtill=20(d23d58be8)=20-=20Bug=201155081=20-=20Part?= =?UTF-8?q?=208:=20Replace=20ThrowError=20with=20ThrowTypeError/ThrowRange?= =?UTF-8?q?Error=20in=20String.js.=20r=3Dtill=20(c80fa9060)=20-=20=20Bug?= =?UTF-8?q?=201155081=20-=20Part=209:=20Replace=20ThrowError=20with=20Thro?= =?UTF-8?q?wTypeError=20in=20TypedArray.js.=20r=3Dtill=20(8311344de)=20-?= =?UTF-8?q?=20Bug=201155081=20-=20Part=2010:=20Replace=20ThrowError=20for?= =?UTF-8?q?=20TypeError=20with=20ThrowTypeError=20in=20TypedObject.js.=20r?= =?UTF-8?q?=3Dtill=20(44cee08bb)=20-=20Bug=201145058=20-=20Annotate=20the?= =?UTF-8?q?=20SpeciesConstructor=20utility=20function=20with=20step-by-ste?= =?UTF-8?q?p=20comment=20numbering=20to=20make=20clear=20where=20we=20do?= =?UTF-8?q?=20(and=20do=20not!)=20follow=20the=20spec.=20Also=20fix=20issu?= =?UTF-8?q?es=20related=20to=20our=20half-pretense=20of=20implementing=20t?= =?UTF-8?q?his=20method=20without=20having=20first=20implemented=20the=20w?= =?UTF-8?q?ell-known=20@@species=20symbol.=20r=3Defaust=20(c0c60afe1)=20-?= =?UTF-8?q?=20Bug=201154532=20-=20Add=20ThrowRangeError=20and=20ThrowTypeE?= =?UTF-8?q?rror=20intrinsics=20to=20make=20self-hosted=20code's=20behavior?= =?UTF-8?q?=20clearer=20--=20and=20also=20have=20each=20assert=20that=20er?= =?UTF-8?q?ror=20number=20and=20requested=20error=20type=20are=20consisten?= =?UTF-8?q?t.=20(It=20appears=20no=20self-hosted=20code=20throws=20SyntaxE?= =?UTF-8?q?rror,=20ReferenceError,=20or=20URIError=20yet,=20so=20no=20addi?= =?UTF-8?q?ng=20functions=20for=20those=20yet.)=20r=3Dtill=20(12b2f552f)?= =?UTF-8?q?=20-=20Bug=201155081=20-=20Part=2011:=20Replace=20ThrowError=20?= =?UTF-8?q?with=20ThrowTypeError=20in=20Utilities.js.=20r=3Dtill=20(b06c0c?= =?UTF-8?q?6d9)=20-=20Bug=201154542=20-=20Remove=20a=20few=20unused=20erro?= =?UTF-8?q?r=20messages.=20r=3Dtill=20(f0248f8cd)=20-=20Bug=201147214=20-?= =?UTF-8?q?=20Allow=20app:=20urls=20to=20use=20sw.=20r=3Dbaku=20(e3e1c02f5?= =?UTF-8?q?)=20-=20Bug=201099149=20-=20Part=202:=20Add=20a=20more=20specif?= =?UTF-8?q?ic=20error=20message=20when=20'js::ToSimdConstant'=20argument?= =?UTF-8?q?=20is=20of=20wrong=20type.=20r=3Dbbouvier=20(3281bf2d6)=20-=20B?= =?UTF-8?q?ug=201161628=20-=20Fix=20leak=20in=20ErrorWrongTypeArg.=20r=3Db?= =?UTF-8?q?bouvier=20(7e9c0f5f9)=20-=20Bug=201146718=20-=20Split=20the=20'?= =?UTF-8?q?J'=20tag=20in=20the=20profiler=20into=20'J'=20(JIT=20frames=20w?= =?UTF-8?q?ithout=20opt=20info)=20and=20'O'=20(with=20opt=20info).=20(r=3D?= =?UTF-8?q?djvj)=20(d5ab6dff8)=20-=20Bug=201137569=20-=20Stream=20and=20sa?= =?UTF-8?q?ve=20samples=20and=20markers=20in=20the=20profiler=20on=20JSRun?= =?UTF-8?q?time=20destruction.=20(r=3Dmstange)=20(b1a6b9a7f)=20-=20Pointer?= =?UTF-8?q?=20style=20(67b999b2b)=20-=20Bug=201137569=20-=20Delay=20string?= =?UTF-8?q?ification=20of=20JIT=20frames=20until=20streaming=20time.=20(r?= =?UTF-8?q?=3Ddjvj)=20(78d743512)=20-=20pointer=20style=20(716a0f543)=20-?= =?UTF-8?q?=20Bug=201140180=20-=20Stop=20leaking=20TypeLists=20when=20trac?= =?UTF-8?q?king=20optimizations.=20(r=3Ddjvj)=20(cda2fe426)=20-=20Bug=2011?= =?UTF-8?q?47224=20-=20Only=20keep=20the=20optimization=20information=20of?= =?UTF-8?q?=20the=20last=20time=20IonBuilder=20visits=20a=20bytecode=20loc?= =?UTF-8?q?ation.=20(r=3Ddjvj)=20(ad93a60b3)=20-=20pointer=20style=20(d29b?= =?UTF-8?q?2ac28)=20-=20Bug=201150714=20-=20Fix=20streaming=20tracked=20op?= =?UTF-8?q?timizations=20for=20functions=20that=20don't=20have=20a=20displ?= =?UTF-8?q?ayAtom.=20(r=3Ddjvj)=20(c38845397)=20-=20pointer=20style=20(073?= =?UTF-8?q?a0adf4)=20-=20Bug=201150714=20-=20Mark=20and=20sweep=20JitcodeG?= =?UTF-8?q?lobalMap=20IC=20entries.=20(r=3Ddjvj)=20(e9fe3dc34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dom/base/Navigator.cpp | 23 + dom/base/Navigator.h | 3 + dom/base/nsDOMClassInfo.cpp | 5 +- dom/base/nsDOMClassInfo.h | 3 +- dom/base/nsGlobalWindow.cpp | 40 + dom/base/nsGlobalWindow.h | 3 + dom/base/nsJSEnvironment.cpp | 6 + dom/base/nsJSEnvironment.h | 7 +- dom/base/nsObjectLoadingContent.cpp | 8 + dom/base/nsObjectLoadingContent.h | 4 + dom/bindings/BindingUtils.cpp | 6 + dom/bindings/BindingUtils.h | 3 + dom/bindings/Codegen.py | 127 +- dom/cache/TypeUtils.cpp | 3 +- dom/indexedDB/ActorsParent.cpp | 1 + dom/plugins/base/nsJSNPRuntime.cpp | 7 +- dom/workers/ServiceWorkerScriptCache.cpp | 50 +- dom/workers/WorkerScope.cpp | 1 + dom/xbl/nsXBLBinding.cpp | 2 +- js/public/Class.h | 28 +- js/public/GCAPI.h | 3 - js/public/MemoryMetrics.h | 12 +- js/public/ProfilingFrameIterator.h | 31 +- js/public/Proxy.h | 2 +- js/public/TracingAPI.h | 61 +- js/public/UbiNode.h | 87 +- js/src/NamespaceImports.h | 24 +- js/src/asmjs/AsmJSFrameIterator.cpp | 22 +- js/src/asmjs/AsmJSModule.cpp | 1 + js/src/asmjs/AsmJSValidate.cpp | 85 +- js/src/builtin/Array.js | 72 +- js/src/builtin/Error.js | 2 +- js/src/builtin/Generator.js | 12 +- js/src/builtin/Intl.cpp | 3 + js/src/builtin/Intl.js | 24 +- js/src/builtin/Map.js | 6 +- js/src/builtin/MapObject.cpp | 7 +- js/src/builtin/Object.cpp | 2 + js/src/builtin/Object.js | 4 +- js/src/builtin/RegExp.js | 4 +- js/src/builtin/SIMD.cpp | 185 ++- js/src/builtin/SIMD.h | 4 +- js/src/builtin/Set.js | 6 +- js/src/builtin/String.js | 16 +- js/src/builtin/SymbolObject.cpp | 1 + js/src/builtin/TestingFunctions.cpp | 62 +- js/src/builtin/TypedArray.js | 48 +- js/src/builtin/TypedObject.cpp | 5 + js/src/builtin/TypedObject.js | 107 +- js/src/builtin/Utilities.js | 44 +- js/src/builtin/WeakSet.js | 18 +- js/src/ctypes/CTypes.cpp | 101 +- js/src/ctypes/Library.cpp | 2 +- js/src/devtools/rootAnalysis/annotations.js | 6 + js/src/doc/Debugger/Debugger.Frame.md | 6 + js/src/doc/Debugger/Debugger.Memory.md | 23 +- js/src/ds/IdValuePair.h | 4 +- js/src/frontend/BytecodeEmitter.cpp | 69 +- js/src/frontend/BytecodeEmitter.h | 2 +- js/src/gc/Barrier.h | 24 +- js/src/gc/GCInternals.h | 62 +- js/src/gc/GCRuntime.h | 5 - js/src/gc/Marking.cpp | 479 ++++++- js/src/gc/Marking.h | 10 +- js/src/gc/Nursery-inl.h | 53 +- js/src/gc/Nursery.cpp | 710 +++------- js/src/gc/Nursery.h | 149 +- js/src/gc/RootMarking.cpp | 13 +- js/src/gc/StoreBuffer.cpp | 100 +- js/src/gc/StoreBuffer.h | 43 +- js/src/gc/Tracer.cpp | 1 - js/src/gc/Verifier.cpp | 188 --- js/src/gdb/gdb-tests.cpp | 2 +- .../irregexp/NativeRegExpMacroAssembler.cpp | 56 +- js/src/irregexp/NativeRegExpMacroAssembler.h | 2 +- js/src/jit-test/tests/SIMD/bool.js | 15 + js/src/jit-test/tests/SIMD/convert.js | 38 +- js/src/jit-test/tests/SIMD/shift.js | 57 + .../tests/arguments/arguments-on-proto.js | 27 + .../jit-test/tests/arrays/sort-getter-only.js | 6 +- js/src/jit-test/tests/basic/bug1153057.js | 1 + js/src/jit-test/tests/basic/bug1161762.js | 24 + .../jit-test/tests/basic/constructor-name.js | 28 + .../collections/Array-of-nonconfigurable-1.js | 8 + .../collections/Array-of-nonconfigurable-2.js | 16 + .../jit-test/tests/debug/Environment-gc-03.js | 21 + .../tests/debug/Environment-identity-05.js | 19 + .../tests/debug/Frame-environment-05.js | 9 + .../Frame-onPop-error-scope-unwind-01.js | 7 +- .../Frame-onPop-error-scope-unwind-02.js | 7 +- .../debug/Memory-drainAllocationsLog-15.js | 31 + .../debug/Memory-drainAllocationsLog-16.js | 47 + .../tests/heap-analysis/byteSize-of-object.js | 83 ++ .../tests/heap-analysis/byteSize-of-string.js | 131 ++ .../tests/heap-analysis/pointerByteSize.js | 3 + js/src/jit-test/tests/ion/bug1155807.js | 15 + js/src/jit-test/tests/ion/recover-arrays.js | 4 +- js/src/jit-test/tests/ion/recover-objects.js | 38 +- .../tests/proxy/proxy-array-length.js | 4 + ...stDirectProxyGetOwnPropertyDescriptor11.js | 6 +- .../tests/proxy/testDirectProxySetFailure.js | 8 - ...stIndirectProxyGetOwnPropertyDescriptor.js | 6 +- js/src/jit/BaselineCompiler.cpp | 69 +- js/src/jit/BaselineCompiler.h | 1 + js/src/jit/BaselineDebugModeOSR.cpp | 4 +- js/src/jit/BaselineIC.cpp | 926 ++++++++----- js/src/jit/BaselineIC.h | 654 ++++----- js/src/jit/BaselineInspector.cpp | 47 +- js/src/jit/BaselineInspector.h | 6 +- js/src/jit/CodeGenerator.cpp | 495 ++++--- js/src/jit/CodeGenerator.h | 3 + js/src/jit/Ion.cpp | 7 +- js/src/jit/IonBuilder.cpp | 415 +++--- js/src/jit/IonBuilder.h | 19 +- js/src/jit/IonCaches.cpp | 66 +- js/src/jit/IonTypes.h | 14 +- js/src/jit/JitFrames.cpp | 11 +- js/src/jit/JitcodeMap.cpp | 19 +- js/src/jit/JitcodeMap.h | 90 +- js/src/jit/LIR-Common.h | 79 +- js/src/jit/LOpcodes.h | 3 + js/src/jit/Lowering.cpp | 37 +- js/src/jit/Lowering.h | 3 + js/src/jit/MCallOptimize.cpp | 74 +- js/src/jit/MIR.cpp | 115 +- js/src/jit/MIR.h | 252 +++- js/src/jit/MIRGenerator.h | 25 +- js/src/jit/MIRGraph.cpp | 7 +- js/src/jit/MOpcodes.h | 3 + js/src/jit/MacroAssembler.cpp | 60 +- js/src/jit/MacroAssembler.h | 64 +- js/src/jit/OptimizationTracking.cpp | 56 +- js/src/jit/OptimizationTracking.h | 16 +- js/src/jit/Recover.cpp | 4 - js/src/jit/RematerializedFrame.cpp | 2 +- js/src/jit/ScalarReplacement.cpp | 4 + js/src/jit/VMFunctions.cpp | 40 +- js/src/jit/VMFunctions.h | 28 +- js/src/jit/arm/Assembler-arm.h | 4 + js/src/jit/mips/Assembler-mips.h | 4 + js/src/jit/none/MacroAssembler-none.h | 2 + js/src/jit/shared/CodeGenerator-shared.cpp | 17 +- js/src/jit/x64/MacroAssembler-x64.cpp | 7 +- js/src/jit/x86-shared/Assembler-x86-shared.h | 4 + .../x86-shared/CodeGenerator-x86-shared.cpp | 62 +- .../jit/x86-shared/CodeGenerator-x86-shared.h | 37 +- .../x86-shared/MacroAssembler-x86-shared.h | 5 +- js/src/js.msg | 8 +- js/src/jsapi-tests/moz.build | 1 - .../jsapi-tests/testAddPropertyPropcache.cpp | 2 +- js/src/jsapi-tests/testChromeBuffer.cpp | 1 + .../testFreshGlobalEvalRedefinition.cpp | 2 +- js/src/jsapi-tests/testGCNursery.cpp | 2 + js/src/jsapi-tests/testNewObject.cpp | 2 +- js/src/jsapi-tests/testOps.cpp | 2 +- js/src/jsapi-tests/testPersistentRooted.cpp | 1 + js/src/jsapi-tests/testPropCache.cpp | 2 +- js/src/jsapi-tests/testUbiNode.cpp | 15 + js/src/jsapi-tests/testWeakMap.cpp | 2 + js/src/jsapi-tests/tests.h | 2 +- js/src/jsapi.cpp | 40 +- js/src/jsapi.h | 152 +-- js/src/jsarray.cpp | 161 +-- js/src/jscntxt.cpp | 7 +- js/src/jscntxt.h | 45 +- js/src/jscntxtinlines.h | 16 +- js/src/jscompartment.cpp | 8 +- js/src/jscompartment.h | 5 +- js/src/jsdate.cpp | 1 + js/src/jsexn.cpp | 2 + js/src/jsfriendapi.h | 23 +- js/src/jsfun.cpp | 13 +- js/src/jsfun.h | 7 +- js/src/jsgc.cpp | 59 +- js/src/jsgc.h | 13 +- js/src/jsiter.cpp | 153 ++- js/src/jsiter.h | 7 +- js/src/jsobj.cpp | 617 +++++---- js/src/jsobj.h | 151 ++- js/src/jsobjinlines.h | 57 +- js/src/jspubtd.h | 17 +- js/src/jsscript.cpp | 1 + js/src/jsscript.h | 9 + js/src/jsstr.cpp | 14 +- js/src/jsstr.h | 3 - js/src/jsweakmap.cpp | 1 + js/src/moz.build | 1 + js/src/perf/jsperf.cpp | 2 +- js/src/proxy/BaseProxyHandler.cpp | 4 +- js/src/proxy/ScriptedDirectProxyHandler.cpp | 2 +- js/src/proxy/ScriptedIndirectProxyHandler.cpp | 3 +- js/src/shell/js.cpp | 26 +- js/src/tests/ecma_6/Class/classPrototype.js | 12 - .../tests/ecma_6/Class/superPropHomeObject.js | 3 +- .../ecma_6/Object/getOwnPropertyDescriptor.js | 12 +- .../Object/property-descriptor-order.js | 17 + js/src/tests/ecma_6/Symbol/property-basics.js | 6 +- .../ecma_6/Symbol/property-reflection.js | 12 +- js/src/tests/ecma_6/TypedArray/of.js | 6 +- js/src/tests/ecma_6/TypedArray/slice.js | 13 +- js/src/tests/ecma_7/SIMD/conversions.js | 309 +++++ .../ecma_7/SIMD/float32x4fromfloat64x2.js | 60 - .../ecma_7/SIMD/float32x4fromfloat64x2bits.js | 43 - .../tests/ecma_7/SIMD/float32x4fromint32x4.js | 35 - .../ecma_7/SIMD/float32x4fromint32x4bits.js | 33 - .../ecma_7/SIMD/float64x2fromfloat32x4.js | 41 - .../ecma_7/SIMD/float64x2fromfloat32x4bits.js | 32 - .../tests/ecma_7/SIMD/float64x2fromint32x4.js | 31 - .../ecma_7/SIMD/float64x2fromint32x4bits.js | 31 - .../tests/ecma_7/SIMD/int32x4fromfloat32x4.js | 30 - .../ecma_7/SIMD/int32x4fromfloat32x4bits.js | 30 - .../tests/ecma_7/SIMD/int32x4fromfloat64x2.js | 51 - .../ecma_7/SIMD/int32x4fromfloat64x2bits.js | 49 - js/src/tests/ecma_7/SIMD/shell.js | 31 + js/src/tests/ecma_7/SIMD/shifts.js | 8 +- js/src/vm/ArgumentsObject.cpp | 8 +- js/src/vm/ArrayBufferObject.cpp | 1 + js/src/vm/ArrayObject-inl.h | 2 + js/src/vm/Debugger-inl.h | 2 +- js/src/vm/Debugger.cpp | 70 +- js/src/vm/Debugger.h | 32 +- js/src/vm/DebuggerMemory.cpp | 13 + js/src/vm/GlobalObject.cpp | 2 +- js/src/vm/HelperThreads.cpp | 2 +- js/src/vm/Interpreter-inl.h | 2 +- js/src/vm/Interpreter.cpp | 487 ++++--- js/src/vm/Interpreter.h | 94 +- js/src/vm/NativeObject-inl.h | 35 +- js/src/vm/NativeObject.cpp | 1070 +++++++-------- js/src/vm/NativeObject.h | 37 +- js/src/vm/ObjectGroup.cpp | 22 +- js/src/vm/ObjectGroup.h | 80 +- js/src/vm/Opcodes.h | 32 +- js/src/vm/PIC.cpp | 2 +- js/src/vm/ReceiverGuard.cpp | 64 + js/src/vm/ReceiverGuard.h | 137 ++ js/src/vm/RegExpObject.cpp | 1 + js/src/vm/RegExpStatics.cpp | 1 + js/src/vm/Runtime-inl.h | 2 + js/src/vm/Runtime.cpp | 4 +- js/src/vm/SavedStacks.cpp | 7 +- js/src/vm/SavedStacks.h | 4 +- js/src/vm/ScopeObject.cpp | 3 + js/src/vm/SelfHosting.cpp | 42 +- js/src/vm/Shape.cpp | 2 +- js/src/vm/Shape.h | 8 +- js/src/vm/SharedArrayObject.cpp | 1 + js/src/vm/SharedTypedArrayObject.cpp | 2 + js/src/vm/Stack.cpp | 127 +- js/src/vm/String.cpp | 18 + js/src/vm/String.h | 6 +- js/src/vm/Symbol.h | 5 + js/src/vm/TypeInference.cpp | 185 +-- js/src/vm/TypeInference.h | 48 +- js/src/vm/TypedArrayObject.cpp | 49 + js/src/vm/TypedArrayObject.h | 8 + js/src/vm/UbiNode.cpp | 39 +- js/src/vm/UnboxedObject.cpp | 1207 ++++++++++++++--- js/src/vm/UnboxedObject.h | 291 +++- js/src/vm/Xdr.h | 4 +- js/xpconnect/idl/nsIXPCScriptable.idl | 4 +- js/xpconnect/public/xpc_map_end.h | 2 +- js/xpconnect/src/Sandbox.cpp | 14 +- js/xpconnect/src/XPCJSRuntime.cpp | 4 +- js/xpconnect/src/XPCWrappedJSClass.cpp | 1 + js/xpconnect/src/XPCWrappedNativeJSOps.cpp | 18 +- js/xpconnect/tests/chrome/test_xrayToJS.xul | 11 +- js/xpconnect/wrappers/FilteringWrapper.h | 4 +- js/xpconnect/wrappers/XrayWrapper.cpp | 2 +- netwerk/base/ProxyAutoConfig.cpp | 2 +- .../FinalizationWitnessService.cpp | 1 + toolkit/components/telemetry/Histograms.json | 2 +- .../debugger/test/browser_dbg_step-out.js | 2 +- .../server/tests/unit/test_stepping-06.js | 2 +- tools/profiler/JSStreamWriter.cpp | 31 + tools/profiler/JSStreamWriter.h | 11 + tools/profiler/ProfileEntry.cpp | 482 ++++--- tools/profiler/ProfileEntry.h | 18 + tools/profiler/PseudoStack.h | 14 +- tools/profiler/TableTicker.cpp | 67 +- tools/profiler/TableTicker.h | 1 + xpcom/glue/tests/gtest/TestGCPostBarriers.cpp | 2 +- 282 files changed, 9591 insertions(+), 5838 deletions(-) create mode 100644 js/src/jit-test/tests/SIMD/bool.js create mode 100644 js/src/jit-test/tests/SIMD/shift.js create mode 100644 js/src/jit-test/tests/arguments/arguments-on-proto.js create mode 100644 js/src/jit-test/tests/basic/bug1153057.js create mode 100644 js/src/jit-test/tests/basic/bug1161762.js create mode 100644 js/src/jit-test/tests/basic/constructor-name.js create mode 100644 js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js create mode 100644 js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js create mode 100644 js/src/jit-test/tests/debug/Environment-gc-03.js create mode 100644 js/src/jit-test/tests/debug/Environment-identity-05.js create mode 100644 js/src/jit-test/tests/debug/Frame-environment-05.js create mode 100644 js/src/jit-test/tests/debug/Memory-drainAllocationsLog-15.js create mode 100644 js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js create mode 100644 js/src/jit-test/tests/heap-analysis/byteSize-of-object.js create mode 100644 js/src/jit-test/tests/heap-analysis/byteSize-of-string.js create mode 100644 js/src/jit-test/tests/heap-analysis/pointerByteSize.js create mode 100644 js/src/jit-test/tests/ion/bug1155807.js create mode 100644 js/src/jit-test/tests/proxy/proxy-array-length.js create mode 100644 js/src/tests/ecma_6/Object/property-descriptor-order.js create mode 100644 js/src/tests/ecma_7/SIMD/conversions.js delete mode 100644 js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js delete mode 100644 js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js delete mode 100644 js/src/tests/ecma_7/SIMD/float32x4fromint32x4.js delete mode 100644 js/src/tests/ecma_7/SIMD/float32x4fromint32x4bits.js delete mode 100644 js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js delete mode 100644 js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js delete mode 100644 js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js delete mode 100644 js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js delete mode 100644 js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4.js delete mode 100644 js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4bits.js delete mode 100644 js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js delete mode 100644 js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js create mode 100644 js/src/vm/ReceiverGuard.cpp create mode 100644 js/src/vm/ReceiverGuard.h diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 486cdc9340..634bf7754d 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -2100,6 +2100,7 @@ Navigator::DoResolve(JSContext* aCx, JS::Handle aObject, JS::Handle aId, JS::MutableHandle aDesc) { + // Note: Keep this in sync with MayResolve. if (!JSID_IS_STRING(aId)) { return true; } @@ -2251,6 +2252,28 @@ Navigator::DoResolve(JSContext* aCx, JS::Handle aObject, return true; } +/* static */ +bool +Navigator::MayResolve(jsid aId) +{ + // Note: This function does not fail and may not have any side-effects. + // Note: Keep this in sync with DoResolve. + if (!JSID_IS_STRING(aId)) { + return false; + } + + nsScriptNameSpaceManager *nameSpaceManager = PeekNameSpaceManager(); + if (!nameSpaceManager) { + // Really shouldn't happen here. Fail safe. + return true; + } + + nsAutoString name; + AssignJSFlatString(name, JSID_TO_FLAT_STRING(aId)); + + return nameSpaceManager->LookupNavigatorName(name); +} + struct NavigatorNameEnumeratorClosure { NavigatorNameEnumeratorClosure(JSContext* aCx, JSObject* aWrapper, diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 8c53c5d27d..5568dda809 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -283,6 +283,9 @@ public: bool DoResolve(JSContext* aCx, JS::Handle aObject, JS::Handle aId, JS::MutableHandle aDesc); + // The return value is whether DoResolve might end up resolving the given id. + // If in doubt, return true. + static bool MayResolve(jsid aId); void GetOwnPropertyNames(JSContext* aCx, nsTArray& aNames, ErrorResult& aRv); void GetLanguages(nsTArray& aLanguages); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 9de95e0c97..58f26004dd 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -909,7 +909,7 @@ nsDOMClassInfo::PreCreate(nsISupports *nativeObj, JSContext *cx, NS_IMETHODIMP nsDOMClassInfo::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, + JSObject *obj, jsid id, JS::Handle val, bool *_retval) { NS_WARNING("nsDOMClassInfo::AddProperty Don't call me!"); @@ -2423,7 +2423,8 @@ nsEventTargetSH::PreCreate(nsISupports *nativeObj, JSContext *cx, NS_IMETHODIMP nsEventTargetSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, bool *_retval) + JSObject *obj, jsid id, JS::Handle val, + bool *_retval) { nsEventTargetSH::PreserveWrapper(GetNative(wrapper, obj)); diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 9a321ad14e..4367dfcc18 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -205,7 +205,8 @@ public: NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj) override; NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, JS::Value *vp, bool *_retval) override; + JSObject *obj, jsid id, JS::Handle val, + bool *_retval) override; virtual void PreserveWrapper(nsISupports *aNative) override; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index e941d052a3..402d9ce92f 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -4274,6 +4274,8 @@ nsGlobalWindow::DoResolve(JSContext* aCx, JS::Handle aObj, { MOZ_ASSERT(IsInnerWindow()); + // Note: Keep this in sync with MayResolve. + // Note: The infallibleInit call in GlobalResolve depends on this check. if (!JSID_IS_STRING(aId)) { return true; @@ -4287,6 +4289,44 @@ nsGlobalWindow::DoResolve(JSContext* aCx, JS::Handle aObj, return true; } +/* static */ +bool +nsGlobalWindow::MayResolve(jsid aId) +{ + // Note: This function does not fail and may not have any side-effects. + // Note: Keep this in sync with DoResolve. + if (!JSID_IS_STRING(aId)) { + return false; + } + + if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) { + return true; + } + + if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_CONTROLLERS)) { + // We only resolve .controllers in release builds and on non-chrome windows, + // but let's not worry about any of that stuff. + return true; + } + + nsScriptNameSpaceManager *nameSpaceManager = PeekNameSpaceManager(); + if (!nameSpaceManager) { + // Really shouldn't happen. Fail safe. + return true; + } + + nsAutoString name; + AssignJSFlatString(name, JSID_TO_FLAT_STRING(aId)); + + const nsGlobalNameStruct *name_struct = + nameSpaceManager->LookupName(name); + + // LookupName only returns structs for the global. + MOZ_ASSERT_IF(name_struct, + name_struct->mType != nsGlobalNameStruct::eTypeNavigatorProperty); + return name_struct; +} + struct GlobalNameEnumeratorClosure { GlobalNameEnumeratorClosure(JSContext* aCx, nsGlobalWindow* aWindow, diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 9143fd897c..c631e1a776 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -511,6 +511,9 @@ public: bool DoResolve(JSContext* aCx, JS::Handle aObj, JS::Handle aId, JS::MutableHandle aDesc); + // The return value is whether DoResolve might end up resolving the given id. + // If in doubt, return true. + static bool MayResolve(jsid aId); void GetOwnPropertyNames(JSContext* aCx, nsTArray& aNames, mozilla::ErrorResult& aRv); diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 237f41eb0b..bdff07e4db 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2821,6 +2821,12 @@ mozilla::dom::GetNameSpaceManager() return gNameSpaceManager; } +nsScriptNameSpaceManager* +mozilla::dom::PeekNameSpaceManager() +{ + return gNameSpaceManager; +} + void mozilla::dom::ShutdownJSEnvironment() { diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index c36c1ba60f..6a4429b7a8 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -23,7 +23,9 @@ class nsICycleCollectorListener; class nsScriptNameSpaceManager; namespace JS { -class AutoValueVector; +template +class AutoVectorRooter; +typedef AutoVectorRooter AutoValueVector; } namespace mozilla { @@ -183,6 +185,9 @@ void ShutdownJSEnvironment(); // Get the NameSpaceManager, creating if necessary nsScriptNameSpaceManager* GetNameSpaceManager(); +// Peek the NameSpaceManager, without creating it. +nsScriptNameSpaceManager* PeekNameSpaceManager(); + // Runnable that's used to do async error reporting class AsyncErrorReporter : public nsRunnable { diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 25efdaefd2..964bd68575 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -3625,6 +3625,14 @@ nsObjectLoadingContent::DoResolve(JSContext* aCx, JS::Handle aObject, return true; } +/* static */ +bool +nsObjectLoadingContent::MayResolve(jsid aId) +{ + // We can resolve anything, really. + return true; +} + void nsObjectLoadingContent::GetOwnPropertyNames(JSContext* aCx, nsTArray& /* unused */, diff --git a/dom/base/nsObjectLoadingContent.h b/dom/base/nsObjectLoadingContent.h index b7204e4945..6569407ceb 100644 --- a/dom/base/nsObjectLoadingContent.h +++ b/dom/base/nsObjectLoadingContent.h @@ -171,6 +171,10 @@ class nsObjectLoadingContent : public nsImageLoadingContent bool DoResolve(JSContext* aCx, JS::Handle aObject, JS::Handle aId, JS::MutableHandle aDesc); + // The return value is whether DoResolve might end up resolving the given + // id. If in doubt, return true. + static bool MayResolve(jsid aId); + // Helper for WebIDL enumeration void GetOwnPropertyNames(JSContext* aCx, nsTArray& /* unused */, mozilla::ErrorResult& aRv); diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 51a53caf50..da5bb2c0d2 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -2423,6 +2423,12 @@ ResolveGlobal(JSContext* aCx, JS::Handle aObj, return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp); } +bool +MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj) +{ + return JS_MayResolveStandardClass(aNames, aId, aMaybeObj); +} + bool EnumerateGlobal(JSContext* aCx, JS::Handle aObj) { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 1aa8e708f6..bd7ff01f83 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -2975,6 +2975,9 @@ bool ResolveGlobal(JSContext* aCx, JS::Handle aObj, JS::Handle aId, bool* aResolvedp); +bool +MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj); + bool EnumerateGlobal(JSContext* aCx, JS::Handle aObj); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 36292a73b9..bea3ec482b 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -23,6 +23,7 @@ CONSTRUCT_HOOK_NAME = '_constructor' LEGACYCALLER_HOOK_NAME = '_legacycaller' HASINSTANCE_HOOK_NAME = '_hasInstance' RESOLVE_HOOK_NAME = '_resolve' +MAY_RESOLVE_HOOK_NAME = '_mayResolve' ENUMERATE_HOOK_NAME = '_enumerate' ENUM_ENTRY_VARIABLE_NAME = 'strings' INSTANCE_RESERVED_SLOTS = 1 @@ -452,12 +453,15 @@ class CGDOMJSClass(CGThing): reservedSlots = slotCount if self.descriptor.interface.getExtendedAttribute("NeedResolve"): resolveHook = RESOLVE_HOOK_NAME + mayResolveHook = MAY_RESOLVE_HOOK_NAME enumerateHook = ENUMERATE_HOOK_NAME elif self.descriptor.isGlobal(): resolveHook = "mozilla::dom::ResolveGlobal" + mayResolveHook = "mozilla::dom::MayResolveGlobal" enumerateHook = "mozilla::dom::EnumerateGlobal" else: resolveHook = "nullptr" + mayResolveHook = "nullptr" enumerateHook = "nullptr" return fill( @@ -471,6 +475,7 @@ class CGDOMJSClass(CGThing): nullptr, /* setProperty */ ${enumerate}, /* enumerate */ ${resolve}, /* resolve */ + ${mayResolve}, /* mayResolve */ nullptr, /* convert */ ${finalize}, /* finalize */ ${call}, /* call */ @@ -492,6 +497,7 @@ class CGDOMJSClass(CGThing): addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr', enumerate=enumerateHook, resolve=resolveHook, + mayResolve=mayResolveHook, finalize=FINALIZE_HOOK_NAME, call=callHook, trace=traceHook, @@ -611,6 +617,7 @@ class CGPrototypeJSClass(CGThing): nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -704,6 +711,7 @@ class CGInterfaceObjectJSClass(CGThing): nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ ${ctorname}, /* call */ @@ -1503,7 +1511,7 @@ class CGAddPropertyHook(CGAbstractClassHook): args = [Argument('JSContext*', 'cx'), Argument('JS::Handle', 'obj'), Argument('JS::Handle', 'id'), - Argument('JS::MutableHandle', 'vp')] + Argument('JS::Handle', 'val')] CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME, 'bool', args) @@ -7698,7 +7706,7 @@ class CGLegacyCallHook(CGAbstractBindingMethod): self._legacycaller) -class CGResolveHook(CGAbstractBindingMethod): +class CGResolveHook(CGAbstractClassHook): """ Resolve hook for objects that have the NeedResolve extended attribute. """ @@ -7709,13 +7717,11 @@ class CGResolveHook(CGAbstractBindingMethod): Argument('JS::Handle', 'obj'), Argument('JS::Handle', 'id'), Argument('bool*', 'resolvedp')] - # Our "self" is actually the "obj" argument in this case, not the thisval. - CGAbstractBindingMethod.__init__( - self, descriptor, RESOLVE_HOOK_NAME, - args, getThisObj="", callArgs="") + CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, + "bool", args) def generate_code(self): - return CGGeneric(dedent(""" + return dedent(""" JS::Rooted desc(cx); if (!self->DoResolve(cx, obj, id, &desc)) { return false; @@ -7732,7 +7738,7 @@ class CGResolveHook(CGAbstractBindingMethod): } *resolvedp = true; return true; - """)) + """) def definition_body(self): if self.descriptor.isGlobal(): @@ -7748,7 +7754,35 @@ class CGResolveHook(CGAbstractBindingMethod): """) else: prefix = "" - return prefix + CGAbstractBindingMethod.definition_body(self) + return prefix + CGAbstractClassHook.definition_body(self) + + +class CGMayResolveHook(CGAbstractStaticMethod): + """ + Resolve hook for objects that have the NeedResolve extended attribute. + """ + def __init__(self, descriptor): + assert descriptor.interface.getExtendedAttribute("NeedResolve") + + args = [Argument('const JSAtomState&', 'names'), + Argument('jsid', 'id'), + Argument('JSObject*', 'maybeObj')] + CGAbstractStaticMethod.__init__(self, descriptor, MAY_RESOLVE_HOOK_NAME, + "bool", args) + + def definition_body(self): + if self.descriptor.isGlobal(): + # Check whether this would resolve as a standard class. + prefix = dedent(""" + if (MayResolveGlobal(names, id, maybeObj)) { + return true; + } + + """) + else: + prefix = "" + return (prefix + + "return %s::MayResolve(id);\n" % self.descriptor.nativeType) class CGEnumerateHook(CGAbstractBindingMethod): @@ -8128,8 +8162,8 @@ class CGMemberJITInfo(CGThing): return "" def defineJitInfo(self, infoName, opName, opType, infallible, movable, - aliasSet, alwaysInSlot, lazilyInSlot, slotIndex, - returnTypes, args): + eliminatable, aliasSet, alwaysInSlot, lazilyInSlot, + slotIndex, returnTypes, args): """ aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit. @@ -8139,12 +8173,14 @@ class CGMemberJITInfo(CGThing): """ assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things assert(not alwaysInSlot or movable) # Things always in slots had better be movable + assert(not eliminatable or aliasSet != "AliasEverything") # Can't eliminate write-aliasing things + assert(not alwaysInSlot or eliminatable) # Things always in slots had better be eliminatable def jitInfoInitializer(isTypedMethod): initializer = fill( """ { - { ${opName} }, + { ${opName} }, prototypes::id::${name}, PrototypeTraits::Depth, JSJitInfo::${opType}, @@ -8152,6 +8188,7 @@ class CGMemberJITInfo(CGThing): ${returnType}, /* returnType. Not relevant for setters. */ ${isInfallible}, /* isInfallible. False in setters. */ ${isMovable}, /* isMovable. Not relevant for setters. */ + ${isEliminatable}, /* isEliminatable. Not relevant for setters. */ ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */ ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */ ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */ @@ -8166,12 +8203,17 @@ class CGMemberJITInfo(CGThing): ""), isInfallible=toStringBool(infallible), isMovable=toStringBool(movable), + isEliminatable=toStringBool(eliminatable), isAlwaysInSlot=toStringBool(alwaysInSlot), isLazilyCachedInSlot=toStringBool(lazilyInSlot), isTypedMethod=toStringBool(isTypedMethod), slotIndex=slotIndex) return initializer.rstrip() + slotAssert = dedent( + """ + static_assert(%s <= JSJitInfo::maxSlotIndex, "We won't fit"); + """ % slotIndex) if args is not None: argTypes = "%s_argTypes" % infoName args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] @@ -8181,21 +8223,27 @@ class CGMemberJITInfo(CGThing): (argTypes, ", ".join(args))) return fill( """ - $*{argTypesDecl} static const JSTypedMethodJitInfo ${infoName} = { - ${jitInfo}, + ${jitInfo}, ${argTypes} }; + $*{slotAssert} """, argTypesDecl=argTypesDecl, infoName=infoName, - jitInfo=jitInfoInitializer(True), - argTypes=argTypes) + jitInfo=indent(jitInfoInitializer(True)), + argTypes=argTypes, + slotAssert=slotAssert) - return ("\n" - "static const JSJitInfo %s = %s;\n" - % (infoName, jitInfoInitializer(False))) + return fill( + """ + static const JSJitInfo ${infoName} = ${jitInfo}; + $*{slotAssert} + """, + infoName=infoName, + jitInfo=jitInfoInitializer(False), + slotAssert=slotAssert) def define(self): if self.member.isAttr(): @@ -8208,6 +8256,7 @@ class CGMemberJITInfo(CGThing): getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) movable = self.mayBeMovable() and getterinfal + eliminatable = self.mayBeEliminatable() and getterinfal aliasSet = self.aliasSet() getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor) @@ -8224,9 +8273,9 @@ class CGMemberJITInfo(CGThing): slotIndex = "0" result = self.defineJitInfo(getterinfo, getter, "Getter", - getterinfal, movable, aliasSet, - isAlwaysInSlot, isLazilyCachedInSlot, - slotIndex, + getterinfal, movable, eliminatable, + aliasSet, isAlwaysInSlot, + isLazilyCachedInSlot, slotIndex, [self.member.type], None) if (not self.member.readonly or self.member.getExtendedAttribute("PutForwards") is not None or @@ -8239,7 +8288,7 @@ class CGMemberJITInfo(CGThing): IDLToCIdentifier(self.member.identifier.name)) # Setters are always fallible, since they have to do a typed unwrap. result += self.defineJitInfo(setterinfo, setter, "Setter", - False, False, "AliasEverything", + False, False, False, "AliasEverything", False, False, "0", [BuiltinTypes[IDLBuiltinType.Types.void]], None) @@ -8264,6 +8313,7 @@ class CGMemberJITInfo(CGThing): methodInfal = False args = None movable = False + eliminatable = False else: sig = sigs[0] # For methods that affect nothing, it's OK to set movable to our @@ -8273,6 +8323,7 @@ class CGMemberJITInfo(CGThing): # move effectful things. hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member) movable = self.mayBeMovable() and hasInfallibleImpl + eliminatable = self.mayBeEliminatable() and hasInfallibleImpl # XXXbz can we move the smarts about fallibility due to arg # conversions into the JIT, using our new args stuff? if (len(sig[1]) != 0 or @@ -8289,8 +8340,8 @@ class CGMemberJITInfo(CGThing): aliasSet = self.aliasSet() result = self.defineJitInfo(methodinfo, method, "Method", - methodInfal, movable, aliasSet, - False, False, "0", + methodInfal, movable, eliminatable, + aliasSet, False, False, "0", [s[0] for s in sigs], args) return result raise TypeError("Illegal member type to CGPropertyJITInfo") @@ -8311,12 +8362,33 @@ class CGMemberJITInfo(CGThing): return (affects == "Nothing" and (dependsOn != "Everything" and dependsOn != "DeviceState")) + def mayBeEliminatable(self): + """ + Returns whether this attribute or method may be eliminatable, just + based on Affects/DependsOn annotations. + """ + # dependsOn shouldn't affect this decision at all, except in jitinfo we + # have no way to express "Depends on everything, affects nothing", + # because we only have three alias set values: AliasNone ("depends on + # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets, + # affects nothing"), AliasEverything ("depends on everything, affects + # everything"). So the [Affects=Nothing, DependsOn=Everything] case + # gets encoded as AliasEverything and defineJitInfo asserts that if our + # alias state is AliasEverything then we're not eliminatable (because it + # thinks we might have side-effects at that point). Bug 1155796 is + # tracking possible solutions for this. + affects = self.member.affects + dependsOn = self.member.dependsOn + assert affects in IDLInterfaceMember.AffectsValues + assert dependsOn in IDLInterfaceMember.DependsOnValues + return affects == "Nothing" and dependsOn != "Everything" + def aliasSet(self): - """Returns the alias set to store in the jitinfo. This may not be the + """ + Returns the alias set to store in the jitinfo. This may not be the effective alias set the JIT uses, depending on whether we have enough information about our args to allow the JIT to prove that effectful argument conversions won't happen. - """ dependsOn = self.member.dependsOn assert dependsOn in IDLInterfaceMember.DependsOnValues @@ -11224,6 +11296,7 @@ class CGDescriptor(CGThing): cgThings.append(CGLegacyCallHook(descriptor)) if descriptor.interface.getExtendedAttribute("NeedResolve"): cgThings.append(CGResolveHook(descriptor)) + cgThings.append(CGMayResolveHook(descriptor)) cgThings.append(CGEnumerateHook(descriptor)) if descriptor.hasNamedPropertiesObject: diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp index 9b5f708ee2..e79ec4cc0c 100644 --- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -68,7 +68,8 @@ ProcessURL(nsAString& aUrl, bool* aSchemeValidOut, if (aSchemeValidOut) { nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen)); *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") || - scheme.LowerCaseEqualsLiteral("https"); + scheme.LowerCaseEqualsLiteral("https") || + scheme.LowerCaseEqualsLiteral("app"); } uint32_t queryPos; diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 805cc76bf5..7a0f0a162c 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -19839,6 +19839,7 @@ const JSClass CreateIndexOp::ThreadLocalJSRuntime::kGlobalClass = { /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ nullptr, + /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ nullptr, diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index f58058f930..1423ad39db 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -163,7 +163,7 @@ NPClass nsJSObjWrapper::sJSObjWrapperNPClass = }; static bool -NPObjWrapper_AddProperty(JSContext *cx, JS::Handle obj, JS::Handle id, JS::MutableHandle vp); +NPObjWrapper_AddProperty(JSContext *cx, JS::Handle obj, JS::Handle id, JS::Handle v); static bool NPObjWrapper_DelProperty(JSContext *cx, JS::Handle obj, JS::Handle id, @@ -214,6 +214,7 @@ const static js::Class sNPObjectJSWrapperClass = NPObjWrapper_SetProperty, nullptr, NPObjWrapper_Resolve, + nullptr, /* mayResolve */ NPObjWrapper_Convert, NPObjWrapper_Finalize, NPObjWrapper_Call, @@ -266,7 +267,7 @@ static const JSClass sNPObjectMemberClass = { "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, NPObjectMember_Convert, + nullptr, nullptr, nullptr, NPObjectMember_Convert, NPObjectMember_Finalize, NPObjectMember_Call, nullptr, nullptr, NPObjectMember_Trace }; @@ -1254,7 +1255,7 @@ GetNPObject(JSContext *cx, JS::Handle aObj) // Does not actually add a property because this is always followed by a // SetProperty call. static bool -NPObjWrapper_AddProperty(JSContext *cx, JS::Handle obj, JS::Handle id, JS::MutableHandle vp) +NPObjWrapper_AddProperty(JSContext *cx, JS::Handle obj, JS::Handle id, JS::Handle v) { NPObject *npobj = GetNPObject(cx, obj); diff --git a/dom/workers/ServiceWorkerScriptCache.cpp b/dom/workers/ServiceWorkerScriptCache.cpp index b3833add97..69d1034d14 100644 --- a/dom/workers/ServiceWorkerScriptCache.cpp +++ b/dom/workers/ServiceWorkerScriptCache.cpp @@ -635,21 +635,47 @@ CompareNetwork::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext } nsCOMPtr httpChannel = do_QueryInterface(request); - if (!httpChannel) { - mManager->NetworkFinished(NS_ERROR_FAILURE); - return NS_OK; - } + if (httpChannel) { + bool requestSucceeded; + rv = httpChannel->GetRequestSucceeded(&requestSucceeded); + if (NS_WARN_IF(NS_FAILED(rv))) { + mManager->NetworkFinished(rv); + return NS_OK; + } - bool requestSucceeded; - rv = httpChannel->GetRequestSucceeded(&requestSucceeded); - if (NS_WARN_IF(NS_FAILED(rv))) { - mManager->NetworkFinished(rv); - return NS_OK; + if (!requestSucceeded) { + mManager->NetworkFinished(NS_ERROR_FAILURE); + return NS_OK; + } } + else { + // The only supported request schemes are http, https, and app. + // Above, we check to ensure that the request is http or https + // based on the channel qi. Here we test the scheme to ensure + // that it is app. Otherwise, bail. + nsCOMPtr channel = do_QueryInterface(request); + if (NS_WARN_IF(!channel)) { + mManager->NetworkFinished(NS_ERROR_FAILURE); + return NS_OK; + } + nsCOMPtr uri; + rv = channel->GetURI(getter_AddRefs(uri)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mManager->NetworkFinished(rv); + return NS_OK; + } - if (!requestSucceeded) { - mManager->NetworkFinished(NS_ERROR_FAILURE); - return NS_OK; + nsAutoCString scheme; + rv = uri->GetScheme(scheme); + if (NS_WARN_IF(NS_FAILED(rv))) { + mManager->NetworkFinished(rv); + return NS_OK; + } + + if (!scheme.LowerCaseEqualsLiteral("app")) { + mManager->NetworkFinished(NS_ERROR_FAILURE); + return NS_OK; + } } // FIXME(nsm): "Extract mime type..." diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 38213ac56b..4e50652995 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -627,6 +627,7 @@ const js::Class workerdebuggersandbox_class = { nullptr, workerdebuggersandbox_enumerate, workerdebuggersandbox_resolve, + nullptr, /* mayResolve */ workerdebuggersandbox_convert, workerdebuggersandbox_finalize, nullptr, diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index a760ae9200..c84af30858 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -93,7 +93,7 @@ static const JSClass gPrototypeJSClass = { // Our one reserved slot holds the relevant nsXBLPrototypeBinding JSCLASS_HAS_RESERVED_SLOTS(1), nullptr, nullptr, nullptr, nullptr, - XBLEnumerate, nullptr, + XBLEnumerate, nullptr, nullptr, nullptr, XBLFinalize, nullptr, nullptr, nullptr, nullptr }; diff --git a/js/public/Class.h b/js/public/Class.h index f693cf5eb2..25608fd330 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -24,6 +24,7 @@ * object behavior and, e.g., allows custom slow layout. */ +struct JSAtomState; struct JSFreeOp; struct JSFunctionSpec; @@ -41,7 +42,9 @@ extern JS_FRIEND_DATA(const js::Class* const) FunctionClassPtr; namespace JS { -class AutoIdVector; +template +class AutoVectorRooter; +typedef AutoVectorRooter AutoIdVector; /* * Per ES6, the [[DefineOwnProperty]] internal method has three different @@ -213,14 +216,16 @@ class ObjectOpResult // JSClass operation signatures. -// Add or get a property named by id in obj. Note the jsid id type -- id may +// Get a property named by id in obj. Note the jsid id type -- id may // be a string (Unicode property identifier) or an int (element index). The // *vp out parameter, on success, is the new property value after the action. typedef bool (* JSGetterOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp); -typedef JSGetterOp JSAddPropertyOp; +// Add a property named by id to obj. +typedef bool +(* JSAddPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v); // Set a property named by id in obj, treating the assignment as strict // mode code if strict is true. Note the jsid id type -- id may be a string @@ -279,6 +284,18 @@ typedef bool (* JSResolveOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp); +// A class with a resolve hook can optionally have a mayResolve hook. This hook +// must have no side effects and must return true for a given id if the resolve +// hook may resolve this id. This is useful when we're doing a "pure" lookup: if +// mayResolve returns false, we know we don't have to call the effectful resolve +// hook. +// +// maybeObj, if non-null, is the object on which we're doing the lookup. This +// can be nullptr: during JIT compilation we sometimes know the Class but not +// the object. +typedef bool +(* JSMayResolveOp)(const JSAtomState& names, jsid id, JSObject* maybeObj); + // Convert obj to the given type, returning true with the resulting value in // *vp on success, and returning false on error or exception. typedef bool @@ -418,6 +435,7 @@ typedef void JSSetterOp setProperty; \ JSEnumerateOp enumerate; \ JSResolveOp resolve; \ + JSMayResolveOp mayResolve; \ JSConvertOp convert; \ FinalizeOpType finalize; \ JSNative call; \ @@ -573,7 +591,7 @@ struct JSClass { #define JSCLASS_IS_PROXY (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4)) -#define JSCLASS_FINALIZE_FROM_NURSERY (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5)) +#define JSCLASS_SKIP_NURSERY_FINALIZE (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5)) // Reserved for embeddings. #define JSCLASS_USERBIT2 (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6)) @@ -686,6 +704,8 @@ static_assert(offsetof(JSClass, enumerate) == offsetof(Class, enumerate), "Class and JSClass must be consistent"); static_assert(offsetof(JSClass, resolve) == offsetof(Class, resolve), "Class and JSClass must be consistent"); +static_assert(offsetof(JSClass, mayResolve) == offsetof(Class, mayResolve), + "Class and JSClass must be consistent"); static_assert(offsetof(JSClass, convert) == offsetof(Class, convert), "Class and JSClass must be consistent"); static_assert(offsetof(JSClass, finalize) == offsetof(Class, finalize), diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index c287748a57..59fc410d54 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -345,9 +345,6 @@ WasIncrementalGC(JSRuntime* rt); class JS_PUBLIC_API(AutoDisableGenerationalGC) { js::gc::GCRuntime* gc; -#ifdef JS_GC_ZEAL - bool restartVerifier; -#endif public: explicit AutoDisableGenerationalGC(JSRuntime* rt); diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h index 7ffc16ca35..b35eadae74 100644 --- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -147,11 +147,15 @@ struct ClassInfo FOR_EACH_SIZE(SUB_OTHER_SIZE) } - bool isNotable() const { - static const size_t NotabilityThreshold = 16 * 1024; + size_t sizeOfAllThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N) - return n >= NotabilityThreshold; + return n; + } + + bool isNotable() const { + static const size_t NotabilityThreshold = 16 * 1024; + return sizeOfAllThings() >= NotabilityThreshold; } size_t sizeOfLiveGCThings() const { @@ -221,7 +225,7 @@ struct GCSizes macro(_, _, marker) \ macro(_, _, nurseryCommitted) \ macro(_, _, nurseryDecommitted) \ - macro(_, _, nurseryHugeSlots) \ + macro(_, _, nurseryMallocedBuffers) \ macro(_, _, storeBufferVals) \ macro(_, _, storeBufferCells) \ macro(_, _, storeBufferSlots) \ diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h index 0aac0b803a..efe1763cca 100644 --- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -8,6 +8,7 @@ #define js_ProfilingFrameIterator_h #include "mozilla/Alignment.h" +#include "mozilla/Maybe.h" #include @@ -21,6 +22,7 @@ namespace js { namespace jit { class JitActivation; class JitProfilingFrameIterator; + class JitcodeGlobalEntry; } } @@ -81,7 +83,7 @@ class JS_PUBLIC_API(ProfilingFrameIterator) void* lr; }; - ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state, + ProfilingFrameIterator(JSRuntime* rt, const RegisterState &state, uint32_t sampleBufferGen = UINT32_MAX); ~ProfilingFrameIterator(); void operator++(); @@ -108,25 +110,29 @@ class JS_PUBLIC_API(ProfilingFrameIterator) void* returnAddress; void* activation; const char* label; - bool hasTrackedOptimizations; }; + + bool isAsmJS() const; + bool isJit() const; + uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const; + mozilla::Maybe getPhysicalFrameWithoutLabel() const; + private: + mozilla::Maybe getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const; + void iteratorConstruct(const RegisterState& state); void iteratorConstruct(); void iteratorDestroy(); bool iteratorDone(); - - bool isAsmJS() const; - bool isJit() const; }; extern JS_PUBLIC_API(ProfilingFrameIterator::FrameKind) -GetProfilingFrameKindFromNativeAddr(JSRuntime *runtime, void *pc); +GetProfilingFrameKindFromNativeAddr(JSRuntime* runtime, void* pc); JS_FRIEND_API(bool) -IsProfilingEnabledForRuntime(JSRuntime *runtime); +IsProfilingEnabledForRuntime(JSRuntime* runtime); /** * After each sample run, this method should be called with the latest sample @@ -137,9 +143,18 @@ IsProfilingEnabledForRuntime(JSRuntime *runtime); * JSRuntime for documentation about what these values are used for. */ JS_FRIEND_API(void) -UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime *runtime, uint32_t generation, +UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime* runtime, uint32_t generation, uint32_t lapCount); +struct ForEachProfiledFrameOp +{ + // Called once per frame. + virtual void operator()(const char* label, bool mightHaveTrackedOptimizations) = 0; +}; + +JS_PUBLIC_API(void) +ForEachProfiledFrame(JSRuntime* rt, void* addr, ForEachProfiledFrameOp& op); + } // namespace JS #endif /* js_ProfilingFrameIterator_h */ diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 88e9674724..7b54d983aa 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -425,7 +425,7 @@ class JS_FRIEND_API(DirectProxyHandler) : public BaseProxyHandler extern JS_FRIEND_DATA(const js::Class* const) ProxyClassPtr; -inline bool IsProxy(JSObject* obj) +inline bool IsProxy(const JSObject* obj) { return GetObjectClass(obj)->isProxy(); } diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h index 4e64a34768..56832cb11d 100644 --- a/js/public/TracingAPI.h +++ b/js/public/TracingAPI.h @@ -98,12 +98,14 @@ class JS_PUBLIC_API(JSTracer) WeakMapTraceKind eagerlyTraceWeakMaps() const { return eagerlyTraceWeakMaps_; } // An intermediate state on the road from C to C++ style dispatch. - enum TracerKindTag { - MarkingTracer, - CallbackTracer + enum class TracerKindTag { + Marking, + Tenuring, + Callback }; - bool isMarkingTracer() const { return tag_ == MarkingTracer; } - bool isCallbackTracer() const { return tag_ == CallbackTracer; } + bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking; } + bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; } + bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; } inline JS::CallbackTracer* asCallbackTracer(); protected: @@ -123,16 +125,14 @@ namespace JS { class AutoTracingName; class AutoTracingIndex; class AutoTracingCallback; -class AutoOriginalTraceLocation; class JS_PUBLIC_API(CallbackTracer) : public JSTracer { public: CallbackTracer(JSRuntime* rt, JSTraceCallback traceCallback, WeakMapTraceKind weakTraceKind = TraceWeakMapValues) - : JSTracer(rt, JSTracer::CallbackTracer, weakTraceKind), callback(traceCallback), - contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr), - contextRealLocation_(nullptr) + : JSTracer(rt, JSTracer::TracerKindTag::Callback, weakTraceKind), callback(traceCallback), + contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr) {} // Update the trace callback. @@ -195,14 +195,6 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0; }; - // Return the original heap tracing location if the raw thingp reference - // has been moved. This is generally only useful for heap analyses that - // need to build an accurate model of the heap, and thus is only accurate - // when built with JS_GC_ZEAL. - void*const* tracingLocation(void** thingp) { - return contextRealLocation_ ? contextRealLocation_ : thingp; - } - private: // Exposed publicly for several callers that need to check if the tracer // calling them is of the right type. @@ -216,9 +208,6 @@ class JS_PUBLIC_API(CallbackTracer) : public JSTracer friend class AutoTracingDetails; ContextFunctor* contextFunctor_; - - friend class AutoOriginalTraceLocation; - void*const* contextRealLocation_; }; // Set the name portion of the tracer's context for the current edge. @@ -288,37 +277,6 @@ class AutoTracingDetails } }; -// Some dynamic analyses depend on knowing the edge source location as it -// exists in the object graph. When marking some types of things, e.g. Value -// edges, it is necessary to copy into a temporary on the stack. This class -// records the original location if we need to copy the tracee, so that the -// relevant analyses can continue to operate correctly. -class AutoOriginalTraceLocation -{ -#ifdef JS_GC_ZEAL - CallbackTracer* trc_; - - public: - template - AutoOriginalTraceLocation(JSTracer* trc, T*const* realLocation) : trc_(nullptr) { - if (trc->isCallbackTracer() && trc->asCallbackTracer()->contextRealLocation_ == nullptr) { - trc_ = trc->asCallbackTracer(); - trc_->contextRealLocation_ = reinterpret_cast(realLocation); - } - } - ~AutoOriginalTraceLocation() { - if (trc_) { - MOZ_ASSERT(trc_->contextRealLocation_); - trc_->contextRealLocation_ = nullptr; - } - } -#else - public: - template - AutoOriginalTraceLocation(JSTracer* trc, T*const* realLocation) {} -#endif -}; - } // namespace JS JS::CallbackTracer* @@ -381,7 +339,6 @@ inline void JS_CallHashSetObjectTracer(JSTracer* trc, HashSetEnum& e, JSObject* const& key, const char* name) { JSObject* updated = key; - JS::AutoOriginalTraceLocation reloc(trc, &key); JS_CallUnbarrieredObjectTracer(trc, &updated, name); if (updated != key) e.rekeyFront(updated); diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 4fc089293c..306839012d 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -13,6 +13,7 @@ #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" +#include "mozilla/UniquePtr.h" #include "jspubtd.h" @@ -142,6 +143,7 @@ namespace JS { namespace ubi { using mozilla::Maybe; +using mozilla::UniquePtr; class Edge; class EdgeRange; @@ -194,22 +196,37 @@ class Base { // // If wantNames is true, compute names for edges. Doing so can be expensive // in time and memory. - virtual EdgeRange *edges(JSContext* cx, bool wantNames) const = 0; + virtual EdgeRange* edges(JSContext* cx, bool wantNames) const = 0; // Return the Zone to which this node's referent belongs, or nullptr if the // referent is not of a type allocated in SpiderMonkey Zones. - virtual JS::Zone *zone() const { return nullptr; } + virtual JS::Zone* zone() const { return nullptr; } // Return the compartment for this node. Some ubi::Node referents are not // associated with JSCompartments, such as JSStrings (which are associated // with Zones). When the referent is not associated with a compartment, // nullptr is returned. - virtual JSCompartment *compartment() const { return nullptr; } + virtual JSCompartment* compartment() const { return nullptr; } - // If this node references a JSObject in the live heap, or represents a - // previously existing JSObject from some deserialized heap snapshot, return - // the object's [[Class]]'s name. Otherwise, return nullptr. - virtual const char *jsObjectClassName() const { return nullptr; } + // Methods for JSObject Referents + // + // These methods are only semantically valid if the referent is either a + // JSObject in the live heap, or represents a previously existing JSObject + // from some deserialized heap snapshot. + + // Return the object's [[Class]]'s name. + virtual const char* jsObjectClassName() const { return nullptr; } + + // If this object was constructed with `new` and we have the data available, + // place the contructor function's display name in the out parameter. + // Otherwise, place nullptr in the out parameter. Caller maintains ownership + // of the out parameter. True is returned on success, false is returned on + // OOM. + virtual bool jsObjectConstructorName(JSContext* cx, + UniquePtr& outName) const { + outName.reset(nullptr); + return true; + } private: Base(const Base &rhs) = delete; @@ -332,10 +349,14 @@ class Node { // not all!) JSObjects can be exposed. JS::Value exposeToJS() const; - const char16_t *typeName() const { return base()->typeName(); } - JS::Zone *zone() const { return base()->zone(); } - JSCompartment *compartment() const { return base()->compartment(); } - const char *jsObjectClassName() const { return base()->jsObjectClassName(); } + const char16_t* typeName() const { return base()->typeName(); } + JS::Zone* zone() const { return base()->zone(); } + JSCompartment* compartment() const { return base()->compartment(); } + const char* jsObjectClassName() const { return base()->jsObjectClassName(); } + bool jsObjectConstructorName(JSContext* cx, + UniquePtr& outName) const { + return base()->jsObjectConstructorName(cx, outName); + } size_t size(mozilla::MallocSizeOf mallocSizeof) const { return base()->size(mallocSizeof); @@ -554,10 +575,10 @@ class TracerConcrete : public Base { template class TracerConcreteWithCompartment : public TracerConcrete { typedef TracerConcrete TracerBase; - JSCompartment *compartment() const override; + JSCompartment* compartment() const override; protected: - explicit TracerConcreteWithCompartment(Referent *ptr) : TracerBase(ptr) { } + explicit TracerConcreteWithCompartment(Referent* ptr) : TracerBase(ptr) { } public: static void construct(void* storage, Referent* ptr) { @@ -565,26 +586,38 @@ class TracerConcreteWithCompartment : public TracerConcrete { } }; -// For JS_TraceChildren-based types that have both a 'compartment' method and a -// JSObject's [[Class]] name. -template -class TracerConcreteWithCompartmentAndClassName : public TracerConcreteWithCompartment { - typedef TracerConcreteWithCompartment TracerBase; - const char *jsObjectClassName() const override; +// Define specializations for some commonly-used public JSAPI types. +// These can use the generic templates above. +template<> struct Concrete : TracerConcrete { }; +template<> struct Concrete : TracerConcreteWithCompartment { }; - explicit TracerConcreteWithCompartmentAndClassName(Referent *ptr) : TracerBase(ptr) { } +// The JSObject specialization. +template<> +class Concrete : public TracerConcreteWithCompartment { + const char* jsObjectClassName() const override; + bool jsObjectConstructorName(JSContext* cx, + UniquePtr& outName) const override; + size_t size(mozilla::MallocSizeOf mallocSizeOf) const override; + + protected: + explicit Concrete(JSObject* ptr) : TracerConcreteWithCompartment(ptr) { } public: - static void construct(void *storage, Referent *ptr) { - new (storage) TracerConcreteWithCompartmentAndClassName(ptr); + static void construct(void* storage, JSObject* ptr) { + new (storage) Concrete(ptr); } }; -// Define specializations for some commonly-used public JSAPI types. -template<> struct Concrete : TracerConcrete { }; -template<> struct Concrete : TracerConcrete { }; -template<> struct Concrete : TracerConcreteWithCompartment { }; -template<> struct Concrete : TracerConcreteWithCompartmentAndClassName { }; +// For JSString, we extend the generic template with a 'size' implementation. +template<> struct Concrete : TracerConcrete { + size_t size(mozilla::MallocSizeOf mallocSizeOf) const override; + + protected: + explicit Concrete(JSString* ptr) : TracerConcrete(ptr) { } + + public: + static void construct(void* storage, JSString* ptr) { new (storage) Concrete(ptr); } +}; // The ubi::Node null pointer. Any attempt to operate on a null ubi::Node asserts. template<> diff --git a/js/src/NamespaceImports.h b/js/src/NamespaceImports.h index 551c645a74..a2e792cf23 100644 --- a/js/src/NamespaceImports.h +++ b/js/src/NamespaceImports.h @@ -28,11 +28,13 @@ class TwoByteCharsZ; class UTF8Chars; class UTF8CharsZ; -class AutoFunctionVector; -class AutoIdVector; -class AutoObjectVector; -class AutoScriptVector; -class AutoValueVector; +template +class AutoVectorRooter; +typedef AutoVectorRooter AutoValueVector; +typedef AutoVectorRooter AutoIdVector; +typedef AutoVectorRooter AutoObjectVector; +typedef AutoVectorRooter AutoFunctionVector; +typedef AutoVectorRooter AutoVector; class AutoIdArray; @@ -75,17 +77,17 @@ using JS::TwoByteCharsZ; using JS::UTF8Chars; using JS::UTF8CharsZ; -using JS::AutoFunctionVector; -using JS::AutoIdVector; -using JS::AutoObjectVector; -using JS::AutoScriptVector; -using JS::AutoValueVector; +using JS::AutoVectorRooter; +typedef AutoVectorRooter AutoValueVector; +typedef AutoVectorRooter AutoIdVector; +typedef AutoVectorRooter AutoObjectVector; +typedef AutoVectorRooter AutoFunctionVector; +typedef AutoVectorRooter AutoScriptVector; using JS::AutoIdArray; using JS::AutoHashMapRooter; using JS::AutoHashSetRooter; -using JS::AutoVectorRooter; using JS::RootedGeneric; using JS::CallArgs; diff --git a/js/src/asmjs/AsmJSFrameIterator.cpp b/js/src/asmjs/AsmJSFrameIterator.cpp index d829aa725a..a8915335b8 100644 --- a/js/src/asmjs/AsmJSFrameIterator.cpp +++ b/js/src/asmjs/AsmJSFrameIterator.cpp @@ -193,7 +193,7 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, AsmJSExit: masm.push(Address(scratch, AsmJSActivation::offsetOfFP())); MOZ_ASSERT(PushedFP == masm.currentOffset() - offsetAtBegin); - masm.storePtr(StackPointer, Address(scratch, AsmJSActivation::offsetOfFP())); + masm.storePtr(masm.getStackPointer(), Address(scratch, AsmJSActivation::offsetOfFP())); MOZ_ASSERT(StoredFP == masm.currentOffset() - offsetAtBegin); } @@ -205,7 +205,7 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, AsmJSExit: #endif if (framePushed) - masm.subPtr(Imm32(framePushed), StackPointer); + masm.subFromStackPtr(Imm32(framePushed)); } // Generate the inverse of GenerateProfilingPrologue. @@ -219,7 +219,7 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, AsmJSExit: #endif if (framePushed) - masm.addPtr(Imm32(framePushed), StackPointer); + masm.addToStackPtr(Imm32(framePushed)); masm.loadAsmJSActivation(scratch); @@ -239,10 +239,10 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, AsmJSExit: // time and still points to the current frame, be careful to only update // sp after activation.fp has been repointed to the caller's frame. #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) - masm.loadPtr(Address(StackPointer, 0), scratch2); + masm.loadPtr(Address(masm.getStackPointer(), 0), scratch2); masm.storePtr(scratch2, Address(scratch, AsmJSActivation::offsetOfFP())); DebugOnly prePop = masm.currentOffset(); - masm.add32(Imm32(4), StackPointer); + masm.add32(Imm32(4), masm.getStackPointer()); MOZ_ASSERT(PostStorePrePopFP == masm.currentOffset() - prePop); #else masm.pop(Address(scratch, AsmJSActivation::offsetOfFP())); @@ -281,7 +281,7 @@ js::GenerateAsmJSFunctionPrologue(MacroAssembler& masm, unsigned framePushed, masm.haltingAlign(CodeAlignment); masm.bind(&labels->entry); PushRetAddr(masm); - masm.subPtr(Imm32(framePushed + AsmJSFrameBytesAfterReturnAddress), StackPointer); + masm.subFromStackPtr(Imm32(framePushed + AsmJSFrameBytesAfterReturnAddress)); // Prologue join point, body begin: masm.bind(&body); @@ -295,7 +295,7 @@ js::GenerateAsmJSFunctionPrologue(MacroAssembler& masm, unsigned framePushed, Label* target = framePushed ? labels->overflowThunk.ptr() : &labels->overflowExit; masm.branchPtr(Assembler::AboveOrEqual, AsmJSAbsoluteAddress(AsmJSImm_StackLimit), - StackPointer, + masm.getStackPointer(), target); } } @@ -341,7 +341,7 @@ js::GenerateAsmJSFunctionEpilogue(MacroAssembler& masm, unsigned framePushed, } // Normal epilogue: - masm.addPtr(Imm32(framePushed + AsmJSFrameBytesAfterReturnAddress), StackPointer); + masm.addToStackPtr(Imm32(framePushed + AsmJSFrameBytesAfterReturnAddress)); masm.ret(); masm.setFramePushed(0); @@ -354,7 +354,7 @@ js::GenerateAsmJSFunctionEpilogue(MacroAssembler& masm, unsigned framePushed, // have been pushed. The overflow check occurs after incrementing by // framePushed, so pop that before jumping to the overflow exit. masm.bind(labels->overflowThunk.ptr()); - masm.addPtr(Imm32(framePushed), StackPointer); + masm.addToStackPtr(Imm32(framePushed)); masm.jump(&labels->overflowExit); } } @@ -372,11 +372,11 @@ js::GenerateAsmJSStackOverflowExit(MacroAssembler& masm, Label* overflowExit, La // the profiling case, it is already correct. Register activation = ABIArgGenerator::NonArgReturnReg0; masm.loadAsmJSActivation(activation); - masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfFP())); + masm.storePtr(masm.getStackPointer(), Address(activation, AsmJSActivation::offsetOfFP())); // Prepare the stack for calling C++. if (uint32_t d = StackDecrementForCall(ABIStackAlignment, sizeof(AsmJSFrame), ShadowStackSpace)) - masm.subPtr(Imm32(d), StackPointer); + masm.subFromStackPtr(Imm32(d)); // No need to restore the stack; the throw stub pops everything. masm.assertStackAlignment(ABIStackAlignment); diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index d074dd73ba..722d128999 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -955,6 +955,7 @@ const Class AsmJSModuleObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ AsmJSModuleObject_finalize, nullptr, /* call */ diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index c65656c4dd..37ba1244e3 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -2523,6 +2523,7 @@ class FunctionCompiler options, alloc_, graph_, info_, optimizationInfo, &m().onOutOfBoundsLabel(), + /* conversionErrorLabel = */ nullptr, m().usesSignalHandlersForOOB()); if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_)) @@ -8323,7 +8324,7 @@ GenerateEntry(ModuleCompiler& m, unsigned exportIndex) Register argv = ABIArgGenerator::NonArgReturnReg0; Register scratch = ABIArgGenerator::NonArgReturnReg1; #if defined(JS_CODEGEN_X86) - masm.loadPtr(Address(StackPointer, EntryFrameSize + masm.framePushed()), argv); + masm.loadPtr(Address(masm.getStackPointer(), EntryFrameSize + masm.framePushed()), argv); #else masm.movePtr(IntArgReg0, argv); #endif @@ -8335,12 +8336,12 @@ GenerateEntry(ModuleCompiler& m, unsigned exportIndex) // GlobalReg. MOZ_ASSERT(masm.framePushed() == FramePushedForEntrySP); masm.loadAsmJSActivation(scratch); - masm.storePtr(StackPointer, Address(scratch, AsmJSActivation::offsetOfEntrySP())); + masm.storeStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP())); // Dynamically align the stack since ABIStackAlignment is not necessarily // AsmJSStackAlignment. We'll use entrySP to recover the original stack // pointer on return. - masm.andPtr(Imm32(~(AsmJSStackAlignment - 1)), StackPointer); + masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1))); // Bump the stack for the call. PropertyName* funcName = m.module().exportedFunction(exportIndex).name(); @@ -8383,25 +8384,25 @@ GenerateEntry(ModuleCompiler& m, unsigned exportIndex) switch (type) { case MIRType_Int32: masm.load32(src, scratch); - masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase())); + masm.storePtr(scratch, Address(masm.getStackPointer(), iter->offsetFromArgBase())); break; case MIRType_Double: masm.loadDouble(src, ScratchDoubleReg); - masm.storeDouble(ScratchDoubleReg, Address(StackPointer, iter->offsetFromArgBase())); + masm.storeDouble(ScratchDoubleReg, Address(masm.getStackPointer(), iter->offsetFromArgBase())); break; case MIRType_Float32: masm.loadFloat32(src, ScratchFloat32Reg); - masm.storeFloat32(ScratchFloat32Reg, Address(StackPointer, iter->offsetFromArgBase())); + masm.storeFloat32(ScratchFloat32Reg, Address(masm.getStackPointer(), iter->offsetFromArgBase())); break; case MIRType_Int32x4: masm.loadUnalignedInt32x4(src, ScratchSimdReg); masm.storeAlignedInt32x4(ScratchSimdReg, - Address(StackPointer, iter->offsetFromArgBase())); + Address(masm.getStackPointer(), iter->offsetFromArgBase())); break; case MIRType_Float32x4: masm.loadUnalignedFloat32x4(src, ScratchSimdReg); masm.storeAlignedFloat32x4(ScratchSimdReg, - Address(StackPointer, iter->offsetFromArgBase())); + Address(masm.getStackPointer(), iter->offsetFromArgBase())); break; default: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected stack arg type"); @@ -8416,7 +8417,7 @@ GenerateEntry(ModuleCompiler& m, unsigned exportIndex) // Recover the stack pointer value before dynamic alignment. masm.loadAsmJSActivation(scratch); - masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()), StackPointer); + masm.loadStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP())); masm.setFramePushed(FramePushedForEntrySP); // Recover the 'argv' pointer which was saved before aligning the stack. @@ -8464,7 +8465,7 @@ FillArgumentArray(ModuleCompiler& m, const VarTypeVector& argTypes, MacroAssembler& masm = m.masm(); for (ABIArgTypeIter i(argTypes); !i.done(); i++) { - Address dstAddr(StackPointer, offsetToArgs + i.index() * sizeof(Value)); + Address dstAddr(masm.getStackPointer(), offsetToArgs + i.index() * sizeof(Value)); switch (i->kind()) { case ABIArg::GPR: masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr); @@ -8475,7 +8476,7 @@ FillArgumentArray(ModuleCompiler& m, const VarTypeVector& argTypes, break; case ABIArg::Stack: if (i.mirType() == MIRType_Int32) { - Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); + Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase()); #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) masm.load32(src, scratch); masm.storeValue(JSVAL_TYPE_INT32, scratch, dstAddr); @@ -8484,7 +8485,7 @@ FillArgumentArray(ModuleCompiler& m, const VarTypeVector& argTypes, #endif } else { MOZ_ASSERT(i.mirType() == MIRType_Double); - Address src(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); + Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase()); masm.loadDouble(src, ScratchDoubleReg); masm.canonicalizeDouble(ScratchDoubleReg); masm.storeDouble(ScratchDoubleReg, dstAddr); @@ -8554,7 +8555,7 @@ GenerateFFIInterpExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& e if (i->kind() == ABIArg::GPR) masm.mov(ImmWord(exitIndex), i->gpr()); else - masm.store32(Imm32(exitIndex), Address(StackPointer, i->offsetFromArgBase())); + masm.store32(Imm32(exitIndex), Address(masm.getStackPointer(), i->offsetFromArgBase())); i++; // argument 1: argc @@ -8562,16 +8563,16 @@ GenerateFFIInterpExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& e if (i->kind() == ABIArg::GPR) masm.mov(ImmWord(argc), i->gpr()); else - masm.store32(Imm32(argc), Address(StackPointer, i->offsetFromArgBase())); + masm.store32(Imm32(argc), Address(masm.getStackPointer(), i->offsetFromArgBase())); i++; // argument 2: argv - Address argv(StackPointer, offsetToArgv); + Address argv(masm.getStackPointer(), offsetToArgv); if (i->kind() == ABIArg::GPR) { masm.computeEffectiveAddress(argv, i->gpr()); } else { masm.computeEffectiveAddress(argv, scratch); - masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase())); + masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase())); } i++; MOZ_ASSERT(i.done()); @@ -8641,7 +8642,7 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit // 1. Descriptor size_t argOffset = 0; uint32_t descriptor = MakeFrameDescriptor(ionFramePushed, JitFrame_Entry); - masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset)); + masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(size_t); // 2. Callee @@ -8662,7 +8663,7 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee); // 2.3. Save callee - masm.storePtr(callee, Address(StackPointer, argOffset)); + masm.storePtr(callee, Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(size_t); // 2.4. Load callee executable entry point @@ -8671,11 +8672,11 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit // 3. Argc unsigned argc = exit.sig().args().length(); - masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset)); + masm.storePtr(ImmWord(uintptr_t(argc)), Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(size_t); // 4. |this| value - masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset)); + masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), argOffset)); argOffset += sizeof(Value); // 5. Fill the arguments @@ -8692,7 +8693,7 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit // heap may change during the FFI call. #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting"); - masm.storePtr(GlobalReg, Address(StackPointer, ionFrameBytes)); + masm.storePtr(GlobalReg, Address(masm.getStackPointer(), ionFrameBytes)); #endif { @@ -8808,7 +8809,7 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit // Reload the global register since Ion code can clobber any register. #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting"); - masm.loadPtr(Address(StackPointer, ionFrameBytes), GlobalReg); + masm.loadPtr(Address(masm.getStackPointer(), ionFrameBytes), GlobalReg); #endif // As explained above, the frame was aligned for Ion such that @@ -8864,16 +8865,16 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit AssertStackAlignment(masm, ABIStackAlignment); // Store return value into argv[0] - masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToCoerceArgv)); + masm.storeValue(JSReturnOperand, Address(masm.getStackPointer(), offsetToCoerceArgv)); // argument 0: argv ABIArgMIRTypeIter i(coerceArgTypes); - Address argv(StackPointer, offsetToCoerceArgv); + Address argv(masm.getStackPointer(), offsetToCoerceArgv); if (i->kind() == ABIArg::GPR) { masm.computeEffectiveAddress(argv, i->gpr()); } else { masm.computeEffectiveAddress(argv, scratch); - masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase())); + masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase())); } i++; MOZ_ASSERT(i.done()); @@ -8884,12 +8885,12 @@ GenerateFFIIonExit(ModuleCompiler& m, const ModuleCompiler::ExitDescriptor& exit case RetType::Signed: masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32)); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); - masm.unboxInt32(Address(StackPointer, offsetToCoerceArgv), ReturnReg); + masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnReg); break; case RetType::Double: masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber)); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); - masm.loadDouble(Address(StackPointer, offsetToCoerceArgv), ReturnDoubleReg); + masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg); break; default: MOZ_CRASH("Unsupported convert type"); @@ -8987,8 +8988,8 @@ GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin) continue; #if !defined(JS_CODEGEN_ARM) unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed(); - Address srcAddr(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase()); - Address dstAddr(StackPointer, i->offsetFromArgBase()); + Address srcAddr(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase()); + Address dstAddr(masm.getStackPointer(), i->offsetFromArgBase()); if (i.mirType() == MIRType_Int32 || i.mirType() == MIRType_Float32) { masm.load32(srcAddr, ABIArgGenerator::NonArg_VolatileReg); masm.store32(ABIArgGenerator::NonArg_VolatileReg, dstAddr); @@ -9040,7 +9041,7 @@ GenerateOnOutOfBoundsLabelExit(ModuleCompiler& m, Label* throwLabel) // sp can be anything at this point, so ensure it is aligned when calling // into C++. We unconditionally jump to throw so don't worry about restoring sp. - masm.andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer); + masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1))); // OnOutOfBounds always throws. masm.assertStackAlignment(ABIStackAlignment); @@ -9084,22 +9085,22 @@ GenerateAsyncInterruptExit(ModuleCompiler& m, Label *throwLabel) // Store resumePC into the reserved space. masm.loadAsmJSActivation(scratch); masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfResumePC()), scratch); - masm.storePtr(scratch, Address(StackPointer, masm.framePushed() + sizeof(void*))); + masm.storePtr(scratch, Address(masm.getStackPointer(), masm.framePushed() + sizeof(void*))); // We know that StackPointer is word-aligned, but not necessarily // stack-aligned, so we need to align it dynamically. - masm.mov(StackPointer, ABIArgGenerator::NonVolatileReg); - masm.andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer); + masm.moveStackPtrTo(ABIArgGenerator::NonVolatileReg); + masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1))); if (ShadowStackSpace) - masm.subPtr(Imm32(ShadowStackSpace), StackPointer); + masm.subFromStackPtr(Imm32(ShadowStackSpace)); masm.assertStackAlignment(ABIStackAlignment); masm.call(AsmJSImmPtr(AsmJSImm_HandleExecutionInterrupt)); masm.branchIfFalseBool(ReturnReg, throwLabel); - // Restore the StackPointer to it's position before the call. - masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer); + // Restore the StackPointer to its position before the call. + masm.moveToStackPtr(ABIArgGenerator::NonVolatileReg); // Restore the machine state to before the interrupt. masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP) @@ -9107,7 +9108,7 @@ GenerateAsyncInterruptExit(ModuleCompiler& m, Label *throwLabel) masm.ret(); // pop resumePC into PC #elif defined(JS_CODEGEN_MIPS) // Reserve space to store resumePC. - masm.subPtr(Imm32(sizeof(intptr_t)), StackPointer); + masm.subFromStackPtr(Imm32(sizeof(intptr_t))); // set to zero so we can use masm.framePushed() below. masm.setFramePushed(0); // When this platform supports SIMD extensions, we'll need to push high lanes @@ -9117,7 +9118,7 @@ GenerateAsyncInterruptExit(ModuleCompiler& m, Label *throwLabel) masm.PushRegsInMask(AllRegsExceptSP); // Save the stack pointer in a non-volatile register. - masm.movePtr(StackPointer, s0); + masm.moveStackPtrTo(s0); // Align the stack. masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1))); @@ -9127,17 +9128,17 @@ GenerateAsyncInterruptExit(ModuleCompiler& m, Label *throwLabel) masm.storePtr(IntArgReg1, Address(s0, masm.framePushed())); // MIPS ABI requires rewserving stack for registes $a0 to $a3. - masm.subPtr(Imm32(4 * sizeof(intptr_t)), StackPointer); + masm.subFromStackPtr(Imm32(4 * sizeof(intptr_t))); masm.assertStackAlignment(ABIStackAlignment); masm.call(AsmJSImm_HandleExecutionInterrupt); - masm.addPtr(Imm32(4 * sizeof(intptr_t)), StackPointer); + masm.addToStackPtr(Imm32(4 * sizeof(intptr_t))); masm.branchIfFalseBool(ReturnReg, throwLabel); // This will restore stack to the address before the call. - masm.movePtr(s0, StackPointer); + masm.moveToStackPtr(s0); masm.PopRegsInMask(AllRegsExceptSP); // Pop resumePC into PC. Clobber HeapReg to make the jump and restore it @@ -9254,7 +9255,7 @@ GenerateThrowStub(ModuleCompiler& m, Label *throwLabel) masm.storePtr(ImmWord(0), Address(scratch, AsmJSActivation::offsetOfFP())); masm.setFramePushed(FramePushedForEntrySP); - masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()), StackPointer); + masm.loadStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP())); masm.Pop(scratch); masm.PopRegsInMask(NonVolatileRegs); MOZ_ASSERT(masm.framePushed() == 0); diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 7fc0b6fd95..fccb051299 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -53,7 +53,7 @@ function ArrayIndexOf(searchElement/*, fromIndex*/) { function ArrayStaticIndexOf(list, searchElement/*, fromIndex*/) { if (arguments.length < 1) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.indexOf'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.indexOf'); var fromIndex = arguments.length > 2 ? arguments[2] : 0; return callFunction(ArrayIndexOf, list, searchElement, fromIndex); } @@ -101,7 +101,7 @@ function ArrayLastIndexOf(searchElement/*, fromIndex*/) { function ArrayStaticLastIndexOf(list, searchElement/*, fromIndex*/) { if (arguments.length < 1) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.lastIndexOf'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.lastIndexOf'); var fromIndex; if (arguments.length > 2) { fromIndex = arguments[2]; @@ -123,9 +123,9 @@ function ArrayEvery(callbackfn/*, thisArg*/) { /* Step 4. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 5. */ var T = arguments.length > 1 ? arguments[1] : void 0; @@ -147,9 +147,9 @@ function ArrayEvery(callbackfn/*, thisArg*/) { function ArrayStaticEvery(list, callbackfn/*, thisArg*/) { if (arguments.length < 2) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.every'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.every'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); var T = arguments.length > 2 ? arguments[2] : void 0; return callFunction(ArrayEvery, list, callbackfn, T); } @@ -164,9 +164,9 @@ function ArraySome(callbackfn/*, thisArg*/) { /* Step 4. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.some'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.some'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 5. */ var T = arguments.length > 1 ? arguments[1] : void 0; @@ -188,9 +188,9 @@ function ArraySome(callbackfn/*, thisArg*/) { function ArrayStaticSome(list, callbackfn/*, thisArg*/) { if (arguments.length < 2) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.some'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.some'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); var T = arguments.length > 2 ? arguments[2] : void 0; return callFunction(ArraySome, list, callbackfn, T); } @@ -205,9 +205,9 @@ function ArrayForEach(callbackfn/*, thisArg*/) { /* Step 4. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.forEach'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.forEach'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 5. */ var T = arguments.length > 1 ? arguments[1] : void 0; @@ -236,9 +236,9 @@ function ArrayMap(callbackfn/*, thisArg*/) { /* Step 4. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.map'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.map'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 5. */ var T = arguments.length > 1 ? arguments[1] : void 0; @@ -264,18 +264,18 @@ function ArrayMap(callbackfn/*, thisArg*/) { function ArrayStaticMap(list, callbackfn/*, thisArg*/) { if (arguments.length < 2) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.map'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.map'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); var T = arguments.length > 2 ? arguments[2] : void 0; return callFunction(ArrayMap, list, callbackfn, T); } function ArrayStaticForEach(list, callbackfn/*, thisArg*/) { if (arguments.length < 2) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.forEach'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.forEach'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); var T = arguments.length > 2 ? arguments[2] : void 0; callFunction(ArrayForEach, list, callbackfn, T); } @@ -290,9 +290,9 @@ function ArrayReduce(callbackfn/*, initialValue*/) { /* Step 4. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 6. */ var k = 0; @@ -304,7 +304,7 @@ function ArrayReduce(callbackfn/*, initialValue*/) { } else { /* Step 5. */ if (len === 0) - ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); + ThrowTypeError(JSMSG_EMPTY_ARRAY_REDUCE); if (IsPackedArray(O)) { accumulator = O[k++]; } else { @@ -318,7 +318,7 @@ function ArrayReduce(callbackfn/*, initialValue*/) { } } if (!kPresent) - ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); + ThrowTypeError(JSMSG_EMPTY_ARRAY_REDUCE); } } @@ -338,9 +338,9 @@ function ArrayReduce(callbackfn/*, initialValue*/) { function ArrayStaticReduce(list, callbackfn) { if (arguments.length < 2) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduce'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduce'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); if (arguments.length > 2) return callFunction(ArrayReduce, list, callbackfn, arguments[2]); else @@ -357,9 +357,9 @@ function ArrayReduceRight(callbackfn/*, initialValue*/) { /* Step 4. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.reduce'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 6. */ var k = len - 1; @@ -371,7 +371,7 @@ function ArrayReduceRight(callbackfn/*, initialValue*/) { } else { /* Step 5. */ if (len === 0) - ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); + ThrowTypeError(JSMSG_EMPTY_ARRAY_REDUCE); if (IsPackedArray(O)) { accumulator = O[k--]; } else { @@ -385,7 +385,7 @@ function ArrayReduceRight(callbackfn/*, initialValue*/) { } } if (!kPresent) - ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); + ThrowTypeError(JSMSG_EMPTY_ARRAY_REDUCE); } } @@ -405,9 +405,9 @@ function ArrayReduceRight(callbackfn/*, initialValue*/) { function ArrayStaticReduceRight(list, callbackfn) { if (arguments.length < 2) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduceRight'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reduceRight'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); if (arguments.length > 2) return callFunction(ArrayReduceRight, list, callbackfn, arguments[2]); else @@ -424,9 +424,9 @@ function ArrayFind(predicate/*, thisArg*/) { /* Step 6. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find'); if (!IsCallable(predicate)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); /* Step 7. */ var T = arguments.length > 1 ? arguments[1] : undefined; @@ -460,9 +460,9 @@ function ArrayFindIndex(predicate/*, thisArg*/) { /* Step 6. */ if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.find'); if (!IsCallable(predicate)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); /* Step 7. */ var T = arguments.length > 1 ? arguments[1] : undefined; @@ -711,7 +711,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { // Steps 2-3. var mapping = mapfn !== undefined; if (mapping && !IsCallable(mapfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn)); var T = thisArg; // All elements defined by this algorithm have the same attrs: @@ -738,7 +738,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) { // Steps 6.g.i-iii. var next = iterator.next(); if (!IsObject(next)) - ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE); + ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE); // Step 6.g.iv. if (next.done) { diff --git a/js/src/builtin/Error.js b/js/src/builtin/Error.js index a737857c1e..8dacf8b8c1 100644 --- a/js/src/builtin/Error.js +++ b/js/src/builtin/Error.js @@ -8,7 +8,7 @@ function ErrorToString() /* Steps 1-2. */ var obj = this; if (!IsObject(obj)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Error", "toString", "value"); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Error", "toString", "value"); /* Steps 3-5. */ var name = obj.name; diff --git a/js/src/builtin/Generator.js b/js/src/builtin/Generator.js index 0639455d53..b3758c6090 100644 --- a/js/src/builtin/Generator.js +++ b/js/src/builtin/Generator.js @@ -15,7 +15,7 @@ function StarGeneratorNext(val) { return { value: undefined, done: true }; if (GeneratorIsRunning(this)) - ThrowError(JSMSG_NESTING_GENERATOR); + ThrowTypeError(JSMSG_NESTING_GENERATOR); } try { @@ -36,7 +36,7 @@ function StarGeneratorThrow(val) { throw val; if (GeneratorIsRunning(this)) - ThrowError(JSMSG_NESTING_GENERATOR); + ThrowTypeError(JSMSG_NESTING_GENERATOR); } try { @@ -57,7 +57,7 @@ function StarGeneratorReturn(val) { return { value: val, done: true }; if (GeneratorIsRunning(this)) - ThrowError(JSMSG_NESTING_GENERATOR); + ThrowTypeError(JSMSG_NESTING_GENERATOR); } try { @@ -78,7 +78,7 @@ function LegacyGeneratorNext(val) { ThrowStopIteration(); if (GeneratorIsRunning(this)) - ThrowError(JSMSG_NESTING_GENERATOR); + ThrowTypeError(JSMSG_NESTING_GENERATOR); try { return resumeGenerator(this, val, 'next'); @@ -97,7 +97,7 @@ function LegacyGeneratorThrow(val) { throw val; if (GeneratorIsRunning(this)) - ThrowError(JSMSG_NESTING_GENERATOR); + ThrowTypeError(JSMSG_NESTING_GENERATOR); try { return resumeGenerator(this, val, 'throw'); @@ -115,7 +115,7 @@ function LegacyGeneratorCloseInternal() { assert(!LegacyGeneratorObjectIsClosed(this), "Already closed: " + ToString(this)); if (GeneratorIsRunning(this)) - ThrowError(JSMSG_NESTING_GENERATOR); + ThrowTypeError(JSMSG_NESTING_GENERATOR); resumeGenerator(this, undefined, 'close'); if (!LegacyGeneratorObjectIsClosed(this)) diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 3d31849788..5aaa4c5647 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -565,6 +565,7 @@ static const Class CollatorClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ collator_finalize }; @@ -1057,6 +1058,7 @@ static const Class NumberFormatClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ numberFormat_finalize }; @@ -1516,6 +1518,7 @@ static const Class DateTimeFormatClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ dateTimeFormat_finalize }; diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 77f3280c19..d806a5c405 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -639,10 +639,10 @@ function CanonicalizeLocaleList(locales) { if (kPresent) { var kValue = O[k]; if (!(typeof kValue === "string" || IsObject(kValue))) - ThrowError(JSMSG_INVALID_LOCALES_ELEMENT); + ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT); var tag = ToString(kValue); if (!IsStructurallyValidLanguageTag(tag)) - ThrowError(JSMSG_INVALID_LANGUAGE_TAG, tag); + ThrowRangeError(JSMSG_INVALID_LANGUAGE_TAG, tag); tag = CanonicalizeLanguageTag(tag); if (seen.indexOf(tag) === -1) seen.push(tag); @@ -982,7 +982,7 @@ function SupportedLocales(availableLocales, requestedLocales, options) { if (matcher !== undefined) { matcher = ToString(matcher); if (matcher !== "lookup" && matcher !== "best fit") - ThrowError(JSMSG_INVALID_LOCALE_MATCHER, matcher); + ThrowRangeError(JSMSG_INVALID_LOCALE_MATCHER, matcher); } } @@ -1027,7 +1027,7 @@ function GetOption(options, property, type, values, fallback) { // Step 2.d. if (values !== undefined && callFunction(std_Array_indexOf, values, value) === -1) - ThrowError(JSMSG_INVALID_OPTION_VALUE, property, value); + ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, property, value); // Step 2.e. return value; @@ -1056,7 +1056,7 @@ function GetNumberOption(options, property, minimum, maximum, fallback) { if (value !== undefined) { value = ToNumber(value); if (Number_isNaN(value) || value < minimum || value > maximum) - ThrowError(JSMSG_INVALID_DIGITS_VALUE, value); + ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value); return std_Math_floor(value); } @@ -1220,7 +1220,7 @@ function getIntlObjectInternals(obj, className, methodName) { assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap"); if (internals === undefined || internals.type !== className) - ThrowError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className); + ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className); return internals; } @@ -1394,7 +1394,7 @@ function InitializeCollator(collator, locales, options) { // Step 1. if (isInitializedIntlObject(collator)) - ThrowError(JSMSG_INTL_OBJECT_REINITED); + ThrowTypeError(JSMSG_INTL_OBJECT_REINITED); // Step 2. var internals = initializeIntlObject(collator); @@ -1740,7 +1740,7 @@ function InitializeNumberFormat(numberFormat, locales, options) { // Step 1. if (isInitializedIntlObject(numberFormat)) - ThrowError(JSMSG_INTL_OBJECT_REINITED); + ThrowTypeError(JSMSG_INTL_OBJECT_REINITED); // Step 2. var internals = initializeIntlObject(numberFormat); @@ -1809,11 +1809,11 @@ function InitializeNumberFormat(numberFormat, locales, options) { // Steps 17-20. var c = GetOption(options, "currency", "string", undefined, undefined); if (c !== undefined && !IsWellFormedCurrencyCode(c)) - ThrowError(JSMSG_INVALID_CURRENCY_CODE, c); + ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c); var cDigits; if (s === "currency") { if (c === undefined) - ThrowError(JSMSG_UNDEFINED_CURRENCY); + ThrowTypeError(JSMSG_UNDEFINED_CURRENCY); // Steps 20.a-c. c = toASCIIUpperCase(c); @@ -2189,7 +2189,7 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) { // Step 1. if (isInitializedIntlObject(dateTimeFormat)) - ThrowError(JSMSG_INTL_OBJECT_REINITED); + ThrowTypeError(JSMSG_INTL_OBJECT_REINITED); // Step 2. var internals = initializeIntlObject(dateTimeFormat); @@ -2245,7 +2245,7 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) { if (tz !== undefined) { tz = toASCIIUpperCase(ToString(tz)); if (tz !== "UTC") - ThrowError(JSMSG_INVALID_TIME_ZONE, tz); + ThrowRangeError(JSMSG_INVALID_TIME_ZONE, tz); } lazyDateTimeFormatData.timeZone = tz; diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index 9f134170da..6bb7283a9d 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -8,19 +8,19 @@ function MapForEach(callbackfn, thisArg = undefined) { /* Step 1-2. */ var M = this; if (!IsObject(M)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Map", "forEach", typeof M); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Map", "forEach", typeof M); /* Step 3-4. */ try { callFunction(std_Map_has, M); } catch (e) { // has will throw on non-Map objects, throw our own error in that case. - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Map", "forEach", typeof M); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Map", "forEach", typeof M); } /* Step 5. */ if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 6-8. */ var entries = callFunction(std_Map_iterator, M); diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index dc30494fee..f5439eae2e 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -839,7 +839,6 @@ HashableValue HashableValue::mark(JSTracer* trc) const { HashableValue hv(*this); - JS::AutoOriginalTraceLocation reloc(trc, (void**)this); TraceEdge(trc, &hv.value, "key"); return hv; } @@ -880,6 +879,7 @@ const Class MapIteratorObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ MapIteratorObject::finalize }; @@ -1021,6 +1021,7 @@ const Class MapObject::class_ = { nullptr, // setProperty nullptr, // enumerate nullptr, // resolve + nullptr, // mayResolve nullptr, // convert finalize, nullptr, // call @@ -1136,7 +1137,7 @@ class OrderedHashTableRef : public gc::BufferableRef public: explicit OrderedHashTableRef(TableType* t, const Value& k) : table(t), key(k) {} - void mark(JSTracer* trc) { + void trace(JSTracer* trc) override { MOZ_ASSERT(UnbarrieredHashPolicy::hash(key) == HashableValue::Hasher::hash(*reinterpret_cast(&key))); Value prior = key; @@ -1621,6 +1622,7 @@ const Class SetIteratorObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ SetIteratorObject::finalize }; @@ -1758,6 +1760,7 @@ const Class SetObject::class_ = { nullptr, // setProperty nullptr, // enumerate nullptr, // resolve + nullptr, // mayResolve nullptr, // convert finalize, nullptr, // call diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index a323c6aa6b..c117932d41 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -555,6 +555,7 @@ js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp) jsid id; if (args.thisv().isObject() && ValueToId(cx, idValue, &id)) { JSObject* obj = &args.thisv().toObject(); + Shape* prop; if (obj->isNative() && NativeLookupOwnProperty(cx, &obj->as(), id, &prop)) @@ -1181,6 +1182,7 @@ const Class PlainObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js index 2d96ddd170..c48817fc91 100644 --- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -49,7 +49,7 @@ function ObjectDefineSetter(name, setter) { object = ToObject(this); if (!IsCallable(setter)) - ThrowError(JSMSG_BAD_GETTER_OR_SETTER, "setter"); + ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter"); var key = ToPropertyKey(name); @@ -71,7 +71,7 @@ function ObjectDefineGetter(name, getter) { object = ToObject(this); if (!IsCallable(getter)) - ThrowError(JSMSG_BAD_GETTER_OR_SETTER, "getter"); + ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "getter"); var key = ToPropertyKey(name); diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js index 25732fece0..13cc5ba894 100644 --- a/js/src/builtin/RegExp.js +++ b/js/src/builtin/RegExp.js @@ -7,7 +7,7 @@ function RegExpFlagsGetter() { // Steps 1-2. var R = this; if (!IsObject(R)) - ThrowError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); // Step 3. var result = ""; @@ -43,7 +43,7 @@ function RegExpToString() // Steps 1-2. var R = this; if (!IsObject(R)) - ThrowError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); // Steps 3-4. var pattern = R.source; diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index df6f2ea4f5..38d9b4a7e9 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -14,6 +14,7 @@ #include "builtin/SIMD.h" #include "mozilla/IntegerTypeTraits.h" + #include "jsapi.h" #include "jsfriendapi.h" @@ -62,15 +63,39 @@ template bool js::IsVectorObject(HandleValue v); template bool js::IsVectorObject(HandleValue v); template bool js::IsVectorObject(HandleValue v); +static inline bool +ErrorBadArgs(JSContext* cx) +{ + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); + return false; +} + +static inline bool +ErrorWrongTypeArg(JSContext* cx, size_t argIndex, Handle typeDescr) +{ + MOZ_ASSERT(argIndex < 10); + char charArgIndex[2]; + JS_snprintf(charArgIndex, sizeof charArgIndex, "%d", argIndex); + + HeapSlot& typeNameSlot = typeDescr->getReservedSlotRef(JS_DESCR_SLOT_STRING_REPR); + char* typeNameStr = JS_EncodeString(cx, typeNameSlot.toString()); + if (!typeNameStr) + return false; + + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR, + typeNameStr, charArgIndex); + JS_free(cx, typeNameStr); + return false; +} + template bool js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out) { typedef typename V::Elem Elem; - if (!IsVectorObject(v)) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR); - return false; - } + Rooted typeDescr(cx, &V::GetTypeDescr(*cx->global())); + if (!IsVectorObject(v)) + return ErrorWrongTypeArg(cx, 1, typeDescr); Elem* mem = reinterpret_cast(v.toObject().as().typedMem()); *out = jit::SimdConstant::CreateX4(mem); @@ -180,6 +205,7 @@ const Class SimdTypeDescr::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ TypeDescr::finalize, call @@ -488,137 +514,139 @@ namespace js { // Unary SIMD operators template struct Identity { - static inline T apply(T x) { return x; } + static T apply(T x) { return x; } }; template struct Abs { - static inline T apply(T x) { return mozilla::Abs(x); } + static T apply(T x) { return mozilla::Abs(x); } }; template struct Neg { - static inline T apply(T x) { return -1 * x; } + static T apply(T x) { return -1 * x; } }; template struct Not { - static inline T apply(T x) { return ~x; } + static T apply(T x) { return ~x; } }; template struct RecApprox { - static inline T apply(T x) { return 1 / x; } + static T apply(T x) { return 1 / x; } }; template struct RecSqrtApprox { - static inline T apply(T x) { return 1 / sqrt(x); } + static T apply(T x) { return 1 / sqrt(x); } }; template struct Sqrt { - static inline T apply(T x) { return sqrt(x); } + static T apply(T x) { return sqrt(x); } }; // Binary SIMD operators template struct Add { - static inline T apply(T l, T r) { return l + r; } + static T apply(T l, T r) { return l + r; } }; template struct Sub { - static inline T apply(T l, T r) { return l - r; } + static T apply(T l, T r) { return l - r; } }; template struct Div { - static inline T apply(T l, T r) { return l / r; } + static T apply(T l, T r) { return l / r; } }; template struct Mul { - static inline T apply(T l, T r) { return l * r; } + static T apply(T l, T r) { return l * r; } }; template struct Minimum { - static inline T apply(T l, T r) { return math_min_impl(l, r); } + static T apply(T l, T r) { return math_min_impl(l, r); } }; template struct MinNum { - static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); } + static T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); } }; template struct Maximum { - static inline T apply(T l, T r) { return math_max_impl(l, r); } + static T apply(T l, T r) { return math_max_impl(l, r); } }; template struct MaxNum { - static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); } + static T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); } }; template struct LessThan { - static inline int32_t apply(T l, T r) { return l < r ? 0xFFFFFFFF : 0x0; } + static int32_t apply(T l, T r) { return l < r ? 0xFFFFFFFF : 0x0; } }; template struct LessThanOrEqual { - static inline int32_t apply(T l, T r) { return l <= r ? 0xFFFFFFFF : 0x0; } + static int32_t apply(T l, T r) { return l <= r ? 0xFFFFFFFF : 0x0; } }; template struct GreaterThan { - static inline int32_t apply(T l, T r) { return l > r ? 0xFFFFFFFF : 0x0; } + static int32_t apply(T l, T r) { return l > r ? 0xFFFFFFFF : 0x0; } }; template struct GreaterThanOrEqual { - static inline int32_t apply(T l, T r) { return l >= r ? 0xFFFFFFFF : 0x0; } + static int32_t apply(T l, T r) { return l >= r ? 0xFFFFFFFF : 0x0; } }; template struct Equal { - static inline int32_t apply(T l, T r) { return l == r ? 0xFFFFFFFF : 0x0; } + static int32_t apply(T l, T r) { return l == r ? 0xFFFFFFFF : 0x0; } }; template struct NotEqual { - static inline int32_t apply(T l, T r) { return l != r ? 0xFFFFFFFF : 0x0; } + static int32_t apply(T l, T r) { return l != r ? 0xFFFFFFFF : 0x0; } }; template struct Xor { - static inline T apply(T l, T r) { return l ^ r; } + static T apply(T l, T r) { return l ^ r; } }; template struct And { - static inline T apply(T l, T r) { return l & r; } + static T apply(T l, T r) { return l & r; } }; template struct Or { - static inline T apply(T l, T r) { return l | r; } + static T apply(T l, T r) { return l | r; } }; template struct WithX { - static inline T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; } + static T apply(int32_t lane, T scalar, T x) { return lane == 0 ? scalar : x; } }; template struct WithY { - static inline T apply(int32_t lane, T scalar, T x) { return lane == 1 ? scalar : x; } + static T apply(int32_t lane, T scalar, T x) { return lane == 1 ? scalar : x; } }; template struct WithZ { - static inline T apply(int32_t lane, T scalar, T x) { return lane == 2 ? scalar : x; } + static T apply(int32_t lane, T scalar, T x) { return lane == 2 ? scalar : x; } }; template struct WithW { - static inline T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; } + static T apply(int32_t lane, T scalar, T x) { return lane == 3 ? scalar : x; } }; +// For the following three operators, if the value v we're trying to shift is +// such that v << bits can't fit in the int32 range, then we have undefined +// behavior, according to C++11 [expr.shift]p2. struct ShiftLeft { - static inline int32_t apply(int32_t v, int32_t bits) { return v << bits; } + static int32_t apply(int32_t v, int32_t bits) { + return uint32_t(bits) >= 32 ? 0 : v << bits; + } }; -struct ShiftRight { - static inline int32_t apply(int32_t v, int32_t bits) { return v >> bits; } +struct ShiftRightArithmetic { + static int32_t apply(int32_t v, int32_t bits) { + return v >> (uint32_t(bits) >= 32 ? 31 : bits); + } }; struct ShiftRightLogical { - static inline int32_t apply(int32_t v, int32_t bits) { return uint32_t(v) >> (bits & 31); } + static int32_t apply(int32_t v, int32_t bits) { + return uint32_t(bits) >= 32 ? 0 : uint32_t(v) >> bits; + } }; } // namespace js -static inline bool -ErrorBadArgs(JSContext* cx) -{ - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); - return false; -} - template static bool StoreResult(JSContext* cx, CallArgs& args, typename Out::Elem* result) @@ -813,6 +841,62 @@ CompareFunc(JSContext* cx, unsigned argc, Value* vp) return StoreResult(cx, args, result); } +// This struct defines whether we should throw during a conversion attempt, +// when trying to convert a value of type from From to the type To. This +// happens whenever a C++ conversion would have undefined behavior (and perhaps +// be platform-dependent). +template +struct ThrowOnConvert; + +struct NeverThrow +{ + static bool value(int32_t v) { + return false; + } +}; + +// While int32 to float conversions can be lossy, these conversions have +// defined behavior in C++, so we don't need to care about them here. In practice, +// this means round to nearest, tie with even (zero bit in significand). +template<> +struct ThrowOnConvert : public NeverThrow {}; + +// All int32 can be safely converted to doubles. +template<> +struct ThrowOnConvert : public NeverThrow {}; + +// All floats can be safely converted to doubles. +template<> +struct ThrowOnConvert : public NeverThrow {}; + +// Double to float conversion for inputs which aren't in the float range are +// undefined behavior in C++, but they're defined in IEEE754. +template<> +struct ThrowOnConvert : public NeverThrow {}; + +// Float to integer conversions have undefined behavior if the float value +// is out of the representable integer range (on x86, will yield the undefined +// value pattern, namely 0x80000000; on arm, will clamp the input value), so +// check this here. +template +struct ThrowIfNotInRange +{ + static_assert(mozilla::IsIntegral::value, "bad destination type"); + + static bool value(From v) { + double d(v); + return mozilla::IsNaN(d) || + d < double(mozilla::MinValue::value) || + d > double(mozilla::MaxValue::value); + } +}; + +template<> +struct ThrowOnConvert : public ThrowIfNotInRange {}; + +template<> +struct ThrowOnConvert : public ThrowIfNotInRange {}; + template static bool FuncConvert(JSContext* cx, unsigned argc, Value* vp) @@ -825,9 +909,20 @@ FuncConvert(JSContext* cx, unsigned argc, Value* vp) return ErrorBadArgs(cx); Elem* val = TypedObjectMemory(args[0]); + RetElem result[Vret::lanes]; - for (unsigned i = 0; i < Vret::lanes; i++) - result[i] = i < V::lanes ? ConvertScalar(val[i]) : 0; + for (unsigned i = 0; i < Min(V::lanes, Vret::lanes); i++) { + if (ThrowOnConvert::value(val[i])) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + JSMSG_SIMD_FAILED_CONVERSION); + return false; + } + result[i] = ConvertScalar(val[i]); + } + + // Fill remaining lanes with 0 + for (unsigned i = V::lanes; i < Vret::lanes; i++) + result[i] = 0; return StoreResult(cx, args, result); } diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 76c7cb8ba5..ece9927981 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -157,7 +157,7 @@ V(or, (BinaryFunc), 2) \ V(sub, (BinaryFunc), 2) \ V(shiftLeftByScalar, (Int32x4BinaryScalar), 2) \ - V(shiftRightArithmeticByScalar, (Int32x4BinaryScalar), 2) \ + V(shiftRightArithmeticByScalar, (Int32x4BinaryScalar), 2) \ V(shiftRightLogicalByScalar, (Int32x4BinaryScalar), 2) \ V(store, (Store), 3) \ V(storeXYZ, (Store), 3) \ @@ -252,6 +252,8 @@ _(storeXY) \ _(storeXYZ) \ _(check) +#define ION_ONLY_INT32X4_SIMD_OP(_) \ + _(bool) #define FOREACH_COMMONX4_SIMD_OP(_) \ ION_COMMONX4_SIMD_OP(_) \ COMP_COMMONX4_TO_INT32X4_SIMD_OP(_) diff --git a/js/src/builtin/Set.js b/js/src/builtin/Set.js index 3dc39bc35e..64c0355ed0 100644 --- a/js/src/builtin/Set.js +++ b/js/src/builtin/Set.js @@ -8,19 +8,19 @@ function SetForEach(callbackfn, thisArg = undefined) { /* Step 1-2. */ var S = this; if (!IsObject(S)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Set", "forEach", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Set", "forEach", typeof S); /* Step 3-4. */ try { callFunction(std_Set_has, S); } catch (e) { // has will throw on non-Set objects, throw our own error in that case. - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Set", "forEach", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Set", "forEach", typeof S); } /* Step 5-6. */ if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); /* Step 7-8. */ var values = callFunction(std_Set_iterator, S); diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index 06aba71c17..ccf0d74523 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -44,7 +44,7 @@ function String_substring(start, end) { function String_static_substring(string, start, end) { if (arguments.length < 1) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.substring'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.substring'); return callFunction(String_substring, string, start, end); } @@ -83,7 +83,7 @@ function String_substr(start, length) { function String_static_substr(string, start, length) { if (arguments.length < 1) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.substr'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.substr'); return callFunction(String_substr, string, start, length); } @@ -120,7 +120,7 @@ function String_slice(start, end) { function String_static_slice(string, start, end) { if (arguments.length < 1) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.slice'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.slice'); return callFunction(String_slice, string, start, end); } @@ -167,10 +167,10 @@ function String_repeat(count) { // Steps 6-7. if (n < 0) - ThrowError(JSMSG_NEGATIVE_REPETITION_COUNT); // a RangeError + ThrowRangeError(JSMSG_NEGATIVE_REPETITION_COUNT); if (!(n * S.length < (1 << 28))) - ThrowError(JSMSG_RESULTING_STRING_TOO_LARGE); // a RangeError + ThrowRangeError(JSMSG_RESULTING_STRING_TOO_LARGE); // Communicate |n|'s possible range to the compiler. n = n & ((1 << 28) - 1); @@ -286,11 +286,11 @@ function String_static_fromCodePoint(codePoints) { // Step 5d. if (nextCP !== ToInteger(nextCP) || Number_isNaN(nextCP)) - ThrowError(JSMSG_NOT_A_CODEPOINT, ToString(nextCP)); + ThrowRangeError(JSMSG_NOT_A_CODEPOINT, ToString(nextCP)); // Step 5e. if (nextCP < 0 || nextCP > 0x10FFFF) - ThrowError(JSMSG_NOT_A_CODEPOINT, ToString(nextCP)); + ThrowRangeError(JSMSG_NOT_A_CODEPOINT, ToString(nextCP)); // Step 5f. // Inlined UTF-16 Encoding @@ -369,7 +369,7 @@ function String_static_raw(callSite, ...substitutions) { */ function String_static_localeCompare(str1, str2) { if (arguments.length < 1) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "String.localeCompare"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "String.localeCompare"); var locales = arguments.length > 2 ? arguments[2] : undefined; var options = arguments.length > 3 ? arguments[3] : undefined; return callFunction(String_localeCompare, str1, str2, locales, options); diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp index 5f5b3aff2d..914827e557 100644 --- a/js/src/builtin/SymbolObject.cpp +++ b/js/src/builtin/SymbolObject.cpp @@ -25,6 +25,7 @@ const Class SymbolObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ convert }; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 322f31642d..8e6730a486 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -195,6 +195,10 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, jsval* vp) if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) return false; + value.setInt32(sizeof(void *)); + if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) + return false; + args.rval().setObject(*info); return true; } @@ -574,13 +578,13 @@ VerifyPreBarriers(JSContext* cx, unsigned argc, jsval* vp) static bool VerifyPostBarriers(JSContext* cx, unsigned argc, jsval* vp) { + // This is a no-op since the post barrier verifier was removed. CallArgs args = CallArgsFromVp(argc, vp); if (args.length()) { RootedObject callee(cx, &args.callee()); ReportUsageError(cx, callee, "Too many arguments"); return false; } - gc::VerifyBarriers(cx->runtime(), gc::PostBarrierVerifier); args.rval().setUndefined(); return true; } @@ -1118,6 +1122,7 @@ static const JSClass FinalizeCounterClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ finalize_counter_finalize }; @@ -1386,8 +1391,8 @@ DisplayName(JSContext* cx, unsigned argc, jsval* vp) return true; } -static JSObject * -ShellObjectMetadataCallback(JSContext *cx) +static JSObject* +ShellObjectMetadataCallback(JSContext* cx, JSObject*) { RootedObject obj(cx, NewBuiltinClassInstance(cx)); if (!obj) @@ -1795,6 +1800,7 @@ const Class CloneBufferObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ Finalize }; @@ -2439,11 +2445,18 @@ ByteSize(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf; - JS::ubi::Node node = args.get(0); - if (node) - args.rval().set(NumberValue(node.size(mallocSizeOf))); - else - args.rval().setUndefined(); + + { + // We can't tolerate the GC moving things around while we're using a + // ubi::Node. Check that nothing we do causes a GC. + JS::AutoCheckCannotGC autoCannotGC; + + JS::ubi::Node node = args.get(0); + if (node) + args.rval().setNumber(uint32_t(node.size(mallocSizeOf))); + else + args.rval().setUndefined(); + } return true; } @@ -2483,6 +2496,32 @@ SetLazyParsingEnabled(JSContext *cx, unsigned argc, Value *vp) return true; } +static bool +GetConstructorName(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "getConstructorName", 1)) + return false; + + if (!args[0].isObject()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + "getConstructorName", "Object", + InformalValueTypeName(args[0])); + return false; + } + + RootedAtom name(cx); + if (!args[0].toObject().constructorDisplayAtom(cx, &name)) + return false; + + if (name) { + args.rval().setString(name); + } else { + args.rval().setNull(); + } + return true; +} + static const JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gc", ::GC, 0, 0, "gc([obj] | 'compartment' [, 'shrinking'])", @@ -2607,7 +2646,7 @@ gc::ZealModeHelpText), JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0, "verifypostbarriers()", -" Start or end a run of the post-write barrier verifier."), +" Does nothing (the post-write barrier verifier has been remove)."), JS_FN_HELP("gcstate", GCState, 0, 0, "gcstate()", @@ -2871,6 +2910,11 @@ gc::ZealModeHelpText), "setLazyParsingEnabled(bool)", " Enable or disable lazy parsing in the current compartment. The default is enabled."), + JS_FN_HELP("getConstructorName", GetConstructorName, 1, 0, +"getConstructorName(object)", +" If the given object was created with `new Ctor`, return the constructor's display name. " +" Otherwise, return null."), + JS_FS_HELP_END }; diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 26eeeeda51..844ea33224 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -34,9 +34,9 @@ function TypedArrayEvery(callbackfn, thisArg = undefined) { // Step 6. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every"); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 7. var T = thisArg; @@ -115,9 +115,9 @@ function TypedArrayFilter(callbackfn, thisArg = undefined) { // Step 5. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter"); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 6. var T = thisArg; @@ -178,9 +178,9 @@ function TypedArrayFind(predicate, thisArg = undefined) { // Step 6. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.find"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.find"); if (!IsCallable(predicate)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); // Step 7. var T = thisArg; @@ -215,9 +215,9 @@ function TypedArrayFindIndex(predicate, thisArg = undefined) { // Step 6. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.findIndex"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.findIndex"); if (!IsCallable(predicate)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate)); // Step 7. var T = thisArg; @@ -250,9 +250,9 @@ function TypedArrayForEach(callbackfn, thisArg = undefined) { // Step 5. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 6. var T = thisArg; @@ -434,9 +434,9 @@ function TypedArrayMap(callbackfn, thisArg = undefined) { // Step 5. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, '%TypedArray%.prototype.map'); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, '%TypedArray%.prototype.map'); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 6. var T = thisArg; @@ -476,13 +476,13 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) { // Step 6. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.reduce"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.reduce"); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 7. if (len === 0 && arguments.length === 1) - ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); + ThrowTypeError(JSMSG_EMPTY_ARRAY_REDUCE); // Step 8. var k = 0; @@ -515,13 +515,13 @@ function TypedArrayReduceRight(callbackfn/*, initialValue*/) { // Step 6. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.reduceRight"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.reduceRight"); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 7. if (len === 0 && arguments.length === 1) - ThrowError(JSMSG_EMPTY_ARRAY_REDUCE); + ThrowTypeError(JSMSG_EMPTY_ARRAY_REDUCE); // Step 8. var k = len - 1; @@ -653,9 +653,9 @@ function TypedArraySome(callbackfn, thisArg = undefined) { // Step 6. if (arguments.length === 0) - ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.some"); + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.some"); if (!IsCallable(callbackfn)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); // Step 7. var T = thisArg; @@ -751,14 +751,14 @@ function TypedArrayStaticFrom(source, mapfn = undefined, thisArg = undefined) { // Step 2. if (!IsConstructor(C)) - ThrowError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(1, C)); + ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(1, C)); // Step 3. var f = mapfn; // Step 4. if (f !== undefined && !IsCallable(f)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, f)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, f)); // Steps 5-6. return TypedArrayFrom(C, undefined, source, f, thisArg); @@ -805,7 +805,7 @@ function TypedArrayFrom(constructor, target, items, mapfn, thisArg) { // Steps 10.e.i-ii. var next = iterator.next(); if (!IsObject(next)) - ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE); + ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE); // Steps 10.e.iii-vi. if (next.done) @@ -884,7 +884,7 @@ function TypedArrayStaticOf(/*...items*/) { // Steps 4-5. if (!IsConstructor(C)) - ThrowError(JSMSG_NOT_CONSTRUCTOR, typeof C); + ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, typeof C); var newObj = new C(len); diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 267bf511d2..d6ef81bbb1 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -224,6 +224,7 @@ const Class js::ScalarTypeDescr::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ TypeDescr::finalize, ScalarTypeDescr::call @@ -321,6 +322,7 @@ const Class js::ReferenceTypeDescr::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ TypeDescr::finalize, ReferenceTypeDescr::call @@ -500,6 +502,7 @@ const Class ArrayTypeDescr::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ TypeDescr::finalize, nullptr, /* call */ @@ -727,6 +730,7 @@ const Class StructTypeDescr::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ TypeDescr::finalize, nullptr, /* call */ @@ -2246,6 +2250,7 @@ OutlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx) nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ nullptr, /* convert */ \ nullptr, /* finalize */ \ nullptr, /* call */ \ diff --git a/js/src/builtin/TypedObject.js b/js/src/builtin/TypedObject.js index 06240fc635..59145e1511 100644 --- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -49,7 +49,7 @@ function TypedObjectGet(descr, typedObj, offset) { "get() called with bad type descr"); if (!TypedObjectIsAttached(typedObj)) - ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); + ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); switch (DESCR_KIND(descr)) { case JS_TYPEREPR_SCALAR_KIND: @@ -179,7 +179,7 @@ function TypedObjectGetSimd(descr, typedObj, offset) { // and works for any type. function TypedObjectSet(descr, typedObj, offset, name, fromValue) { if (!TypedObjectIsAttached(typedObj)) - ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); + ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); switch (DESCR_KIND(descr)) { case JS_TYPEREPR_SCALAR_KIND: @@ -218,9 +218,9 @@ function TypedObjectSet(descr, typedObj, offset, name, fromValue) { return; } - ThrowError(JSMSG_CANT_CONVERT_TO, - typeof(fromValue), - DESCR_STRING_REPR(descr)); + ThrowTypeError(JSMSG_CANT_CONVERT_TO, + typeof(fromValue), + DESCR_STRING_REPR(descr)); } function TypedObjectSetArray(descr, length, typedObj, offset, fromValue) { @@ -310,14 +310,14 @@ function TypedObjectSetReference(descr, typedObj, offset, name, fromValue) { // Sets `fromValue` to `this` assuming that `this` is a scalar type. function TypedObjectSetSimd(descr, typedObj, offset, fromValue) { if (!IsObject(fromValue) || !ObjectIsTypedObject(fromValue)) - ThrowError(JSMSG_CANT_CONVERT_TO, - typeof(fromValue), - DESCR_STRING_REPR(descr)); + ThrowTypeError(JSMSG_CANT_CONVERT_TO, + typeof(fromValue), + DESCR_STRING_REPR(descr)); if (!DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) - ThrowError(JSMSG_CANT_CONVERT_TO, - typeof(fromValue), - DESCR_STRING_REPR(descr)); + ThrowTypeError(JSMSG_CANT_CONVERT_TO, + typeof(fromValue), + DESCR_STRING_REPR(descr)); var type = DESCR_TYPE(descr); switch (type) { @@ -360,7 +360,7 @@ function ConvertAndCopyTo(destDescr, "ConvertAndCopyTo: not type typedObj"); if (!TypedObjectIsAttached(destTypedObj)) - ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); + ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); TypedObjectSet(destDescr, destTypedObj, destOffset, fieldName, fromValue); } @@ -375,7 +375,7 @@ function Reify(sourceDescr, "Reify: not type typedObj"); if (!TypedObjectIsAttached(sourceTypedObj)) - ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); + ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); return TypedObjectGet(sourceDescr, sourceTypedObj, sourceOffset); } @@ -383,9 +383,9 @@ function Reify(sourceDescr, // Warning: user exposed! function TypeDescrEquivalent(otherDescr) { if (!IsObject(this) || !ObjectIsTypeDescr(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); if (!IsObject(otherDescr) || !ObjectIsTypeDescr(otherDescr)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); return DescrsEquiv(this, otherDescr); } @@ -411,10 +411,10 @@ function TypeDescrEquivalent(otherDescr) { // Warning: user exposed! function TypedObjectArrayRedimension(newArrayType) { if (!IsObject(this) || !ObjectIsTypedObject(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); if (!IsObject(newArrayType) || !ObjectIsTypeDescr(newArrayType)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); // Peel away the outermost array layers from the type of `this` to find // the core element type. In the process, count the number of elements. @@ -423,7 +423,7 @@ function TypedObjectArrayRedimension(newArrayType) { var oldElementCount = 1; if (DESCR_KIND(oldArrayType) != JS_TYPEREPR_ARRAY_KIND) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); while (DESCR_KIND(oldElementType) === JS_TYPEREPR_ARRAY_KIND) { oldElementCount *= oldElementType.length; @@ -441,12 +441,12 @@ function TypedObjectArrayRedimension(newArrayType) { // Check that the total number of elements does not change. if (oldElementCount !== newElementCount) { - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } // Check that the element types are equivalent. if (!DescrsEquiv(oldElementType, newElementType)) { - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } // Together, this should imply that the sizes are unchanged. @@ -489,12 +489,12 @@ function SimdTypeToLength(type) { function SimdToSource() { if (!IsObject(this) || !ObjectIsTypedObject(this)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); var descr = TypedObjectTypeDescr(this); if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this); var type = DESCR_TYPE(descr); var protoString = SimdProtoString(type); @@ -526,7 +526,7 @@ function DescrsEquiv(descr1, descr2) { // Warning: user exposed! function DescrToSource() { if (!IsObject(this) || !ObjectIsTypeDescr(this)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "toSource", "value"); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Type", "toSource", "value"); return DESCR_STRING_REPR(this); } @@ -534,12 +534,12 @@ function DescrToSource() { // Warning: user exposed! function ArrayShorthand(...dims) { if (!IsObject(this) || !ObjectIsTypeDescr(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var T = GetTypedObjectModule(); if (dims.length == 0) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var accum = this; for (var i = dims.length - 1; i >= 0; i--) @@ -560,7 +560,7 @@ function StorageOfTypedObject(obj) { if (ObjectIsTransparentTypedObject(obj)) { if (!TypedObjectIsAttached(obj)) - ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); + ThrowTypeError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED); var descr = TypedObjectTypeDescr(obj); var byteLength = DESCR_SIZE(descr); @@ -571,7 +571,7 @@ function StorageOfTypedObject(obj) { } } - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } // This is the `objectType()` function defined in the spec. @@ -607,7 +607,7 @@ function TypedObjectArrayTypeBuild(a,b,c) { // Arguments : [depth], func if (!IsObject(this) || !ObjectIsTypeDescr(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var kind = DESCR_KIND(this); switch (kind) { case JS_TYPEREPR_ARRAY_KIND: @@ -616,11 +616,11 @@ function TypedObjectArrayTypeBuild(a,b,c) { else if (typeof a === "number" && typeof b === "function") return BuildTypedSeqImpl(this, this.length, a, b); else if (typeof a === "number") - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); else - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); default: - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } } @@ -629,7 +629,7 @@ function TypedObjectArrayTypeFrom(a, b, c) { // Arguments: arrayLike, [depth], func if (!IsObject(this) || !ObjectIsTypeDescr(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var untypedInput = !IsObject(a) || !ObjectIsTypedObject(a); @@ -645,7 +645,7 @@ function TypedObjectArrayTypeFrom(a, b, c) { else if (IsCallable(b)) return MapUntypedSeqImpl(a, this, b); else - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } else { var explicitDepth = (typeof b === "number"); if (explicitDepth && IsCallable(c)) @@ -653,39 +653,39 @@ function TypedObjectArrayTypeFrom(a, b, c) { else if (IsCallable(b)) return MapTypedSeqImpl(a, 1, this, b); else if (explicitDepth) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); else - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } } // Warning: user exposed! function TypedObjectArrayMap(a, b) { if (!IsObject(this) || !ObjectIsTypedObject(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var thisType = TypedObjectTypeDescr(this); if (!TypeDescrIsArrayType(thisType)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); // Arguments: [depth], func if (typeof a === "number" && typeof b === "function") return MapTypedSeqImpl(this, a, thisType, b); else if (typeof a === "function") return MapTypedSeqImpl(this, 1, thisType, a); - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } // Warning: user exposed! function TypedObjectArrayReduce(a, b) { // Arguments: func, [initial] if (!IsObject(this) || !ObjectIsTypedObject(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var thisType = TypedObjectTypeDescr(this); if (!TypeDescrIsArrayType(thisType)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); if (a !== undefined && typeof a !== "function") - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var outputType = thisType.elementType; return ReduceTypedSeqImpl(this, outputType, a, b); @@ -695,13 +695,13 @@ function TypedObjectArrayReduce(a, b) { function TypedObjectArrayFilter(func) { // Arguments: predicate if (!IsObject(this) || !ObjectIsTypedObject(this)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var thisType = TypedObjectTypeDescr(this); if (!TypeDescrIsArrayType(thisType)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); if (typeof func !== "function") - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); return FilterTypedSeqImpl(this, func); } @@ -725,9 +725,10 @@ function GET_BIT(data, index) { function BuildTypedSeqImpl(arrayType, len, depth, func) { assert(IsObject(arrayType) && ObjectIsTypeDescr(arrayType), "Build called on non-type-object"); - if (depth <= 0 || TO_INT32(depth) !== depth) + if (depth <= 0 || TO_INT32(depth) !== depth) { // RangeError("bad depth") - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); + } // For example, if we have as input // ArrayType(ArrayType(T, 4), 5) @@ -787,7 +788,7 @@ function ComputeIterationSpace(arrayType, depth, len) { grainType = grainType.elementType; } else { // RangeError("Depth "+depth+" too high"); - ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); } } return { iterationSpace: iterationSpace, @@ -829,7 +830,7 @@ function MapUntypedSeqImpl(inArray, outputType, maybeFunc) { assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); if (!IsCallable(maybeFunc)) - ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc)); + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, maybeFunc)); var func = maybeFunc; // Skip check for compatible iteration spaces; any normal JS array @@ -880,20 +881,20 @@ function MapTypedSeqImpl(inArray, depth, outputType, func) { assert(TypeDescrIsArrayType(outputType), "Map/From called on non array-type outputType"); if (depth <= 0 || TO_INT32(depth) !== depth) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); // Compute iteration space for input and output and check for compatibility. var inputType = TypeOfTypedObject(inArray); var {iterationSpace:inIterationSpace, grainType:inGrainType} = ComputeIterationSpace(inputType, depth, inArray.length); if (!IsObject(inGrainType) || !ObjectIsTypeDescr(inGrainType)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var {iterationSpace, grainType:outGrainType, totalLength} = ComputeIterationSpace(outputType, depth, outputType.length); for (var i = 0; i < depth; i++) if (inIterationSpace[i] !== iterationSpace[i]) // TypeError("Incompatible iteration space in input and output type"); - ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); // Create a zeroed instance with no data var result = new outputType(); @@ -986,7 +987,7 @@ function ReduceTypedSeqImpl(array, outputType, func, initial) { if (initial === undefined && array.length < 1) // RangeError("reduce requires array of length > 0") - ThrowError(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); // FIXME bug 950106 Should reduce method supply an outptr handle? // For now, reduce never supplies an outptr, regardless of outputType. @@ -1025,7 +1026,7 @@ function FilterTypedSeqImpl(array, func) { var arrayType = TypeOfTypedObject(array); if (!TypeDescrIsArrayType(arrayType)) - ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS); + ThrowTypeError(JSMSG_TYPEDOBJECT_BAD_ARGS); var elementType = arrayType.elementType; var flags = new Uint8Array(NUM_BYTES(array.length)); diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index 024676abcc..c546c0b290 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -10,7 +10,8 @@ */ /*global ToObject: false, ToInteger: false, IsCallable: false, - ThrowError: false, AssertionFailed: false, + ThrowRangeError: false, ThrowTypeError: false, + AssertionFailed: false, MakeConstructible: false, DecompileArg: false, RuntimeDefaultLocale: false, ParallelDo: false, ParallelSlices: false, NewDenseArray: false, @@ -92,7 +93,7 @@ function ToNumber(v) { /* Spec: ECMAScript Language Specification, 5.1 edition, 9.10 */ function CheckObjectCoercible(v) { if (v === undefined || v === null) - ThrowError(JSMSG_CANT_CONVERT_TO, ToString(v), "object"); + ThrowTypeError(JSMSG_CANT_CONVERT_TO, ToString(v), "object"); } /* Spec: ECMAScript Draft, 6 edition May 22, 2014, 7.1.15 */ @@ -125,7 +126,7 @@ function GetMethod(O, P) { // Step 5. if (!IsCallable(func)) - ThrowError(JSMSG_NOT_FUNCTION, typeof func); + ThrowTypeError(JSMSG_NOT_FUNCTION, typeof func); // Step 6. return func; @@ -148,7 +149,7 @@ function GetIterator(obj, method) { // Step 5. if (!IsObject(iterator)) - ThrowError(JSMSG_NOT_ITERABLE, ToString(iterator)); + ThrowTypeError(JSMSG_NOT_ITERABLE, ToString(iterator)); // Step 6. return iterator; @@ -165,13 +166,34 @@ function GetBuiltinPrototype(builtinName) { return (_builtinCtorsCache[builtinName] || GetBuiltinConstructor(builtinName)).prototype; } +// ES6 draft 20150317 7.3.20. function SpeciesConstructor(obj, defaultConstructor) { - var C = obj.constructor; - if (C === undefined) { + // Step 1. + assert(IsObject(obj), "not passed an object"); + + // Steps 2-3. + var ctor = obj.constructor; + + // Step 4. + if (ctor === undefined) return defaultConstructor; - } - if (!IsConstructor(C)) { - ThrowError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(1, C)); - } - return C; + + // Step 5. + if (!IsObject(ctor)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, "object's 'constructor' property"); + + // Steps 6-7. We don't yet implement @@species and Symbol.species, so we + // don't implement this correctly right now. Somebody fix this! + var s = /* ctor[Symbol.species] */ undefined; + + // Step 8. + if (s === undefined || s === null) + return defaultConstructor; + + // Step 9. + if (IsConstructor(s)) + return s; + + // Step 10. + ThrowTypeError(JSMSG_NOT_CONSTRUCTOR, "@@species property of object's constructor"); } diff --git a/js/src/builtin/WeakSet.js b/js/src/builtin/WeakSet.js index e7913a4651..7004e635d5 100644 --- a/js/src/builtin/WeakSet.js +++ b/js/src/builtin/WeakSet.js @@ -7,16 +7,16 @@ function WeakSet_add(value) { // Steps 1-3. var S = this; if (!IsObject(S) || !IsWeakSet(S)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S); // Step 4.,6. let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT); if (!entries) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S); // Step 5. if (!IsObject(value)) - ThrowError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value)); + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value)); // Steps 7-8. callFunction(std_WeakMap_set, entries, value, true); @@ -30,12 +30,12 @@ function WeakSet_clear() { // Step 1-3. var S = this; if (!IsObject(S) || !IsWeakSet(S)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S); // Step 4. let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT); if (!entries) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S); // Step 5. callFunction(std_WeakMap_clear, entries); @@ -49,12 +49,12 @@ function WeakSet_delete(value) { // Steps 1-3. var S = this; if (!IsObject(S) || !IsWeakSet(S)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S); // Step 4.,6. let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT); if (!entries) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S); // Step 5. if (!IsObject(value)) @@ -69,12 +69,12 @@ function WeakSet_has(value) { // Steps 1-3. var S = this; if (!IsObject(S) || !IsWeakSet(S)) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S); // Step 4-5. let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT); if (!entries) - ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S); + ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S); // Step 6. if (!IsObject(value)) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 58768d4483..0587598dda 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -300,10 +300,12 @@ namespace StructType { bool FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args); - static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, - MutableHandleValue vp); - static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, - MutableHandleValue vp, ObjectOpResult &result); + enum { + SLOT_FIELDNAME + }; + + static bool FieldGetter(JSContext* cx, unsigned argc, Value* vp); + static bool FieldSetter(JSContext* cx, unsigned argc, Value* vp); static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp); static bool Define(JSContext* cx, unsigned argc, jsval* vp); } // namespace StructType @@ -528,7 +530,7 @@ static const JSClass sCABIClass = { static const JSClass sCTypeProtoClass = { "CType", JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, ConstructAbstract, nullptr, ConstructAbstract }; @@ -543,7 +545,7 @@ static const JSClass sCDataProtoClass = { static const JSClass sCTypeClass = { "CType", JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, CType::Finalize, CType::ConstructData, CType::HasInstance, CType::ConstructData, CType::Trace @@ -553,14 +555,14 @@ static const JSClass sCDataClass = { "CData", JSCLASS_HAS_RESERVED_SLOTS(CDATA_SLOTS), nullptr, nullptr, ArrayType::Getter, ArrayType::Setter, - nullptr, nullptr, nullptr, CData::Finalize, + nullptr, nullptr, nullptr, nullptr, CData::Finalize, FunctionType::Call, nullptr, FunctionType::Call }; static const JSClass sCClosureClass = { "CClosure", JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, CClosure::Finalize, nullptr, nullptr, nullptr, CClosure::Trace }; @@ -582,7 +584,7 @@ static const JSClass sCDataFinalizerProtoClass = { static const JSClass sCDataFinalizerClass = { "CDataFinalizer", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, CDataFinalizer::Finalize }; @@ -768,14 +770,14 @@ static const JSClass sUInt64ProtoClass = { static const JSClass sInt64Class = { "Int64", JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Int64Base::Finalize }; static const JSClass sUInt64Class = { "UInt64", JSCLASS_HAS_RESERVED_SLOTS(INT64_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Int64Base::Finalize }; @@ -783,7 +785,7 @@ static const JSFunctionSpec sInt64StaticFunctions[] = { JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS), JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS), JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS), - JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS), + // "join" is defined specially; see InitInt64Class. JS_FS_END }; @@ -791,7 +793,7 @@ static const JSFunctionSpec sUInt64StaticFunctions[] = { JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS), JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS), JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS), - JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS), + // "join" is defined specially; see InitInt64Class. JS_FS_END }; @@ -1102,10 +1104,8 @@ InitInt64Class(JSContext* cx, RootedObject ctor(cx, JS_GetConstructor(cx, prototype)); if (!ctor) return nullptr; - if (!JS_FreezeObject(cx, ctor)) - return nullptr; - // Redefine the 'join' function as an extended native and stash + // Define the 'join' function as an extended native and stash // ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function. MOZ_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass); JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join; @@ -1117,6 +1117,8 @@ InitInt64Class(JSContext* cx, js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO, OBJECT_TO_JSVAL(prototype)); + if (!JS_FreezeObject(cx, ctor)) + return nullptr; if (!JS_FreezeObject(cx, prototype)) return nullptr; @@ -4923,14 +4925,30 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsOb // Add the field to the StructType's 'prototype' property. AutoStableStringChars nameChars(cx); if (!nameChars.initTwoByte(cx, name)) - return false; + return false; + + RootedFunction getter(cx, NewFunctionWithReserved(cx, StructType::FieldGetter, 0, 0, nullptr)); + if (!getter) + return false; + SetFunctionNativeReserved(getter, StructType::SLOT_FIELDNAME, + StringValue(JS_FORGET_STRING_FLATNESS(name))); + RootedObject getterObj(cx, JS_GetFunctionObject(getter)); + + RootedFunction setter(cx, NewFunctionWithReserved(cx, StructType::FieldSetter, 1, 0, nullptr)); + if (!setter) + return false; + SetFunctionNativeReserved(setter, StructType::SLOT_FIELDNAME, + StringValue(JS_FORGET_STRING_FLATNESS(name))); + RootedObject setterObj(cx, JS_GetFunctionObject(setter)); if (!JS_DefineUCProperty(cx, prototype, nameChars.twoByteChars(), name->length(), UndefinedHandleValue, - JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(StructType::FieldGetter), - JS_PROPERTYOP_SETTER(StructType::FieldSetter))) + JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER, + JS_DATA_TO_FUNC_PTR(JSNative, getterObj.get()), + JS_DATA_TO_FUNC_PTR(JSNative, setterObj.get()))) + { return false; + } size_t fieldSize = CType::GetSize(fieldType); size_t fieldAlign = CType::GetAlignment(fieldType); @@ -5271,8 +5289,16 @@ StructType::FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args) } bool -StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp) +StructType::FieldGetter(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args.thisv().isObject()) { + JS_ReportError(cx, "not a CData"); + return false; + } + + RootedObject obj(cx, &args.thisv().toObject()); if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return false; @@ -5284,19 +5310,31 @@ StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, Mutable return false; } - const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); + RootedValue nameVal(cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME)); + Rooted name(cx, JS_FlattenString(cx, nameVal.toString())); + if (!name) + return false; + + const FieldInfo* field = LookupField(cx, typeObj, name); if (!field) return false; char* data = static_cast(CData::GetData(obj)) + field->mOffset; RootedObject fieldType(cx, field->mType); - return ConvertToJS(cx, fieldType, obj, data, false, false, vp); + return ConvertToJS(cx, fieldType, obj, data, false, false, args.rval()); } bool -StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp, - ObjectOpResult &result) +StructType::FieldSetter(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args.thisv().isObject()) { + JS_ReportError(cx, "not a CData"); + return false; + } + + RootedObject obj(cx, &args.thisv().toObject()); if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); return false; @@ -5308,14 +5346,19 @@ StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, Mutable return false; } - const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); + RootedValue nameVal(cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME)); + Rooted name(cx, JS_FlattenString(cx, nameVal.toString())); + if (!name) + return false; + + const FieldInfo* field = LookupField(cx, typeObj, name); if (!field) return false; + args.rval().setUndefined(); + char* data = static_cast(CData::GetData(obj)) + field->mOffset; - if (!ImplicitConvert(cx, vp, field->mType, data, false, nullptr)) - return false; - return result.succeed(); + return ImplicitConvert(cx, args.get(0), field->mType, data, false, nullptr); } bool diff --git a/js/src/ctypes/Library.cpp b/js/src/ctypes/Library.cpp index ddb6971ded..2523f8583d 100644 --- a/js/src/ctypes/Library.cpp +++ b/js/src/ctypes/Library.cpp @@ -35,7 +35,7 @@ typedef Rooted RootedFlatString; static const JSClass sLibraryClass = { "Library", JSCLASS_HAS_RESERVED_SLOTS(LIBRARY_SLOTS), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Library::Finalize }; diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 8f0f5027ca..2a3f23789a 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -174,6 +174,12 @@ var ignoreFunctions = { // And these are workarounds to avoid even more analysis work, // which would sadly still be needed even with bug 898815. "void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true, + + // The nsScriptNameSpaceManager functions can't actually GC. They + // just use a pldhash which has function pointers, which makes the + // analysis think maybe they can. + "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupNavigatorName(nsAString_internal*)": true, + "nsGlobalNameStruct* nsScriptNameSpaceManager::LookupName(nsAString_internal*, uint16**)": true, }; function ignoreGCFunction(mangled) diff --git a/js/src/doc/Debugger/Debugger.Frame.md b/js/src/doc/Debugger/Debugger.Frame.md index 333f686072..4cbeb6a9f7 100644 --- a/js/src/doc/Debugger/Debugger.Frame.md +++ b/js/src/doc/Debugger/Debugger.Frame.md @@ -225,6 +225,12 @@ the compartment to which the handler method belongs. how execution should proceed. On newly created frames, this property's value is `undefined`. + When this handler is called, this frame's current execution location, as + reflected in its `offset` and `environment` properties, is the operation + which caused it to be unwound. In frames returning or throwing an + exception, the location is often a return or a throw statement. In frames + propagating exceptions, the location is a call. + When an `onPop` call reports the completion of a construction call (that is, a function called via the `new` operator), the completion value passed to the handler describes the value returned by the diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md index 4a346be710..ee1f1dfd5c 100644 --- a/js/src/doc/Debugger/Debugger.Memory.md +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -198,14 +198,27 @@ Function Properties of the `Debugger.Memory.prototype` Object

     {
       "timestamp": timestamp,
-      "frame": allocationSite
+      "frame": allocationSite,
+      "class": className,
+      "constructor": constructorName
     }
     
- Here timestamp is the [timestamp][timestamps] of the allocation event and - allocationSite is an allocation site (as a - [captured stack][saved-frame]). allocationSite is `null` for objects - allocated with no JavaScript frames on the stack. + Where + + * *timestamp* is the [timestamp][timestamps] of the allocation event. + + * *allocationSite* is an allocation site (as a + [captured stack][saved-frame]). Note that this property can be null if the + object was allocated with no JavaScript frames on the stack. + + * *className* is the string name of the allocated object's internal + `[[Class]]` property, for example "Array", "Date", "RegExp", or (most + commonly) "Object". + + * *constructorName* is the constructor function's display name for objects + created by `new Ctor`. If that data is not available, or the object was + not created with a `new` expression, this property is `null`. When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an `Error`. diff --git a/js/src/ds/IdValuePair.h b/js/src/ds/IdValuePair.h index b77c307919..df993fcdd9 100644 --- a/js/src/ds/IdValuePair.h +++ b/js/src/ds/IdValuePair.h @@ -29,12 +29,12 @@ struct IdValuePair {} }; -class MOZ_STACK_CLASS AutoIdValueVector : public AutoVectorRooter +class MOZ_STACK_CLASS AutoIdValueVector : public JS::AutoVectorRooterBase { public: explicit AutoIdValueVector(ContextFriendFields* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, IDVALVECTOR) + : AutoVectorRooterBase(cx, IDVALVECTOR) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 3dca31998d..34585fd02a 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -360,7 +360,7 @@ const char js_with_statement_str[] = "with statement"; const char js_finally_block_str[] = "finally block"; const char js_script_str[] = "script"; -static const char* const statementName[] = { +static const char * const statementName[] = { "label statement", /* LABEL */ "if statement", /* IF */ "else statement", /* ELSE */ @@ -615,7 +615,7 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE* toStmt) #define FLUSH_POPS() if (npops && !bce->flushPops(&npops)) return false - for (StmtInfoBCE *stmt = bce->topStmt; stmt != toStmt; stmt = stmt->down) { + for (StmtInfoBCE* stmt = bce->topStmt; stmt != toStmt; stmt = stmt->down) { switch (stmt->type) { case STMT_FINALLY: FLUSH_POPS(); @@ -661,14 +661,15 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE* toStmt) if (stmt->isBlockScope) { MOZ_ASSERT(stmt->isNestedScope); StaticBlockObject& blockObj = stmt->staticBlock(); - if (!bce->emit1(JSOP_DEBUGLEAVEBLOCK)) - return false; - if (!popScopeForNonLocalExit(stmt->blockScopeIndex)) - return false; if (blockObj.needsClone()) { if (!bce->emit1(JSOP_POPBLOCKSCOPE)) return false; + } else { + if (!bce->emit1(JSOP_DEBUGLEAVEBLOCK)) + return false; } + if (!popScopeForNonLocalExit(stmt->blockScopeIndex)) + return false; } } @@ -880,12 +881,18 @@ BytecodeEmitter::computeLocalOffset(Handle blockObj) // Only functions have fixed var bindings. // // To assist the debugger, we emit a DEBUGLEAVEBLOCK opcode before leaving a -// block scope, even if the block has no aliased locals. This allows -// DebugScopes to invalidate any association between a debugger scope object, -// which can proxy access to unaliased stack locals, and the actual live frame. -// In normal, non-debug mode, this opcode does not cause any baseline code to be +// block scope, if the block has no aliased locals. This allows DebugScopes +// to invalidate any association between a debugger scope object, which can +// proxy access to unaliased stack locals, and the actual live frame. In +// normal, non-debug mode, this opcode does not cause any baseline code to be // emitted. // +// If the block has aliased locals, no DEBUGLEAVEBLOCK is emitted, and +// POPBLOCKSCOPE itself balances the debug scope mapping. This gets around a +// comedic situation where DEBUGLEAVEBLOCK may remove a block scope from the +// debug scope map, but the immediate following POPBLOCKSCOPE adds it back due +// to an onStep hook. +// // Enter a nested scope with enterNestedScope. It will emit // PUSHBLOCKSCOPE/ENTERWITH if needed, and arrange to record the PC bounds of // the scope. Leave a nested scope with leaveNestedScope, which, for blocks, @@ -937,7 +944,7 @@ BytecodeEmitter::enterNestedScope(StmtInfoBCE* stmt, ObjectBox* objbox, StmtType pushStatement(stmt, stmtType, offset()); scopeObj->initEnclosingNestedScope(enclosingStaticScope()); - FinishPushNestedScope(this, stmt,* scopeObj); + FinishPushNestedScope(this, stmt, *scopeObj); MOZ_ASSERT(stmt->isNestedScope); stmt->isBlockScope = (stmtType == STMT_BLOCK); @@ -977,16 +984,21 @@ BytecodeEmitter::leaveNestedScope(StmtInfoBCE* stmt) popStatement(); - if (!emit1(stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH)) - return false; - - blockScopeList.recordEnd(blockScopeIndex, offset()); - - if (stmt->isBlockScope && stmt->staticScope->as().needsClone()) { - if (!emit1(JSOP_POPBLOCKSCOPE)) + if (stmt->isBlockScope) { + if (stmt->staticScope->as().needsClone()) { + if (!emit1(JSOP_POPBLOCKSCOPE)) + return false; + } else { + if (!emit1(JSOP_DEBUGLEAVEBLOCK)) + return false; + } + } else { + if (!emit1(JSOP_LEAVEWITH)) return false; } + blockScopeList.recordEnd(blockScopeIndex, offset()); + return true; } @@ -5395,7 +5407,7 @@ BytecodeEmitter::emitNormalFor(ParseNode* pn, ptrdiff_t top) return true; } -inline bool +bool BytecodeEmitter::emitFor(ParseNode* pn, ptrdiff_t top) { if (pn->pn_left->isKind(PNK_FORIN)) @@ -6394,7 +6406,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) return false; } } else { - if (!emitArray(pn2->pn_next, argc)) + if (!emitArray(pn2->pn_next, argc, JSOP_SPREADCALLARRAY)) return false; } emittingForInit = oldEmittingForInit; @@ -6730,7 +6742,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, propdef->pn_right->pn_funbox->needsHomeObject()) { MOZ_ASSERT(propdef->pn_right->pn_funbox->function()->isMethod()); - if (!emit1(JSOP_INITHOMEOBJECT)) + if (!emit2(JSOP_INITHOMEOBJECT, isIndex)) return false; } @@ -6860,7 +6872,7 @@ BytecodeEmitter::emitSpread() } bool -BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) +BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) { /* * Emit code for [a, b, c] that is equivalent to constructing a new @@ -6870,6 +6882,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) * to avoid dup'ing and popping the array as each element is added, as * JSOP_SETELEM/JSOP_SETPROP would do. */ + MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY); int32_t nspread = 0; for (ParseNode* elt = pn; elt; elt = elt->pn_next) { @@ -6878,9 +6891,9 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) } ptrdiff_t off; - if (!emitN(JSOP_NEWARRAY, 3, &off)) // ARRAY + if (!emitN(op, 3, &off)) // ARRAY return false; - checkTypeSet(JSOP_NEWARRAY); + checkTypeSet(op); jsbytecode* pc = code(off); // For arrays with spread, this is a very pessimistic allocation, the @@ -7087,7 +7100,7 @@ BytecodeEmitter::emitClass(ParseNode* pn) return false; if (constructor->pn_funbox->needsHomeObject()) { - if (!emit1(JSOP_INITHOMEOBJECT)) + if (!emit2(JSOP_INITHOMEOBJECT, 0)) return false; } @@ -7539,7 +7552,7 @@ BytecodeEmitter::emitTree(ParseNode* pn) if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj)) return false; - ObjectBox* objbox = parser->newObjectBox(obj); + ObjectBox* objbox = parser->newObjectBox(obj); if (!objbox) return false; @@ -7549,7 +7562,7 @@ BytecodeEmitter::emitTree(ParseNode* pn) } } - ok = emitArray(pn->pn_head, pn->pn_count); + ok = emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY); break; case PNK_ARRAYCOMP: @@ -7740,7 +7753,7 @@ BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offs SrcNotesVector& notes = this->notes(); /* Find the offset numbered which (i.e., skip exactly which offsets). */ - jssrcnote *sn = ¬es[index]; + jssrcnote* sn = ¬es[index]; MOZ_ASSERT(SN_TYPE(sn) != SRC_XDELTA); MOZ_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity); for (sn++; which; sn++, which--) { diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index e42405ae4b..1e274ad33a 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -427,7 +427,7 @@ struct BytecodeEmitter bool emitAtomOp(JSAtom* atom, JSOp op); bool emitAtomOp(ParseNode* pn, JSOp op); - bool emitArray(ParseNode* pn, uint32_t count); + bool emitArray(ParseNode* pn, uint32_t count, JSOp op); bool emitArrayComp(ParseNode* pn); bool emitInternedObjectOp(uint32_t index, JSOp op); diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 5f88c640f9..ec0575ec1a 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -794,6 +794,8 @@ class HeapSlot : public BarrieredBase reinterpret_cast(const_cast(&target))->post(owner, kind, slot, target); } + Value* unsafeGet() { return &value; } + private: void post(NativeObject* owner, Kind kind, uint32_t slot, const Value& target) { MOZ_ASSERT(preconditionForWriteBarrierPost(owner, kind, slot, target)); @@ -805,22 +807,6 @@ class HeapSlot : public BarrieredBase } }; -static inline const Value* -Valueify(const BarrieredBase* array) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (const Value*)array; -} - -static inline HeapValue* -HeapValueify(Value* v) -{ - JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); - JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); - return (HeapValue*)v; -} - class HeapSlotArray { HeapSlot* array; @@ -839,7 +825,11 @@ class HeapSlotArray #endif {} - operator const Value*() const { return Valueify(array); } + operator const Value*() const { + JS_STATIC_ASSERT(sizeof(HeapValue) == sizeof(Value)); + JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value)); + return reinterpret_cast(array); + } operator HeapSlot*() const { MOZ_ASSERT(allowWrite()); return array; } HeapSlotArray operator +(int offset) const { return HeapSlotArray(array + offset, allowWrite()); } diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index ca402aad70..ad7c3375fe 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -7,6 +7,9 @@ #ifndef gc_GCInternals_h #define gc_GCInternals_h +#include "mozilla/ArrayUtils.h" +#include "mozilla/PodOperations.h" + #include "jscntxt.h" #include "gc/Zone.h" @@ -93,7 +96,6 @@ class AutoStopVerifyingBarriers { GCRuntime* gc; bool restartPreVerifier; - bool restartPostVerifier; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER public: @@ -102,8 +104,6 @@ class AutoStopVerifyingBarriers : gc(&rt->gc) { restartPreVerifier = gc->endVerifyPreBarriers() && !isShutdown; - restartPostVerifier = gc->endVerifyPostBarriers() && !isShutdown && - JS::IsGenerationalGCEnabled(rt); MOZ_GUARD_OBJECT_NOTIFIER_INIT; } @@ -121,8 +121,6 @@ class AutoStopVerifyingBarriers if (restartPreVerifier) gc->startVerifyPreBarriers(); - if (restartPostVerifier) - gc->startVerifyPostBarriers(); if (outer != gcstats::PHASE_NONE) gc->stats.beginPhase(outer); @@ -149,6 +147,29 @@ struct MovingTracer : JS::CallbackTracer { } }; +class AutoMaybeStartBackgroundAllocation +{ + private: + JSRuntime *runtime; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + + public: + explicit AutoMaybeStartBackgroundAllocation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) + : runtime(nullptr) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + void tryToStartBackgroundAllocation(JSRuntime *rt) { + runtime = rt; + } + + ~AutoMaybeStartBackgroundAllocation() { + if (runtime) + runtime->gc.startBackgroundAllocTaskIfIdle(); + } +}; + // In debug builds, set/unset the GC sweeping flag for the current thread. struct AutoSetThreadIsSweeping { @@ -174,26 +195,25 @@ struct AutoSetThreadIsSweeping #endif }; -class AutoMaybeStartBackgroundAllocation +// Structure for counting how many times objects in a particular group have +// been tenured during a minor collection. +struct TenureCount { - private: - JSRuntime *runtime; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + ObjectGroup* group; + int count; +}; - public: - explicit AutoMaybeStartBackgroundAllocation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) - : runtime(nullptr) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } +// Keep rough track of how many times we tenure objects in particular groups +// during minor collections, using a fixed size hash for efficiency at the cost +// of potential collisions. +struct TenureCountCache +{ + TenureCount entries[16]; - void tryToStartBackgroundAllocation(JSRuntime *rt) { - runtime = rt; - } + TenureCountCache() { mozilla::PodZero(this); } - ~AutoMaybeStartBackgroundAllocation() { - if (runtime) - runtime->gc.startBackgroundAllocTaskIfIdle(); + TenureCount& findEntry(ObjectGroup* group) { + return entries[PointerHasher::hash(group) % mozilla::ArrayLength(entries)]; } }; diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index e1839d917f..025b60c4c3 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -655,9 +655,7 @@ class GCRuntime bool parseAndSetZeal(const char* str); void setNextScheduled(uint32_t count); void verifyPreBarriers(); - void verifyPostBarriers(); void maybeVerifyPreBarriers(bool always); - void maybeVerifyPostBarriers(bool always); bool selectForMarking(JSObject* object); void clearSelectedForMarking(); void setDeterministic(bool enable); @@ -825,8 +823,6 @@ class GCRuntime #ifdef JS_GC_ZEAL void startVerifyPreBarriers(); bool endVerifyPreBarriers(); - void startVerifyPostBarriers(); - bool endVerifyPostBarriers(); void finishVerifier(); bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; } #else @@ -1014,7 +1010,6 @@ class GCRuntime */ mozilla::Atomic numArenasFreeCommitted; void* verifyPreData; - void* verifyPostData; bool chunkAllocationSinceLastGC; int64_t nextFullGCTime; int64_t lastGCTime; diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 44c101eae2..20c5e88d85 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -8,6 +8,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/IntegerRange.h" +#include "mozilla/ReentrancyGuard.h" #include "mozilla/TypeTraits.h" #include "jsgc.h" @@ -33,6 +34,7 @@ using namespace js; using namespace js::gc; +using mozilla::ArrayLength; using mozilla::DebugOnly; using mozilla::IsBaseOf; using mozilla::IsSame; @@ -183,7 +185,7 @@ js::CheckTracedThing(JSTracer* trc, T thing) if (IsInsideNursery(thing)) return; - MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc) && !Nursery::IsMinorCollectionTracer(trc), + MOZ_ASSERT_IF(!MovingTracer::IsMovingTracer(trc) && !trc->isTenuringTracer(), !IsForwarded(thing)); /* @@ -313,23 +315,39 @@ ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Value val) return val.isMarkable() && ShouldMarkCrossCompartment(trc, src, (Cell*)val.toGCThing()); } -template -static bool -ZoneIsAtomsZoneForString(JSRuntime* rt, T* thing) +static void +AssertZoneIsMarking(Cell* thing) { - JSGCTraceKind kind = GetGCThingTraceKind(thing); - if (kind == JSTRACE_STRING || kind == JSTRACE_SYMBOL) - return rt->isAtomsZone(thing->zone()); - return false; + MOZ_ASSERT(TenuredCell::fromPointer(thing)->zone()->isGCMarking()); } -#define JS_COMPARTMENT_ASSERT(rt, thing) \ - MOZ_ASSERT((thing)->zone()->isGCMarking() || ZoneIsAtomsZoneForString((rt), (thing))) +static void +AssertZoneIsMarking(JSString* str) +{ +#ifdef DEBUG + Zone* zone = TenuredCell::fromPointer(str)->zone(); + JSRuntime* rt = str->runtimeFromMainThread(); + MOZ_ASSERT(zone->isGCMarking() || rt->isAtomsZone(zone)); +#endif +} -#define JS_ROOT_MARKING_ASSERT(trc) \ - MOZ_ASSERT_IF(trc->isMarkingTracer(), \ - trc->runtime()->gc.state() == NO_INCREMENTAL || \ +static void +AssertZoneIsMarking(JS::Symbol* sym) +{ +#ifdef DEBUG + Zone* zone = TenuredCell::fromPointer(sym)->zone(); + JSRuntime* rt = sym->runtimeFromMainThread(); + MOZ_ASSERT(zone->isGCMarking() || rt->isAtomsZone(zone)); +#endif +} + +static void +AssertRootMarkingPhase(JSTracer* trc) +{ + MOZ_ASSERT_IF(trc->isMarkingTracer(), + trc->runtime()->gc.state() == NO_INCREMENTAL || trc->runtime()->gc.state() == MARK_ROOTS); +} /*** Tracing Interface ***************************************************************************/ @@ -441,7 +459,7 @@ template void js::TraceRoot(JSTracer* trc, T* thingp, const char* name) { - JS_ROOT_MARKING_ASSERT(trc); + AssertRootMarkingPhase(trc); DispatchToTracer(trc, ConvertToBase(thingp), name); } @@ -461,7 +479,7 @@ template void js::TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name) { - JS_ROOT_MARKING_ASSERT(trc); + AssertRootMarkingPhase(trc); JS::AutoTracingIndex index(trc); for (auto i : MakeRange(len)) { if (InternalGCMethods::isMarkable(vec[i])) @@ -507,7 +525,7 @@ template void js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name) { - JS_ROOT_MARKING_ASSERT(trc); + AssertRootMarkingPhase(trc); MOZ_ASSERT(ThingIsPermanentAtomOrWellKnownSymbol(thing)); // We have to mark permanent atoms and well-known symbols through a special @@ -589,6 +607,9 @@ DispatchToTracer(JSTracer* trc, T* thingp, const char* name) #undef IS_SAME_TYPE_OR if (trc->isMarkingTracer()) return DoMarking(static_cast(trc), *thingp); + if (trc->isTenuringTracer()) + return static_cast(trc)->traverse(thingp); + MOZ_ASSERT(trc->isCallbackTracer()); DoCallback(trc->asCallbackTracer(), thingp, name); } @@ -776,7 +797,7 @@ bool js::GCMarker::mark(T* thing) { CheckTracedThing(this, thing); - JS_COMPARTMENT_ASSERT(runtime(), thing); + AssertZoneIsMarking(thing); MOZ_ASSERT(!IsInsideNursery(gc::TenuredCell::fromPointer(thing))); return gc::ParticipatesInCC::value ? gc::TenuredCell::fromPointer(thing)->markIfUnmarked(markColor()) @@ -900,7 +921,7 @@ JSString::traceBase(JSTracer* trc) inline void js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr) { - JS_COMPARTMENT_ASSERT(runtime(), linearStr); + AssertZoneIsMarking(linearStr); MOZ_ASSERT(linearStr->isMarked()); MOZ_ASSERT(linearStr->JSString::isLinear()); @@ -910,7 +931,7 @@ js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr) MOZ_ASSERT(linearStr->JSString::isLinear()); if (linearStr->isPermanentAtom()) break; - JS_COMPARTMENT_ASSERT(runtime(), linearStr); + AssertZoneIsMarking(linearStr); if (!mark(static_cast(linearStr))) break; } @@ -937,7 +958,7 @@ js::GCMarker::eagerlyMarkChildren(JSRope* rope) while (true) { JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING); JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope()); - JS_COMPARTMENT_ASSERT(runtime(), rope); + AssertZoneIsMarking(rope); MOZ_ASSERT(rope->isMarked()); JSRope* next = nullptr; @@ -1132,7 +1153,7 @@ GCMarker::processMarkStackTop(SliceBudget& budget) case ObjectTag: { obj = reinterpret_cast(addr); - JS_COMPARTMENT_ASSERT(runtime(), obj); + AssertZoneIsMarking(obj); goto scan_obj; } @@ -1146,11 +1167,11 @@ GCMarker::processMarkStackTop(SliceBudget& budget) case SavedValueArrayTag: { MOZ_ASSERT(!(addr & CellMask)); - NativeObject* obj = reinterpret_cast(addr); + JSObject* obj = reinterpret_cast(addr); HeapValue* vp; HeapValue* end; if (restoreValueArray(obj, (void**)&vp, (void**)&end)) - pushValueArray(obj, vp, end); + pushValueArray(&obj->as(), vp, end); else repush(obj); return; @@ -1176,7 +1197,7 @@ GCMarker::processMarkStackTop(SliceBudget& budget) JSObject* obj2 = &v.toObject(); MOZ_ASSERT(obj->compartment() == obj2->compartment()); if (mark(obj2)) { - // Save the rest of this value array for later and start scanning obj2's children.N + // Save the rest of this value array for later and start scanning obj2's children. pushValueArray(obj, vp, end); obj = obj2; goto scan_obj; @@ -1219,7 +1240,7 @@ GCMarker::processMarkStackTop(SliceBudget& budget) scan_obj: { - JS_COMPARTMENT_ASSERT(runtime(), obj); + AssertZoneIsMarking(obj); budget.step(); if (budget.isOverBudget()) { @@ -1374,11 +1395,15 @@ GCMarker::saveValueRanges() } bool -GCMarker::restoreValueArray(NativeObject* obj, void** vpp, void** endp) +GCMarker::restoreValueArray(JSObject* objArg, void** vpp, void** endp) { uintptr_t start = stack.pop(); HeapSlot::Kind kind = (HeapSlot::Kind) stack.pop(); + if (!objArg->isNative()) + return false; + NativeObject* obj = &objArg->as(); + if (kind == HeapSlot::Element) { if (!obj->is()) return false; @@ -1520,7 +1545,7 @@ MarkStack::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const * so we delay visting entries. */ GCMarker::GCMarker(JSRuntime* rt) - : JSTracer(rt, JSTracer::MarkingTracer, DoNotTraceWeakMaps), + : JSTracer(rt, JSTracer::TracerKindTag::Marking, DoNotTraceWeakMaps), stack(size_t(-1)), color(BLACK), unmarkedArenaStackTop(nullptr), @@ -1713,11 +1738,375 @@ GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const return size; } + +/*** Tenuring Tracer *****************************************************************************/ + +namespace js { +template <> void -js::SetMarkStackLimit(JSRuntime* rt, size_t limit) +TenuringTracer::traverse(JSObject** objp) { - rt->gc.setMarkStackLimit(limit); + // We only ever visit the internals of objects after moving them to tenured. + MOZ_ASSERT(!nursery().isInside(objp)); + + if (IsInsideNursery(*objp) && !nursery().getForwardedPointer(objp)) + *objp = moveToTenured(*objp); } + +template <> +void +TenuringTracer::traverse(Value* valp) +{ + if (!valp->isObject()) + return; + + JSObject *obj = &valp->toObject(); + traverse(&obj); + valp->setObject(*obj); +} + +template <> void js::TenuringTracer::traverse(js::BaseShape**) {} +template <> void js::TenuringTracer::traverse(js::jit::JitCode**) {} +template <> void js::TenuringTracer::traverse(JSScript**) {} +template <> void js::TenuringTracer::traverse(js::LazyScript**) {} +template <> void js::TenuringTracer::traverse(js::Shape**) {} +template <> void js::TenuringTracer::traverse(JSString**) {} +template <> void js::TenuringTracer::traverse(JS::Symbol**) {} +template <> void js::TenuringTracer::traverse(js::ObjectGroup**) {} +template <> void js::TenuringTracer::traverse(jsid*) {} +} // namespace js + +template +void +js::gc::StoreBuffer::MonoTypeBuffer::trace(StoreBuffer* owner, TenuringTracer& mover) +{ + mozilla::ReentrancyGuard g(*owner); + MOZ_ASSERT(owner->isEnabled()); + MOZ_ASSERT(stores_.initialized()); + sinkStores(owner); + for (typename StoreSet::Range r = stores_.all(); !r.empty(); r.popFront()) + r.front().trace(mover); +} + +namespace js { +namespace gc { +template void +StoreBuffer::MonoTypeBuffer::trace(StoreBuffer*, TenuringTracer&); +template void +StoreBuffer::MonoTypeBuffer::trace(StoreBuffer*, TenuringTracer&); +template void +StoreBuffer::MonoTypeBuffer::trace(StoreBuffer*, TenuringTracer&); +template void +StoreBuffer::MonoTypeBuffer::trace(StoreBuffer*, TenuringTracer&); +} // namespace js +} // namespace gc + +void +js::gc::StoreBuffer::SlotsEdge::trace(TenuringTracer& mover) const +{ + NativeObject* obj = object(); + + // Beware JSObject::swap exchanging a native object for a non-native one. + if (!obj->isNative()) + return; + + if (IsInsideNursery(obj)) + return; + + if (kind() == ElementKind) { + int32_t initLen = obj->getDenseInitializedLength(); + int32_t clampedStart = Min(start_, initLen); + int32_t clampedEnd = Min(start_ + count_, initLen); + mover.traceSlots(static_cast(obj->getDenseElements() + clampedStart)->unsafeGet(), + clampedEnd - clampedStart); + } else { + int32_t start = Min(uint32_t(start_), obj->slotSpan()); + int32_t end = Min(uint32_t(start_) + count_, obj->slotSpan()); + MOZ_ASSERT(end >= start); + mover.traceObjectSlots(obj, start, end - start); + } +} + +void +js::gc::StoreBuffer::WholeCellEdges::trace(TenuringTracer& mover) const +{ + MOZ_ASSERT(edge->isTenured()); + JSGCTraceKind kind = GetGCThingTraceKind(edge); + if (kind <= JSTRACE_OBJECT) { + JSObject *object = static_cast(edge); + mover.traceObject(object); + + // Additionally trace the expando object attached to any unboxed plain + // objects. Baseline and Ion can write properties to the expando while + // only adding a post barrier to the owning unboxed object. Note that + // it isn't possible for a nursery unboxed object to have a tenured + // expando, so that adding a post barrier on the original object will + // capture any tenured->nursery edges in the expando as well. + if (object->is()) { + if (UnboxedExpandoObject* expando = object->as().maybeExpando()) + expando->traceChildren(&mover); + } + + return; + } + MOZ_ASSERT(kind == JSTRACE_JITCODE); + static_cast(edge)->traceChildren(&mover); +} + +void +js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const +{ + if (!*edge) + return; + + MOZ_ASSERT(GetGCThingTraceKind(*edge) == JSTRACE_OBJECT); + mover.traverse(reinterpret_cast(edge)); +} + +void +js::gc::StoreBuffer::ValueEdge::trace(TenuringTracer& mover) const +{ + if (deref()) + mover.traverse(edge); +} + +/* Insert the given relocation entry into the list of things to visit. */ +void +js::TenuringTracer::insertIntoFixupList(RelocationOverlay* entry) { + *tail = entry; + tail = &entry->next_; + *tail = nullptr; +} + +JSObject* +js::TenuringTracer::moveToTenured(JSObject* src) +{ + MOZ_ASSERT(IsInsideNursery(src)); + + AllocKind dstKind = src->allocKindForTenure(nursery()); + Zone* zone = src->zone(); + TenuredCell* t = zone->arenas.allocateFromFreeList(dstKind, Arena::thingSize(dstKind)); + if (!t) { + zone->arenas.checkEmptyFreeList(dstKind); + AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation; + t = zone->arenas.allocateFromArena(zone, dstKind, maybeStartBackgroundAllocation); + if (!t) + CrashAtUnhandlableOOM("Failed to allocate object while tenuring."); + } + JSObject* dst = reinterpret_cast(t); + + tenuredSize += moveObjectToTenured(dst, src, dstKind); + + RelocationOverlay* overlay = RelocationOverlay::fromCell(src); + overlay->forwardTo(dst); + insertIntoFixupList(overlay); + + TracePromoteToTenured(src, dst); + return dst; +} + +void +js::Nursery::collectToFixedPoint(TenuringTracer& mover, TenureCountCache& tenureCounts) +{ + for (RelocationOverlay* p = mover.head; p; p = p->next()) { + JSObject* obj = static_cast(p->forwardingAddress()); + mover.traceObject(obj); + + TenureCount& entry = tenureCounts.findEntry(obj->group()); + if (entry.group == obj->group()) { + entry.count++; + } else if (!entry.group) { + entry.group = obj->group(); + entry.count = 1; + } + } +} + +// Visit all object children of the object and trace them. +void +js::TenuringTracer::traceObject(JSObject* obj) +{ + const Class* clasp = obj->getClass(); + if (clasp->trace) { + if (clasp->trace == InlineTypedObject::obj_trace) { + TypeDescr* descr = &obj->as().typeDescr(); + if (descr->hasTraceList()) + markTraceList(descr->traceList(), obj->as().inlineTypedMem()); + return; + } + if (clasp == &UnboxedPlainObject::class_) { + JSObject** pexpando = obj->as().addressOfExpando(); + if (*pexpando) + traverse(pexpando); + const UnboxedLayout& layout = obj->as().layoutDontCheckGeneration(); + if (layout.traceList()) + markTraceList(layout.traceList(), obj->as().data()); + return; + } + clasp->trace(this, obj); + } + + MOZ_ASSERT(obj->isNative() == clasp->isNative()); + if (!clasp->isNative()) + return; + NativeObject* nobj = &obj->as(); + + // Note: the contents of copy on write elements pointers are filled in + // during parsing and cannot contain nursery pointers. + if (!nobj->hasEmptyElements() && !nobj->denseElementsAreCopyOnWrite()) { + Value* elems = static_cast(nobj->getDenseElements())->unsafeGet(); + traceSlots(elems, elems + nobj->getDenseInitializedLength()); + } + + traceObjectSlots(nobj, 0, nobj->slotSpan()); +} + +void +js::TenuringTracer::traceObjectSlots(NativeObject* nobj, uint32_t start, uint32_t length) +{ + HeapSlot* fixedStart; + HeapSlot* fixedEnd; + HeapSlot* dynStart; + HeapSlot* dynEnd; + nobj->getSlotRange(start, length, &fixedStart, &fixedEnd, &dynStart, &dynEnd); + traceSlots(fixedStart->unsafeGet(), fixedEnd->unsafeGet()); + traceSlots(dynStart->unsafeGet(), dynEnd->unsafeGet()); +} + +void +js::TenuringTracer::traceSlots(Value* vp, Value* end) +{ + for (; vp != end; ++vp) + traverse(vp); +} + +void +js::TenuringTracer::markTraceList(const int32_t* traceList, uint8_t* memory) +{ + while (*traceList != -1) { + // Strings are not in the nursery and do not need tracing. + traceList++; + } + traceList++; + while (*traceList != -1) { + JSObject** pobj = reinterpret_cast(memory + *traceList); + traverse(pobj); + traceList++; + } + traceList++; + while (*traceList != -1) { + Value* pslot = reinterpret_cast(memory + *traceList); + traverse(pslot); + traceList++; + } +} + +size_t +js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind dstKind) +{ + size_t srcSize = Arena::thingSize(dstKind); + size_t tenuredSize = srcSize; + + /* + * Arrays do not necessarily have the same AllocKind between src and dst. + * We deal with this by copying elements manually, possibly re-inlining + * them if there is adequate room inline in dst. + * + * For Arrays we're reducing tenuredSize to the smaller srcSize + * because moveElementsToTenured() accounts for all Array elements, + * even if they are inlined. + */ + if (src->is()) + tenuredSize = srcSize = sizeof(NativeObject); + + js_memcpy(dst, src, srcSize); + if (src->isNative()) { + NativeObject* ndst = &dst->as(); + NativeObject* nsrc = &src->as(); + tenuredSize += moveSlotsToTenured(ndst, nsrc, dstKind); + tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind); + + // The shape's list head may point into the old object. This can only + // happen for dictionaries, which are native objects. + if (&nsrc->shape_ == ndst->shape_->listp) { + MOZ_ASSERT(nsrc->shape_->inDictionary()); + ndst->shape_->listp = &ndst->shape_; + } + } + + if (src->is()) { + InlineTypedObject::objectMovedDuringMinorGC(this, dst, src); + } else if (src->is()) { + tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind); + } else { + // Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above + // to ensure any additional nursery buffers they hold are moved. + MOZ_ASSERT(!(src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE)); + } + + return tenuredSize; +} + +size_t +js::TenuringTracer::moveSlotsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind) +{ + /* Fixed slots have already been copied over. */ + if (!src->hasDynamicSlots()) + return 0; + + if (!nursery().isInside(src->slots_)) { + nursery().removeMallocedBuffer(src->slots_); + return 0; + } + + Zone* zone = src->zone(); + size_t count = src->numDynamicSlots(); + dst->slots_ = zone->pod_malloc(count); + if (!dst->slots_) + CrashAtUnhandlableOOM("Failed to allocate slots while tenuring."); + PodCopy(dst->slots_, src->slots_, count); + nursery().setSlotsForwardingPointer(src->slots_, dst->slots_, count); + return count * sizeof(HeapSlot); +} + +size_t +js::TenuringTracer::moveElementsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind) +{ + if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite()) + return 0; + + Zone* zone = src->zone(); + ObjectElements* srcHeader = src->getElementsHeader(); + ObjectElements* dstHeader; + + /* TODO Bug 874151: Prefer to put element data inline if we have space. */ + if (!nursery().isInside(srcHeader)) { + MOZ_ASSERT(src->elements_ == dst->elements_); + nursery().removeMallocedBuffer(srcHeader); + return 0; + } + + size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity; + + /* Unlike other objects, Arrays can have fixed elements. */ + if (src->is() && nslots <= GetGCKindSlots(dstKind)) { + dst->as().setFixedElements(); + dstHeader = dst->as().getElementsHeader(); + js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); + nursery().setElementsForwardingPointer(srcHeader, dstHeader, nslots); + return nslots * sizeof(HeapSlot); + } + + MOZ_ASSERT(nslots >= 2); + dstHeader = reinterpret_cast(zone->pod_malloc(nslots)); + if (!dstHeader) + CrashAtUnhandlableOOM("Failed to allocate elements while tenuring."); + js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); + nursery().setElementsForwardingPointer(srcHeader, dstHeader, nslots); + dst->elements_ = dstHeader->elements(); + return nslots * sizeof(HeapSlot); +} + /*** IsMarked / IsAboutToBeFinalized **************************************************************/ @@ -1743,15 +2132,10 @@ CheckIsMarkedThing(T* thingp) template static bool -IsMarkedInternal(T* thingp) +IsMarkedInternalCommon(T* thingp) { CheckIsMarkedThing(thingp); - JSRuntime* rt = (*thingp)->runtimeFromAnyThread(); - - if (IsInsideNursery(*thingp)) { - MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); - return rt->gc.nursery.getForwardedPointer(thingp); - } + MOZ_ASSERT(!IsInsideNursery(*thingp)); Zone* zone = (*thingp)->asTenured().zoneFromAnyThread(); if (!zone->isCollectingFromAnyThread() || zone->isGCFinished()) @@ -1761,6 +2145,25 @@ IsMarkedInternal(T* thingp) return (*thingp)->asTenured().isMarked(); } +template +static bool +IsMarkedInternal(T* thingp) +{ + return IsMarkedInternalCommon(thingp); +} + +template +static bool +IsMarkedInternal(JSObject** thingp) +{ + if (IsInsideNursery(*thingp)) { + JSRuntime* rt = (*thingp)->runtimeFromAnyThread(); + MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt)); + return rt->gc.nursery.getForwardedPointer(thingp); + } + return IsMarkedInternalCommon(thingp); +} + template struct IsMarkedFunctor : public IdentityDefaultAdaptor { template S operator()(T* t, bool* rv) { @@ -1803,7 +2206,7 @@ IsAboutToBeFinalizedInternal(T* thingp) MOZ_ASSERT_IF(!rt->isHeapMinorCollecting(), !IsInsideNursery(thing)); if (rt->isHeapMinorCollecting()) { if (IsInsideNursery(thing)) - return !nursery.getForwardedPointer(thingp); + return !nursery.getForwardedPointer(reinterpret_cast(thingp)); return false; } @@ -1912,7 +2315,7 @@ FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) void TypeSet::MarkTypeRoot(JSTracer* trc, TypeSet::Type* v, const char* name) { - JS_ROOT_MARKING_ASSERT(trc); + AssertRootMarkingPhase(trc); MarkTypeUnbarriered(trc, v, name); } diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 41155c32b7..288e0f4fd8 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -277,7 +277,7 @@ class GCMarker : public JSTracer return stack.isEmpty(); } - bool restoreValueArray(NativeObject* obj, void** vpp, void** endp); + bool restoreValueArray(JSObject* obj, void** vpp, void** endp); void saveValueRanges(); inline void processMarkStackTop(SliceBudget& budget); @@ -300,14 +300,9 @@ class GCMarker : public JSTracer mozilla::DebugOnly strictCompartmentChecking; }; -void -SetMarkStackLimit(JSRuntime* rt, size_t limit); - bool IsBufferingGrayRoots(JSTracer* trc); -} /* namespace js */ -namespace js { namespace gc { /*** Special Cases ***/ @@ -383,12 +378,11 @@ class HashKeyRef : public BufferableRef public: HashKeyRef(Map* m, const Key& k) : map(m), key(k) {} - void mark(JSTracer* trc) { + void trace(JSTracer* trc) override { Key prior = key; typename Map::Ptr p = map->lookup(key); if (!p) return; - JS::AutoOriginalTraceLocation reloc(trc, (void**)&*p); TraceManuallyBarrieredEdge(trc, &key, "HashKeyRef"); map->rekeyIfMoved(prior, key); } diff --git a/js/src/gc/Nursery-inl.h b/js/src/gc/Nursery-inl.h index 81e0507459..8e873bb548 100644 --- a/js/src/gc/Nursery-inl.h +++ b/js/src/gc/Nursery-inl.h @@ -10,13 +10,15 @@ #include "gc/Nursery.h" +#include "jscntxt.h" + #include "gc/Heap.h" +#include "gc/Zone.h" #include "js/TracingAPI.h" #include "vm/Runtime.h" -template MOZ_ALWAYS_INLINE bool -js::Nursery::getForwardedPointer(T** ref) +js::Nursery::getForwardedPointer(JSObject** ref) const { MOZ_ASSERT(ref); MOZ_ASSERT(isInside((void*)*ref)); @@ -24,8 +26,53 @@ js::Nursery::getForwardedPointer(T** ref) if (!overlay->isForwarded()) return false; /* This static cast from Cell* restricts T to valid (GC thing) types. */ - *ref = static_cast(overlay->forwardingAddress()); + *ref = static_cast(overlay->forwardingAddress()); return true; } +namespace js { + +// The allocation methods below will not run the garbage collector. If the +// nursery cannot accomodate the allocation, the malloc heap will be used +// instead. + +template +static inline T* +AllocateObjectBuffer(ExclusiveContext* cx, uint32_t count) +{ + if (cx->isJSContext()) { + Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery; + return static_cast(nursery.allocateBuffer(cx->zone(), count * sizeof(T))); + } + return cx->zone()->pod_malloc(count); +} + +template +static inline T* +AllocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, uint32_t count) +{ + if (cx->isJSContext()) { + Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery; + return static_cast(nursery.allocateBuffer(obj, count * sizeof(T))); + } + return obj->zone()->pod_malloc(count); +} + +// If this returns null then the old buffer will be left alone. +template +static inline T* +ReallocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, T* oldBuffer, + uint32_t oldCount, uint32_t newCount) +{ + if (cx->isJSContext()) { + Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery; + return static_cast(nursery.reallocateBuffer(obj, oldBuffer, + oldCount * sizeof(T), + newCount * sizeof(T))); + } + return obj->zone()->pod_realloc(oldBuffer, oldCount, newCount); +} + +} // namespace js + #endif /* gc_Nursery_inl_h */ diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 2f3801d58f..4e4dcbe463 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -37,16 +37,16 @@ using mozilla::ArrayLength; using mozilla::PodCopy; using mozilla::PodZero; -struct js::Nursery::FreeHugeSlotsTask : public GCParallelTask +struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask { - explicit FreeHugeSlotsTask(FreeOp* fop) : fop_(fop) {} - bool init() { return slots_.init(); } - void transferSlotsToFree(HugeSlotsSet& slotsToFree); - ~FreeHugeSlotsTask() override { join(); } + explicit FreeMallocedBuffersTask(FreeOp* fop) : fop_(fop) {} + bool init() { return buffers_.init(); } + void transferBuffersToFree(MallocedBuffersSet& buffersToFree); + ~FreeMallocedBuffersTask() override { join(); } private: FreeOp* fop_; - HugeSlotsSet slots_; + MallocedBuffersSet buffers_; virtual void run() override; }; @@ -61,15 +61,15 @@ js::Nursery::init(uint32_t maxNurseryBytes) if (numNurseryChunks_ == 0) return true; - if (!hugeSlots.init()) + if (!mallocedBuffers.init()) return false; void* heap = MapAlignedPages(nurserySize(), Alignment); if (!heap) return false; - freeHugeSlotsTask = js_new(runtime()->defaultFreeOp()); - if (!freeHugeSlotsTask || !freeHugeSlotsTask->init()) + freeMallocedBuffersTask = js_new(runtime()->defaultFreeOp()); + if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init()) return false; heapStart_ = uintptr_t(heap); @@ -100,7 +100,7 @@ js::Nursery::~Nursery() if (start()) UnmapPages((void*)start(), nurserySize()); - js_delete(freeHugeSlotsTask); + js_delete(freeMallocedBuffersTask); } void @@ -175,38 +175,19 @@ js::Nursery::leaveZealMode() { } #endif // JS_GC_ZEAL -void -js::Nursery::verifyFinalizerList() -{ -#ifdef DEBUG - for (ListItem* current = finalizers_; current; current = current->next()) { - JSObject* obj = current->get(); - RelocationOverlay* overlay = RelocationOverlay::fromCell(obj); - if (overlay->isForwarded()) - obj = static_cast(overlay->forwardingAddress()); - MOZ_ASSERT(obj); - MOZ_ASSERT(obj->group()); - MOZ_ASSERT(obj->group()->clasp()); - MOZ_ASSERT(obj->group()->clasp()->finalize); - MOZ_ASSERT(obj->group()->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY); - } -#endif // DEBUG -} - JSObject* js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp) { /* Ensure there's enough space to replace the contents with a RelocationOverlay. */ MOZ_ASSERT(size >= sizeof(RelocationOverlay)); - verifyFinalizerList(); - /* If we have a finalizer, get space for the list entry. */ - ListItem* listEntry = nullptr; - if (clasp->finalize) { - listEntry = static_cast(allocate(sizeof(ListItem))); - if (!listEntry) - return nullptr; - } + /* + * Classes with JSCLASS_SKIP_NURSERY_FINALIZE will not have their finalizer + * called if they are nursery allocated and not promoted to the tenured + * heap. The finalizers for these classes must do nothing except free data + * which was allocated via Nursery::allocateBuffer. + */ + MOZ_ASSERT_IF(clasp->finalize, clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE); /* Make the object allocation. */ JSObject* obj = static_cast(allocate(size)); @@ -216,30 +197,19 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const /* If we want external slots, add them. */ HeapSlot* slots = nullptr; if (numDynamic) { - /* Try to allocate in the nursery first. */ - if (numDynamic <= MaxNurserySlots) - slots = static_cast(allocate(numDynamic * sizeof(HeapSlot))); - - /* If we are out of space or too large, use the malloc heap. */ - if (!slots) - slots = allocateHugeSlots(cx->zone(), numDynamic); - - /* It is safe to leave the allocated object uninitialized, since we do - * not visit unallocated things. */ - if (!slots) + slots = static_cast(allocateBuffer(cx->zone(), numDynamic * sizeof(HeapSlot))); + if (!slots) { + /* + * It is safe to leave the allocated object uninitialized, since we + * do not visit unallocated things in the nursery. + */ return nullptr; + } } /* Always initialize the slots field to match the JIT behavior. */ obj->setInitialSlotsMaybeNonNative(slots); - /* If we have a finalizer, link it into the finalizer list. */ - if (clasp->finalize) { - MOZ_ASSERT(listEntry); - new (listEntry) ListItem(finalizers_, obj); - finalizers_ = listEntry; - } - TraceNurseryAlloc(obj, size); return obj; } @@ -264,225 +234,72 @@ js::Nursery::allocate(size_t size) return thing; } -/* Internally, this function is used to allocate elements as well as slots. */ -HeapSlot* -js::Nursery::allocateSlots(JSObject* obj, uint32_t nslots) +void* +js::Nursery::allocateBuffer(Zone* zone, uint32_t nbytes) +{ + MOZ_ASSERT(nbytes > 0); + + if (nbytes <= MaxNurseryBufferSize) { + void* buffer = allocate(nbytes); + if (buffer) + return buffer; + } + + void* buffer = zone->pod_malloc(nbytes); + if (buffer) { + /* If this put fails, we will only leak the slots. */ + (void)mallocedBuffers.put(buffer); + } + return buffer; +} + +void* +js::Nursery::allocateBuffer(JSObject* obj, uint32_t nbytes) { MOZ_ASSERT(obj); - MOZ_ASSERT(nslots > 0); + MOZ_ASSERT(nbytes > 0); if (!IsInsideNursery(obj)) - return obj->zone()->pod_malloc(nslots); - - if (nslots > MaxNurserySlots) - return allocateHugeSlots(obj->zone(), nslots); - - size_t size = sizeof(HeapSlot) * nslots; - HeapSlot* slots = static_cast(allocate(size)); - if (slots) - return slots; - - return allocateHugeSlots(obj->zone(), nslots); + return obj->zone()->pod_malloc(nbytes); + return allocateBuffer(obj->zone(), nbytes); } -ObjectElements* -js::Nursery::allocateElements(JSObject* obj, uint32_t nelems) -{ - MOZ_ASSERT(nelems >= ObjectElements::VALUES_PER_HEADER); - return reinterpret_cast(allocateSlots(obj, nelems)); -} - -HeapSlot* -js::Nursery::reallocateSlots(JSObject* obj, HeapSlot* oldSlots, - uint32_t oldCount, uint32_t newCount) +void* +js::Nursery::reallocateBuffer(JSObject* obj, void* oldBuffer, + uint32_t oldBytes, uint32_t newBytes) { if (!IsInsideNursery(obj)) - return obj->zone()->pod_realloc(oldSlots, oldCount, newCount); + return obj->zone()->pod_realloc((uint8_t*)oldBuffer, oldBytes, newBytes); - if (!isInside(oldSlots)) { - HeapSlot* newSlots = obj->zone()->pod_realloc(oldSlots, oldCount, newCount); - if (newSlots && oldSlots != newSlots) { - hugeSlots.remove(oldSlots); + if (!isInside(oldBuffer)) { + void* newBuffer = obj->zone()->pod_realloc((uint8_t*)oldBuffer, oldBytes, newBytes); + if (newBuffer && oldBytes != newBytes) { + removeMallocedBuffer(oldBuffer); /* If this put fails, we will only leak the slots. */ - (void)hugeSlots.put(newSlots); + (void)mallocedBuffers.put(newBuffer); } - return newSlots; + return newBuffer; } /* The nursery cannot make use of the returned slots data. */ - if (newCount < oldCount) - return oldSlots; + if (newBytes < oldBytes) + return oldBuffer; - HeapSlot* newSlots = allocateSlots(obj, newCount); - if (newSlots) - PodCopy(newSlots, oldSlots, oldCount); - return newSlots; -} - -ObjectElements* -js::Nursery::reallocateElements(JSObject* obj, ObjectElements* oldHeader, - uint32_t oldCount, uint32_t newCount) -{ - HeapSlot* slots = reallocateSlots(obj, reinterpret_cast(oldHeader), - oldCount, newCount); - return reinterpret_cast(slots); + void* newBuffer = allocateBuffer(obj->zone(), newBytes); + if (newBuffer) + PodCopy((uint8_t*)newBuffer, (uint8_t*)oldBuffer, oldBytes); + return newBuffer; } void -js::Nursery::freeSlots(HeapSlot* slots) +js::Nursery::freeBuffer(void* buffer) { - if (!isInside(slots)) { - hugeSlots.remove(slots); - js_free(slots); + if (!isInside(buffer)) { + removeMallocedBuffer(buffer); + js_free(buffer); } } -HeapSlot* -js::Nursery::allocateHugeSlots(JS::Zone* zone, size_t nslots) -{ - HeapSlot* slots = zone->pod_malloc(nslots); - /* If this put fails, we will only leak the slots. */ - if (slots) - (void)hugeSlots.put(slots); - return slots; -} - -namespace js { -namespace gc { - -class MinorCollectionTracer : public JS::CallbackTracer -{ - public: - Nursery* nursery; - AutoTraceSession session; - - /* Amount of data moved to the tenured generation during collection. */ - size_t tenuredSize; - - /* - * This list is threaded through the Nursery using the space from already - * moved things. The list is used to fix up the moved things and to find - * things held live by intra-Nursery pointers. - */ - RelocationOverlay* head; - RelocationOverlay** tail; - - /* Save and restore all of the runtime state we use during MinorGC. */ - bool savedRuntimeNeedBarrier; - AutoDisableProxyCheck disableStrictProxyChecking; - AutoEnterOOMUnsafeRegion oomUnsafeRegion; - - /* Insert the given relocation entry into the list of things to visit. */ - MOZ_ALWAYS_INLINE void insertIntoFixupList(RelocationOverlay* entry) { - *tail = entry; - tail = &entry->next_; - *tail = nullptr; - } - - MinorCollectionTracer(JSRuntime* rt, Nursery* nursery) - : JS::CallbackTracer(rt, Nursery::MinorGCCallback, TraceWeakMapKeysValues), - nursery(nursery), - session(rt, MinorCollecting), - tenuredSize(0), - head(nullptr), - tail(&head), - savedRuntimeNeedBarrier(rt->needsIncrementalBarrier()), - disableStrictProxyChecking(rt) - { - rt->gc.incGcNumber(); - - /* - * We disable the runtime needsIncrementalBarrier() check so that - * pre-barriers do not fire on objects that have been relocated. The - * pre-barrier's call to obj->zone() will try to look through shape_, - * which is now the relocation magic and will crash. However, - * zone->needsIncrementalBarrier() must still be set correctly so that - * allocations we make in minor GCs between incremental slices will - * allocate their objects marked. - */ - rt->setNeedsIncrementalBarrier(false); - } - - ~MinorCollectionTracer() { - runtime()->setNeedsIncrementalBarrier(savedRuntimeNeedBarrier); - } -}; - -} /* namespace gc */ -} /* namespace js */ - -static AllocKind -GetObjectAllocKindForCopy(const Nursery& nursery, JSObject* obj) -{ - if (obj->is()) { - ArrayObject* aobj = &obj->as(); - MOZ_ASSERT(aobj->numFixedSlots() == 0); - - /* Use minimal size object if we are just going to copy the pointer. */ - if (!nursery.isInside(aobj->getElementsHeader())) - return AllocKind::OBJECT0_BACKGROUND; - - size_t nelements = aobj->getDenseCapacity(); - return GetBackgroundAllocKind(GetGCArrayKind(nelements)); - } - - if (obj->is()) - return obj->as().getAllocKind(); - - /* - * Typed arrays in the nursery may have a lazily allocated buffer, make - * sure there is room for the array's fixed data when moving the array. - */ - if (obj->is() && !obj->as().buffer()) { - size_t nbytes = obj->as().byteLength(); - return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes)); - } - - // Proxies have finalizers and are not nursery allocated. - MOZ_ASSERT(!IsProxy(obj)); - - // Unboxed plain objects are sized according to the data they store. - if (obj->is()) { - size_t nbytes = obj->as().layoutDontCheckGeneration().size(); - return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes); - } - - // Inlined typed objects are followed by their data, so make sure we copy - // it all over to the new object. - if (obj->is()) { - // Figure out the size of this object, from the prototype's TypeDescr. - // The objects we are traversing here are all tenured, so we don't need - // to check forwarding pointers. - TypeDescr* descr = &obj->as().typeDescr(); - MOZ_ASSERT(!IsInsideNursery(descr)); - return InlineTypedObject::allocKindForTypeDescriptor(descr); - } - - // Outline typed objects use the minimum allocation kind. - if (obj->is()) - return AllocKind::OBJECT0; - - // All nursery allocatable non-native objects are handled above. - MOZ_ASSERT(obj->isNative()); - - AllocKind kind = GetGCObjectFixedSlotsKind(obj->as().numFixedSlots()); - MOZ_ASSERT(!IsBackgroundFinalized(kind)); - if (!CanBeFinalizedInBackground(kind, obj->getClass())) - return kind; - return GetBackgroundAllocKind(kind); -} - -MOZ_ALWAYS_INLINE TenuredCell* -js::Nursery::allocateFromTenured(Zone* zone, AllocKind thingKind) -{ - TenuredCell* t = zone->arenas.allocateFromFreeList(thingKind, Arena::thingSize(thingKind)); - if (t) - return t; - zone->arenas.checkEmptyFreeList(thingKind); - AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation; - return zone->arenas.allocateFromArena(zone, thingKind, maybeStartBackgroundAllocation); -} - void Nursery::setForwardingPointer(void* oldData, void* newData, bool direct) { @@ -555,238 +372,29 @@ js::Nursery::forwardBufferPointer(HeapSlot** pSlotsElems) MOZ_ASSERT(IsWriteableAddress(*pSlotsElems)); } -// Structure for counting how many times objects in a particular group have -// been tenured during a minor collection. -struct TenureCount +js::TenuringTracer::TenuringTracer(JSRuntime* rt, Nursery* nursery) + : JSTracer(rt, JSTracer::TracerKindTag::Tenuring, TraceWeakMapKeysValues) + , nursery_(*nursery) + , tenuredSize(0) + , head(nullptr) + , tail(&head) + , savedRuntimeNeedBarrier(rt->needsIncrementalBarrier()) { - ObjectGroup* group; - int count; -}; + rt->gc.incGcNumber(); -// Keep rough track of how many times we tenure objects in particular groups -// during minor collections, using a fixed size hash for efficiency at the cost -// of potential collisions. -struct Nursery::TenureCountCache -{ - TenureCount entries[16]; - - TenureCountCache() { PodZero(this); } - - TenureCount& findEntry(ObjectGroup* group) { - return entries[PointerHasher::hash(group) % ArrayLength(entries)]; - } -}; - -void -js::Nursery::collectToFixedPoint(MinorCollectionTracer* trc, TenureCountCache& tenureCounts) -{ - for (RelocationOverlay* p = trc->head; p; p = p->next()) { - JSObject* obj = static_cast(p->forwardingAddress()); - traceObject(trc, obj); - - TenureCount& entry = tenureCounts.findEntry(obj->group()); - if (entry.group == obj->group()) { - entry.count++; - } else if (!entry.group) { - entry.group = obj->group(); - entry.count = 1; - } - } + // We disable the runtime needsIncrementalBarrier() check so that + // pre-barriers do not fire on objects that have been relocated. The + // pre-barrier's call to obj->zone() will try to look through shape_, + // which is now the relocation magic and will crash. However, + // zone->needsIncrementalBarrier() must still be set correctly so that + // allocations we make in minor GCs between incremental slices will + // allocate their objects marked. + rt->setNeedsIncrementalBarrier(false); } -MOZ_ALWAYS_INLINE void -js::Nursery::traceObject(MinorCollectionTracer* trc, JSObject* obj) +js::TenuringTracer::~TenuringTracer() { - const Class* clasp = obj->getClass(); - if (clasp->trace) - clasp->trace(trc, obj); - - MOZ_ASSERT(obj->isNative() == clasp->isNative()); - if (!clasp->isNative()) - return; - NativeObject* nobj = &obj->as(); - - // Note: the contents of copy on write elements pointers are filled in - // during parsing and cannot contain nursery pointers. - if (!nobj->hasEmptyElements() && !nobj->denseElementsAreCopyOnWrite()) - markSlots(trc, nobj->getDenseElements(), nobj->getDenseInitializedLength()); - - HeapSlot* fixedStart; - HeapSlot* fixedEnd; - HeapSlot* dynStart; - HeapSlot* dynEnd; - nobj->getSlotRange(0, nobj->slotSpan(), &fixedStart, &fixedEnd, &dynStart, &dynEnd); - markSlots(trc, fixedStart, fixedEnd); - markSlots(trc, dynStart, dynEnd); -} - -MOZ_ALWAYS_INLINE void -js::Nursery::markSlots(MinorCollectionTracer* trc, HeapSlot* vp, uint32_t nslots) -{ - markSlots(trc, vp, vp + nslots); -} - -MOZ_ALWAYS_INLINE void -js::Nursery::markSlots(MinorCollectionTracer* trc, HeapSlot* vp, HeapSlot* end) -{ - for (; vp != end; ++vp) - markSlot(trc, vp); -} - -MOZ_ALWAYS_INLINE void -js::Nursery::markSlot(MinorCollectionTracer* trc, HeapSlot* slotp) -{ - if (!slotp->isObject()) - return; - - JSObject* obj = &slotp->toObject(); - if (!IsInsideNursery(obj)) - return; - - if (getForwardedPointer(&obj)) { - slotp->unsafeGet()->setObject(*obj); - return; - } - - JSObject* tenured = static_cast(moveToTenured(trc, obj)); - slotp->unsafeGet()->setObject(*tenured); -} - -void* -js::Nursery::moveToTenured(MinorCollectionTracer* trc, JSObject* src) -{ - - AllocKind dstKind = GetObjectAllocKindForCopy(*this, src); - Zone* zone = src->zone(); - JSObject* dst = reinterpret_cast(allocateFromTenured(zone, dstKind)); - if (!dst) - CrashAtUnhandlableOOM("Failed to allocate object while tenuring."); - - trc->tenuredSize += moveObjectToTenured(trc, dst, src, dstKind); - - RelocationOverlay* overlay = RelocationOverlay::fromCell(src); - overlay->forwardTo(dst); - trc->insertIntoFixupList(overlay); - - TracePromoteToTenured(src, dst); - return static_cast(dst); -} - -MOZ_ALWAYS_INLINE size_t -js::Nursery::moveObjectToTenured(MinorCollectionTracer *trc, - JSObject *dst, JSObject *src, AllocKind dstKind) -{ - size_t srcSize = Arena::thingSize(dstKind); - size_t tenuredSize = srcSize; - - /* - * Arrays do not necessarily have the same AllocKind between src and dst. - * We deal with this by copying elements manually, possibly re-inlining - * them if there is adequate room inline in dst. - * - * For Arrays we're reducing tenuredSize to the smaller srcSize - * because moveElementsToTenured() accounts for all Array elements, - * even if they are inlined. - */ - if (src->is()) - tenuredSize = srcSize = sizeof(NativeObject); - - js_memcpy(dst, src, srcSize); - if (src->isNative()) { - NativeObject* ndst = &dst->as(); - NativeObject* nsrc = &src->as(); - tenuredSize += moveSlotsToTenured(ndst, nsrc, dstKind); - tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind); - - // The shape's list head may point into the old object. This can only - // happen for dictionaries, which are native objects. - if (&nsrc->shape_ == ndst->shape_->listp) { - MOZ_ASSERT(nsrc->shape_->inDictionary()); - ndst->shape_->listp = &ndst->shape_; - } - } - - if (src->is()) - InlineTypedObject::objectMovedDuringMinorGC(trc, dst, src); - - return tenuredSize; -} - -MOZ_ALWAYS_INLINE size_t -js::Nursery::moveSlotsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind) -{ - /* Fixed slots have already been copied over. */ - if (!src->hasDynamicSlots()) - return 0; - - if (!isInside(src->slots_)) { - hugeSlots.remove(src->slots_); - return 0; - } - - Zone* zone = src->zone(); - size_t count = src->numDynamicSlots(); - dst->slots_ = zone->pod_malloc(count); - if (!dst->slots_) - CrashAtUnhandlableOOM("Failed to allocate slots while tenuring."); - PodCopy(dst->slots_, src->slots_, count); - setSlotsForwardingPointer(src->slots_, dst->slots_, count); - return count * sizeof(HeapSlot); -} - -MOZ_ALWAYS_INLINE size_t -js::Nursery::moveElementsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind) -{ - if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite()) - return 0; - - Zone* zone = src->zone(); - ObjectElements* srcHeader = src->getElementsHeader(); - ObjectElements* dstHeader; - - /* TODO Bug 874151: Prefer to put element data inline if we have space. */ - if (!isInside(srcHeader)) { - MOZ_ASSERT(src->elements_ == dst->elements_); - hugeSlots.remove(reinterpret_cast(srcHeader)); - return 0; - } - - size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity; - - /* Unlike other objects, Arrays can have fixed elements. */ - if (src->is() && nslots <= GetGCKindSlots(dstKind)) { - dst->as().setFixedElements(); - dstHeader = dst->as().getElementsHeader(); - js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); - setElementsForwardingPointer(srcHeader, dstHeader, nslots); - return nslots * sizeof(HeapSlot); - } - - MOZ_ASSERT(nslots >= 2); - dstHeader = reinterpret_cast(zone->pod_malloc(nslots)); - if (!dstHeader) - CrashAtUnhandlableOOM("Failed to allocate elements while tenuring."); - js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot)); - setElementsForwardingPointer(srcHeader, dstHeader, nslots); - dst->elements_ = dstHeader->elements(); - return nslots * sizeof(HeapSlot); -} - -static bool -ShouldMoveToTenured(MinorCollectionTracer* trc, void** thingp) -{ - Cell* cell = static_cast(*thingp); - Nursery& nursery = *trc->nursery; - return !nursery.isInside(thingp) && IsInsideNursery(cell) && - !nursery.getForwardedPointer(thingp); -} - -/* static */ void -js::Nursery::MinorGCCallback(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) -{ - MinorCollectionTracer* trc = static_cast(jstrc); - if (ShouldMoveToTenured(trc, thingp)) - *thingp = trc->nursery->moveToTenured(trc, static_cast(*thingp)); + runtime()->setNeedsIncrementalBarrier(savedRuntimeNeedBarrier); } #define TIME_START(name) int64_t timstampStart_##name = enableProfiling_ ? PRMJ_Now() : 0 @@ -821,55 +429,51 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList TIME_START(total); + AutoTraceSession session(rt, MinorCollecting); AutoStopVerifyingBarriers av(rt, false); + AutoDisableProxyCheck disableStrictProxyChecking(rt); + mozilla::DebugOnly oomUnsafeRegion; // Move objects pointed to by roots from the nursery to the major heap. - MinorCollectionTracer trc(rt, this); + TenuringTracer mover(rt, this); // Mark the store buffer. This must happen first. - TIME_START(markValues); - sb.markValues(&trc); - TIME_END(markValues); + TIME_START(traceValues); + sb.traceValues(mover); + TIME_END(traceValues); - TIME_START(markCells); - sb.markCells(&trc); - TIME_END(markCells); + TIME_START(traceCells); + sb.traceCells(mover); + TIME_END(traceCells); - TIME_START(markSlots); - sb.markSlots(&trc); - TIME_END(markSlots); + TIME_START(traceSlots); + sb.traceSlots(mover); + TIME_END(traceSlots); - TIME_START(markWholeCells); - sb.markWholeCells(&trc); - TIME_END(markWholeCells); + TIME_START(traceWholeCells); + sb.traceWholeCells(mover); + TIME_END(traceWholeCells); - TIME_START(markRelocatableValues); - sb.markRelocatableValues(&trc); - TIME_END(markRelocatableValues); + TIME_START(traceRelocatableValues); + sb.traceRelocatableValues(mover); + TIME_END(traceRelocatableValues); - TIME_START(markRelocatableCells); - sb.markRelocatableCells(&trc); - TIME_END(markRelocatableCells); + TIME_START(traceRelocatableCells); + sb.traceRelocatableCells(mover); + TIME_END(traceRelocatableCells); - TIME_START(markGenericEntries); - sb.markGenericEntries(&trc); - TIME_END(markGenericEntries); - - TIME_START(checkHashTables); -#ifdef JS_GC_ZEAL - if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC) - CheckHashTablesAfterMovingGC(rt); -#endif - TIME_END(checkHashTables); + TIME_START(traceGenericEntries); + sb.traceGenericEntries(&mover); + TIME_END(traceGenericEntries); TIME_START(markRuntime); - rt->gc.markRuntime(&trc); + rt->gc.markRuntime(&mover); TIME_END(markRuntime); TIME_START(markDebugger); { gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS); - Debugger::markAll(&trc); + Debugger::markAll(&mover); } TIME_END(markDebugger); @@ -883,7 +487,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList // objects are left to move. That is, we iterate to a fixed point. TIME_START(collectToFP); TenureCountCache tenureCounts; - collectToFixedPoint(&trc, tenureCounts); + collectToFixedPoint(mover, tenureCounts); TIME_END(collectToFP); // Update the array buffer object's view lists. @@ -896,18 +500,14 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList // Update any slot or element pointers whose destination has been tenured. TIME_START(updateJitActivations); - js::jit::UpdateJitActivationsForMinorGC(rt, &trc); + js::jit::UpdateJitActivationsForMinorGC(rt, &mover); forwardedBuffers.finish(); TIME_END(updateJitActivations); // Sweep. - TIME_START(runFinalizers); - runFinalizers(); - TIME_END(runFinalizers); - - TIME_START(freeHugeSlots); - freeHugeSlots(); - TIME_END(freeHugeSlots); + TIME_START(freeMallocedBuffers); + freeMallocedBuffers(); + TIME_END(freeMallocedBuffers); TIME_START(sweep); sweep(); @@ -917,9 +517,17 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList rt->gc.storeBuffer.clear(); TIME_END(clearStoreBuffer); + // Make sure hashtables have been updated after the collection. + TIME_START(checkHashTables); +#ifdef JS_GC_ZEAL + if (rt->gcZeal() == ZealCheckHashTablesOnMinorGC) + CheckHashTablesAfterMovingGC(rt); +#endif + TIME_END(checkHashTables); + // Resize the nursery. TIME_START(resize); - double promotionRate = trc.tenuredSize / double(allocationEnd() - start()); + double promotionRate = mover.tenuredSize / double(allocationEnd() - start()); if (promotionRate > 0.05) growAllocableSpace(); else if (promotionRate < 0.01) @@ -961,18 +569,18 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList #define FMT " %6" PRIu64 fprintf(stderr, - "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n", + "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n", js::gcstats::ExplainReason(reason), promotionRate * 100, numActiveChunks_, totalTime, - TIME_TOTAL(markValues), - TIME_TOTAL(markCells), - TIME_TOTAL(markSlots), - TIME_TOTAL(markWholeCells), - TIME_TOTAL(markRelocatableValues), - TIME_TOTAL(markRelocatableCells), - TIME_TOTAL(markGenericEntries), + TIME_TOTAL(traceValues), + TIME_TOTAL(traceCells), + TIME_TOTAL(traceSlots), + TIME_TOTAL(traceWholeCells), + TIME_TOTAL(traceRelocatableValues), + TIME_TOTAL(traceRelocatableCells), + TIME_TOTAL(traceGenericEntries), TIME_TOTAL(checkHashTables), TIME_TOTAL(markRuntime), TIME_TOTAL(markDebugger), @@ -980,8 +588,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList TIME_TOTAL(collectToFP), TIME_TOTAL(sweepArrayBufferViewList), TIME_TOTAL(updateJitActivations), - TIME_TOTAL(runFinalizers), - TIME_TOTAL(freeHugeSlots), + TIME_TOTAL(freeMallocedBuffers), TIME_TOTAL(clearStoreBuffer), TIME_TOTAL(sweep), TIME_TOTAL(resize), @@ -995,62 +602,47 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList #undef TIME_TOTAL void -js::Nursery::FreeHugeSlotsTask::transferSlotsToFree(HugeSlotsSet& slotsToFree) +js::Nursery::FreeMallocedBuffersTask::transferBuffersToFree(MallocedBuffersSet& buffersToFree) { - // Transfer the contents of the source set to the task's slots_ member by + // Transfer the contents of the source set to the task's buffers_ member by // swapping the sets, which also clears the source. MOZ_ASSERT(!isRunning()); - MOZ_ASSERT(slots_.empty()); - mozilla::Swap(slots_, slotsToFree); + MOZ_ASSERT(buffers_.empty()); + mozilla::Swap(buffers_, buffersToFree); } void -js::Nursery::FreeHugeSlotsTask::run() +js::Nursery::FreeMallocedBuffersTask::run() { - for (HugeSlotsSet::Range r = slots_.all(); !r.empty(); r.popFront()) + for (MallocedBuffersSet::Range r = buffers_.all(); !r.empty(); r.popFront()) fop_->free_(r.front()); - slots_.clear(); + buffers_.clear(); } void -js::Nursery::freeHugeSlots() +js::Nursery::freeMallocedBuffers() { - if (hugeSlots.empty()) + if (mallocedBuffers.empty()) return; bool started; { AutoLockHelperThreadState lock; - freeHugeSlotsTask->joinWithLockHeld(); - freeHugeSlotsTask->transferSlotsToFree(hugeSlots); - started = freeHugeSlotsTask->startWithLockHeld(); + freeMallocedBuffersTask->joinWithLockHeld(); + freeMallocedBuffersTask->transferBuffersToFree(mallocedBuffers); + started = freeMallocedBuffersTask->startWithLockHeld(); } if (!started) - freeHugeSlotsTask->runFromMainThread(runtime()); + freeMallocedBuffersTask->runFromMainThread(runtime()); - MOZ_ASSERT(hugeSlots.empty()); + MOZ_ASSERT(mallocedBuffers.empty()); } void js::Nursery::waitBackgroundFreeEnd() { - freeHugeSlotsTask->join(); -} - -void -js::Nursery::runFinalizers() -{ - verifyFinalizerList(); - - FreeOp* fop = runtime()->defaultFreeOp(); - for (ListItem* current = finalizers_; current; current = current->next()) { - JSObject* obj = current->get(); - RelocationOverlay* overlay = RelocationOverlay::fromCell(obj); - if (!overlay->isForwarded()) - obj->getClass()->finalize(fop, obj); - } - finalizers_ = nullptr; + freeMallocedBuffersTask->join(); } void diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 75f12b8c6e..b5d9e15c53 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -29,6 +29,7 @@ namespace js { class ObjectElements; class NativeObject; +class Nursery; class HeapSlot; class ObjectGroup; @@ -37,12 +38,59 @@ void SetGCZeal(JSRuntime*, uint8_t, uint32_t); namespace gc { struct Cell; class MinorCollectionTracer; +class RelocationOverlay; +struct TenureCountCache; } /* namespace gc */ namespace jit { class MacroAssembler; } +class TenuringTracer : public JSTracer +{ + friend class Nursery; + Nursery& nursery_; + + // Amount of data moved to the tenured generation during collection. + size_t tenuredSize; + + // This list is threaded through the Nursery using the space from already + // moved things. The list is used to fix up the moved things and to find + // things held live by intra-Nursery pointers. + gc::RelocationOverlay* head; + gc::RelocationOverlay** tail; + + // Save and restore all of the runtime state we use during MinorGC. + bool savedRuntimeNeedBarrier; + + TenuringTracer(JSRuntime* rt, Nursery* nursery); + ~TenuringTracer(); + + public: + const Nursery& nursery() const { return nursery_; } + + // Returns true if the pointer was updated. + template void traverse(T* thingp); + + void insertIntoFixupList(gc::RelocationOverlay* entry); + + // The store buffers need to be able to call these directly. + void traceObject(JSObject* src); + void traceObjectSlots(NativeObject* nobj, uint32_t start, uint32_t length); + void traceSlots(JS::Value* vp, uint32_t nslots) { traceSlots(vp, vp + nslots); } + + private: + Nursery& nursery() { return nursery_; } + + JSObject* moveToTenured(JSObject* src); + size_t moveObjectToTenured(JSObject* dst, JSObject* src, gc::AllocKind dstKind); + size_t moveElementsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind); + size_t moveSlotsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind); + + void traceSlots(JS::Value* vp, JS::Value* end); + void markTraceList(const int32_t* traceList, uint8_t* memory); +}; + class Nursery { public: @@ -59,10 +107,9 @@ class Nursery currentChunk_(0), numActiveChunks_(0), numNurseryChunks_(0), - finalizers_(nullptr), profileThreshold_(0), enableProfiling_(false), - freeHugeSlotsTask(nullptr) + freeMallocedBuffersTask(nullptr) {} ~Nursery(); @@ -94,22 +141,21 @@ class Nursery */ JSObject* allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp); - /* Allocate a slots array for the given object. */ - HeapSlot* allocateSlots(JSObject* obj, uint32_t nslots); + /* Allocate a buffer for a given zone, using the nursery if possible. */ + void* allocateBuffer(JS::Zone* zone, uint32_t nbytes); - /* Allocate an elements vector for the given object. */ - ObjectElements* allocateElements(JSObject* obj, uint32_t nelems); + /* + * Allocate a buffer for a given object, using the nursery if possible and + * obj is in the nursery. + */ + void* allocateBuffer(JSObject* obj, uint32_t nbytes); - /* Resize an existing slots array. */ - HeapSlot* reallocateSlots(JSObject* obj, HeapSlot* oldSlots, - uint32_t oldCount, uint32_t newCount); + /* Resize an existing object buffer. */ + void* reallocateBuffer(JSObject* obj, void* oldBuffer, + uint32_t oldBytes, uint32_t newBytes); - /* Resize an existing elements vector. */ - ObjectElements* reallocateElements(JSObject* obj, ObjectElements* oldHeader, - uint32_t oldCount, uint32_t newCount); - - /* Free a slots array. */ - void freeSlots(HeapSlot* slots); + /* Free an object buffer. */ + void freeBuffer(void* buffer); typedef Vector ObjectGroupList; @@ -124,17 +170,21 @@ class Nursery * sets |*ref| to the new location of the object and returns true. Otherwise * returns false and leaves |*ref| unset. */ - template - MOZ_ALWAYS_INLINE bool getForwardedPointer(T** ref); + MOZ_ALWAYS_INLINE bool getForwardedPointer(JSObject** ref) const; /* Forward a slots/elements pointer stored in an Ion frame. */ void forwardBufferPointer(HeapSlot** pSlotsElems); void maybeSetForwardingPointer(JSTracer* trc, void* oldData, void* newData, bool direct) { - if (IsMinorCollectionTracer(trc) && isInside(oldData)) + if (trc->isTenuringTracer() && isInside(oldData)) setForwardingPointer(oldData, newData, direct); } + /* Mark a malloced buffer as no longer needing to be freed. */ + void removeMallocedBuffer(void* buffer) { + mallocedBuffers.remove(buffer); + } + void waitBackgroundFreeEnd(); size_t sizeOfHeapCommitted() const { @@ -143,11 +193,11 @@ class Nursery size_t sizeOfHeapDecommitted() const { return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize; } - size_t sizeOfHugeSlots(mozilla::MallocSizeOf mallocSizeOf) const { + size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const { size_t total = 0; - for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront()) + for (MallocedBuffersSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront()) total += mallocSizeOf(r.front()); - total += hugeSlots.sizeOfExcludingThis(mallocSizeOf); + total += mallocedBuffers.sizeOfExcludingThis(mallocSizeOf); return total; } @@ -159,10 +209,6 @@ class Nursery return heapEnd_; } - static bool IsMinorCollectionTracer(JSTracer* trc) { - return trc->isCallbackTracer() && trc->asCallbackTracer()->hasCallback(MinorGCCallback); - } - #ifdef JS_GC_ZEAL void enterZealMode(); void leaveZealMode(); @@ -198,31 +244,21 @@ class Nursery /* Number of chunks allocated for the nursery. */ int numNurseryChunks_; - /* Keep track of objects that need finalization. */ - class ListItem { - ListItem* next_; - JSObject* object_; - public: - ListItem(ListItem* tail, JSObject* obj) : next_(tail), object_(obj) {} - ListItem* next() const { return next_; } - JSObject* get() { return object_; } - } *finalizers_; - /* Report minor collections taking more than this many us, if enabled. */ int64_t profileThreshold_; bool enableProfiling_; /* - * The set of externally malloced slots potentially kept live by objects - * stored in the nursery. Any external slots that do not belong to a + * The set of externally malloced buffers potentially kept live by objects + * stored in the nursery. Any external buffers that do not belong to a * tenured thing at the end of a minor GC must be freed. */ - typedef HashSet, SystemAllocPolicy> HugeSlotsSet; - HugeSlotsSet hugeSlots; + typedef HashSet, SystemAllocPolicy> MallocedBuffersSet; + MallocedBuffersSet mallocedBuffers; - /* A task structure used to free the huge slots on a background thread. */ - struct FreeHugeSlotsTask; - FreeHugeSlotsTask* freeHugeSlotsTask; + /* A task structure used to free the malloced bufers on a background thread. */ + struct FreeMallocedBuffersTask; + FreeMallocedBuffersTask* freeMallocedBuffersTask; /* * During a collection most hoisted slot and element buffers indicate their @@ -234,8 +270,8 @@ class Nursery typedef HashMap, SystemAllocPolicy> ForwardedBufferMap; ForwardedBufferMap forwardedBuffers; - /* The maximum number of slots allowed to reside inline in the nursery. */ - static const size_t MaxNurserySlots = 128; + /* The maximum number of bytes allowed to reside in nursery buffers. */ + static const size_t MaxNurseryBufferSize = 1024; /* The amount of space in the mapped nursery available to allocations. */ static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer); @@ -292,32 +328,17 @@ class Nursery JSRuntime* runtime() const { return runtime_; } - /* Allocates and registers external slots with the nursery. */ - HeapSlot* allocateHugeSlots(JS::Zone* zone, size_t nslots); - /* Allocates a new GC thing from the tenured generation during minor GC. */ gc::TenuredCell* allocateFromTenured(JS::Zone* zone, gc::AllocKind thingKind); - struct TenureCountCache; - /* Common internal allocator function. */ void* allocate(size_t size); - void verifyFinalizerList(); /* * Move the object at |src| in the Nursery to an already-allocated cell * |dst| in Tenured. */ - void collectToFixedPoint(gc::MinorCollectionTracer* trc, TenureCountCache& tenureCounts); - MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer* trc, JSObject* src); - MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer* trc, HeapSlot* vp, uint32_t nslots); - MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer* trc, HeapSlot* vp, HeapSlot* end); - MOZ_ALWAYS_INLINE void markSlot(gc::MinorCollectionTracer* trc, HeapSlot* slotp); - void* moveToTenured(gc::MinorCollectionTracer* trc, JSObject* src); - size_t moveObjectToTenured(gc::MinorCollectionTracer* trc, JSObject* dst, JSObject* src, - gc::AllocKind dstKind); - size_t moveElementsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind); - size_t moveSlotsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind); + void collectToFixedPoint(TenuringTracer& trc, gc::TenureCountCache& tenureCounts); /* Handle relocation of slots/elements pointers stored in Ion frames. */ void setForwardingPointer(void* oldData, void* newData, bool direct); @@ -326,11 +347,8 @@ class Nursery void setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader, uint32_t nelems); - /* Run finalizers on all finalizable things in the nursery. */ - void runFinalizers(); - /* Free malloced pointers owned by freed things in the nursery. */ - void freeHugeSlots(); + void freeMallocedBuffers(); /* * Frees all non-live nursery-allocated things at the end of a minor @@ -342,8 +360,7 @@ class Nursery void growAllocableSpace(); void shrinkAllocableSpace(); - static void MinorGCCallback(JS::CallbackTracer* trc, void** thingp, JSGCTraceKind kind); - + friend class TenuringTracer; friend class gc::MinorCollectionTracer; friend class jit::MacroAssembler; }; diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index fed7d0c8f5..1befaae27e 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -151,13 +151,13 @@ AutoGCRooter::trace(JSTracer* trc) case VALVECTOR: { AutoValueVector::VectorImpl& vector = static_cast(this)->vector; - TraceRootRange(trc, vector.length(), vector.begin(), "js::AutoValueVector.vector"); + TraceRootRange(trc, vector.length(), vector.begin(), "JS::AutoValueVector.vector"); return; } case IDVECTOR: { AutoIdVector::VectorImpl& vector = static_cast(this)->vector; - TraceRootRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector"); + TraceRootRange(trc, vector.length(), vector.begin(), "JS::AutoIdVector.vector"); return; } @@ -179,13 +179,7 @@ AutoGCRooter::trace(JSTracer* trc) case OBJVECTOR: { AutoObjectVector::VectorImpl& vector = static_cast(this)->vector; - TraceRootRange(trc, vector.length(), vector.begin(), "js::AutoObjectVector.vector"); - return; - } - - case FUNVECTOR: { - AutoFunctionVector::VectorImpl& vector = static_cast(this)->vector; - TraceRootRange(trc, vector.length(), vector.begin(), "js::AutoFunctionVector.vector"); + TraceRootRange(trc, vector.length(), vector.begin(), "JS::AutoObjectVector.vector"); return; } @@ -222,7 +216,6 @@ AutoGCRooter::trace(JSTracer* trc) for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) { TraceRoot(trc, &e.front().value(), "AutoObjectObjectHashMap value"); JSObject* key = e.front().key(); - JS::AutoOriginalTraceLocation reloc(trc, &e.front().key()); TraceRoot(trc, &key, "AutoObjectObjectHashMap key"); if (key != e.front().key()) e.rekeyFront(key); diff --git a/js/src/gc/StoreBuffer.cpp b/js/src/gc/StoreBuffer.cpp index 1a97683df5..eac72a9e32 100644 --- a/js/src/gc/StoreBuffer.cpp +++ b/js/src/gc/StoreBuffer.cpp @@ -16,91 +16,11 @@ using namespace js; using namespace js::gc; -using mozilla::ReentrancyGuard; - -/*** Edges ***/ void -StoreBuffer::SlotsEdge::mark(JSTracer* trc) const +StoreBuffer::GenericBuffer::trace(StoreBuffer* owner, JSTracer* trc) { - NativeObject* obj = object(); - - // Beware JSObject::swap exchanging a native object for a non-native one. - if (!obj->isNative()) - return; - - if (IsInsideNursery(obj)) - return; - - if (kind() == ElementKind) { - int32_t initLen = obj->getDenseInitializedLength(); - int32_t clampedStart = Min(start_, initLen); - int32_t clampedEnd = Min(start_ + count_, initLen); - TraceRange(trc, clampedEnd - clampedStart, - static_cast(obj->getDenseElements() + clampedStart), "element"); - } else { - int32_t start = Min(uint32_t(start_), obj->slotSpan()); - int32_t end = Min(uint32_t(start_) + count_, obj->slotSpan()); - MOZ_ASSERT(end >= start); - TraceObjectSlots(trc, obj, start, end - start); - } -} - -void -StoreBuffer::WholeCellEdges::mark(JSTracer *trc) const -{ - MOZ_ASSERT(edge->isTenured()); - JSGCTraceKind kind = GetGCThingTraceKind(edge); - if (kind <= JSTRACE_OBJECT) { - JSObject *object = static_cast(edge); - if (object->is()) - ArgumentsObject::trace(trc, object); - object->traceChildren(trc); - return; - } - MOZ_ASSERT(kind == JSTRACE_JITCODE); - static_cast(edge)->traceChildren(trc); -} - -void -StoreBuffer::CellPtrEdge::mark(JSTracer *trc) const -{ - if (!*edge) - return; - - MOZ_ASSERT(GetGCThingTraceKind(*edge) == JSTRACE_OBJECT); - TraceRoot(trc, reinterpret_cast(edge), "store buffer edge"); -} - -void -StoreBuffer::ValueEdge::mark(JSTracer* trc) const -{ - if (!deref()) - return; - - TraceRoot(trc, edge, "store buffer edge"); -} - -/*** MonoTypeBuffer ***/ - -template -void -StoreBuffer::MonoTypeBuffer::mark(StoreBuffer* owner, JSTracer* trc) -{ - ReentrancyGuard g(*owner); - MOZ_ASSERT(owner->isEnabled()); - MOZ_ASSERT(stores_.initialized()); - sinkStores(owner); - for (typename StoreSet::Range r = stores_.all(); !r.empty(); r.popFront()) - r.front().mark(trc); -} - -/*** GenericBuffer ***/ - -void -StoreBuffer::GenericBuffer::mark(StoreBuffer* owner, JSTracer* trc) -{ - ReentrancyGuard g(*owner); + mozilla::ReentrancyGuard g(*owner); MOZ_ASSERT(owner->isEnabled()); if (!storage_) return; @@ -109,13 +29,11 @@ StoreBuffer::GenericBuffer::mark(StoreBuffer* owner, JSTracer* trc) unsigned size = *e.get(); e.popFront(); BufferableRef* edge = e.get(size); - edge->mark(trc); + edge->trace(trc); e.popFront(size); } } -/*** StoreBuffer ***/ - bool StoreBuffer::enable() { @@ -167,18 +85,6 @@ StoreBuffer::clear() return true; } -void -StoreBuffer::markAll(JSTracer* trc) -{ - bufferVal.mark(this, trc); - bufferCell.mark(this, trc); - bufferSlot.mark(this, trc); - bufferWholeCell.mark(this, trc); - bufferRelocVal.mark(this, trc); - bufferRelocCell.mark(this, trc); - bufferGeneric.mark(this, trc); -} - void StoreBuffer::setAboutToOverflow() { diff --git a/js/src/gc/StoreBuffer.h b/js/src/gc/StoreBuffer.h index 7fccfadc99..cee5949d11 100644 --- a/js/src/gc/StoreBuffer.h +++ b/js/src/gc/StoreBuffer.h @@ -29,7 +29,7 @@ namespace gc { class BufferableRef { public: - virtual void mark(JSTracer* trc) = 0; + virtual void trace(JSTracer* trc) = 0; bool maybeInRememberedSet(const Nursery&) const { return true; } }; @@ -121,8 +121,8 @@ class StoreBuffer stores_.remove(v); } - /* Mark the source of all edges in the store buffer. */ - void mark(StoreBuffer* owner, JSTracer* trc); + /* Trace the source of all edges in the store buffer. */ + void trace(StoreBuffer* owner, TenuringTracer& mover); size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { return stores_.sizeOfExcludingThis(mallocSizeOf); @@ -158,8 +158,8 @@ class StoreBuffer storage_->availableInCurrentChunk() < LowAvailableThreshold; } - /* Mark all generic edges. */ - void mark(StoreBuffer* owner, JSTracer* trc); + /* Trace all generic edges. */ + void trace(StoreBuffer* owner, JSTracer* trc); template void put(StoreBuffer* owner, const T& t) { @@ -212,7 +212,7 @@ class StoreBuffer return !nursery.isInside(edge); } - void mark(JSTracer* trc) const; + void trace(TenuringTracer& mover) const; CellPtrEdge tagged() const { return CellPtrEdge((Cell**)(uintptr_t(edge) | 1)); } CellPtrEdge untagged() const { return CellPtrEdge((Cell**)(uintptr_t(edge) & ~1)); } @@ -237,7 +237,7 @@ class StoreBuffer return !nursery.isInside(edge); } - void mark(JSTracer* trc) const; + void trace(TenuringTracer& mover) const; ValueEdge tagged() const { return ValueEdge((JS::Value*)(uintptr_t(edge) | 1)); } ValueEdge untagged() const { return ValueEdge((JS::Value*)(uintptr_t(edge) & ~1)); } @@ -283,7 +283,7 @@ class StoreBuffer return !IsInsideNursery(reinterpret_cast(object())); } - void mark(JSTracer* trc) const; + void trace(TenuringTracer& mover) const; typedef struct { typedef SlotsEdge Lookup; @@ -309,7 +309,7 @@ class StoreBuffer static bool supportsDeduplication() { return true; } void* deduplicationKey() const { return (void*)edge; } - void mark(JSTracer* trc) const; + void trace(TenuringTracer& mover) const; typedef PointerEdgeHasher Hasher; }; @@ -317,16 +317,16 @@ class StoreBuffer template struct CallbackRef : public BufferableRef { - typedef void (*MarkCallback)(JSTracer* trc, Key* key, void* data); + typedef void (*TraceCallback)(JSTracer* trc, Key* key, void* data); - CallbackRef(MarkCallback cb, Key* k, void* d) : callback(cb), key(k), data(d) {} + CallbackRef(TraceCallback cb, Key* k, void* d) : callback(cb), key(k), data(d) {} - virtual void mark(JSTracer* trc) { + virtual void trace(JSTracer* trc) { callback(trc, key, data); } private: - MarkCallback callback; + TraceCallback callback; Key* key; void* data; }; @@ -444,15 +444,14 @@ class StoreBuffer putFromAnyThread(bufferGeneric, CallbackRef(callback, key, data)); } - /* Methods to mark the source of all edges in the store buffer. */ - void markAll(JSTracer* trc); - void markValues(JSTracer* trc) { bufferVal.mark(this, trc); } - void markCells(JSTracer* trc) { bufferCell.mark(this, trc); } - void markSlots(JSTracer* trc) { bufferSlot.mark(this, trc); } - void markWholeCells(JSTracer* trc) { bufferWholeCell.mark(this, trc); } - void markRelocatableValues(JSTracer* trc) { bufferRelocVal.mark(this, trc); } - void markRelocatableCells(JSTracer* trc) { bufferRelocCell.mark(this, trc); } - void markGenericEntries(JSTracer* trc) { bufferGeneric.mark(this, trc); } + /* Methods to trace the source of all edges in the store buffer. */ + void traceValues(TenuringTracer& mover) { bufferVal.trace(this, mover); } + void traceCells(TenuringTracer& mover) { bufferCell.trace(this, mover); } + void traceSlots(TenuringTracer& mover) { bufferSlot.trace(this, mover); } + void traceWholeCells(TenuringTracer& mover) { bufferWholeCell.trace(this, mover); } + void traceRelocatableValues(TenuringTracer& mover) { bufferRelocVal.trace(this, mover); } + void traceRelocatableCells(TenuringTracer& mover) { bufferRelocCell.trace(this, mover); } + void traceGenericEntries(JSTracer *trc) { bufferGeneric.trace(this, trc); } /* For use by our owned buffers and for testing. */ void setAboutToOverflow(); diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index a977583bf5..f5c676eccc 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -168,7 +168,6 @@ JS_CallTenuredObjectTracer(JSTracer* trc, JS::TenuredHeap* objp, cons if (!obj) return; - JS::AutoOriginalTraceLocation reloc(trc, (void**)objp); TraceManuallyBarrieredEdge(trc, &obj, name); objp->setPtr(obj); diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index da649b0358..e68baceddf 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -45,13 +45,6 @@ using namespace js::gc; * the snapshot and are no longer found must be marked; otherwise an assertion * triggers. Note that we must not GC in between starting and finishing a * verification phase. - * - * Post-Barrier Verifier: - * When StartVerifyBarriers is called, we create a virtual "Nursery Set" which - * future allocations are recorded in and turn on the StoreBuffer. Later, - * EndVerifyBarriers traverses the heap and ensures that the set of cross- - * generational pointers we find is a subset of the pointers recorded in our - * StoreBuffer. */ struct EdgeValue @@ -174,15 +167,6 @@ gc::GCRuntime::startVerifyPreBarriers() if (verifyPreData || isIncrementalGCInProgress()) return; - /* - * The post barrier verifier requires the storebuffer to be enabled, but the - * pre barrier verifier disables it as part of disabling GGC. Don't allow - * starting the pre barrier verifier if the post barrier verifier is already - * running. - */ - if (verifyPostData) - return; - evictNursery(); AutoPrepareForTracing prep(rt, WithAtoms); @@ -376,144 +360,6 @@ gc::GCRuntime::endVerifyPreBarriers() return true; } -/*** Post-Barrier Verifyier ***/ - -struct VerifyPostTracer : JS::CallbackTracer -{ - /* The gcNumber when the verification began. */ - uint64_t number; - - /* This counts up to gcZealFrequency to decide whether to verify. */ - int count; - - /* The set of edges in the StoreBuffer at the end of verification. */ - typedef HashSet, SystemAllocPolicy> EdgeSet; - EdgeSet* edges; - - VerifyPostTracer(JSRuntime* rt, JSTraceCallback callback) - : JS::CallbackTracer(rt, callback), number(rt->gc.gcNumber()), count(0) - {} -}; - -/* - * The post-barrier verifier runs the full store buffer and a fake nursery when - * running and when it stops, walks the full heap to ensure that all the - * important edges were inserted into the storebuffer. - */ -void -gc::GCRuntime::startVerifyPostBarriers() -{ - if (!JS::IsGenerationalGCEnabled(rt) || verifyPostData || isIncrementalGCInProgress()) - return; - - evictNursery(); - - number++; - - VerifyPostTracer* trc = js_new(rt, JSTraceCallback(nullptr)); - if (!trc) - return; - - verifyPostData = trc; -} - -void -PostVerifierCollectStoreBufferEdges(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) -{ - VerifyPostTracer* trc = (VerifyPostTracer*)jstrc; - - /* The nursery only stores objects. */ - if (kind != JSTRACE_OBJECT) - return; - - /* The store buffer may store extra, non-cross-generational edges. */ - JSObject* dst = *reinterpret_cast(thingp); - if (trc->runtime()->gc.nursery.isInside(thingp) || !IsInsideNursery(dst)) - return; - - /* - * Values will be unpacked to the stack before getting here. However, the - * only things that enter this callback are marked by the store buffer. The - * store buffer ensures that the real tracing location is set correctly. - */ - void*const* loc = trc->tracingLocation(thingp); - - trc->edges->put(loc); -} - -static void -AssertStoreBufferContainsEdge(VerifyPostTracer::EdgeSet* edges, void*const* loc, JSObject* dst) -{ - if (edges->has(loc)) - return; - - char msgbuf[1024]; - JS_snprintf(msgbuf, sizeof(msgbuf), "[post-barrier verifier] Missing edge @ %p to %p", - (void*)loc, (void*)dst); - MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__); - MOZ_CRASH(); -} - -void -PostVerifierVisitEdge(JS::CallbackTracer* jstrc, void** thingp, JSGCTraceKind kind) -{ - VerifyPostTracer* trc = (VerifyPostTracer*)jstrc; - - /* The nursery only stores objects. */ - if (kind != JSTRACE_OBJECT) - return; - - /* Filter out non cross-generational edges. */ - MOZ_ASSERT(!trc->runtime()->gc.nursery.isInside(thingp)); - JSObject* dst = *reinterpret_cast(thingp); - if (!IsInsideNursery(dst)) - return; - - /* - * Values will be unpacked to the stack before getting here. However, the - * only things that enter this callback are marked by the JS_TraceChildren - * below. Since JSObject::markChildren handles this, the real trace - * location will be set correctly in these cases. - */ - void*const* loc = trc->tracingLocation(thingp); - - AssertStoreBufferContainsEdge(trc->edges, loc, dst); -} - -bool -js::gc::GCRuntime::endVerifyPostBarriers() -{ - VerifyPostTracer* trc = (VerifyPostTracer*)verifyPostData; - if (!trc) - return false; - - VerifyPostTracer::EdgeSet edges; - AutoPrepareForTracing prep(rt, SkipAtoms); - - /* Visit every entry in the store buffer and put the edges in a hash set. */ - trc->setTraceCallback(PostVerifierCollectStoreBufferEdges); - if (!edges.init()) - goto oom; - trc->edges = &edges; - storeBuffer.markAll(trc); - - /* Walk the heap to find any edges not the the |edges| set. */ - trc->setTraceCallback(PostVerifierVisitEdge); - for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) { - for (auto kind : AllAllocKinds()) { - for (ZoneCellIterUnderGC cells(zone, kind); !cells.done(); cells.next()) { - Cell *src = cells.getCell(); - JS_TraceChildren(trc, src, MapAllocToTraceKind(kind)); - } - } - } - -oom: - js_delete(trc); - verifyPostData = nullptr; - return true; -} - /*** Barrier Verifier Scheduling ***/ void @@ -525,22 +371,11 @@ gc::GCRuntime::verifyPreBarriers() startVerifyPreBarriers(); } -void -gc::GCRuntime::verifyPostBarriers() -{ - if (verifyPostData) - endVerifyPostBarriers(); - else - startVerifyPostBarriers(); -} - void gc::VerifyBarriers(JSRuntime* rt, VerifierType type) { if (type == PreBarrierVerifier) rt->gc.verifyPreBarriers(); - else - rt->gc.verifyPostBarriers(); } void @@ -562,30 +397,11 @@ gc::GCRuntime::maybeVerifyPreBarriers(bool always) startVerifyPreBarriers(); } -void -gc::GCRuntime::maybeVerifyPostBarriers(bool always) -{ - if (zealMode != ZealVerifierPostValue) - return; - - if (rt->mainThread.suppressGC || !storeBuffer.isEnabled()) - return; - - if (VerifyPostTracer* trc = (VerifyPostTracer*)verifyPostData) { - if (++trc->count < zealFrequency && !always) - return; - - endVerifyPostBarriers(); - } - startVerifyPostBarriers(); -} - void js::gc::MaybeVerifyBarriers(JSContext* cx, bool always) { GCRuntime* gc = &cx->runtime()->gc; gc->maybeVerifyPreBarriers(always); - gc->maybeVerifyPostBarriers(always); } void @@ -595,10 +411,6 @@ js::gc::GCRuntime::finishVerifier() js_delete(trc); verifyPreData = nullptr; } - if (VerifyPostTracer* trc = (VerifyPostTracer*)verifyPostData) { - js_delete(trc); - verifyPostData = nullptr; - } } #endif /* JS_GC_ZEAL */ diff --git a/js/src/gdb/gdb-tests.cpp b/js/src/gdb/gdb-tests.cpp index 9ab81825c7..ada42171ba 100644 --- a/js/src/gdb/gdb-tests.cpp +++ b/js/src/gdb/gdb-tests.cpp @@ -17,7 +17,7 @@ const JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp index 9396d9a130..f9f704abdf 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp +++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp @@ -141,7 +141,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // Check if we have space on the stack. Label stack_ok; void* stack_limit = runtime->addressOfJitStackLimit(); - masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok); + masm.branchStackPtrRhs(Assembler::Below, AbsoluteAddress(stack_limit), &stack_ok); // Exit with an exception. There is not enough space on the stack // for our working registers. @@ -155,15 +155,16 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // on Windows can cause segmentation faults. Assuming page size is 4k. const int kPageSize = 4096; for (int i = frameSize - sizeof(void*); i >= 0; i -= kPageSize) - masm.storePtr(temp0, Address(StackPointer, i)); + masm.storePtr(temp0, Address(masm.getStackPointer(), i)); #endif // XP_WIN #ifndef JS_CODEGEN_X86 // The InputOutputData* is stored on the stack immediately above the frame. - Address inputOutputAddress(StackPointer, frameSize); + Address inputOutputAddress(masm.getStackPointer(), frameSize); #else // The InputOutputData* is left in its original on stack location. - Address inputOutputAddress(StackPointer, frameSize + (pushedNonVolatileRegisters + 1) * sizeof(void*)); + Address inputOutputAddress(masm.getStackPointer(), + frameSize + (pushedNonVolatileRegisters + 1) * sizeof(void*)); #endif masm.loadPtr(inputOutputAddress, temp0); @@ -173,10 +174,10 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) Register matchPairsRegister = input_end_pointer; masm.loadPtr(Address(temp0, offsetof(InputOutputData, matches)), matchPairsRegister); masm.loadPtr(Address(matchPairsRegister, MatchPairs::offsetOfPairs()), temp1); - masm.storePtr(temp1, Address(StackPointer, offsetof(FrameData, outputRegisters))); + masm.storePtr(temp1, Address(masm.getStackPointer(), offsetof(FrameData, outputRegisters))); masm.load32(Address(matchPairsRegister, MatchPairs::offsetOfPairCount()), temp1); masm.lshiftPtr(Imm32(1), temp1); - masm.store32(temp1, Address(StackPointer, offsetof(FrameData, numOutputRegisters))); + masm.store32(temp1, Address(masm.getStackPointer(), offsetof(FrameData, numOutputRegisters))); #ifdef DEBUG // Bounds check numOutputRegisters. @@ -193,11 +194,11 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // Load input start pointer, and copy to FrameData. masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputStart)), current_position); - masm.storePtr(current_position, Address(StackPointer, offsetof(FrameData, inputStart))); + masm.storePtr(current_position, Address(masm.getStackPointer(), offsetof(FrameData, inputStart))); // Load start index, and copy to FrameData. masm.loadPtr(Address(temp0, offsetof(InputOutputData, startIndex)), temp1); - masm.storePtr(temp1, Address(StackPointer, offsetof(FrameData, startIndex))); + masm.storePtr(temp1, Address(masm.getStackPointer(), offsetof(FrameData, startIndex))); // Set up input position to be negative offset from string end. masm.subPtr(input_end_pointer, current_position); @@ -208,7 +209,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // Store this value on the frame, for use when clearing // position registers. - masm.storePtr(temp0, Address(StackPointer, offsetof(FrameData, inputStartMinusOne))); + masm.storePtr(temp0, Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne))); // Update current position based on start index. masm.computeEffectiveAddress(BaseIndex(current_position, temp1, factor()), current_position); @@ -217,7 +218,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // Load newline if index is at start, previous character otherwise. masm.branchPtr(Assembler::NotEqual, - Address(StackPointer, offsetof(FrameData, startIndex)), ImmWord(0), + Address(masm.getStackPointer(), offsetof(FrameData, startIndex)), ImmWord(0), &load_char_start_regexp); masm.movePtr(ImmWord('\n'), current_character); masm.jump(&start_regexp); @@ -239,7 +240,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) masm.movePtr(ImmWord(register_offset(0)), temp1); Label init_loop; masm.bind(&init_loop); - masm.storePtr(temp0, BaseIndex(StackPointer, temp1, TimesOne)); + masm.storePtr(temp0, BaseIndex(masm.getStackPointer(), temp1, TimesOne)); masm.addPtr(ImmWord(sizeof(void*)), temp1); masm.branchPtr(Assembler::LessThan, temp1, ImmWord(register_offset(num_saved_registers_)), &init_loop); @@ -251,7 +252,8 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // Initialize backtrack stack pointer. masm.loadPtr(AbsoluteAddress(runtime->regexpStack.addressOfBase()), backtrack_stack_pointer); - masm.storePtr(backtrack_stack_pointer, Address(StackPointer, offsetof(FrameData, backtrackStackBase))); + masm.storePtr(backtrack_stack_pointer, + Address(masm.getStackPointer(), offsetof(FrameData, backtrackStackBase))); masm.jump(&start_label_); @@ -259,7 +261,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) if (success_label_.used()) { MOZ_ASSERT(num_saved_registers_ > 0); - Address outputRegistersAddress(StackPointer, offsetof(FrameData, outputRegisters)); + Address outputRegistersAddress(masm.getStackPointer(), offsetof(FrameData, outputRegisters)); // Save captures when successful. masm.bind(&success_label_); @@ -297,9 +299,9 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) // Restart matching if the regular expression is flagged as global. if (global()) { // Increment success counter. - masm.add32(Imm32(1), Address(StackPointer, offsetof(FrameData, successfulCaptures))); + masm.add32(Imm32(1), Address(masm.getStackPointer(), offsetof(FrameData, successfulCaptures))); - Address numOutputRegistersAddress(StackPointer, offsetof(FrameData, numOutputRegisters)); + Address numOutputRegistersAddress(masm.getStackPointer(), offsetof(FrameData, numOutputRegisters)); // Capture results have been stored, so the number of remaining global // output registers is reduced by the number of stored captures. @@ -316,7 +318,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) masm.add32(Imm32(num_saved_registers_ * sizeof(void*)), outputRegistersAddress); // Prepare temp0 to initialize registers with its value in the next run. - masm.loadPtr(Address(StackPointer, offsetof(FrameData, inputStartMinusOne)), temp0); + masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)), temp0); if (global_with_zero_length_check()) { // Special case for zero-length matches. @@ -343,7 +345,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only) if (global()) { // Return the number of successful captures. - masm.load32(Address(StackPointer, offsetof(FrameData, successfulCaptures)), temp0); + masm.load32(Address(masm.getStackPointer(), offsetof(FrameData, successfulCaptures)), temp0); } masm.bind(&return_temp0); @@ -517,13 +519,13 @@ NativeRegExpMacroAssembler::CheckAtStart(Label* on_at_start) Label not_at_start; // Did we start the match at the start of the string at all? - Address startIndex(StackPointer, offsetof(FrameData, startIndex)); + Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex)); masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), ¬_at_start); // If we did, are we still at the start of the input? masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0); - Address inputStart(StackPointer, offsetof(FrameData, inputStart)); + Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart)); masm.branchPtr(Assembler::Equal, inputStart, temp0, BranchOrBacktrack(on_at_start)); masm.bind(¬_at_start); @@ -535,13 +537,13 @@ NativeRegExpMacroAssembler::CheckNotAtStart(Label* on_not_at_start) JitSpew(SPEW_PREFIX "CheckNotAtStart"); // Did we start the match at the start of the string at all? - Address startIndex(StackPointer, offsetof(FrameData, startIndex)); + Address startIndex(masm.getStackPointer(), offsetof(FrameData, startIndex)); masm.branchPtr(Assembler::NotEqual, startIndex, ImmWord(0), BranchOrBacktrack(on_not_at_start)); // If we did, are we still at the start of the input? masm.computeEffectiveAddress(BaseIndex(input_end_pointer, current_position, TimesOne), temp0); - Address inputStart(StackPointer, offsetof(FrameData, inputStart)); + Address inputStart(masm.getStackPointer(), offsetof(FrameData, inputStart)); masm.branchPtr(Assembler::NotEqual, inputStart, temp0, BranchOrBacktrack(on_not_at_start)); } @@ -776,7 +778,7 @@ NativeRegExpMacroAssembler::CheckNotBackReferenceIgnoreCase(int start_reg, Label masm.bind(&success); // Drop original character position value. - masm.addPtr(Imm32(sizeof(uintptr_t)), StackPointer); + masm.addToStackPtr(Imm32(sizeof(uintptr_t))); // Compute new value of character position after the matched part. masm.subPtr(input_end_pointer, current_position); @@ -1045,7 +1047,7 @@ NativeRegExpMacroAssembler::CheckBacktrackStackLimit() backtrack_stack_pointer, &no_stack_overflow); // Copy the stack pointer before the call() instruction modifies it. - masm.movePtr(StackPointer, temp2); + masm.moveStackPtrTo(temp2); masm.call(&stack_overflow_label_); masm.bind(&no_stack_overflow); @@ -1100,7 +1102,8 @@ NativeRegExpMacroAssembler::ReadBacktrackStackPointerFromRegister(int reg) JitSpew(SPEW_PREFIX "ReadBacktrackStackPointerFromRegister(%d)", reg); masm.loadPtr(register_location(reg), backtrack_stack_pointer); - masm.addPtr(Address(StackPointer, offsetof(FrameData, backtrackStackBase)), backtrack_stack_pointer); + masm.addPtr(Address(masm.getStackPointer(), + offsetof(FrameData, backtrackStackBase)), backtrack_stack_pointer); } void @@ -1109,7 +1112,8 @@ NativeRegExpMacroAssembler::WriteBacktrackStackPointerToRegister(int reg) JitSpew(SPEW_PREFIX "WriteBacktrackStackPointerToRegister(%d)", reg); masm.movePtr(backtrack_stack_pointer, temp0); - masm.subPtr(Address(StackPointer, offsetof(FrameData, backtrackStackBase)), temp0); + masm.subPtr(Address(masm.getStackPointer(), + offsetof(FrameData, backtrackStackBase)), temp0); masm.storePtr(temp0, register_location(reg)); } @@ -1154,7 +1158,7 @@ NativeRegExpMacroAssembler::ClearRegisters(int reg_from, int reg_to) JitSpew(SPEW_PREFIX "ClearRegisters(%d, %d)", reg_from, reg_to); MOZ_ASSERT(reg_from <= reg_to); - masm.loadPtr(Address(StackPointer, offsetof(FrameData, inputStartMinusOne)), temp0); + masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, inputStartMinusOne)), temp0); for (int reg = reg_from; reg <= reg_to; reg++) masm.storePtr(temp0, register_location(reg)); } diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.h b/js/src/irregexp/NativeRegExpMacroAssembler.h index 7993dd6659..5311eec29e 100644 --- a/js/src/irregexp/NativeRegExpMacroAssembler.h +++ b/js/src/irregexp/NativeRegExpMacroAssembler.h @@ -210,7 +210,7 @@ class MOZ_STACK_CLASS NativeRegExpMacroAssembler : public RegExpMacroAssembler // The frame_pointer-relative location of a regexp register. jit::Address register_location(int register_index) { checkRegister(register_index); - return jit::Address(jit::StackPointer, register_offset(register_index)); + return jit::Address(masm.getStackPointer(), register_offset(register_index)); } int32_t register_offset(int register_index) { diff --git a/js/src/jit-test/tests/SIMD/bool.js b/js/src/jit-test/tests/SIMD/bool.js new file mode 100644 index 0000000000..f5394f0e43 --- /dev/null +++ b/js/src/jit-test/tests/SIMD/bool.js @@ -0,0 +1,15 @@ +load(libdir + 'simd.js'); + +setJitCompilerOption("ion.warmup.trigger", 50); + +const T = -1, F = 0; + +function f() { + for (var i = 0; i < 150; i++) { + assertEqX4(SIMD.int32x4.bool(i + 1, true, 'hey', null), [T, T, T, F]); + assertEqX4(SIMD.int32x4.bool(undefined, '', {}, objectEmulatingUndefined()), [F, F, T, F]); + assertEqX4(SIMD.int32x4.bool(null, NaN, false, Infinity), [F, F, F, T]); + } +} + +f(); diff --git a/js/src/jit-test/tests/SIMD/convert.js b/js/src/jit-test/tests/SIMD/convert.js index aab00d31e5..d127a60c1d 100644 --- a/js/src/jit-test/tests/SIMD/convert.js +++ b/js/src/jit-test/tests/SIMD/convert.js @@ -1,6 +1,6 @@ load(libdir + 'simd.js'); -setJitCompilerOption("ion.warmup.trigger", 50); +setJitCompilerOption("ion.warmup.trigger", 30); var cast = (function() { var i32 = new Int32Array(1); @@ -19,6 +19,7 @@ var cast = (function() { })(); function f() { + // No bailout here. var f4 = SIMD.float32x4(1, 2, 3, 4); var i4 = SIMD.int32x4(1, 2, 3, 4); var BitOrZero = (x) => x | 0; @@ -30,5 +31,40 @@ function f() { } } +function uglyDuckling(val) { + // We bail out when i == 149 because the conversion will return + // 0x80000000 and the input actually wasn't in bounds. + print('entering uglyDuckling'); + val = Math.fround(val); + for (var i = 0; i < 150; i++) { + var caught = false; + try { + var v = SIMD.float32x4(i < 149 ? 0 : val, 0, 0, 0) + SIMD.int32x4.fromFloat32x4(v); + } catch(e) { + assertEq(e instanceof RangeError, true); + assertEq(i, 149); + caught = true; + } + assertEq(i < 149 || caught, true); + } +} + +function dontBail() { + print('entering dontbail'); + // On x86, the conversion will return 0x80000000, which will imply that we + // check the input values. However, we shouldn't bail out in this case. + for (var i = 0; i < 150; i++) { + var v = SIMD.float32x4(i < 149 ? 0 : -Math.pow(2, 31), 0, 0, 0) + SIMD.int32x4.fromFloat32x4(v); + } +} + f(); +dontBail(); +dontBail(); + +uglyDuckling(Math.pow(2, 31)); +uglyDuckling(NaN); +uglyDuckling(-Math.pow(2, 32)); diff --git a/js/src/jit-test/tests/SIMD/shift.js b/js/src/jit-test/tests/SIMD/shift.js new file mode 100644 index 0000000000..303a48e3a3 --- /dev/null +++ b/js/src/jit-test/tests/SIMD/shift.js @@ -0,0 +1,57 @@ +load(libdir + 'simd.js'); + +setJitCompilerOption("ion.warmup.trigger", 50); + +function curry(f, arg) { return f.bind(null, arg); } + +function binaryLsh(count, v) { if (count>>>0 >= 32) return 0; return (v << count) | 0; } +function lsh(count) { return curry(binaryLsh, count); } + +function binaryRsh(count, v) { if (count>>>0 >= 32) count = 31; return (v >> count) | 0; } +function rsh(count) { return curry(binaryRsh, count); } + +function binaryUrsh(count, v) { if (count>>>0 >= 32) return 0; return (v >>> count) | 0; } +function ursh(count) { return curry(binaryUrsh, count); } + +function f() { + var v = SIMD.int32x4(1, 2, -3, 4); + var a = [1, 2, -3, 4]; + var zeros = [0,0,0,0]; + + var shifts = [-1, 0, 1, 31, 32]; + + var r; + for (var i = 0; i < 150; i++) { + // Constant shift counts + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, -1), a.map(lsh(-1))); + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 0), a.map(lsh(0))); + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 1), a.map(lsh(1))); + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 2), a.map(lsh(2))); + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 31), a.map(lsh(31))); + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, 32), a.map(lsh(32))); + + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, -1), a.map(rsh(31))); + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 0), a.map(rsh(0))); + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 1), a.map(rsh(1))); + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 2), a.map(rsh(2))); + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 31), a.map(rsh(31))); + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, 32), a.map(rsh(31))); + + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, -1), a.map(ursh(-1))); + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 0), a.map(ursh(0))); + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 1), a.map(ursh(1))); + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 2), a.map(ursh(2))); + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 31), a.map(ursh(31))); + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, 32), a.map(ursh(32))); + + // Non constant shift counts + var c = shifts[i % shifts.length]; + assertEqX4(SIMD.int32x4.shiftLeftByScalar(v, c), a.map(lsh(c))); + assertEqX4(SIMD.int32x4.shiftRightArithmeticByScalar(v, c), a.map(rsh(c))); + assertEqX4(SIMD.int32x4.shiftRightLogicalByScalar(v, c), a.map(ursh(c))); + } + return r; +} + +f(); + diff --git a/js/src/jit-test/tests/arguments/arguments-on-proto.js b/js/src/jit-test/tests/arguments/arguments-on-proto.js new file mode 100644 index 0000000000..745c77b9da --- /dev/null +++ b/js/src/jit-test/tests/arguments/arguments-on-proto.js @@ -0,0 +1,27 @@ +function create() { + return Object.create(arguments, {2: {value: "shadowed"}}); +} + +function createStrict() { + "use strict"; + return Object.create(arguments, {40: {value: "shadowed2"}}); +} + +function f() { + var args = [createStrict(10, 20, 30, 40), create(1, 2, 3)]; + + var threshold = getJitCompilerOptions()["ion.warmup.trigger"] + 101; + + for (var i = 0; i < threshold; i++) { + // We switch between different arguments objects, to make + // sure the right IC is triggered. + var a = args[i % 2]; + assertEq(a.length, (i % 2) ? 3 : 4); + assertEq(a[0], (i % 2) ? 1 : 10); + assertEq(a[1], (i % 2) ? 2 : 20); + assertEq(a[2], (i % 2) ? "shadowed" : 30); + assertEq(a[3], (i % 2) ? undefined : 40); + } +} + +f(); diff --git a/js/src/jit-test/tests/arrays/sort-getter-only.js b/js/src/jit-test/tests/arrays/sort-getter-only.js index c799de03ff..d17e8556d6 100644 --- a/js/src/jit-test/tests/arrays/sort-getter-only.js +++ b/js/src/jit-test/tests/arrays/sort-getter-only.js @@ -5,10 +5,10 @@ load(libdir + "asserts.js"); var a = ["A", , "B", "C", "D"]; var normalArrayElementDesc = Object.getOwnPropertyDescriptor(a, 0); var getterDesc = { - configurable: false, - enumerable: true, get: function () { return "F"; }, - set: undefined + set: undefined, + enumerable: true, + configurable: false }; Object.defineProperty(a, 1, getterDesc); diff --git a/js/src/jit-test/tests/basic/bug1153057.js b/js/src/jit-test/tests/basic/bug1153057.js new file mode 100644 index 0000000000..7f4ab1b110 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1153057.js @@ -0,0 +1 @@ +({ "0"() { eval(); } }); diff --git a/js/src/jit-test/tests/basic/bug1161762.js b/js/src/jit-test/tests/basic/bug1161762.js new file mode 100644 index 0000000000..ba072c507f --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1161762.js @@ -0,0 +1,24 @@ + +for (var actual = .5; actual < 100; actual++) { + var test2 = { + test4: actual + 6, + test2: actual + 9, + printStatus: actual + 10, + isPrototypeOf: actual + 12, + expect: actual + 14, + printErr: actual + 17, + ret2: actual + 19, + printBugNumber: actual + 32, + test3: actual + 33, + String: actual + 34, + summary: actual + 40, + test1: actual + 42, + Array: actual + 43, + BUGNUMBER: actual + 44, + assertEq: actual + 45, + __call__: actual + 47, + x: actual + 48, + test0: actual + 49, + res: actual + 50 + }; +} diff --git a/js/src/jit-test/tests/basic/constructor-name.js b/js/src/jit-test/tests/basic/constructor-name.js new file mode 100644 index 0000000000..4bc6a61ead --- /dev/null +++ b/js/src/jit-test/tests/basic/constructor-name.js @@ -0,0 +1,28 @@ +function Ctor() {} + +var nested = {}; +nested.Ctor = function () {}; +nested.object = {}; + +function makeInstance() { + let LexicalCtor = function () {}; + return new LexicalCtor; +} + +function makeObject() { + let object = {}; + return object; +} + +let tests = [ + { name: "Ctor", object: new Ctor }, + { name: "nested.Ctor", object: new nested.Ctor }, + { name: "makeInstance/LexicalCtor", object: makeInstance() }, + { name: null, object: {} }, + { name: null, object: nested.object }, + { name: null, object: makeObject() }, +]; + +for (let { name, object } of tests) { + assertEq(getConstructorName(object), name); +} diff --git a/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js new file mode 100644 index 0000000000..d516a1a40c --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-1.js @@ -0,0 +1,8 @@ +// If Array.of tries to overwrite a non-configurable property, it throws a TypeError. + +load(libdir + "asserts.js"); + +function C() { + Object.defineProperty(this, 0, {value: "v", configurable: false}); +} +assertThrowsInstanceOf(() => Array.of.call(C, 1, 2, 3), TypeError); diff --git a/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js new file mode 100644 index 0000000000..f5c783714a --- /dev/null +++ b/js/src/jit-test/tests/collections/Array-of-nonconfigurable-2.js @@ -0,0 +1,16 @@ +// Array.of does not overwrite non-configurable properties. + +load(libdir + "asserts.js"); + +var obj; +function C() { + obj = this; + Object.defineProperty(this, 0, {value: "v", configurable: false}); +} +try { Array.of.call(C, 1); } catch (e) {} +assertDeepEq(Object.getOwnPropertyDescriptor(obj, 0), { + value: "v", + writable: false, + enumerable: false, + configurable: false +}); diff --git a/js/src/jit-test/tests/debug/Environment-gc-03.js b/js/src/jit-test/tests/debug/Environment-gc-03.js new file mode 100644 index 0000000000..e3bbad052f --- /dev/null +++ b/js/src/jit-test/tests/debug/Environment-gc-03.js @@ -0,0 +1,21 @@ +// Test that block scopes cannot be resurrected by onStep. + +var g = newGlobal(); +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function(frame) { + frame.onStep = (function() { + frame.environment; + }); +}; + +g.eval("debugger; for (let i = 0; i < 1; i++) (function(){});"); + +// If the last freshened block scope was incorrectly resurrected by onStep +// above, GCing will assert. +gc(); + +g.eval("debugger; { let i = 0; (function(){ i = 42; }); }"); +gc(); + +g.eval("debugger; try { throw 42; } catch (e) { };"); +gc(); diff --git a/js/src/jit-test/tests/debug/Environment-identity-05.js b/js/src/jit-test/tests/debug/Environment-identity-05.js new file mode 100644 index 0000000000..c1d06b7e78 --- /dev/null +++ b/js/src/jit-test/tests/debug/Environment-identity-05.js @@ -0,0 +1,19 @@ +// Tests that freshened blocks behave correctly in Debugger. + +var g = newGlobal(); +var dbg = Debugger(g); +var log = ''; +var oldEnv = null; +dbg.onDebuggerStatement = function (frame) { + if (!oldEnv) { + oldEnv = frame.environment; + } else { + // Block has been freshened by |for (let ...)|, should be different + // identity. + log += (oldEnv === frame.environment); + } + log += frame.environment.getVariable("x"); +}; +g.eval("for (let x = 0; x < 2; x++) debugger;"); +gc(); +assertEq(log, "0false1"); diff --git a/js/src/jit-test/tests/debug/Frame-environment-05.js b/js/src/jit-test/tests/debug/Frame-environment-05.js new file mode 100644 index 0000000000..98f1c9a126 --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-environment-05.js @@ -0,0 +1,9 @@ +// Test that Debugger.Frame.prototype.environment works at all pcs of a script +// with an aliased block scope. + +var g = newGlobal(); +var dbg = new Debugger(g); +dbg.onDebuggerStatement = function (frame) { + frame.onStep = (function () { frame.environment; }); +}; +g.eval("debugger; for (let i of [1,2,3]) print(i);"); diff --git a/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-01.js b/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-01.js index 08d2bd56d7..9c7b4f92e0 100644 --- a/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-01.js +++ b/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-01.js @@ -6,9 +6,10 @@ var correct; dbg.onEnterFrame = function (f) { if (f.callee && f.callee.name == "f") { f.onPop = function() { - // The scope at the point of onPop is the outermost. - correct = (f.environment.getVariable("e") === undefined && - f.environment.getVariable("outer") === 43); + // The scope at the point of onPop is at the point of popping (the + // noSuchFn call). + correct = (f.environment.getVariable("e") === 42 && + f.environment.getVariable("outer") === undefined); }; } }; diff --git a/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-02.js b/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-02.js index 5938f1bb47..157dbf5bbb 100644 --- a/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-02.js +++ b/js/src/jit-test/tests/debug/Frame-onPop-error-scope-unwind-02.js @@ -6,9 +6,10 @@ var correct; dbg.onEnterFrame = function (f) { if (f.callee && f.callee.name == "f") { f.onPop = function() { - // The scope at the point of onPop is the outermost. - correct = (f.environment.getVariable("e") === undefined && - f.environment.getVariable("outer") === 43); + // The scope at the point of onPop is at the point of popping (the + // noSuchFn call). + correct = (f.environment.getVariable("e") === 42 && + f.environment.getVariable("outer") === undefined); }; } }; diff --git a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-15.js b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-15.js new file mode 100644 index 0000000000..197642868b --- /dev/null +++ b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-15.js @@ -0,0 +1,31 @@ +// Test drainAllocationsLog() and [[Class]] names. + +const root = newGlobal(); +const dbg = new Debugger(); +const wrappedRoot = dbg.addDebuggee(root) + +root.eval( + ` + this.tests = [ + { expected: "Object", test: () => new Object }, + { expected: "Array", test: () => [] }, + { expected: "Date", test: () => new Date }, + { expected: "RegExp", test: () => /problems/ }, + { expected: "Int8Array", test: () => new Int8Array }, + { expected: "Promise", test: makeFakePromise }, + ]; + ` +); + + +for (let { expected, test } of root.tests) { + print(expected); + + dbg.memory.trackingAllocationSites = true; + test(); + let allocs = dbg.memory.drainAllocationsLog(); + dbg.memory.trackingAllocationSites = false; + + assertEq(allocs.some(a => a.class === expected), true); +} + diff --git a/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js new file mode 100644 index 0000000000..b147d6ded0 --- /dev/null +++ b/js/src/jit-test/tests/debug/Memory-drainAllocationsLog-16.js @@ -0,0 +1,47 @@ +// Test drainAllocationsLog() and constructor names. + +const root = newGlobal(); +const dbg = new Debugger(); +const wrappedRoot = dbg.addDebuggee(root); + +root.eval( + ` + function Ctor() {} + + var nested = {}; + nested.Ctor = function () {}; + + function makeInstance() { + let LexicalCtor = function () {}; + return new LexicalCtor; + } + + function makeObject() { + let object = {}; + return object; + } + + this.tests = [ + { name: "Ctor", fn: () => new Ctor }, + { name: "nested.Ctor", fn: () => new nested.Ctor }, + { name: "makeInstance/LexicalCtor", fn: () => makeInstance() }, + { name: null, fn: () => ({}) }, + { name: null, fn: () => (nested.object = {}) }, + { name: null, fn: () => makeObject() }, + ]; + ` +); + +for (let { name, fn } of root.tests) { + print(name); + + dbg.memory.trackingAllocationSites = true; + + fn(); + + let entries = dbg.memory.drainAllocationsLog(); + let ctors = entries.map(e => e.constructor); + assertEq(ctors.some(ctor => ctor === name), true); + + dbg.memory.trackingAllocationSites = false; +} diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js new file mode 100644 index 0000000000..ae452decb3 --- /dev/null +++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js @@ -0,0 +1,83 @@ +// Check that JS::ubi::Node::size returns reasonable results for objects. + +// We actually hard-code specific sizes into this test, even though they're +// implementation details, because in practice there are only two architecture +// variants to consider (32-bit and 64-bit), and if these sizes change, that's +// something SpiderMonkey hackers really want to know; they're supposed to be +// stable. + +// Run this test only if we're using jemalloc. Other malloc implementations +// exhibit surprising behaviors. For example, 32-bit Fedora builds have +// non-deterministic allocation sizes. +if (!getBuildConfiguration()['moz-memory']) + quit(0); + +if (getBuildConfiguration()['pointer-byte-size'] == 4) + var s = (s32, s64) => s32 +else + var s = (s32, s64) => s64 + +function tenure(obj) { + gc(); + return obj; +} + +// Return the byte size of |obj|, ensuring that the size is not affected by +// being tenured. (We use 'survives a GC' as an approximation for 'tenuring'.) +function tByteSize(obj) { + var size = byteSize(obj); + minorgc(); + if (size != byteSize(obj)) + return 0; + return size; +} + +assertEq(tByteSize({}), s(16, 32)); + +// Try objects with only named properties. +assertEq(tByteSize({ w: 1 }), s(32, 48)); +assertEq(tByteSize({ w: 1, x: 2 }), s(32, 48)); +assertEq(tByteSize({ w: 1, x: 2, y: 3 }), s(48, 64)); +assertEq(tByteSize({ w: 1, x: 2, y: 3, z:4 }), s(48, 64)); +assertEq(tByteSize({ w: 1, x: 2, y: 3, z:4, a: 5 }), s(80, 96)); + +// Try objects with only indexed properties. +assertEq(tByteSize({ 0:0 }), s(96, 112)); +assertEq(tByteSize({ 0:0, 1:1 }), s(96, 112)); +assertEq(tByteSize({ 0:0, 1:1, 2:2 }), s(112, 128)); +assertEq(tByteSize({ 0:0, 1:1, 2:2, 3:3 }), s(112, 128)); +assertEq(tByteSize({ 0:0, 1:1, 2:2, 3:3, 4:4 }), s(144, 160)); + +// Mix indexed and named properties, exploring each combination of the size +// classes above. +// +// Oddly, the changes here as the objects grow are not simply the sums of the +// changes above: for example, with one named property, the objects with three +// and five indexed properties are in different size classes; but with three +// named properties, there's no break there. +assertEq(tByteSize({ w:1, 0:0 }), s(96, 112)); +assertEq(tByteSize({ w:1, 0:0, 1:1, 2:2 }), s(112, 128)); +assertEq(tByteSize({ w:1, 0:0, 1:1, 2:2, 3:3, 4:4 }), s(144, 160)); +assertEq(tByteSize({ w:1, x:2, y:3, 0:0 }), s(112, 128)); +assertEq(tByteSize({ w:1, x:2, y:3, 0:0, 1:1, 2:2 }), s(144, 160)); +assertEq(tByteSize({ w:1, x:2, y:3, 0:0, 1:1, 2:2, 3:3, 4:4 }), s(144, 160)); +assertEq(tByteSize({ w:1, x:2, y:3, z:4, a:6, 0:0 }), s(144, 160)); +assertEq(tByteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2 }), s(144, 160)); +assertEq(tByteSize({ w:1, x:2, y:3, z:4, a:6, 0:0, 1:1, 2:2, 3:3, 4:4 }), s(176, 192)); + +// Check various lengths of array. +assertEq(tByteSize([]), s(80, 96)); +assertEq(tByteSize([1]), s(48, 64)); +assertEq(tByteSize([1, 2]), s(48, 64)); +assertEq(tByteSize([1, 2, 3]), s(80, 96)); +assertEq(tByteSize([1, 2, 3, 4]), s(80, 96)); +assertEq(tByteSize([1, 2, 3, 4, 5]), s(80, 96)); +assertEq(tByteSize([1, 2, 3, 4, 5, 6]), s(80, 96)); +assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7]), s(112, 128)); +assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7, 8]), s(112, 128)); + +// Various forms of functions. +assertEq(tByteSize(function () {}), s(32, 64)); +assertEq(tByteSize(function () {}.bind()), s(96, 128)); +assertEq(tByteSize(() => 1), s(48, 96)); +assertEq(tByteSize(Math.sin), s(32, 64)); diff --git a/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js new file mode 100644 index 0000000000..4d342cff33 --- /dev/null +++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js @@ -0,0 +1,131 @@ +// Check JS::ubi::Node::size results for strings. + +// We actually hard-code specific sizes into this test, even though they're +// implementation details, because in practice there are only two architecture +// variants to consider (32-bit and 64-bit), and if these sizes change, that's +// something SpiderMonkey hackers really want to know; they're supposed to be +// stable. + +// Run this test only if we're using jemalloc. Other malloc implementations +// exhibit surprising behaviors. For example, 32-bit Fedora builds have +// non-deterministic allocation sizes. +var config = getBuildConfiguration(); +if (!config['moz-memory']) + quit(0); + +if (config['pointer-byte-size'] == 4) + var s = (s32, s64) => s32 +else + var s = (s32, s64) => s64 + +// Return the byte size of |obj|, ensuring that the size is not affected by +// being tenured. (We use 'survives a GC' as an approximation for 'tenuring'.) +function tByteSize(obj) { + var nurserySize = byteSize(obj); + minorgc(); + var tenuredSize = byteSize(obj); + if (nurserySize != tenuredSize) { + print("nursery size: " + nurserySize + " tenured size: " + tenuredSize); + return -1; // make the stack trace point at the real test + } + + return tenuredSize; +} + +// There are four representations of flat strings, with the following capacities +// (excluding a terminating null character): +// +// 32-bit 64-bit test +// representation Latin-1 char16_t Latin-1 char16_t label +// ======================================================================== +// JSExternalString (cannot be tested in shell) - +// JSThinInlineString 7 3 15 7 T +// JSFatInlineString 23 11 23 11 F +// JSExtensibleString - limited by available memory - X +// JSUndependedString - same as JSExtensibleString - + +// Latin-1 +assertEq(tByteSize(""), s(16, 24)); // T, T +assertEq(tByteSize("1"), s(16, 24)); // T, T +assertEq(tByteSize("1234567"), s(16, 24)); // T, T +assertEq(tByteSize("12345678"), s(32, 24)); // F, T +assertEq(tByteSize("123456789.12345"), s(32, 24)); // F, T +assertEq(tByteSize("123456789.123456"), s(32, 32)); // F, F +assertEq(tByteSize("123456789.123456789.123"), s(32, 32)); // F, F +assertEq(tByteSize("123456789.123456789.1234"), s(48, 56)); // X, X +assertEq(tByteSize("123456789.123456789.123456789.1"), s(48, 56)); // X, X +assertEq(tByteSize("123456789.123456789.123456789.12"), s(64, 72)); // X, X + +// Inline char16_t atoms. +// "Impassionate gods have never seen the red that is the Tatsuta River." +// - Ariwara no Narihira +assertEq(tByteSize("千"), s(16, 24)); // T, T +assertEq(tByteSize("千早"), s(16, 24)); // T, T +assertEq(tByteSize("千早ぶ"), s(16, 24)); // T, T +assertEq(tByteSize("千早ぶる"), s(32, 24)); // F, T +assertEq(tByteSize("千早ぶる神"), s(32, 24)); // F, T +assertEq(tByteSize("千早ぶる神代"), s(32, 24)); // F, T +assertEq(tByteSize("千早ぶる神代も"), s(32, 24)); // F, T +assertEq(tByteSize("千早ぶる神代もき"), s(32, 32)); // F, F +assertEq(tByteSize("千早ぶる神代もきかず龍"), s(32, 32)); // F, F +assertEq(tByteSize("千早ぶる神代もきかず龍田"), s(48, 56)); // X, X +assertEq(tByteSize("千早ぶる神代もきかず龍田川 か"), s(48, 56)); // X, X +assertEq(tByteSize("千早ぶる神代もきかず龍田川 から"), s(64, 72)); // X, X +assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水く"), s(64, 72)); // X, X +assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くく"), s(80, 88)); // X, X +assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くくるとは"), s(80, 88)); // X, X + +// A Latin-1 rope. This changes size when flattened. +// "In a village of La Mancha, the name of which I have no desire to call to mind" +// - Miguel de Cervantes, Don Quixote +var fragment8 = "En un lugar de la Mancha, de cuyo nombre no quiero acordarme"; // 60 characters +var rope8 = fragment8; +for (var i = 0; i < 10; i++) // 1024 repetitions + rope8 = rope8 + rope8; +assertEq(tByteSize(rope8), s(16, 24)); +var matches8 = rope8.match(/(de cuyo nombre no quiero acordarme)/); +assertEq(tByteSize(rope8), s(16 + 65536, 24 + 65536)); + +// Test extensible strings. +// +// Appending another copy of the fragment should yield another rope. +// +// Flatting that should turn the original rope into a dependent string, and +// yield a new linear string, of the some size as the original. +rope8a = rope8 + fragment8; +assertEq(tByteSize(rope8a), s(16, 24)); +rope8a.match(/x/, function() { assertEq(true, false); }); +assertEq(tByteSize(rope8a), s(16 + 65536, 24 + 65536)); +assertEq(tByteSize(rope8), s(16, 24)); + + +// A char16_t rope. This changes size when flattened. +// "From the Heliconian Muses let us begin to sing" +// --- Hesiod, Theogony +var fragment16 = "μουσάων Ἑλικωνιάδων ἀρχώμεθ᾽ ἀείδειν"; +var rope16 = fragment16; +for (var i = 0; i < 10; i++) // 1024 repetitions + rope16 = rope16 + rope16; +assertEq(tByteSize(rope16), s(16, 24)); +let matches16 = rope16.match(/(Ἑλικωνιάδων ἀρχώμεθ᾽)/); +assertEq(tByteSize(rope16), s(16 + 131072, 24 + 131072)); + +// Latin-1 and char16_t dependent strings. +assertEq(tByteSize(rope8.substr(1000, 2000)), s(16, 24)); +assertEq(tByteSize(rope16.substr(1000, 2000)), s(16, 24)); +assertEq(tByteSize(matches8[0]), s(16, 24)); +assertEq(tByteSize(matches8[1]), s(16, 24)); +assertEq(tByteSize(matches16[0]), s(16, 24)); +assertEq(tByteSize(matches16[1]), s(16, 24)); + +// Test extensible strings. +// +// Appending another copy of the fragment should yield another rope. +// +// Flatting that should turn the original rope into a dependent string, and +// yield a new linear string, of the some size as the original. +rope16a = rope16 + fragment16; +assertEq(tByteSize(rope16a), s(16, 24)); +rope16a.match(/x/, function() { assertEq(true, false); }); +assertEq(tByteSize(rope16a), s(16 + 131072, 24 + 131072)); +assertEq(tByteSize(rope16), s(16, 24)); diff --git a/js/src/jit-test/tests/heap-analysis/pointerByteSize.js b/js/src/jit-test/tests/heap-analysis/pointerByteSize.js new file mode 100644 index 0000000000..617972deb0 --- /dev/null +++ b/js/src/jit-test/tests/heap-analysis/pointerByteSize.js @@ -0,0 +1,3 @@ +// Try out the pointerByteSize shell function. +var size = getBuildConfiguration()["pointer-byte-size"]; +assertEq(size == 4 || size == 8, true); diff --git a/js/src/jit-test/tests/ion/bug1155807.js b/js/src/jit-test/tests/ion/bug1155807.js new file mode 100644 index 0000000000..7dd3a6de5d --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1155807.js @@ -0,0 +1,15 @@ + +for (var i = 0; i < 2; i++) { + setJitCompilerOption("ion.warmup.trigger", 8) + function f(state) { + this.s = state + } + f.prototype.g = function(v, y) { + this.t + } + x = ['', ''] + j = new f(false) + x.filter(j.g, j) + x.filter(j.g, new f(false)) + j.__proto__ = {} +} diff --git a/js/src/jit-test/tests/ion/recover-arrays.js b/js/src/jit-test/tests/ion/recover-arrays.js index 23d5e3465a..9cf8651cae 100644 --- a/js/src/jit-test/tests/ion/recover-arrays.js +++ b/js/src/jit-test/tests/ion/recover-arrays.js @@ -1,8 +1,8 @@ // Ion eager fails the test below because we have not yet created any // template object in baseline before running the content of the top-level // function. -if (getJitCompilerOptions()["ion.warmup.trigger"] <= 20) - setJitCompilerOption("ion.warmup.trigger", 20); +if (getJitCompilerOptions()["ion.warmup.trigger"] <= 100) + setJitCompilerOption("ion.warmup.trigger", 100); // This function is used to force a bailout when it is inlined, and to recover // the frame which is inlining this function. diff --git a/js/src/jit-test/tests/ion/recover-objects.js b/js/src/jit-test/tests/ion/recover-objects.js index 68a339cf13..07a990b606 100644 --- a/js/src/jit-test/tests/ion/recover-objects.js +++ b/js/src/jit-test/tests/ion/recover-objects.js @@ -19,11 +19,11 @@ function inline_notSoEmpty1(a, b, c, d) { } var uceFault_notSoEmpty1 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty1')); function notSoEmpty1() { - var a = { v: i }; - var b = { v: 1 + a.v }; - var c = { v: 2 + b.v }; - var d = { v: 3 + c.v }; - var unused = { v: 4 + d.v }; + var a = { v: i, notunboxed: undefined }; + var b = { v: 1 + a.v, notunboxed: undefined }; + var c = { v: 2 + b.v, notunboxed: undefined }; + var d = { v: 3 + c.v, notunboxed: undefined }; + var unused = { v: 4 + d.v, notunboxed: undefined }; var res = inline_notSoEmpty1(a, b, c, d); if (uceFault_notSoEmpty1(i) || uceFault_notSoEmpty1(i)) assertEq(i, res.v); @@ -44,15 +44,15 @@ function notSoEmpty1() { // Check that we can recover objects with their content. function inline_notSoEmpty2(a, b, c, d) { "use strict"; - return { v: (a.v + b.v + c.v + d.v - 10) / 4 }; + return { v: (a.v + b.v + c.v + d.v - 10) / 4, notunboxed: undefined }; } var uceFault_notSoEmpty2 = eval(uneval(uceFault).replace('uceFault', 'uceFault_notSoEmpty2')); function notSoEmpty2(i) { - var a = { v: i }; - var b = { v: 1 + a.v }; - var c = { v: 2 + b.v }; - var d = { v: 3 + c.v }; - var unused = { v: 4 + d.v }; + var a = { v: i, notunboxed: undefined }; + var b = { v: 1 + a.v, notunboxed: undefined }; + var c = { v: 2 + b.v, notunboxed: undefined }; + var d = { v: 3 + c.v, notunboxed: undefined }; + var unused = { v: 4 + d.v, notunboxed: undefined }; var res = inline_notSoEmpty2(a, b, c, d); if (uceFault_notSoEmpty2(i) || uceFault_notSoEmpty2(i)) assertEq(i, res.v); @@ -70,13 +70,13 @@ function notSoEmpty2(i) { var argFault_observeArg = function (i) { if (i > 98) return inline_observeArg.arguments[0]; - return { test : i }; + return { test : i, notunboxed: undefined }; }; function inline_observeArg(obj, i) { return argFault_observeArg(i); } function observeArg(i) { - var obj = { test: i }; + var obj = { test: i, notunboxed: undefined }; var res = inline_observeArg(obj, i); assertEq(res.test, i); assertRecoveredOnBailout(obj, true); @@ -84,7 +84,7 @@ function observeArg(i) { // Check case where one successor can have multiple times the same predecessor. function complexPhi(i) { - var obj = { test: i }; + var obj = { test: i, notunboxed: undefined }; switch (i) { // TableSwitch case 0: obj.test = 0; break; case 1: obj.test = 1; break; @@ -103,12 +103,12 @@ function complexPhi(i) { function withinIf(i) { var x = undefined; if (i % 2 == 0) { - let obj = { foo: i }; + let obj = { foo: i, notunboxed: undefined }; x = obj.foo; assertRecoveredOnBailout(obj, true); obj = undefined; } else { - let obj = { bar: i }; + let obj = { bar: i, notunboxed: undefined }; x = obj.bar; assertRecoveredOnBailout(obj, true); obj = undefined; @@ -118,7 +118,7 @@ function withinIf(i) { // Check case where one successor can have multiple times the same predecessor. function unknownLoad(i) { - var obj = { foo: i }; + var obj = { foo: i, notunboxed: undefined }; assertEq(obj.bar, undefined); // Unknown properties are using GetPropertyCache. assertRecoveredOnBailout(obj, false); @@ -132,7 +132,8 @@ function dynamicSlots(i) { p11: i + 11, p12: i + 12, p13: i + 13, p14: i + 14, p15: i + 15, p16: i + 16, p17: i + 17, p18: i + 18, p19: i + 19, p20: i + 20, p21: i + 21, p22: i + 22, p23: i + 23, p24: i + 24, p25: i + 25, p26: i + 26, p27: i + 27, p28: i + 28, p29: i + 29, p30: i + 30, p31: i + 31, p32: i + 32, p33: i + 33, p34: i + 34, p35: i + 35, p36: i + 36, p37: i + 37, p38: i + 38, p39: i + 39, p40: i + 40, - p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50 + p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50, + notunboxed: undefined }; // Add a function call to capture a resumepoint at the end of the call or // inside the inlined block, such as the bailout does not rewind to the @@ -147,6 +148,7 @@ function Point(x, y) { this.x = x; this.y = y; + this.notUnboxed = undefined; } function createThisWithTemplate(i) diff --git a/js/src/jit-test/tests/proxy/proxy-array-length.js b/js/src/jit-test/tests/proxy/proxy-array-length.js new file mode 100644 index 0000000000..11045aac0f --- /dev/null +++ b/js/src/jit-test/tests/proxy/proxy-array-length.js @@ -0,0 +1,4 @@ +var a = [1, 2, 3]; +var p = new Proxy(a, {}); +assertEq(p.length, 3); +assertEq(JSON.stringify(p), "[1,2,3]"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js index 1588dd15b1..7af839477d 100644 --- a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js @@ -7,8 +7,8 @@ var p = new Proxy({}, { }); var desc = Object.getOwnPropertyDescriptor(p, "x"); assertDeepEq(desc, { - configurable: true, - enumerable: false, value: undefined, - writable: false + writable: false, + enumerable: false, + configurable: true }); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js b/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js index c4715f9c35..3646fc69b3 100644 --- a/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js +++ b/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js @@ -26,14 +26,6 @@ assertEq("z" in obj, false); // [].sort() mutates its operand only by doing strict [[Set]] calls. var arr = ["not", "already", "in", "order"]; var p2 = new Proxy(arr, { - get(target, key, receiver) { - if (key === "length" && new Proxy([1], {}).length === 1) { - throw new Error("Congratulations! You have fixed bug 895223 at last! " + - "Please delete this whole get() method which is a " + - "workaround for that bug."); - } - return target[key]; - }, set(target, key, value, receiver) { return false; } }); assertThrowsInstanceOf(() => p2.sort(), TypeError); diff --git a/js/src/jit-test/tests/proxy/testIndirectProxyGetOwnPropertyDescriptor.js b/js/src/jit-test/tests/proxy/testIndirectProxyGetOwnPropertyDescriptor.js index da26c08dd7..923d0a0f6a 100644 --- a/js/src/jit-test/tests/proxy/testIndirectProxyGetOwnPropertyDescriptor.js +++ b/js/src/jit-test/tests/proxy/testIndirectProxyGetOwnPropertyDescriptor.js @@ -5,8 +5,8 @@ load(libdir + "asserts.js"); var p = Proxy.create({ getOwnPropertyDescriptor() { return {}; } }); var desc = Object.getOwnPropertyDescriptor(p, "x"); assertDeepEq(desc, { - configurable: false, - enumerable: false, value: undefined, - writable: false + writable: false, + enumerable: false, + configurable: false }); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 77a105dfa2..8f69c59b98 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1714,7 +1714,7 @@ BaselineCompiler::emit_JSOP_CASE() masm.branch32(Assembler::Equal, payload, Imm32(0), &done); { // Pop the switch value if the case matches. - masm.addPtr(Imm32(sizeof(Value)), StackPointer); + masm.addToStackPtr(Imm32(sizeof(Value))); masm.jump(labelOf(target)); } masm.bind(&done); @@ -1740,23 +1740,15 @@ BaselineCompiler::emit_JSOP_NEWARRAY() frame.syncStack(0); uint32_t length = GET_UINT24(pc); - RootedObjectGroup group(cx); - if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) { - group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); - if (!group) - return false; - } - // Pass length in R0, group in R1. + // Pass length in R0. masm.move32(Imm32(length), R0.scratchReg()); - masm.movePtr(ImmGCPtr(group), R1.scratchReg()); - ArrayObject* templateObject = NewDenseUnallocatedArray(cx, length, NullPtr(), TenuredObject); - if (!templateObject) + ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); + if (!group) return false; - templateObject->setGroup(group); - ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); + ICNewArray_Fallback::Compiler stubCompiler(cx, group); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; @@ -1764,6 +1756,12 @@ BaselineCompiler::emit_JSOP_NEWARRAY() return true; } +bool +BaselineCompiler::emit_JSOP_SPREADCALLARRAY() +{ + return emit_JSOP_NEWARRAY(); +} + typedef JSObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap); const VMFunction jit::NewArrayCopyOnWriteInfo = FunctionInfo(js::NewDenseCopyOnWriteArray); @@ -1829,24 +1827,15 @@ BaselineCompiler::emit_JSOP_NEWINIT() frame.syncStack(0); JSProtoKey key = JSProtoKey(GET_UINT8(pc)); - RootedObjectGroup group(cx); - if (!ObjectGroup::useSingletonForAllocationSite(script, pc, key)) { - group = ObjectGroup::allocationSiteGroup(cx, script, pc, key); + if (key == JSProto_Array) { + // Pass length in R0. + masm.move32(Imm32(0), R0.scratchReg()); + + ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; - } - if (key == JSProto_Array) { - // Pass length in R0, group in R1. - masm.move32(Imm32(0), R0.scratchReg()); - masm.movePtr(ImmGCPtr(group), R1.scratchReg()); - - ArrayObject* templateObject = NewDenseUnallocatedArray(cx, 0, NullPtr(), TenuredObject); - if (!templateObject) - return false; - templateObject->setGroup(group); - - ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); + ICNewArray_Fallback::Compiler stubCompiler(cx, group); if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) return false; } else { @@ -3010,14 +2999,24 @@ BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE() typedef bool (*PopBlockScopeFn)(JSContext*, BaselineFrame*); static const VMFunction PopBlockScopeInfo = FunctionInfo(jit::PopBlockScope); +typedef bool (*DebugLeaveThenPopBlockScopeFn)(JSContext*, BaselineFrame*, jsbytecode*); +static const VMFunction DebugLeaveThenPopBlockScopeInfo = + FunctionInfo(jit::DebugLeaveThenPopBlockScope); + bool BaselineCompiler::emit_JSOP_POPBLOCKSCOPE() { prepareVMCall(); masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); - pushArg(R0.scratchReg()); + if (compileDebugInstrumentation_) { + pushArg(ImmPtr(pc)); + pushArg(R0.scratchReg()); + return callVM(DebugLeaveThenPopBlockScopeInfo); + } + + pushArg(R0.scratchReg()); return callVM(PopBlockScopeInfo); } @@ -3025,14 +3024,24 @@ typedef bool (*FreshenBlockScopeFn)(JSContext *, BaselineFrame *); static const VMFunction FreshenBlockScopeInfo = FunctionInfo(jit::FreshenBlockScope); +typedef bool (*DebugLeaveThenFreshenBlockScopeFn)(JSContext*, BaselineFrame*, jsbytecode*); +static const VMFunction DebugLeaveThenFreshenBlockScopeInfo = + FunctionInfo(jit::DebugLeaveThenFreshenBlockScope); + bool BaselineCompiler::emit_JSOP_FRESHENBLOCKSCOPE() { prepareVMCall(); masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); - pushArg(R0.scratchReg()); + if (compileDebugInstrumentation_) { + pushArg(ImmPtr(pc)); + pushArg(R0.scratchReg()); + return callVM(DebugLeaveThenFreshenBlockScopeInfo); + } + + pushArg(R0.scratchReg()); return callVM(FreshenBlockScopeInfo); } diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index ddc9646d42..1cb5c5f274 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -93,6 +93,7 @@ namespace jit { _(JSOP_BITNOT) \ _(JSOP_NEG) \ _(JSOP_NEWARRAY) \ + _(JSOP_SPREADCALLARRAY) \ _(JSOP_NEWARRAY_COPYONWRITE) \ _(JSOP_INITELEM_ARRAY) \ _(JSOP_NEWOBJECT) \ diff --git a/js/src/jit/BaselineDebugModeOSR.cpp b/js/src/jit/BaselineDebugModeOSR.cpp index a935a3d53c..599856315f 100644 --- a/js/src/jit/BaselineDebugModeOSR.cpp +++ b/js/src/jit/BaselineDebugModeOSR.cpp @@ -1113,7 +1113,7 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrame CodeOffsetLabel noFrameRegPopOffset(masm.currentOffset()); // Record the stack pointer for syncing. - masm.movePtr(StackPointer, syncedStackStart); + masm.moveStackPtrTo(syncedStackStart); masm.push(ReturnReg); masm.push(BaselineFrameReg); @@ -1132,7 +1132,7 @@ JitRuntime::generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrame masm.pop(BaselineFrameReg); masm.pop(ReturnReg); masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp); - masm.addPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)), StackPointer); + masm.addToStackPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust))); // Emit two tails for the case of returning from a callVM and all other // cases, as the state we need to restore differs depending on the case. diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 0f43346ae4..e643183477 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -165,57 +165,6 @@ ICStub::updateCode(JitCode* code) stubCode_ = code->raw(); } -void -ReceiverGuard::trace(JSTracer* trc) -{ - if (shape_) - TraceEdge(trc, &shape_, "receiver_guard_shape"); - else - TraceEdge(trc, &group_, "receiver_guard_group"); -} - -ReceiverGuard::StackGuard::StackGuard(JSObject* obj) - : group(nullptr), shape(nullptr) -{ - if (obj) { - if (obj->is()) { - group = obj->group(); - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) - shape = expando->lastProperty(); - } else if (obj->is()) { - group = obj->group(); - } else { - shape = obj->maybeShape(); - } - } -} - -ReceiverGuard::StackGuard::StackGuard(ObjectGroup* group, Shape* shape) - : group(group), shape(shape) -{ - if (group) { - if (IsTypedObjectClass(group->clasp())) - this->shape = nullptr; - else if (group->clasp() != &UnboxedPlainObject::class_) - this->group = nullptr; - } -} - -/* static */ int32_t -ReceiverGuard::keyBits(JSObject* obj) -{ - if (obj->is()) { - // Both the group and shape need to be guarded for unboxed plain objects. - return obj->as().maybeExpando() ? 0 : 1; - } - if (obj->is()) { - // Only the group needs to be guarded for typed objects. - return 2; - } - // Other objects only need the shape to be guarded. - return 3; -} - /* static */ void ICStub::trace(JSTracer* trc) { @@ -298,22 +247,28 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape"); break; } + case ICStub::GetElem_UnboxedArray: { + ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray(); + TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group"); + break; + } case ICStub::GetElem_TypedArray: { ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray(); TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape"); break; } - case ICStub::SetElem_Dense: { - ICSetElem_Dense* setElemStub = toSetElem_Dense(); - TraceEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); + case ICStub::SetElem_DenseOrUnboxedArray: { + ICSetElem_DenseOrUnboxedArray* setElemStub = toSetElem_DenseOrUnboxedArray(); + if (setElemStub->shape()) + TraceEdge(trc, &setElemStub->shape(), "baseline-getelem-dense-shape"); TraceEdge(trc, &setElemStub->group(), "baseline-setelem-dense-group"); break; } - case ICStub::SetElem_DenseAdd: { - ICSetElem_DenseAdd* setElemStub = toSetElem_DenseAdd(); + case ICStub::SetElem_DenseOrUnboxedArrayAdd: { + ICSetElem_DenseOrUnboxedArrayAdd* setElemStub = toSetElem_DenseOrUnboxedArrayAdd(); TraceEdge(trc, &setElemStub->group(), "baseline-setelem-denseadd-group"); - JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4); + JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4); switch (setElemStub->protoChainDepth()) { case 0: setElemStub->toImpl<0>()->traceShapes(trc); break; @@ -560,7 +515,9 @@ ICStub::trace(JSTracer* trc) } case ICStub::NewArray_Fallback: { ICNewArray_Fallback* stub = toNewArray_Fallback(); - TraceEdge(trc, &stub->templateObject(), "baseline-newarray-template"); + if (stub->templateObject()) + TraceEdge(trc, &stub->templateObject(), "baseline-newarray-template"); + TraceEdge(trc, &stub->templateGroup(), "baseline-newarray-template-group"); break; } case ICStub::NewObject_Fallback: { @@ -1496,9 +1453,8 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H RootedId id(cx); switch(stub->kind()) { - case ICStub::SetElem_Dense: - case ICStub::SetElem_DenseAdd: { - MOZ_ASSERT(obj->isNative()); + case ICStub::SetElem_DenseOrUnboxedArray: + case ICStub::SetElem_DenseOrUnboxedArrayAdd: { id = JSID_VOID; AddTypePropertyId(cx, obj, id, value); break; @@ -1708,20 +1664,37 @@ ICThis_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // static bool -DoNewArray(JSContext* cx, ICNewArray_Fallback* stub, uint32_t length, - HandleObjectGroup group, MutableHandleValue res) +DoNewArray(JSContext* cx, BaselineFrame* frame, ICNewArray_Fallback* stub, uint32_t length, + MutableHandleValue res) { FallbackICSpew(cx, stub, "NewArray"); - JSObject* obj = NewDenseArray(cx, length, group, NewArray_FullyAllocating); - if (!obj) - return false; + RootedObject obj(cx); + if (stub->templateObject()) { + RootedObject templateObject(cx, stub->templateObject()); + obj = NewArrayOperationWithTemplate(cx, templateObject); + if (!obj) + return false; + } else { + RootedScript script(cx, frame->script()); + jsbytecode* pc = stub->icEntry()->pc(script); + obj = NewArrayOperation(cx, script, pc, length); + if (!obj) + return false; + + if (obj && !obj->isSingleton() && !obj->group()->maybePreliminaryObjects()) { + JSObject* templateObject = NewArrayOperation(cx, script, pc, length, TenuredObject); + if (!templateObject) + return false; + stub->setTemplateObject(templateObject); + } + } res.setObject(*obj); return true; } -typedef bool(*DoNewArrayFn)(JSContext*, ICNewArray_Fallback*, uint32_t, HandleObjectGroup, +typedef bool(*DoNewArrayFn)(JSContext*, BaselineFrame*, ICNewArray_Fallback*, uint32_t, MutableHandleValue); static const VMFunction DoNewArrayInfo = FunctionInfo(DoNewArray, TailCall); @@ -1730,9 +1703,9 @@ ICNewArray_Fallback::Compiler::generateStubCode(MacroAssembler& masm) { EmitRestoreTailCallReg(masm); - masm.push(R1.scratchReg()); // type masm.push(R0.scratchReg()); // length masm.push(BaselineStubReg); // stub. + masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); return tailCallVM(DoNewArrayInfo, masm); } @@ -3404,6 +3377,9 @@ CheckHasNoSuchProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, } else if (curObj->is()) { if (curObj->as().containsUnboxedOrExpandoProperty(cx, NameToId(name))) return false; + } else if (curObj->is()) { + if (name == cx->names().length) + return false; } else if (curObj->is()) { if (curObj->as().typeDescr().hasProperty(cx->names(), NameToId(name))) return false; @@ -3432,8 +3408,12 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy=false) if (!isDOMProxy && !obj->isNative()) { if (obj == holder) return false; - if (!obj->is() && !obj->is()) + if (!obj->is() && + !obj->is() && + !obj->is()) + { return false; + } } // Don't handle objects which require a prototype guard. This should @@ -4025,6 +4005,16 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key) return false; } +static bool +IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key) +{ + if (!obj->isNative() && !obj->is()) + return false; + if (key.isInt32() && key.toInt32() >= 0 && !IsAnyTypedArray(obj.get())) + return true; + return false; +} + static bool TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_Fallback* stub, HandleValue lhs, HandleValue rhs, HandleValue res) @@ -4112,6 +4102,19 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ script = rootedScript; } + // Check for UnboxedArray[int] accesses. + if (obj->is() && rhs.isInt32() && rhs.toInt32() >= 0) { + JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub"); + ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), + obj->group()); + ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script)); + if (!unboxedStub) + return false; + + stub->addNewStub(unboxedStub); + return true; + } + // Check for TypedArray[int] => Number and TypedObject[int] => Number accesses. if ((IsAnyTypedArray(obj.get()) || IsPrimitiveArrayTypedObject(obj)) && rhs.isNumber() && @@ -4743,6 +4746,54 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) return true; } +// +// GetElem_UnboxedArray +// + +bool +ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + masm.branchTestInt32(Assembler::NotEqual, R1, &failure); + + AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); + Register scratchReg = regs.takeAny(); + + // Unbox R0 and group guard. + Register obj = masm.extractObject(R0, ExtractTemp0); + masm.loadPtr(Address(BaselineStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg); + masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); + + // Unbox key. + Register key = masm.extractInt32(R1, ExtractTemp1); + + // Bounds check. + masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), + scratchReg); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); + masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); + + // Load obj->elements. + masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); + + // Load value. + size_t width = UnboxedTypeSize(elementType_); + BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width)); + masm.loadUnboxedProperty(addr, elementType_, R0); + + // Only monitor the result if its type might change. + if (elementType_ == JSVAL_TYPE_OBJECT) + EmitEnterTypeMonitorIC(masm); + else + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + // // GetElem_TypedArray // @@ -5044,42 +5095,48 @@ ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler& masm) // static bool -SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd* stub, NativeObject* obj) +SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj) { - size_t numShapes = stub->protoChainDepth() + 1; - for (size_t i = 0; i < numShapes; i++) { - static const size_t MAX_DEPTH = ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH; - if (obj->lastProperty() != stub->toImplUnchecked()->shape(i)) + static const size_t MAX_DEPTH = ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH; + ICSetElem_DenseOrUnboxedArrayAddImpl* nstub = stub->toImplUnchecked(); + + if (obj->maybeShape() != nstub->shape(0)) + return false; + + JSObject* proto = obj->getProto(); + for (size_t i = 0; i < stub->protoChainDepth(); i++) { + if (!proto->isNative()) return false; - JSObject* proto = obj->getProto(); + if (proto->as().lastProperty() != nstub->shape(i + 1)) + return false; + proto = obj->getProto(); if (!proto) { - if (i != numShapes - 1) + if (i != stub->protoChainDepth() - 1) return false; break; } - if (!proto->isNative()) - return false; - obj = &proto->as(); } return true; } static bool -DenseSetElemStubExists(JSContext* cx, ICStub::Kind kind, ICSetElem_Fallback* stub, HandleNativeObject obj) +DenseOrUnboxedArraySetElemStubExists(JSContext* cx, ICStub::Kind kind, + ICSetElem_Fallback* stub, HandleObject obj) { - MOZ_ASSERT(kind == ICStub::SetElem_Dense || kind == ICStub::SetElem_DenseAdd); + MOZ_ASSERT(kind == ICStub::SetElem_DenseOrUnboxedArray || + kind == ICStub::SetElem_DenseOrUnboxedArrayAdd); for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) { - if (kind == ICStub::SetElem_Dense && iter->isSetElem_Dense()) { - ICSetElem_Dense* dense = iter->toSetElem_Dense(); - if (obj->lastProperty() == dense->shape() && obj->getGroup(cx) == dense->group()) + if (kind == ICStub::SetElem_DenseOrUnboxedArray && iter->isSetElem_DenseOrUnboxedArray()) { + ICSetElem_DenseOrUnboxedArray* nstub = iter->toSetElem_DenseOrUnboxedArray(); + if (obj->maybeShape() == nstub->shape() && obj->getGroup(cx) == nstub->group()) return true; } - if (kind == ICStub::SetElem_DenseAdd && iter->isSetElem_DenseAdd()) { - ICSetElem_DenseAdd* dense = iter->toSetElem_DenseAdd(); - if (obj->getGroup(cx) == dense->group() && SetElemDenseAddHasSameShapes(dense, obj)) + if (kind == ICStub::SetElem_DenseOrUnboxedArrayAdd && iter->isSetElem_DenseOrUnboxedArrayAdd()) { + ICSetElem_DenseOrUnboxedArrayAdd* nstub = iter->toSetElem_DenseOrUnboxedArrayAdd(); + if (obj->getGroup(cx) == nstub->group() && SetElemAddHasSameShapes(nstub, obj)) return true; } } @@ -5118,13 +5175,29 @@ RemoveExistingTypedArraySetElemStub(JSContext* cx, ICSetElem_Fallback* stub, Han return false; } -static bool -CanOptimizeDenseSetElem(NativeObject* obj, uint32_t index, - Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength, - bool* isAddingCaseOut, size_t* protoDepthOut) +static size_t +SetElemObjectInitializedLength(JSObject *obj) { - uint32_t initLength = obj->getDenseInitializedLength(); - uint32_t capacity = obj->getDenseCapacity(); + if (obj->isNative()) + return obj->as().getDenseInitializedLength(); + return obj->as().initializedLength(); +} + +static size_t +SetElemObjectCapacity(JSObject *obj) +{ + if (obj->isNative()) + return obj->as().getDenseCapacity(); + return obj->as().capacity(); +} + +static bool +CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, + Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength, + bool* isAddingCaseOut, size_t* protoDepthOut) +{ + uint32_t initLength = SetElemObjectInitializedLength(obj); + uint32_t capacity = SetElemObjectCapacity(obj); *isAddingCaseOut = false; *protoDepthOut = 0; @@ -5133,7 +5206,7 @@ CanOptimizeDenseSetElem(NativeObject* obj, uint32_t index, if (initLength < oldInitLength || capacity < oldCapacity) return false; - Shape* shape = obj->lastProperty(); + Shape* shape = obj->maybeShape(); // Cannot optimize if the shape changed. if (oldShape != shape) @@ -5148,7 +5221,7 @@ CanOptimizeDenseSetElem(NativeObject* obj, uint32_t index, return false; // Cannot optimize if the value at position after the set is a hole. - if (!obj->containsDenseElement(index)) + if (obj->isNative() && !obj->as().containsDenseElement(index)) return false; // At this point, if we know that the initLength did not change, then @@ -5167,26 +5240,20 @@ CanOptimizeDenseSetElem(NativeObject* obj, uint32_t index, // either directly, or via a prototype, or via the target object for a prototype // which is a proxy, that handles a particular integer write. // Scan the prototype and shape chain to make sure that this is not the case. - JSObject* curObj = obj; + if (obj->isIndexed()) + return false; + JSObject* curObj = obj->getProto(); while (curObj) { - // Ensure object is native. - if (!curObj->isNative()) + ++*protoDepthOut; + if (!curObj->isNative() || curObj->isIndexed()) return false; - - // Ensure all indexed properties are stored in dense elements. - if (curObj->isIndexed()) - return false; - curObj = curObj->getProto(); - if (curObj) - ++*protoDepthOut; } - if (*protoDepthOut > ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH) + if (*protoDepthOut > ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH) return false; *isAddingCaseOut = true; - return true; } @@ -5252,49 +5319,57 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_ } // Try to generate new stubs. - if (IsNativeDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) - { - HandleNativeObject nobj = obj.as(); - + if (IsNativeOrUnboxedDenseElementAccess(obj, index) && !rhs.isMagic(JS_ELEMENTS_HOLE)) { bool addingCase; size_t protoDepth; - if (CanOptimizeDenseSetElem(nobj, index.toInt32(), - oldShape, oldCapacity, oldInitLength, - &addingCase, &protoDepth)) + if (CanOptimizeDenseOrUnboxedArraySetElem(obj, index.toInt32(), + oldShape, oldCapacity, oldInitLength, + &addingCase, &protoDepth)) { - RootedShape shape(cx, nobj->lastProperty()); + RootedShape shape(cx, obj->maybeShape()); RootedObjectGroup group(cx, obj->getGroup(cx)); if (!group) return false; - if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, nobj)) { - JitSpew(JitSpew_BaselineIC, - " Generating SetElem_DenseAdd stub " - "(shape=%p, group=%p, protoDepth=%u)", - nobj->lastProperty(), group.get(), protoDepth); - ICSetElemDenseAddCompiler compiler(cx, obj, protoDepth); - ICUpdatedStub* denseStub = compiler.getStub(compiler.getStubSpace(script)); - if (!denseStub) - return false; - if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) - return false; - - stub->addNewStub(denseStub); - } else if (!addingCase && - !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, nobj)) + if (addingCase && + !DenseOrUnboxedArraySetElemStubExists(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, + stub, obj)) { JitSpew(JitSpew_BaselineIC, - " Generating SetElem_Dense stub (shape=%p, group=%p)", - nobj->lastProperty(), group.get()); - ICSetElem_Dense::Compiler compiler(cx, shape, group); - ICUpdatedStub* denseStub = compiler.getStub(compiler.getStubSpace(script)); - if (!denseStub) + " Generating SetElem_DenseOrUnboxedArrayAdd stub " + "(shape=%p, group=%p, protoDepth=%u)", + shape.get(), group.get(), protoDepth); + ICSetElemDenseOrUnboxedArrayAddCompiler compiler(cx, obj, protoDepth); + ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) return false; - if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) + if (compiler.needsUpdateStubs() && + !newStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) + { return false; + } - stub->addNewStub(denseStub); + stub->addNewStub(newStub); + } else if (!addingCase && + !DenseOrUnboxedArraySetElemStubExists(cx, + ICStub::SetElem_DenseOrUnboxedArray, + stub, obj)) + { + JitSpew(JitSpew_BaselineIC, + " Generating SetElem_DenseOrUnboxedArray stub (shape=%p, group=%p)", + shape.get(), group.get()); + ICSetElem_DenseOrUnboxedArray::Compiler compiler(cx, shape, group); + ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + if (compiler.needsUpdateStubs() && + !newStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs)) + { + return false; + } + + stub->addNewStub(newStub); } } @@ -5404,125 +5479,163 @@ BaselineScript::noteArrayWriteHole(uint32_t pcOffset) } // -// SetElem_Dense +// SetElem_DenseOrUnboxedArray // +template +void +EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type) +{ + if (type == JSVAL_TYPE_OBJECT) + EmitPreBarrier(masm, address, MIRType_Object); + else if (type == JSVAL_TYPE_STRING) + EmitPreBarrier(masm, address, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); +} + bool -ICSetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) +ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) { // R0 = object // R1 = key // Stack = { ... rhs-value, ? } - Label failure; - Label failureUnstow; + Label failure, failurePopR0; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); Register scratchReg = regs.takeAny(); - // Unbox R0 and guard on its shape. + // Unbox R0 and guard on its group and, if this is a native access, its shape. Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfShape()), scratchReg); - masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfGroup()), + scratchReg); + masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); + if (unboxedType_ == JSVAL_TYPE_MAGIC) { + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArray::offsetOfShape()), + scratchReg); + masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); + } - // Stow both R0 and R1 (object and key) - // But R0 and R1 still hold their values. - EmitStowICValues(masm, 2); + if (needsUpdateStubs()) { + // Stow both R0 and R1 (object and key) + // But R0 and R1 still hold their values. + EmitStowICValues(masm, 2); - // We may need to free up some registers. - regs = availableGeneralRegs(0); - regs.take(R0); + // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } + // Load rhs-value into R0 + masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); - // Guard that the object group matches. - Register typeReg = regs.takeAny(); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfGroup()), typeReg); - masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfGroup()), typeReg, - &failureUnstow); - regs.add(typeReg); + // Call the type-update stub. + if (!callTypeUpdateIC(masm, sizeof(Value))) + return false; - // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } - // Load rhs-value in to R0 - masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); + // Unstow R0 and R1 (object and key) + EmitUnstowICValues(masm, 2); - // Call the type-update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; + // Restore object. + obj = masm.extractObject(R0, ExtractTemp0); - // Unstow R0 and R1 (object and key) - EmitUnstowICValues(masm, 2); + // Trigger post barriers here on the value being written. Fields which + // objects can be written to also need update stubs. + masm.Push(R1); + masm.loadValue(Address(BaselineStackReg, sizeof(Value) + ICStackValueOffset), R1); - // Reset register set. - regs = availableGeneralRegs(2); - scratchReg = regs.takeAny(); + LiveGeneralRegisterSet saveRegs; + saveRegs.add(R0); + saveRegs.addUnchecked(obj); + saveRegs.add(BaselineStubReg); + emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs); - // Unbox object and key. - obj = masm.extractObject(R0, ExtractTemp0); + masm.Pop(R1); + } + + // Unbox key. Register key = masm.extractInt32(R1, ExtractTemp1); - // Load obj->elements in scratchReg. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); - - // Bounds check. - Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure); - - // Hole check. - BaseIndex element(scratchReg, key, TimesEight); - masm.branchTestMagic(Assembler::Equal, element, &failure); - - // Perform a single test to see if we either need to convert double - // elements or clone the copy on write elements in the object. - Label noSpecialHandling; - Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); - masm.branchTest32(Assembler::Zero, elementsFlags, - Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS | - ObjectElements::COPY_ON_WRITE), - &noSpecialHandling); - - // Fail if we need to clone copy on write elements. - masm.branchTest32(Assembler::NonZero, elementsFlags, - Imm32(ObjectElements::COPY_ON_WRITE), - &failure); - - // Failure is not possible now. Free up registers. - regs.add(R0); - regs.add(R1); - regs.takeUnchecked(obj); - regs.takeUnchecked(key); Address valueAddr(BaselineStackReg, ICStackValueOffset); - // We need to convert int32 values being stored into doubles. In this case - // the heap typeset is guaranteed to contain both int32 and double, so it's - // okay to store a double. Note that double arrays are only created by - // IonMonkey, so if we have no floating-point support Ion is disabled and - // there should be no double arrays. - if (cx->runtime()->jitSupportsFloatingPoint) - masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling); - else - masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); + if (unboxedType_ == JSVAL_TYPE_MAGIC) { + // Set element on a native object. - masm.bind(&noSpecialHandling); + // Load obj->elements in scratchReg. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); - // Don't overwrite R0 becuase |obj| might overlap with it, and it's needed - // for post-write barrier later. - ValueOperand tmpVal = regs.takeAnyValue(); - masm.loadValue(valueAddr, tmpVal); - EmitPreBarrier(masm, element, MIRType_Value); - masm.storeValue(tmpVal, element); - regs.add(key); - if (cx->runtime()->gc.nursery.exists()) { - Register r = regs.takeAny(); - LiveGeneralRegisterSet saveRegs; - emitPostWriteBarrierSlot(masm, obj, tmpVal, r, saveRegs); - regs.add(r); + // Bounds check. + Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure); + + // Hole check. + BaseIndex element(scratchReg, key, TimesEight); + masm.branchTestMagic(Assembler::Equal, element, &failure); + + // Perform a single test to see if we either need to convert double + // elements or clone the copy on write elements in the object. + Label noSpecialHandling; + Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); + masm.branchTest32(Assembler::Zero, elementsFlags, + Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS | + ObjectElements::COPY_ON_WRITE), + &noSpecialHandling); + + // Fail if we need to clone copy on write elements. + masm.branchTest32(Assembler::NonZero, elementsFlags, + Imm32(ObjectElements::COPY_ON_WRITE), + &failure); + + // Failure is not possible now. Free up registers. + regs.add(R0); + regs.add(R1); + regs.takeUnchecked(obj); + regs.takeUnchecked(key); + + // We need to convert int32 values being stored into doubles. In this case + // the heap typeset is guaranteed to contain both int32 and double, so it's + // okay to store a double. Note that double arrays are only created by + // IonMonkey, so if we have no floating-point support Ion is disabled and + // there should be no double arrays. + if (cx->runtime()->jitSupportsFloatingPoint) + masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling); + else + masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); + + masm.bind(&noSpecialHandling); + + ValueOperand tmpVal = regs.takeAnyValue(); + masm.loadValue(valueAddr, tmpVal); + EmitPreBarrier(masm, element, MIRType_Value); + masm.storeValue(tmpVal, element); + } else { + // Set element on an unboxed array. + + // Bounds check. + Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + masm.load32(initLength, scratchReg); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); + masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); + + // Load obj->elements. + masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); + + // Compute the address being written to. + BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); + + EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_); + + masm.Push(R0); + masm.loadValue(valueAddr, R0); + masm.storeUnboxedProperty(address, unboxedType_, + ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); + masm.Pop(R0); } + EmitReturnFromIC(masm); - - // Failure case - fail but first unstow R0 and R1 - masm.bind(&failureUnstow); - EmitUnstowICValues(masm, 2); + if (failurePopR0.used()) { + masm.bind(&failurePopR0); + masm.Pop(R0); + } // Failure case - jump to next stub masm.bind(&failure); @@ -5544,20 +5657,20 @@ GetProtoShapes(JSObject* obj, size_t protoChainDepth, AutoShapeVector* shapes) } // -// SetElem_DenseAdd +// SetElem_DenseOrUnboxedArrayAdd // ICUpdatedStub* -ICSetElemDenseAddCompiler::getStub(ICStubSpace* space) +ICSetElemDenseOrUnboxedArrayAddCompiler::getStub(ICStubSpace* space) { AutoShapeVector shapes(cx); - if (!shapes.append(obj_->as().lastProperty())) + if (!shapes.append(obj_->maybeShape())) return nullptr; if (!GetProtoShapes(obj_, protoChainDepth_, &shapes)) return nullptr; - JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4); + JS_STATIC_ASSERT(ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH == 4); ICUpdatedStub* stub = nullptr; switch (protoChainDepth_) { @@ -5574,24 +5687,28 @@ ICSetElemDenseAddCompiler::getStub(ICStubSpace* space) } bool -ICSetElemDenseAddCompiler::generateStubCode(MacroAssembler& masm) +ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) { // R0 = object // R1 = key // Stack = { ... rhs-value, ? } - Label failure; - Label failureUnstow; + Label failure, failurePopR0, failureUnstow; masm.branchTestObject(Assembler::NotEqual, R0, &failure); masm.branchTestInt32(Assembler::NotEqual, R1, &failure); AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); Register scratchReg = regs.takeAny(); - // Unbox R0 and guard on its shape. + // Unbox R0 and guard on its group and, if this is a native access, its shape. Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAddImpl<0>::offsetOfShape(0)), + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArrayAdd::offsetOfGroup()), scratchReg); - masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); + masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); + if (unboxedType_ == JSVAL_TYPE_MAGIC) { + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArrayAddImpl<0>::offsetOfShape(0)), + scratchReg); + masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure); + } // Stow both R0 and R1 (object and key) // But R0 and R1 still hold their values. @@ -5600,110 +5717,160 @@ ICSetElemDenseAddCompiler::generateStubCode(MacroAssembler& masm) // We may need to free up some registers. regs = availableGeneralRegs(0); regs.take(R0); - - // Guard that the object group matches. - Register typeReg = regs.takeAny(); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAdd::offsetOfGroup()), typeReg); - masm.branchPtr(Assembler::NotEqual, Address(obj, JSObject::offsetOfGroup()), typeReg, - &failureUnstow); - regs.add(typeReg); + regs.take(scratchReg); // Shape guard objects on the proto chain. - scratchReg = regs.takeAny(); Register protoReg = regs.takeAny(); for (size_t i = 0; i < protoChainDepth_; i++) { masm.loadObjProto(i == 0 ? obj : protoReg, protoReg); masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failureUnstow); - masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAddImpl<0>::offsetOfShape(i + 1)), + masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseOrUnboxedArrayAddImpl<0>::offsetOfShape(i + 1)), scratchReg); masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratchReg, &failureUnstow); } regs.add(protoReg); regs.add(scratchReg); - // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } - // Load rhs-value in to R0 - masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); + if (needsUpdateStubs()) { + // Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR } + // Load rhs-value in to R0 + masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0); - // Call the type-update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; + // Call the type-update stub. + if (!callTypeUpdateIC(masm, sizeof(Value))) + return false; + } // Unstow R0 and R1 (object and key) EmitUnstowICValues(masm, 2); + // Restore object. + obj = masm.extractObject(R0, ExtractTemp0); + + if (needsUpdateStubs()) { + // Trigger post barriers here on the value being written. Fields which + // objects can be written to also need update stubs. + masm.Push(R1); + masm.loadValue(Address(BaselineStackReg, sizeof(Value) + ICStackValueOffset), R1); + + LiveGeneralRegisterSet saveRegs; + saveRegs.add(R0); + saveRegs.addUnchecked(obj); + saveRegs.add(BaselineStubReg); + emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs); + + masm.Pop(R1); + } + // Reset register set. regs = availableGeneralRegs(2); scratchReg = regs.takeAny(); - // Unbox obj and key. - obj = masm.extractObject(R0, ExtractTemp0); + // Unbox key. Register key = masm.extractInt32(R1, ExtractTemp1); - // Load obj->elements in scratchReg. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); - - // Bounds check (key == initLength) - Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, &failure); - - // Capacity check. - Address capacity(scratchReg, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::BelowOrEqual, capacity, key, &failure); - - // Check for copy on write elements. - Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); - masm.branchTest32(Assembler::NonZero, elementsFlags, - Imm32(ObjectElements::COPY_ON_WRITE), - &failure); - - // Failure is not possible now. Free up registers. - regs.add(R0); - regs.add(R1); - regs.takeUnchecked(obj); - regs.takeUnchecked(key); - - // Increment initLength before write. - masm.add32(Imm32(1), initLength); - - // If length is now <= key, increment length before write. - Label skipIncrementLength; - Address length(scratchReg, ObjectElements::offsetOfLength()); - masm.branch32(Assembler::Above, length, key, &skipIncrementLength); - masm.add32(Imm32(1), length); - masm.bind(&skipIncrementLength); - Address valueAddr(BaselineStackReg, ICStackValueOffset); - // Convert int32 values to double if convertDoubleElements is set. In this - // case the heap typeset is guaranteed to contain both int32 and double, so - // it's okay to store a double. - Label dontConvertDoubles; - masm.branchTest32(Assembler::Zero, elementsFlags, - Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS), - &dontConvertDoubles); - // Note that double arrays are only created by IonMonkey, so if we have no - // floating-point support Ion is disabled and there should be no double arrays. - if (cx->runtime()->jitSupportsFloatingPoint) - masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles); - else - masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); - masm.bind(&dontConvertDoubles); + if (unboxedType_ == JSVAL_TYPE_MAGIC) { + // Adding element to a native object. - // Write the value. No need for pre-barrier since we're not overwriting an old value. - ValueOperand tmpVal = regs.takeAnyValue(); - BaseIndex element(scratchReg, key, TimesEight); - masm.loadValue(valueAddr, tmpVal); - masm.storeValue(tmpVal, element); - regs.add(key); - if (cx->runtime()->gc.nursery.exists()) { - Register r = regs.takeAny(); - LiveGeneralRegisterSet saveRegs; - emitPostWriteBarrierSlot(masm, obj, tmpVal, r, saveRegs); - regs.add(r); + // Load obj->elements in scratchReg. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg); + + // Bounds check (key == initLength) + Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, &failure); + + // Capacity check. + Address capacity(scratchReg, ObjectElements::offsetOfCapacity()); + masm.branch32(Assembler::BelowOrEqual, capacity, key, &failure); + + // Check for copy on write elements. + Address elementsFlags(scratchReg, ObjectElements::offsetOfFlags()); + masm.branchTest32(Assembler::NonZero, elementsFlags, + Imm32(ObjectElements::COPY_ON_WRITE), + &failure); + + // Failure is not possible now. Free up registers. + regs.add(R0); + regs.add(R1); + regs.takeUnchecked(obj); + regs.takeUnchecked(key); + + // Increment initLength before write. + masm.add32(Imm32(1), initLength); + + // If length is now <= key, increment length before write. + Label skipIncrementLength; + Address length(scratchReg, ObjectElements::offsetOfLength()); + masm.branch32(Assembler::Above, length, key, &skipIncrementLength); + masm.add32(Imm32(1), length); + masm.bind(&skipIncrementLength); + + // Convert int32 values to double if convertDoubleElements is set. In this + // case the heap typeset is guaranteed to contain both int32 and double, so + // it's okay to store a double. + Label dontConvertDoubles; + masm.branchTest32(Assembler::Zero, elementsFlags, + Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS), + &dontConvertDoubles); + // Note that double arrays are only created by IonMonkey, so if we have no + // floating-point support Ion is disabled and there should be no double arrays. + if (cx->runtime()->jitSupportsFloatingPoint) + masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles); + else + masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support."); + masm.bind(&dontConvertDoubles); + + // Write the value. No need for pre-barrier since we're not overwriting an old value. + ValueOperand tmpVal = regs.takeAnyValue(); + BaseIndex element(scratchReg, key, TimesEight); + masm.loadValue(valueAddr, tmpVal); + masm.storeValue(tmpVal, element); + } else { + // Adding element to an unboxed array. + + // Bounds check (key == initLength) + Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + masm.load32(initLengthAddr, scratchReg); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); + masm.branch32(Assembler::NotEqual, scratchReg, key, &failure); + + Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); + + // Capacity check. + masm.checkUnboxedArrayCapacity(obj, Int32Key(key), scratchReg, &failure); + + // Increment initLength before write. + masm.add32(Imm32(1), initLengthAddr); + + // If length is now <= key, increment length before write. + Label skipIncrementLength; + masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength); + masm.add32(Imm32(1), lengthAddr); + masm.bind(&skipIncrementLength); + + // Load obj->elements. + masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); + + masm.Push(R0); + masm.loadValue(valueAddr, R0); + + // Write the value. No need for pre-barrier since we're not overwriting an old value. + BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); + masm.storeUnboxedProperty(address, unboxedType_, + ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); + masm.Pop(R0); } + EmitReturnFromIC(masm); + if (failurePopR0.used()) { + masm.bind(&failurePopR0); + masm.Pop(R0); + masm.jump(&failure); + } + // Failure case - fail but first unstow R0 and R1 masm.bind(&failureUnstow); EmitUnstowICValues(masm, 2); @@ -6225,7 +6392,7 @@ UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub, bool isOwnGetter = (holder == receiver); bool foundMatchingStub = false; - ReceiverGuard::StackGuard receiverGuard(receiver); + ReceiverGuard receiverGuard(receiver); for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) { if (iter->kind() == kind) { ICGetPropCallGetter* getPropStub = static_cast(*iter); @@ -6271,7 +6438,7 @@ static bool UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub, ICStub::Kind kind, NativeObject* holder, - ReceiverGuard::StackGuard receiverGuard, + ReceiverGuard receiverGuard, JSFunction* setter) { MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || @@ -6844,6 +7011,18 @@ TryAttachLengthStub(JSContext* cx, JSScript* script, ICGetProp_Fallback* stub, H return true; } + if (obj->is() && res.isInt32()) { + JitSpew(JitSpew_BaselineIC, " Generating GetProp(UnboxedArray.length) stub"); + ICGetProp_UnboxedArrayLength::Compiler compiler(cx); + ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); + if (!newStub) + return false; + + *attached = true; + stub->addNewStub(newStub); + return true; + } + if (obj->is() && res.isInt32()) { JitSpew(JitSpew_BaselineIC, " Generating GetProp(ArgsObj.length %s) stub", obj->is() ? "Strict" : "Normal"); @@ -7576,6 +7755,30 @@ ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler& masm) return true; } +bool +ICGetProp_UnboxedArrayLength::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure; + masm.branchTestObject(Assembler::NotEqual, R0, &failure); + + Register scratch = R1.scratchReg(); + + // Unbox R0 and guard it's an unboxed array. + Register obj = masm.extractObject(R0, ExtractTemp0); + masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &UnboxedArrayObject::class_, &failure); + + // Load obj->length. + masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), scratch); + + masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + bool ICGetProp_StringLength::Compiler::generateStubCode(MacroAssembler& masm) { @@ -7645,7 +7848,7 @@ ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler& masm) ICGetPropNativeStub* ICGetPropNativeCompiler::getStub(ICStubSpace* space) { - ReceiverGuard::StackGuard guard(obj_); + ReceiverGuard guard(obj_); switch (kind) { case ICStub::GetProp_Native: { @@ -7666,19 +7869,19 @@ ICGetPropNativeCompiler::getStub(ICStubSpace* space) } static void -GuardReceiverObject(MacroAssembler& masm, ReceiverGuard::StackGuard guard, +GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, Register object, Register scratch, size_t receiverGuardOffset, Label* failure) { - Address groupAddress(BaselineStubReg, receiverGuardOffset + ReceiverGuard::offsetOfGroup()); - Address shapeAddress(BaselineStubReg, receiverGuardOffset + ReceiverGuard::offsetOfShape()); + Address groupAddress(BaselineStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); + Address shapeAddress(BaselineStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); if (guard.group) { masm.loadPtr(groupAddress, scratch); masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); - if (guard.group->maybeUnboxedLayout() && !guard.shape) { + if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { // Guard the unboxed object has no expando object. masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); } @@ -7686,7 +7889,7 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard::StackGuard guard, if (guard.shape) { masm.loadPtr(shapeAddress, scratch); - if (guard.group && guard.group->maybeUnboxedLayout()) { + if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { // Guard the unboxed object has a matching expando object. masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); Label done; @@ -7723,7 +7926,7 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) Register scratch = regs.takeAnyExcluding(BaselineTailCallReg); // Shape/group guard. - GuardReceiverObject(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch, + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, ICGetPropNativeStub::offsetOfReceiverGuard(), &failure); Register holderReg; @@ -7871,7 +8074,7 @@ ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm) // Unbox and guard against old shape/group. Register objReg = masm.extractObject(R0, ExtractTemp0); - GuardReceiverObject(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch, + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, ICGetProp_NativeDoesNotExist::offsetOfGuard(), &failure); Register protoReg = regs.takeAny(); @@ -7910,7 +8113,7 @@ ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) // Unbox and shape guard. Register objReg = masm.extractObject(R0, ExtractTemp0); - GuardReceiverObject(masm, ReceiverGuard::StackGuard(receiver_), objReg, scratch, + GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, ICGetProp_CallScripted::offsetOfReceiverGuard(), &failure); if (receiver_ != holder_) { @@ -8018,7 +8221,7 @@ ICGetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) Register scratch = regs.takeAnyExcluding(BaselineTailCallReg); // Shape guard. - GuardReceiverObject(masm, ReceiverGuard::StackGuard(receiver_), objReg, scratch, + GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch, ICGetProp_CallNative::offsetOfReceiverGuard(), &failure); if (receiver_ != holder_ ) { @@ -8076,7 +8279,7 @@ ICGetPropCallDOMProxyNativeCompiler::generateStubCode(MacroAssembler& masm, // Shape guard. static const size_t receiverShapeOffset = ICGetProp_CallDOMProxyNative::offsetOfReceiverGuard() + - ReceiverGuard::offsetOfShape(); + HeapReceiverGuard::offsetOfShape(); masm.loadPtr(Address(BaselineStubReg, receiverShapeOffset), scratch); masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure); @@ -8614,7 +8817,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC static bool TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, ICSetProp_Fallback* stub, - HandleObject obj, const ReceiverGuard::RootedStackGuard& receiverGuard, + HandleObject obj, const RootedReceiverGuard& receiverGuard, HandlePropertyName name, HandleId id, HandleValue rhs, bool* attached, bool* isTemporarilyUnoptimizable) @@ -8801,7 +9004,7 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ RootedObjectGroup oldGroup(cx, obj->getGroup(cx)); if (!oldGroup) return false; - ReceiverGuard::RootedStackGuard oldGuard(cx, ReceiverGuard::StackGuard(obj)); + RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj)); if (obj->is()) { if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) @@ -9252,13 +9455,7 @@ ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) masm.load32(Address(BaselineStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch); BaseIndex address(object, scratch, TimesOne); - if (fieldType_ == JSVAL_TYPE_OBJECT) - EmitPreBarrier(masm, address, MIRType_Object); - else if (fieldType_ == JSVAL_TYPE_STRING) - EmitPreBarrier(masm, address, MIRType_String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(fieldType_)); - + EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_); masm.storeUnboxedProperty(address, fieldType_, ConstantOrRegister(TypedOrValueRegister(R1)), &failure); @@ -9412,7 +9609,7 @@ ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) // Unbox and shape guard. Register objReg = masm.extractObject(R0, ExtractTemp0); - GuardReceiverObject(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch, + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, ICSetProp_CallScripted::offsetOfGuard(), &failureUnstow); Register holderReg = regs.takeAny(); @@ -9531,7 +9728,7 @@ ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm) // Unbox and shape guard. Register objReg = masm.extractObject(R0, ExtractTemp0); - GuardReceiverObject(masm, ReceiverGuard::StackGuard(obj_), objReg, scratch, + GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratch, ICSetProp_CallNative::offsetOfGuard(), &failureUnstow); Register holderReg = regs.takeAny(); @@ -9705,13 +9902,16 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, } if (native == js::array_concat) { - if (args.thisv().isObject() && args.thisv().toObject().is() && - !args.thisv().toObject().isSingleton()) + if (args.thisv().isObject() && + args.thisv().toObject().is() && + !args.thisv().toObject().isSingleton() && + !args.thisv().toObject().group()->hasUnanalyzedPreliminaryObjects()) { RootedObject proto(cx, args.thisv().toObject().getProto()); res.set(NewDenseEmptyArray(cx, proto, TenuredObject)); if (!res) return false; + res->setGroup(args.thisv().toObject().group()); return true; } @@ -9748,7 +9948,8 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc, ION_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) COMP_COMMONX4_TO_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) COMP_COMMONX4_TO_INT32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_) - CONVERSION_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)) + FOREACH_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) + ION_ONLY_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)) { Rooted descr(cx, &cx->global()->int32x4TypeDescr().as()); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); @@ -11098,7 +11299,7 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) // Initialize vp. Register vpReg = regs.takeAny(); - masm.movePtr(StackPointer, vpReg); + masm.moveStackPtrTo(vpReg); // Construct a native exit frame. masm.push(argcReg); @@ -11129,7 +11330,7 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm) masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); // Load the return value into R0. - masm.loadValue(Address(StackPointer, NativeExitFrameLayout::offsetOfResult()), R0); + masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), R0); leaveStubFrame(masm); @@ -11194,7 +11395,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm) // Initialize vp. Register vpReg = regs.takeAny(); - masm.movePtr(StackPointer, vpReg); + masm.moveStackPtrTo(vpReg); // Construct a native exit frame. masm.push(argcReg); @@ -11216,7 +11417,7 @@ ICCall_ClassHook::Compiler::generateStubCode(MacroAssembler& masm) masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); // Load the return value into R0. - masm.loadValue(Address(StackPointer, NativeExitFrameLayout::offsetOfResult()), R0); + masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), R0); leaveStubFrame(masm); @@ -11597,7 +11798,7 @@ ICTableSwitch::Compiler::generateStubCode(MacroAssembler& masm) } else { // Pass pointer to double value. masm.pushValue(R0); - masm.movePtr(StackPointer, R0.scratchReg()); + masm.moveStackPtrTo(R0.scratchReg()); masm.setupUnalignedABICall(1, scratch); masm.passABIArg(R0.scratchReg()); @@ -12307,6 +12508,19 @@ ICGetElem_Dense::Clone(ICStubSpace* space, ICStub* firstMonitorStub, return New(space, other.jitCode(), firstMonitorStub, other.shape_); } +ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, + ObjectGroup *group) + : ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub), + group_(group) +{ } + +/* static */ ICGetElem_UnboxedArray* +ICGetElem_UnboxedArray::Clone(ICStubSpace* space, ICStub* firstMonitorStub, + ICGetElem_UnboxedArray& other) +{ + return New(space, other.jitCode(), firstMonitorStub, other.group_); +} + ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type) : ICStub(GetElem_TypedArray, stubCode), shape_(shape) @@ -12322,15 +12536,15 @@ ICGetElem_Arguments::Clone(ICStubSpace* space, ICStub* firstMonitorStub, return New(space, other.jitCode(), firstMonitorStub, other.which()); } -ICSetElem_Dense::ICSetElem_Dense(JitCode* stubCode, Shape* shape, ObjectGroup* group) - : ICUpdatedStub(SetElem_Dense, stubCode), +ICSetElem_DenseOrUnboxedArray::ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group) + : ICUpdatedStub(SetElem_DenseOrUnboxedArray, stubCode), shape_(shape), group_(group) { } -ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode* stubCode, ObjectGroup* group, - size_t protoChainDepth) - : ICUpdatedStub(SetElem_DenseAdd, stubCode), +ICSetElem_DenseOrUnboxedArrayAdd::ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group, + size_t protoChainDepth) + : ICUpdatedStub(SetElem_DenseOrUnboxedArrayAdd, stubCode), group_(group) { MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); @@ -12339,13 +12553,13 @@ ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode* stubCode, ObjectGroup* group, template ICUpdatedStub* -ICSetElemDenseAddCompiler::getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) +ICSetElemDenseOrUnboxedArrayAddCompiler::getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) { RootedObjectGroup group(cx, obj_->getGroup(cx)); if (!group) return nullptr; Rooted stubCode(cx, getStubCode()); - return ICStub::New>(space, stubCode, group, shapes); + return ICStub::New>(space, stubCode, group, shapes); } ICSetElem_TypedArray::ICSetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type, @@ -12452,7 +12666,7 @@ ICGetProp_Primitive::ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitor ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard::StackGuard guard, uint32_t offset) + ReceiverGuard guard, uint32_t offset) : ICMonitoredStub(kind, stubCode, firstMonitorStub), receiverGuard_(guard), offset_(offset) @@ -12467,7 +12681,7 @@ ICGetProp_Native::Clone(ICStubSpace* space, ICStub* firstMonitorStub, } ICGetProp_NativePrototype::ICGetProp_NativePrototype(JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard::StackGuard guard, uint32_t offset, + ReceiverGuard guard, uint32_t offset, JSObject* holder, Shape* holderShape) : ICGetPropNativeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, guard, offset), holder_(holder), @@ -12484,7 +12698,7 @@ ICGetProp_NativePrototype::Clone(ICStubSpace* space, ICStub* firstMonitorStub, } ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist( - JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard::StackGuard guard, + JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, size_t protoChainDepth) : ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub), guard_(guard) @@ -12504,7 +12718,7 @@ ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx) template ICGetProp_NativeDoesNotExistImpl::ICGetProp_NativeDoesNotExistImpl( - JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard::StackGuard guard, + JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, const AutoShapeVector* shapes) : ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, guard, ProtoChainDepth) { @@ -12526,7 +12740,7 @@ ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler( } ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, - ReceiverGuard::StackGuard receiverGuard, JSObject* holder, + ReceiverGuard receiverGuard, JSObject* holder, Shape* holderShape, JSFunction* getter, uint32_t pcOffset) : ICMonitoredStub(kind, stubCode, firstMonitorStub), @@ -12637,7 +12851,7 @@ ICSetPropNativeAddCompiler::ICSetPropNativeAddCompiler(JSContext* cx, HandleObje MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH); } -ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard::StackGuard guard, +ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard guard, JSObject* holder, Shape* holderShape, JSFunction* setter, uint32_t pcOffset) : ICStub(kind, stubCode), @@ -12770,7 +12984,7 @@ ICGetPropCallDOMProxyNativeStub::ICGetPropCallDOMProxyNativeStub(Kind kind, JitC Shape* holderShape, JSFunction* getter, uint32_t pcOffset) - : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard::StackGuard(nullptr, shape), + : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, ReceiverGuard(nullptr, shape), holder, holderShape, getter, pcOffset), expandoShape_(expandoShape) { } diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 6c283580e3..04e4885bd3 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -18,6 +18,7 @@ #include "jit/BaselineJIT.h" #include "jit/BaselineRegisters.h" #include "vm/ArrayObject.h" +#include "vm/ReceiverGuard.h" #include "vm/TypedArrayCommon.h" #include "vm/UnboxedObject.h" @@ -397,12 +398,13 @@ class ICEntry _(GetElem_NativePrototypeCallScripted) \ _(GetElem_String) \ _(GetElem_Dense) \ + _(GetElem_UnboxedArray) \ _(GetElem_TypedArray) \ _(GetElem_Arguments) \ \ _(SetElem_Fallback) \ - _(SetElem_Dense) \ - _(SetElem_DenseAdd) \ + _(SetElem_DenseOrUnboxedArray) \ + _(SetElem_DenseOrUnboxedArrayAdd) \ _(SetElem_TypedArray) \ \ _(In_Fallback) \ @@ -428,6 +430,7 @@ class ICEntry \ _(GetProp_Fallback) \ _(GetProp_ArrayLength) \ + _(GetProp_UnboxedArrayLength) \ _(GetProp_Primitive) \ _(GetProp_StringLength) \ _(GetProp_Native) \ @@ -1126,7 +1129,7 @@ class ICStubCompiler // Some stubs need to emit SPS profiler updates. This emits the guarding // jitcode for those stubs. If profiling is not enabled, jumps to the // given label. - void guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip); + void guardProfilingEnabled(MacroAssembler& masm, Register scratch, Label* skip); inline AllocatableGeneralRegisterSet availableGeneralRegs(size_t numInputs) const { AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); @@ -1162,11 +1165,11 @@ class ICStubCompiler return regs; } - inline bool emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, ValueOperand val, + inline bool emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, ValueOperand val, Register scratch, LiveGeneralRegisterSet saveRegs); public: - virtual ICStub *getStub(ICStubSpace *space) = 0; + virtual ICStub* getStub(ICStubSpace* space) = 0; static ICStubSpace* StubSpaceForKind(ICStub::Kind kind, JSScript* script) { if (ICStub::CanMakeCalls(kind)) @@ -1737,31 +1740,45 @@ class ICNewArray_Fallback : public ICFallbackStub { friend class ICStubSpace; - HeapPtrArrayObject templateObject_; + HeapPtrObject templateObject_; - ICNewArray_Fallback(JitCode* stubCode, ArrayObject* templateObject) - : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), templateObject_(templateObject) + // The group used for objects created here is always available, even if the + // template object itself is not. + HeapPtrObjectGroup templateGroup_; + + ICNewArray_Fallback(JitCode* stubCode, ObjectGroup* templateGroup) + : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), + templateObject_(nullptr), templateGroup_(templateGroup) {} public: class Compiler : public ICStubCompiler { - RootedArrayObject templateObject; + RootedObjectGroup templateGroup; bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext* cx, ArrayObject* templateObject) + Compiler(JSContext* cx, ObjectGroup* templateGroup) : ICStubCompiler(cx, ICStub::NewArray_Fallback), - templateObject(cx, templateObject) + templateGroup(cx, templateGroup) {} ICStub* getStub(ICStubSpace* space) { - return ICStub::New(space, getStubCode(), templateObject); + return ICStub::New(space, getStubCode(), templateGroup); } }; - HeapPtrArrayObject& templateObject() { + HeapPtrObject& templateObject() { return templateObject_; } + + void setTemplateObject(JSObject* obj) { + MOZ_ASSERT(obj->group() == templateGroup()); + templateObject_ = obj; + } + + HeapPtrObjectGroup& templateGroup() { + return templateGroup_; + } }; class ICNewObject_Fallback : public ICFallbackStub @@ -1779,7 +1796,7 @@ class ICNewObject_Fallback : public ICFallbackStub bool generateStubCode(MacroAssembler& masm); public: - explicit Compiler(JSContext *cx) + explicit Compiler(JSContext* cx) : ICStubCompiler(cx, ICStub::NewObject_Fallback) {} @@ -1792,7 +1809,7 @@ class ICNewObject_Fallback : public ICFallbackStub return templateObject_; } - void setTemplateObject(JSObject *obj) { + void setTemplateObject(JSObject* obj) { templateObject_ = obj; } }; @@ -1801,7 +1818,7 @@ class ICNewObject_WithTemplate : public ICStub { friend class ICStubSpace; - explicit ICNewObject_WithTemplate(JitCode *stubCode) + explicit ICNewObject_WithTemplate(JitCode* stubCode) : ICStub(ICStub::NewObject_WithTemplate, stubCode) {} }; @@ -3026,6 +3043,54 @@ class ICGetElem_Dense : public ICMonitoredStub }; }; +class ICGetElem_UnboxedArray : public ICMonitoredStub +{ + friend class ICStubSpace; + + HeapPtrObjectGroup group_; + + ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group); + + public: + static ICGetElem_UnboxedArray* Clone(ICStubSpace* space, ICStub* firstMonitorStub, + ICGetElem_UnboxedArray& other); + + static size_t offsetOfGroup() { + return offsetof(ICGetElem_UnboxedArray, group_); + } + + HeapPtrObjectGroup& group() { + return group_; + } + + class Compiler : public ICStubCompiler { + ICStub* firstMonitorStub_; + RootedObjectGroup group_; + JSValueType elementType_; + + protected: + bool generateStubCode(MacroAssembler& masm); + + virtual int32_t getKey() const { + return static_cast(kind) | + (static_cast(elementType_) << 16); + } + + public: + Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group) + : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray), + firstMonitorStub_(firstMonitorStub), + group_(cx, group), + elementType_(group->unboxedLayout().elementType()) + {} + + ICStub* getStub(ICStubSpace* space) { + return ICStub::New(space, getStubCode(), firstMonitorStub_, + group_); + } + }; +}; + // Enum for stubs handling a combination of typed arrays and typed objects. enum TypedThingLayout { Layout_TypedArray, @@ -3183,21 +3248,21 @@ class ICSetElem_Fallback : public ICFallbackStub }; }; -class ICSetElem_Dense : public ICUpdatedStub +class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub { friend class ICStubSpace; - HeapPtrShape shape_; + HeapPtrShape shape_; // null for unboxed arrays HeapPtrObjectGroup group_; - ICSetElem_Dense(JitCode* stubCode, Shape* shape, ObjectGroup* group); + ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group); public: static size_t offsetOfShape() { - return offsetof(ICSetElem_Dense, shape_); + return offsetof(ICSetElem_DenseOrUnboxedArray, shape_); } static size_t offsetOfGroup() { - return offsetof(ICSetElem_Dense, group_); + return offsetof(ICSetElem_DenseOrUnboxedArray, group_); } HeapPtrShape& shape() { @@ -3209,33 +3274,41 @@ class ICSetElem_Dense : public ICUpdatedStub class Compiler : public ICStubCompiler { RootedShape shape_; - - // Compiler is only live on stack during compilation, it should - // outlive any RootedObjectGroup it's passed. So it can just - // use the handle. - HandleObjectGroup group_; + RootedObjectGroup group_; + JSValueType unboxedType_; bool generateStubCode(MacroAssembler& masm); public: + virtual int32_t getKey() const { + return static_cast(kind) | + (static_cast(unboxedType_) << 16); + } + Compiler(JSContext* cx, Shape* shape, HandleObjectGroup group) - : ICStubCompiler(cx, ICStub::SetElem_Dense), + : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray), shape_(cx, shape), - group_(group) + group_(cx, group), + unboxedType_(shape ? JSVAL_TYPE_MAGIC : group->unboxedLayout().elementType()) {} ICUpdatedStub* getStub(ICStubSpace* space) { - ICSetElem_Dense* stub = ICStub::New(space, getStubCode(), shape_, group_); + ICSetElem_DenseOrUnboxedArray* stub = + ICStub::New(space, getStubCode(), shape_, group_); if (!stub || !stub->initUpdatingChain(cx, space)) return nullptr; return stub; } + + bool needsUpdateStubs() { + return unboxedType_ == JSVAL_TYPE_MAGIC || unboxedType_ == JSVAL_TYPE_OBJECT; + } }; }; -template class ICSetElem_DenseAddImpl; +template class ICSetElem_DenseOrUnboxedArrayAddImpl; -class ICSetElem_DenseAdd : public ICUpdatedStub +class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub { friend class ICStubSpace; @@ -3245,11 +3318,11 @@ class ICSetElem_DenseAdd : public ICUpdatedStub protected: HeapPtrObjectGroup group_; - ICSetElem_DenseAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth); + ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth); public: static size_t offsetOfGroup() { - return offsetof(ICSetElem_DenseAdd, group_); + return offsetof(ICSetElem_DenseOrUnboxedArrayAdd, group_); } HeapPtrObjectGroup& group() { @@ -3261,28 +3334,29 @@ class ICSetElem_DenseAdd : public ICUpdatedStub } template - ICSetElem_DenseAddImpl* toImplUnchecked() { - return static_cast*>(this); + ICSetElem_DenseOrUnboxedArrayAddImpl* toImplUnchecked() { + return static_cast*>(this); } template - ICSetElem_DenseAddImpl* toImpl() { + ICSetElem_DenseOrUnboxedArrayAddImpl* toImpl() { MOZ_ASSERT(ProtoChainDepth == protoChainDepth()); return toImplUnchecked(); } }; template -class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd +class ICSetElem_DenseOrUnboxedArrayAddImpl : public ICSetElem_DenseOrUnboxedArrayAdd { friend class ICStubSpace; + // Note: for unboxed arrays, the first shape is null. static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array shapes_; - ICSetElem_DenseAddImpl(JitCode* stubCode, ObjectGroup* group, - const AutoShapeVector* shapes) - : ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth) + ICSetElem_DenseOrUnboxedArrayAddImpl(JitCode* stubCode, ObjectGroup* group, + const AutoShapeVector* shapes) + : ICSetElem_DenseOrUnboxedArrayAdd(stubCode, group, ProtoChainDepth) { MOZ_ASSERT(shapes->length() == NumShapes); for (size_t i = 0; i < NumShapes; i++) @@ -3291,40 +3365,52 @@ class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd public: void traceShapes(JSTracer* trc) { - for (size_t i = 0; i < NumShapes; i++) - TraceEdge(trc, &shapes_[i], "baseline-setelem-denseadd-stub-shape"); + for (size_t i = 0; i < NumShapes; i++) { + if (shapes_[i]) + TraceEdge(trc, &shapes_[i], "baseline-setelem-denseadd-stub-shape"); + } } Shape* shape(size_t i) const { MOZ_ASSERT(i < NumShapes); return shapes_[i]; } static size_t offsetOfShape(size_t idx) { - return offsetof(ICSetElem_DenseAddImpl, shapes_) + idx * sizeof(HeapPtrShape); + return offsetof(ICSetElem_DenseOrUnboxedArrayAddImpl, shapes_) + idx * sizeof(HeapPtrShape); } }; -class ICSetElemDenseAddCompiler : public ICStubCompiler { +class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler { RootedObject obj_; size_t protoChainDepth_; + JSValueType unboxedType_; bool generateStubCode(MacroAssembler& masm); protected: virtual int32_t getKey() const { - return static_cast(kind) | (static_cast(protoChainDepth_) << 16); + return static_cast(kind) | + (static_cast(protoChainDepth_) << 16) | + (static_cast(unboxedType_) << 19); } public: - ICSetElemDenseAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth) - : ICStubCompiler(cx, ICStub::SetElem_DenseAdd), + ICSetElemDenseOrUnboxedArrayAddCompiler(JSContext* cx, HandleObject obj, size_t protoChainDepth) + : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd), obj_(cx, obj), - protoChainDepth_(protoChainDepth) + protoChainDepth_(protoChainDepth), + unboxedType_(obj->is() + ? obj->as().elementType() + : JSVAL_TYPE_MAGIC) {} template ICUpdatedStub* getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes); ICUpdatedStub* getStub(ICStubSpace* space); + + bool needsUpdateStubs() { + return unboxedType_ == JSVAL_TYPE_MAGIC || unboxedType_ == JSVAL_TYPE_OBJECT; + } }; // Accesses scalar elements of a typed array or typed object. @@ -3422,18 +3508,18 @@ class ICInNativeStub : public ICStub HeapPtrPropertyName name_; protected: - ICInNativeStub(ICStub::Kind kind, JitCode *stubCode, HandleShape shape, + ICInNativeStub(ICStub::Kind kind, JitCode* stubCode, HandleShape shape, HandlePropertyName name); public: - HeapPtrShape &shape() { + HeapPtrShape& shape() { return shape_; } static size_t offsetOfShape() { return offsetof(ICInNativeStub, shape_); } - HeapPtrPropertyName &name() { + HeapPtrPropertyName& name() { return name_; } static size_t offsetOfName() { @@ -3446,7 +3532,7 @@ class ICIn_Native : public ICInNativeStub { friend class ICStubSpace; - ICIn_Native(JitCode *stubCode, HandleShape shape, HandlePropertyName name) + ICIn_Native(JitCode* stubCode, HandleShape shape, HandlePropertyName name) : ICInNativeStub(In_Native, stubCode, shape, name) {} }; @@ -3461,11 +3547,11 @@ class ICIn_NativePrototype : public ICInNativeStub HeapPtrObject holder_; HeapPtrShape holderShape_; - ICIn_NativePrototype(JitCode *stubCode, HandleShape shape, HandlePropertyName name, + ICIn_NativePrototype(JitCode* stubCode, HandleShape shape, HandlePropertyName name, HandleObject holder, HandleShape holderShape); public: - HeapPtrObject &holder() { + HeapPtrObject& holder() { return holder_; } HeapPtrShape &holderShape() { @@ -3486,10 +3572,10 @@ class ICInNativeCompiler : public ICStubCompiler RootedObject holder_; RootedPropertyName name_; - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - ICInNativeCompiler(JSContext *cx, ICStub::Kind kind, HandleObject obj, HandleObject holder, + ICInNativeCompiler(JSContext* cx, ICStub::Kind kind, HandleObject obj, HandleObject holder, HandlePropertyName name) : ICStubCompiler(cx, kind), obj_(cx, obj), @@ -3497,7 +3583,7 @@ class ICInNativeCompiler : public ICStubCompiler name_(cx, name) {} - ICStub *getStub(ICStubSpace *space) { + ICStub* getStub(ICStubSpace* space) { RootedShape shape(cx, obj_->as().lastProperty()); if (kind == ICStub::In_Native) { MOZ_ASSERT(obj_ == holder_); @@ -3524,7 +3610,7 @@ class ICIn_NativeDoesNotExist : public ICStub static const size_t MAX_PROTO_CHAIN_DEPTH = 8; protected: - ICIn_NativeDoesNotExist(JitCode *stubCode, size_t protoChainDepth, + ICIn_NativeDoesNotExist(JitCode* stubCode, size_t protoChainDepth, HandlePropertyName name); public: @@ -3532,14 +3618,14 @@ class ICIn_NativeDoesNotExist : public ICStub MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH); return extra_; } - HeapPtrPropertyName &name() { + HeapPtrPropertyName& name() { return name_; } template - ICIn_NativeDoesNotExistImpl *toImpl() { + ICIn_NativeDoesNotExistImpl* toImpl() { MOZ_ASSERT(ProtoChainDepth == protoChainDepth()); - return static_cast *>(this); + return static_cast*>(this); } static size_t offsetOfShape(size_t idx); @@ -3560,11 +3646,11 @@ class ICIn_NativeDoesNotExistImpl : public ICIn_NativeDoesNotExist private: mozilla::Array shapes_; - ICIn_NativeDoesNotExistImpl(JitCode *stubCode, const AutoShapeVector *shapes, + ICIn_NativeDoesNotExistImpl(JitCode* stubCode, const AutoShapeVector* shapes, HandlePropertyName name); public: - void traceShapes(JSTracer *trc) { + void traceShapes(JSTracer* trc) { for (size_t i = 0; i < NumShapes; i++) TraceEdge(trc, &shapes_[i], "baseline-innativedoesnotexist-stub-shape"); } @@ -3585,19 +3671,19 @@ class ICInNativeDoesNotExistCompiler : public ICStubCompiler return static_cast(kind) | (static_cast(protoChainDepth_) << 16); } - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - ICInNativeDoesNotExistCompiler(JSContext *cx, HandleObject obj, HandlePropertyName name, + ICInNativeDoesNotExistCompiler(JSContext* cx, HandleObject obj, HandlePropertyName name, size_t protoChainDepth); template - ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) { + ICStub* getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) { return ICStub::New>(space, getStubCode(), shapes, name_); } - ICStub *getStub(ICStubSpace *space); + ICStub* getStub(ICStubSpace* space); }; class ICIn_Dense : public ICStub @@ -3606,10 +3692,10 @@ class ICIn_Dense : public ICStub HeapPtrShape shape_; - ICIn_Dense(JitCode *stubCode, HandleShape shape); + ICIn_Dense(JitCode* stubCode, HandleShape shape); public: - HeapPtrShape &shape() { + HeapPtrShape& shape() { return shape_; } static size_t offsetOfShape() { @@ -3620,15 +3706,15 @@ class ICIn_Dense : public ICStub RootedShape shape_; protected: - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext *cx, Shape *shape) + Compiler(JSContext* cx, Shape* shape) : ICStubCompiler(cx, ICStub::In_Dense), shape_(cx, shape) {} - ICStub *getStub(ICStubSpace *space) { + ICStub* getStub(ICStubSpace* space) { return ICStub::New(space, getStubCode(), shape_); } }; @@ -3683,10 +3769,10 @@ class ICGetName_Global : public ICMonitoredStub HeapPtrShape shape_; uint32_t slot_; - ICGetName_Global(JitCode *stubCode, ICStub *firstMonitorStub, Shape* shape, uint32_t slot); + ICGetName_Global(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, uint32_t slot); public: - HeapPtrShape &shape() { + HeapPtrShape& shape() { return shape_; } static size_t offsetOfShape() { @@ -3976,6 +4062,30 @@ class ICGetProp_ArrayLength : public ICStub }; }; +// Stub for accessing an unboxed array's length. +class ICGetProp_UnboxedArrayLength : public ICStub +{ + friend class ICStubSpace; + + explicit ICGetProp_UnboxedArrayLength(JitCode* stubCode) + : ICStub(GetProp_UnboxedArrayLength, stubCode) + {} + + public: + class Compiler : public ICStubCompiler { + bool generateStubCode(MacroAssembler& masm); + + public: + explicit Compiler(JSContext* cx) + : ICStubCompiler(cx, ICStub::GetProp_UnboxedArrayLength) + {} + + ICStub* getStub(ICStubSpace* space) { + return ICStub::New(space, getStubCode()); + } + }; +}; + // Stub for accessing a property on a primitive's prototype. class ICGetProp_Primitive : public ICMonitoredStub { @@ -4031,7 +4141,7 @@ class ICGetProp_Primitive : public ICMonitoredStub offset_(offset) {} - ICStub *getStub(ICStubSpace *space) { + ICStub* getStub(ICStubSpace* space) { RootedShape protoShape(cx, prototype_->as().lastProperty()); return ICStub::New(space, getStubCode(), firstMonitorStub_, protoShape, offset_); @@ -4063,111 +4173,21 @@ class ICGetProp_StringLength : public ICStub }; }; -// Structure encapsulating the guarding that needs to be done on an object -// before it can be accessed or modified by an inline cache. -class ReceiverGuard -{ - // Group to guard on, or null. If the object is not unboxed or typed and - // the IC does not require the object to have a specific group, this is - // null. Otherwise, this is the object's group. - HeapPtrObjectGroup group_; - - // Shape to guard on, or null. If the object is not unboxed or typed then - // this is the object's shape. If the object is unboxed, then this is the - // shape of the object's expando, null if the object has no expando. - HeapPtrShape shape_; - - public: - struct StackGuard; - - struct RootedStackGuard - { - RootedObjectGroup group; - RootedShape shape; - - RootedStackGuard(JSContext *cx, const StackGuard &guard) - : group(cx, guard.group), shape(cx, guard.shape) - {} - }; - - struct StackGuard - { - ObjectGroup* group; - Shape* shape; - - StackGuard() - : group(nullptr), shape(nullptr) - {} - - MOZ_IMPLICIT StackGuard(const ReceiverGuard& guard) - : group(guard.group_), shape(guard.shape_) - {} - - MOZ_IMPLICIT StackGuard(const RootedStackGuard& guard) - : group(guard.group), shape(guard.shape) - {} - - explicit StackGuard(JSObject* obj); - StackGuard(ObjectGroup* group, Shape* shape); - - bool operator ==(const StackGuard& other) const { - return group == other.group && shape == other.shape; - } - - bool operator !=(const StackGuard& other) const { - return !(*this == other); - } - }; - - explicit ReceiverGuard(const StackGuard& guard) - : group_(guard.group), shape_(guard.shape) - {} - - bool matches(const StackGuard &guard) { - return group_ == guard.group && shape_ == guard.shape; - } - - void update(const StackGuard &other) { - group_ = other.group; - shape_ = other.shape; - } - - void trace(JSTracer* trc); - - Shape* shape() const { - return shape_; - } - ObjectGroup* group() const { - return group_; - } - - static size_t offsetOfShape() { - return offsetof(ReceiverGuard, shape_); - } - static size_t offsetOfGroup() { - return offsetof(ReceiverGuard, group_); - } - - // Bits to munge into IC compiler keys when that IC has a ReceiverGuard. - // This uses at most two bits for data. - static int32_t keyBits(JSObject* obj); -}; - // Base class for native GetProp stubs. class ICGetPropNativeStub : public ICMonitoredStub { // Object shape/group. - ReceiverGuard receiverGuard_; + HeapReceiverGuard receiverGuard_; // Fixed or dynamic slot offset. uint32_t offset_; protected: - ICGetPropNativeStub(ICStub::Kind kind, JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard guard, uint32_t offset); + ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, uint32_t offset); public: - ReceiverGuard &receiverGuard() { + HeapReceiverGuard& receiverGuard() { return receiverGuard_; } uint32_t offset() const { @@ -4194,14 +4214,14 @@ class ICGetProp_Native : public ICGetPropNativeStub { friend class ICStubSpace; - ICGetProp_Native(JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::StackGuard guard, + ICGetProp_Native(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard, uint32_t offset) : ICGetPropNativeStub(GetProp_Native, stubCode, firstMonitorStub, guard, offset) {} public: - static ICGetProp_Native *Clone(ICStubSpace *space, ICStub *firstMonitorStub, - ICGetProp_Native &other); + static ICGetProp_Native* Clone(ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_Native& other); }; // Stub for accessing a property on the native prototype of a native or unboxed @@ -4216,20 +4236,20 @@ class ICGetProp_NativePrototype : public ICGetPropNativeStub HeapPtrObject holder_; HeapPtrShape holderShape_; - ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard guard, - uint32_t offset, JSObject *holder, Shape *holderShape); + ICGetProp_NativePrototype(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, + uint32_t offset, JSObject* holder, Shape* holderShape); public: - static ICGetProp_NativePrototype *Clone(ICStubSpace *space, - ICStub *firstMonitorStub, - ICGetProp_NativePrototype &other); + static ICGetProp_NativePrototype* Clone(ICStubSpace* space, + ICStub* firstMonitorStub, + ICGetProp_NativePrototype& other); public: - HeapPtrObject &holder() { + HeapPtrObject& holder() { return holder_; } - HeapPtrShape &holderShape() { + HeapPtrShape& holderShape() { return holderShape_; } static size_t offsetOfHolder() { @@ -4244,7 +4264,7 @@ class ICGetProp_NativePrototype : public ICGetPropNativeStub class ICGetPropNativeCompiler : public ICStubCompiler { bool isCallProp_; - ICStub *firstMonitorStub_; + ICStub* firstMonitorStub_; HandleObject obj_; HandleObject holder_; HandlePropertyName propName_; @@ -4260,12 +4280,12 @@ class ICGetPropNativeCompiler : public ICStubCompiler (static_cast(isCallProp_) << 16) | (static_cast(isFixedSlot_) << 17) | (static_cast(inputDefinitelyObject_) << 18) | - (ReceiverGuard::keyBits(obj_) << 19); + (HeapReceiverGuard::keyBits(obj_) << 19); } public: - ICGetPropNativeCompiler(JSContext *cx, ICStub::Kind kind, bool isCallProp, - ICStub *firstMonitorStub, HandleObject obj, HandleObject holder, + ICGetPropNativeCompiler(JSContext* cx, ICStub::Kind kind, bool isCallProp, + ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, HandlePropertyName propName, bool isFixedSlot, uint32_t offset, bool inputDefinitelyObject = false) : ICStubCompiler(cx, kind), @@ -4279,7 +4299,7 @@ class ICGetPropNativeCompiler : public ICStubCompiler inputDefinitelyObject_(inputDefinitelyObject) {} - ICGetPropNativeStub *getStub(ICStubSpace *space); + ICGetPropNativeStub* getStub(ICStubSpace* space); }; template class ICGetProp_NativeDoesNotExistImpl; @@ -4288,13 +4308,13 @@ class ICGetProp_NativeDoesNotExist : public ICMonitoredStub { friend class ICStubSpace; public: - ReceiverGuard guard_; + HeapReceiverGuard guard_; static const size_t MAX_PROTO_CHAIN_DEPTH = 8; protected: - ICGetProp_NativeDoesNotExist(JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard guard, + ICGetProp_NativeDoesNotExist(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, size_t protoChainDepth); public: @@ -4309,7 +4329,7 @@ class ICGetProp_NativeDoesNotExist : public ICMonitoredStub return static_cast*>(this); } - ReceiverGuard &guard() { + HeapReceiverGuard& guard() { return guard_; } @@ -4331,9 +4351,9 @@ class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist private: mozilla::Array shapes_; - ICGetProp_NativeDoesNotExistImpl(JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard guard, - const AutoShapeVector *shapes); + ICGetProp_NativeDoesNotExistImpl(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard guard, + const AutoShapeVector* shapes); public: void traceShapes(JSTracer* trc) { @@ -4349,31 +4369,31 @@ class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler { - ICStub *firstMonitorStub_; + ICStub* firstMonitorStub_; RootedObject obj_; size_t protoChainDepth_; protected: virtual int32_t getKey() const { return static_cast(kind) | - (ReceiverGuard::keyBits(obj_) << 16) | + (HeapReceiverGuard::keyBits(obj_) << 16) | (static_cast(protoChainDepth_) << 18); } - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - ICGetPropNativeDoesNotExistCompiler(JSContext *cx, ICStub *firstMonitorStub, + ICGetPropNativeDoesNotExistCompiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject obj, size_t protoChainDepth); template - ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) { - ReceiverGuard::StackGuard guard(obj_); + ICStub* getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) { + ReceiverGuard guard(obj_); return ICStub::New > (space, getStubCode(), firstMonitorStub_, guard, shapes); } - ICStub *getStub(ICStubSpace *space); + ICStub* getStub(ICStubSpace* space); }; class ICGetProp_Unboxed : public ICMonitoredStub @@ -4383,7 +4403,7 @@ class ICGetProp_Unboxed : public ICMonitoredStub HeapPtrObjectGroup group_; uint32_t fieldOffset_; - ICGetProp_Unboxed(JitCode *stubCode, ICStub *firstMonitorStub, ObjectGroup *group, + ICGetProp_Unboxed(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group, uint32_t fieldOffset) : ICMonitoredStub(ICStub::GetProp_Unboxed, stubCode, firstMonitorStub), group_(group), fieldOffset_(fieldOffset) @@ -4392,7 +4412,7 @@ class ICGetProp_Unboxed : public ICMonitoredStub } public: - HeapPtrObjectGroup &group() { + HeapPtrObjectGroup& group() { return group_; } @@ -4405,20 +4425,20 @@ class ICGetProp_Unboxed : public ICMonitoredStub class Compiler : public ICStubCompiler { protected: - ICStub *firstMonitorStub_; + ICStub* firstMonitorStub_; RootedObjectGroup group_; uint32_t fieldOffset_; JSValueType fieldType_; - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { return static_cast(kind) | (static_cast(fieldType_)) << 16; } public: - Compiler(JSContext *cx, ICStub *firstMonitorStub, - ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) + Compiler(JSContext* cx, ICStub* firstMonitorStub, + ObjectGroup* group, uint32_t fieldOffset, JSValueType fieldType) : ICStubCompiler(cx, ICStub::GetProp_Unboxed), firstMonitorStub_(firstMonitorStub), group_(cx, group), @@ -4426,7 +4446,7 @@ class ICGetProp_Unboxed : public ICMonitoredStub fieldType_(fieldType) {} - ICStub *getStub(ICStubSpace *space) { + ICStub* getStub(ICStubSpace* space) { return ICStub::New(space, getStubCode(), firstMonitorStub_, group_, fieldOffset_); } @@ -4448,7 +4468,7 @@ class ICGetProp_TypedObject : public ICMonitoredStub HeapPtrShape shape_; uint32_t fieldOffset_; - ICGetProp_TypedObject(JitCode *stubCode, ICStub *firstMonitorStub, Shape *shape, + ICGetProp_TypedObject(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape, uint32_t fieldOffset) : ICMonitoredStub(ICStub::GetProp_TypedObject, stubCode, firstMonitorStub), shape_(shape), fieldOffset_(fieldOffset) @@ -4457,7 +4477,7 @@ class ICGetProp_TypedObject : public ICMonitoredStub } public: - HeapPtrShape &shape() { + HeapPtrShape& shape() { return shape_; } @@ -4470,13 +4490,13 @@ class ICGetProp_TypedObject : public ICMonitoredStub class Compiler : public ICStubCompiler { protected: - ICStub *firstMonitorStub_; + ICStub* firstMonitorStub_; RootedShape shape_; uint32_t fieldOffset_; TypedThingLayout layout_; - Rooted fieldDescr_; + Rooted fieldDescr_; - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { return static_cast(kind) | @@ -4485,8 +4505,8 @@ class ICGetProp_TypedObject : public ICMonitoredStub } public: - Compiler(JSContext *cx, ICStub *firstMonitorStub, - Shape *shape, uint32_t fieldOffset, SimpleTypeDescr *fieldDescr) + Compiler(JSContext* cx, ICStub* firstMonitorStub, + Shape* shape, uint32_t fieldOffset, SimpleTypeDescr* fieldDescr) : ICStubCompiler(cx, ICStub::GetProp_TypedObject), firstMonitorStub_(firstMonitorStub), shape_(cx, shape), @@ -4495,7 +4515,7 @@ class ICGetProp_TypedObject : public ICMonitoredStub fieldDescr_(cx, fieldDescr) {} - ICStub *getStub(ICStubSpace *space) { + ICStub* getStub(ICStubSpace* space) { return ICStub::New(space, getStubCode(), firstMonitorStub_, shape_, fieldOffset_); } @@ -4510,7 +4530,7 @@ class ICGetPropCallGetter : public ICMonitoredStub // Shape/group of receiver object. Used for both own and proto getters. // In the GetPropCallDOMProxyNative case, the receiver guard enforces // the proxy handler, because Shape implies Class. - ReceiverGuard receiverGuard_; + HeapReceiverGuard receiverGuard_; // Holder and holder shape. For own getters, guarding on receiverGuard_ is // sufficient, although Ion may use holder_ and holderShape_ even for own @@ -4526,21 +4546,21 @@ class ICGetPropCallGetter : public ICMonitoredStub // PC offset of call uint32_t pcOffset_; - ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard receiverGuard, JSObject *holder, - Shape *holderShape, JSFunction *getter, uint32_t pcOffset); + ICGetPropCallGetter(Kind kind, JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, JSObject* holder, + Shape* holderShape, JSFunction* getter, uint32_t pcOffset); public: - HeapPtrObject &holder() { + HeapPtrObject& holder() { return holder_; } - HeapPtrShape &holderShape() { + HeapPtrShape& holderShape() { return holderShape_; } - HeapPtrFunction &getter() { + HeapPtrFunction& getter() { return getter_; } - ReceiverGuard &receiverGuard() { + HeapReceiverGuard& receiverGuard() { return receiverGuard_; } @@ -4568,26 +4588,26 @@ class ICGetPropCallGetter : public ICMonitoredStub class Compiler : public ICStubCompiler { protected: - ICStub *firstMonitorStub_; + ICStub* firstMonitorStub_; RootedObject receiver_; RootedObject holder_; RootedFunction getter_; uint32_t pcOffset_; - const Class *outerClass_; + const Class* outerClass_; virtual int32_t getKey() const { // ICGetProp_CallNative::Compiler::getKey adds more bits to our // return value, so be careful when making changes here. return static_cast(kind) | - (ReceiverGuard::keyBits(receiver_) << 16) | + (HeapReceiverGuard::keyBits(receiver_) << 16) | (static_cast(!!outerClass_) << 18) | (static_cast(receiver_ != holder_) << 19); } public: - Compiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub, + Compiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub, HandleObject receiver, HandleObject holder, HandleFunction getter, - uint32_t pcOffset, const Class *outerClass) + uint32_t pcOffset, const Class* outerClass) : ICStubCompiler(cx, kind), firstMonitorStub_(firstMonitorStub), receiver_(cx, receiver), @@ -4609,33 +4629,33 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter friend class ICStubSpace; protected: - ICGetProp_CallScripted(JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard receiverGuard, - JSObject *holder, Shape *holderShape, - JSFunction *getter, uint32_t pcOffset) + ICGetProp_CallScripted(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset) : ICGetPropCallGetter(GetProp_CallScripted, stubCode, firstMonitorStub, receiverGuard, holder, holderShape, getter, pcOffset) {} public: - static ICGetProp_CallScripted *Clone(ICStubSpace *space, - ICStub *firstMonitorStub, ICGetProp_CallScripted &other); + static ICGetProp_CallScripted* Clone(ICStubSpace* space, + ICStub* firstMonitorStub, ICGetProp_CallScripted& other); class Compiler : public ICGetPropCallGetter::Compiler { protected: - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj, + Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject obj, HandleObject holder, HandleFunction getter, uint32_t pcOffset) : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, firstMonitorStub, obj, holder, getter, pcOffset, /* outerClass = */ nullptr) {} - ICStub *getStub(ICStubSpace *space) { - ReceiverGuard::StackGuard guard(receiver_); - Shape *holderShape = holder_->as().lastProperty(); + ICStub* getStub(ICStubSpace* space) { + ReceiverGuard guard(receiver_); + Shape* holderShape = holder_->as().lastProperty(); return ICStub::New(space, getStubCode(), firstMonitorStub_, guard, holder_, holderShape, getter_, pcOffset_); @@ -4650,23 +4670,23 @@ class ICGetProp_CallNative : public ICGetPropCallGetter protected: - ICGetProp_CallNative(JitCode *stubCode, ICStub *firstMonitorStub, - ReceiverGuard::StackGuard receiverGuard, - JSObject *holder, Shape *holderShape, - JSFunction *getter, uint32_t pcOffset) + ICGetProp_CallNative(JitCode* stubCode, ICStub* firstMonitorStub, + ReceiverGuard receiverGuard, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset) : ICGetPropCallGetter(GetProp_CallNative, stubCode, firstMonitorStub, receiverGuard, holder, holderShape, getter, pcOffset) {} public: - static ICGetProp_CallNative *Clone(ICStubSpace *space, ICStub *firstMonitorStub, - ICGetProp_CallNative &other); + static ICGetProp_CallNative* Clone(ICStubSpace* space, ICStub* firstMonitorStub, + ICGetProp_CallNative& other); class Compiler : public ICGetPropCallGetter::Compiler { bool inputDefinitelyObject_; protected: - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); virtual int32_t getKey() const { int32_t baseKey = ICGetPropCallGetter::Compiler::getKey(); @@ -4675,18 +4695,18 @@ class ICGetProp_CallNative : public ICGetPropCallGetter } public: - Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject receiver, + Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject receiver, HandleObject holder, HandleFunction getter, uint32_t pcOffset, - const Class *outerClass, bool inputDefinitelyObject = false) + const Class* outerClass, bool inputDefinitelyObject = false) : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallNative, firstMonitorStub, receiver, holder, getter, pcOffset, outerClass), inputDefinitelyObject_(inputDefinitelyObject) {} - ICStub *getStub(ICStubSpace *space) { - ReceiverGuard::StackGuard guard(receiver_); - Shape *holderShape = holder_->as().lastProperty(); + ICStub* getStub(ICStubSpace* space) { + ReceiverGuard guard(receiver_); + Shape* holderShape = holder_->as().lastProperty(); return ICStub::New(space, getStubCode(), firstMonitorStub_, guard, holder_, holderShape, getter_, pcOffset_); @@ -4701,14 +4721,14 @@ class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter // Object shape of expected expando object. (nullptr if no expando object should be there) HeapPtrShape expandoShape_; - ICGetPropCallDOMProxyNativeStub(ICStub::Kind kind, JitCode *stubCode, - ICStub *firstMonitorStub, Shape *shape, - Shape *expandoShape, - JSObject *holder, Shape *holderShape, - JSFunction *getter, uint32_t pcOffset); + ICGetPropCallDOMProxyNativeStub(ICStub::Kind kind, JitCode* stubCode, + ICStub* firstMonitorStub, Shape* shape, + Shape* expandoShape, + JSObject* holder, Shape* holderShape, + JSFunction* getter, uint32_t pcOffset); public: - HeapPtrShape &expandoShape() { + HeapPtrShape& expandoShape() { return expandoShape_; } static size_t offsetOfExpandoShape() { @@ -4969,13 +4989,13 @@ class ICSetProp_Native : public ICUpdatedStub HeapPtrShape shape_; uint32_t offset_; - ICSetProp_Native(JitCode *stubCode, ObjectGroup *group, Shape *shape, uint32_t offset); + ICSetProp_Native(JitCode* stubCode, ObjectGroup* group, Shape* shape, uint32_t offset); public: - HeapPtrObjectGroup &group() { + HeapPtrObjectGroup& group() { return group_; } - HeapPtrShape &shape() { + HeapPtrShape& shape() { return shape_; } void notePreliminaryObject() { @@ -5006,17 +5026,17 @@ class ICSetProp_Native : public ICUpdatedStub (static_cast(obj_->is()) << 17); } - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext *cx, HandleObject obj, bool isFixedSlot, uint32_t offset) + Compiler(JSContext* cx, HandleObject obj, bool isFixedSlot, uint32_t offset) : ICStubCompiler(cx, ICStub::SetProp_Native), obj_(cx, obj), isFixedSlot_(isFixedSlot), offset_(offset) {} - ICSetProp_Native *getStub(ICStubSpace *space); + ICSetProp_Native* getStub(ICStubSpace* space); }; }; @@ -5034,17 +5054,17 @@ class ICSetProp_NativeAdd : public ICUpdatedStub HeapPtrObjectGroup newGroup_; uint32_t offset_; - ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth, - Shape *newShape, ObjectGroup *newGroup, uint32_t offset); + ICSetProp_NativeAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth, + Shape* newShape, ObjectGroup* newGroup, uint32_t offset); public: size_t protoChainDepth() const { return extra_; } - HeapPtrObjectGroup &group() { + HeapPtrObjectGroup& group() { return group_; } - HeapPtrShape &newShape() { + HeapPtrShape& newShape() { return newShape_; } HeapPtrObjectGroup &newGroup() { @@ -5052,9 +5072,9 @@ class ICSetProp_NativeAdd : public ICUpdatedStub } template - ICSetProp_NativeAddImpl *toImpl() { + ICSetProp_NativeAddImpl* toImpl() { MOZ_ASSERT(ProtoChainDepth == protoChainDepth()); - return static_cast *>(this); + return static_cast*>(this); } static size_t offsetOfGroup() { @@ -5079,12 +5099,12 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array shapes_; - ICSetProp_NativeAddImpl(JitCode *stubCode, ObjectGroup *group, - const AutoShapeVector *shapes, - Shape *newShape, ObjectGroup *newGroup, uint32_t offset); + ICSetProp_NativeAddImpl(JitCode* stubCode, ObjectGroup* group, + const AutoShapeVector* shapes, + Shape* newShape, ObjectGroup* newGroup, uint32_t offset); public: - void traceShapes(JSTracer *trc) { + void traceShapes(JSTracer* trc) { for (size_t i = 0; i < NumShapes; i++) TraceEdge(trc, &shapes_[i], "baseline-setpropnativeadd-stub-shape"); } @@ -5111,15 +5131,15 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler (static_cast(protoChainDepth_) << 18); } - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - ICSetPropNativeAddCompiler(JSContext *cx, HandleObject obj, + ICSetPropNativeAddCompiler(JSContext* cx, HandleObject obj, HandleShape oldShape, HandleObjectGroup oldGroup, size_t protoChainDepth, bool isFixedSlot, uint32_t offset); template - ICUpdatedStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) + ICUpdatedStub* getStubSpecific(ICStubSpace* space, const AutoShapeVector* shapes) { RootedObjectGroup newGroup(cx, obj_->getGroup(cx)); if (!newGroup) @@ -5141,7 +5161,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_); } - ICUpdatedStub *getStub(ICStubSpace *space); + ICUpdatedStub* getStub(ICStubSpace* space); }; class ICSetProp_Unboxed : public ICUpdatedStub @@ -5151,7 +5171,7 @@ class ICSetProp_Unboxed : public ICUpdatedStub HeapPtrObjectGroup group_; uint32_t fieldOffset_; - ICSetProp_Unboxed(JitCode *stubCode, ObjectGroup *group, uint32_t fieldOffset) + ICSetProp_Unboxed(JitCode* stubCode, ObjectGroup* group, uint32_t fieldOffset) : ICUpdatedStub(ICStub::SetProp_Unboxed, stubCode), group_(group), fieldOffset_(fieldOffset) @@ -5160,7 +5180,7 @@ class ICSetProp_Unboxed : public ICUpdatedStub } public: - HeapPtrObjectGroup &group() { + HeapPtrObjectGroup& group() { return group_; } @@ -5216,7 +5236,7 @@ class ICSetProp_TypedObject : public ICUpdatedStub uint32_t fieldOffset_; bool isObjectReference_; - ICSetProp_TypedObject(JitCode *stubCode, Shape *shape, ObjectGroup *group, + ICSetProp_TypedObject(JitCode* stubCode, Shape* shape, ObjectGroup* group, uint32_t fieldOffset, bool isObjectReference) : ICUpdatedStub(ICStub::SetProp_TypedObject, stubCode), shape_(shape), @@ -5228,10 +5248,10 @@ class ICSetProp_TypedObject : public ICUpdatedStub } public: - HeapPtrShape &shape() { + HeapPtrShape& shape() { return shape_; } - HeapPtrObjectGroup &group() { + HeapPtrObjectGroup& group() { return group_; } bool isObjectReference() { @@ -5265,8 +5285,8 @@ class ICSetProp_TypedObject : public ICUpdatedStub } public: - Compiler(JSContext *cx, Shape* shape, ObjectGroup *group, uint32_t fieldOffset, - SimpleTypeDescr *fieldDescr) + Compiler(JSContext* cx, Shape* shape, ObjectGroup* group, uint32_t fieldOffset, + SimpleTypeDescr* fieldDescr) : ICStubCompiler(cx, ICStub::SetProp_TypedObject), shape_(cx, shape), group_(cx, group), @@ -5275,7 +5295,7 @@ class ICSetProp_TypedObject : public ICUpdatedStub fieldDescr_(cx, fieldDescr) {} - ICUpdatedStub *getStub(ICStubSpace *space) { + ICUpdatedStub* getStub(ICStubSpace* space) { bool isObjectReference = fieldDescr_->is() && fieldDescr_->as().type() == ReferenceTypeDescr::TYPE_OBJECT; @@ -5301,7 +5321,7 @@ class ICSetPropCallSetter : public ICStub protected: // Object shape/group. - ReceiverGuard guard_; + HeapReceiverGuard guard_; // Holder and shape. HeapPtrObject holder_; @@ -5313,21 +5333,21 @@ class ICSetPropCallSetter : public ICStub // PC of call, for profiler uint32_t pcOffset_; - ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::StackGuard guard, - JSObject *holder, Shape *holderShape, JSFunction *setter, + ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard guard, + JSObject* holder, Shape* holderShape, JSFunction* setter, uint32_t pcOffset); public: - ReceiverGuard &guard() { + HeapReceiverGuard& guard() { return guard_; } - HeapPtrObject &holder() { + HeapPtrObject& holder() { return holder_; } - HeapPtrShape &holderShape() { + HeapPtrShape& holderShape() { return holderShape_; } - HeapPtrFunction &setter() { + HeapPtrFunction& setter() { return setter_; } @@ -5356,11 +5376,11 @@ class ICSetPropCallSetter : public ICStub virtual int32_t getKey() const { return static_cast(kind) | - (ReceiverGuard::keyBits(obj_) << 16); + (HeapReceiverGuard::keyBits(obj_) << 16); } public: - Compiler(JSContext *cx, ICStub::Kind kind, HandleObject obj, HandleObject holder, + Compiler(JSContext* cx, ICStub::Kind kind, HandleObject obj, HandleObject holder, HandleFunction setter, uint32_t pcOffset) : ICStubCompiler(cx, kind), obj_(cx, obj), @@ -5379,30 +5399,30 @@ class ICSetProp_CallScripted : public ICSetPropCallSetter friend class ICStubSpace; protected: - ICSetProp_CallScripted(JitCode *stubCode, ReceiverGuard::StackGuard guard, JSObject *holder, - Shape *holderShape, JSFunction *setter, uint32_t pcOffset) + ICSetProp_CallScripted(JitCode* stubCode, ReceiverGuard guard, JSObject* holder, + Shape* holderShape, JSFunction* setter, uint32_t pcOffset) : ICSetPropCallSetter(SetProp_CallScripted, stubCode, guard, holder, holderShape, setter, pcOffset) {} public: - static ICSetProp_CallScripted *Clone(ICStubSpace *space, ICStub *, + static ICSetProp_CallScripted* Clone(ICStubSpace* space, ICStub* , ICSetProp_CallScripted& other); class Compiler : public ICSetPropCallSetter::Compiler { protected: - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter, + Compiler(JSContext* cx, HandleObject obj, HandleObject holder, HandleFunction setter, uint32_t pcOffset) : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallScripted, obj, holder, setter, pcOffset) {} - ICStub *getStub(ICStubSpace *space) { - ReceiverGuard::StackGuard guard(obj_); - Shape *holderShape = holder_->as().lastProperty(); + ICStub* getStub(ICStubSpace* space) { + ReceiverGuard guard(obj_); + Shape* holderShape = holder_->as().lastProperty(); return ICStub::New(space, getStubCode(), guard, holder_, holderShape, setter_, pcOffset_); } @@ -5415,30 +5435,30 @@ class ICSetProp_CallNative : public ICSetPropCallSetter friend class ICStubSpace; protected: - ICSetProp_CallNative(JitCode *stubCode, ReceiverGuard::StackGuard guard, JSObject *holder, - Shape *holderShape, JSFunction *setter, uint32_t pcOffset) + ICSetProp_CallNative(JitCode* stubCode, ReceiverGuard guard, JSObject* holder, + Shape* holderShape, JSFunction* setter, uint32_t pcOffset) : ICSetPropCallSetter(SetProp_CallNative, stubCode, guard, holder, holderShape, setter, pcOffset) {} public: - static ICSetProp_CallNative *Clone(ICStubSpace *space, ICStub *, - ICSetProp_CallNative &other); + static ICSetProp_CallNative* Clone(ICStubSpace* space, ICStub* , + ICSetProp_CallNative& other); class Compiler : public ICSetPropCallSetter::Compiler { protected: - bool generateStubCode(MacroAssembler &masm); + bool generateStubCode(MacroAssembler& masm); public: - Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter, + Compiler(JSContext* cx, HandleObject obj, HandleObject holder, HandleFunction setter, uint32_t pcOffset) : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallNative, obj, holder, setter, pcOffset) {} - ICStub *getStub(ICStubSpace *space) { - ReceiverGuard::StackGuard guard(obj_); - Shape *holderShape = holder_->as().lastProperty(); + ICStub* getStub(ICStubSpace* space) { + ReceiverGuard guard(obj_); + Shape* holderShape = holder_->as().lastProperty(); return ICStub::New(space, getStubCode(), guard, holder_, holderShape, setter_, pcOffset_); } @@ -5466,16 +5486,16 @@ class ICCallStubCompiler : public ICStubCompiler FunApply_Array }; - void pushCallArguments(MacroAssembler &masm, AllocatableGeneralRegisterSet regs, + void pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, Register argcReg, bool isJitCall); - void pushSpreadCallArguments(MacroAssembler &masm, AllocatableGeneralRegisterSet regs, + void pushSpreadCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, Register argcReg, bool isJitCall); - void guardSpreadCall(MacroAssembler &masm, Register argcReg, Label *failure); - Register guardFunApply(MacroAssembler &masm, AllocatableGeneralRegisterSet regs, + void guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure); + Register guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs, Register argcReg, bool checkNative, FunApplyThing applyThing, - Label *failure); - void pushCallerArguments(MacroAssembler &masm, AllocatableGeneralRegisterSet regs); - void pushArrayArguments(MacroAssembler &masm, Address arrayVal, + Label* failure); + void pushCallerArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs); + void pushArrayArguments(MacroAssembler& masm, Address arrayVal, AllocatableGeneralRegisterSet regs); }; diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index 4e7d6111bf..39db33d04b 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -21,9 +21,9 @@ SetElemICInspector::sawOOBDenseWrite() const if (!icEntry_) return false; - // Check for a SetElem_DenseAdd stub. + // Check for an element adding stub. for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) { - if (stub->isSetElem_DenseAdd()) + if (stub->isSetElem_DenseOrUnboxedArrayAdd()) return true; } @@ -59,7 +59,7 @@ SetElemICInspector::sawDenseWrite() const // Check for a SetElem_DenseAdd or SetElem_Dense stub. for (ICStub* stub = icEntry_->firstStub(); stub; stub = stub->next()) { - if (stub->isSetElem_DenseAdd() || stub->isSetElem_Dense()) + if (stub->isSetElem_DenseOrUnboxedArrayAdd() || stub->isSetElem_DenseOrUnboxedArray()) return true; } return false; @@ -91,7 +91,7 @@ VectorAppendNoDuplicate(S& list, T value) } static bool -AddReceiver(const ReceiverGuard::StackGuard& receiver, +AddReceiver(const ReceiverGuard& receiver, BaselineInspector::ReceiverVector& receivers, BaselineInspector::ObjectGroupVector& convertUnboxedGroups) { @@ -122,16 +122,16 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv ICStub* stub = entry.firstStub(); while (stub->next()) { - ReceiverGuard::StackGuard receiver; + ReceiverGuard receiver; if (stub->isGetProp_Native()) { receiver = stub->toGetProp_Native()->receiverGuard(); } else if (stub->isSetProp_Native()) { - receiver = ReceiverGuard::StackGuard(stub->toSetProp_Native()->group(), - stub->toSetProp_Native()->shape()); + receiver = ReceiverGuard(stub->toSetProp_Native()->group(), + stub->toSetProp_Native()->shape()); } else if (stub->isGetProp_Unboxed()) { - receiver = ReceiverGuard::StackGuard(stub->toGetProp_Unboxed()->group(), nullptr); + receiver = ReceiverGuard(stub->toGetProp_Unboxed()->group(), nullptr); } else if (stub->isSetProp_Unboxed()) { - receiver = ReceiverGuard::StackGuard(stub->toSetProp_Unboxed()->group(), nullptr); + receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr); } else { receivers.clear(); return true; @@ -463,6 +463,25 @@ BaselineInspector::getTemplateObject(jsbytecode* pc) return nullptr; } +ObjectGroup* +BaselineInspector::getTemplateObjectGroup(jsbytecode* pc) +{ + if (!hasBaselineScript()) + return nullptr; + + const ICEntry& entry = icEntryFromPC(pc); + for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { + switch (stub->kind()) { + case ICStub::NewArray_Fallback: + return stub->toNewArray_Fallback()->templateGroup(); + default: + break; + } + } + + return nullptr; +} + JSFunction* BaselineInspector::getSingleCallee(jsbytecode* pc) { @@ -560,16 +579,16 @@ BaselineInspector::templateCallObject() return &res->as(); } -static Shape * -GlobalShapeForGetPropFunction(ICStub *stub) +static Shape* +GlobalShapeForGetPropFunction(ICStub* stub) { if (stub->isGetProp_CallNative()) { - ICGetProp_CallNative *nstub = stub->toGetProp_CallNative(); + ICGetProp_CallNative* nstub = stub->toGetProp_CallNative(); if (nstub->isOwnGetter()) return nullptr; - const ReceiverGuard &guard = nstub->receiverGuard(); - if (Shape *shape = guard.shape()) { + const HeapReceiverGuard& guard = nstub->receiverGuard(); + if (Shape* shape = guard.shape()) { if (shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL) return shape; } diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 7146a43a1e..d0e0965201 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -92,7 +92,7 @@ class BaselineInspector bool dimorphicStub(jsbytecode* pc, ICStub** pfirst, ICStub** psecond); public: - typedef Vector ReceiverVector; + typedef Vector ReceiverVector; typedef Vector ObjectGroupVector; bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups); @@ -117,6 +117,10 @@ class BaselineInspector JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native); JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp); + // Sometimes the group a template object will have is known, even if the + // object itself isn't. + ObjectGroup* getTemplateObjectGroup(jsbytecode* pc); + JSFunction* getSingleCallee(jsbytecode* pc); DeclEnvObject* templateDeclEnvObject(); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 872c31e11f..cd2922cf00 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1023,11 +1023,11 @@ RegExpPairsVectorStartOffset(size_t inputOutputDataStartOffset) } static Address -RegExpPairCountAddress(size_t inputOutputDataStartOffset) +RegExpPairCountAddress(MacroAssembler &masm, size_t inputOutputDataStartOffset) { - return Address(StackPointer, inputOutputDataStartOffset - + sizeof(irregexp::InputOutputData) - + MatchPairs::offsetOfPairCount()); + return Address(masm.getStackPointer(), inputOutputDataStartOffset + + sizeof(irregexp::InputOutputData) + + MatchPairs::offsetOfPairCount()); } // Prepare an InputOutputData and optional MatchPairs which space has been @@ -1044,21 +1044,22 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData); size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); - Address inputStartAddress(StackPointer, + Address inputStartAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputStart)); - Address inputEndAddress(StackPointer, + Address inputEndAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputEnd)); - Address matchesPointerAddress(StackPointer, + Address matchesPointerAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, matches)); - Address startIndexAddress(StackPointer, + Address startIndexAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, startIndex)); - Address matchResultAddress(StackPointer, + Address matchResultAddress(masm.getStackPointer(), inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result)); - Address pairCountAddress = RegExpPairCountAddress(inputOutputDataStartOffset); - Address pairsPointerAddress(StackPointer, matchPairsStartOffset + MatchPairs::offsetOfPairs()); + Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset); + Address pairsPointerAddress(masm.getStackPointer(), + matchPairsStartOffset + MatchPairs::offsetOfPairs()); - Address pairsVectorAddress(StackPointer, pairsVectorStartOffset); + Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); RegExpStatics* res = cx->global()->getRegExpStatics(cx); if (!res) @@ -1132,7 +1133,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re // Finish filling in the InputOutputData instance on the stack. if (mode == RegExpShared::Normal) { - masm.computeEffectiveAddress(Address(StackPointer, matchPairsStartOffset), temp2); + masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2); masm.storePtr(temp2, matchesPointerAddress); } masm.storePtr(ImmWord(0), startIndexAddress); @@ -1146,7 +1147,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re volatileRegs.add(regexp); // Execute the RegExp. - masm.computeEffectiveAddress(Address(StackPointer, inputOutputDataStartOffset), temp2); + masm.computeEffectiveAddress(Address(masm.getStackPointer(), inputOutputDataStartOffset), temp2); masm.PushRegsInMask(volatileRegs); masm.setupUnalignedABICall(1, temp3); masm.passABIArg(temp2); @@ -1243,7 +1244,7 @@ CreateDependentString(MacroAssembler& masm, const JSAtomState& names, masm.push(base); // Adjust the start index address for the above pushes. - MOZ_ASSERT(startIndexAddress.base == StackPointer); + MOZ_ASSERT(startIndexAddress.base == masm.getStackPointer()); BaseIndex newStartIndexAddress = startIndexAddress; newStartIndexAddress.offset += 2 * sizeof(void*); @@ -1365,16 +1366,16 @@ JitCompartment::generateRegExpExecStub(JSContext* cx) masm.move32(Imm32(0), matchIndex); size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset); - Address pairsVectorAddress(StackPointer, pairsVectorStartOffset); - Address pairCountAddress = RegExpPairCountAddress(inputOutputDataStartOffset); + Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset); + Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset); size_t elementsOffset = NativeObject::offsetOfFixedElements(); BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset); JS_STATIC_ASSERT(sizeof(MatchPair) == 8); - BaseIndex stringIndexAddress(StackPointer, matchIndex, TimesEight, + BaseIndex stringIndexAddress(masm.getStackPointer(), matchIndex, TimesEight, pairsVectorStartOffset + offsetof(MatchPair, start)); - BaseIndex stringLimitAddress(StackPointer, matchIndex, TimesEight, + BaseIndex stringLimitAddress(masm.getStackPointer(), matchIndex, TimesEight, pairsVectorStartOffset + offsetof(MatchPair, limit)); // Loop to construct the match strings. There are two different loops, @@ -1487,7 +1488,8 @@ CodeGenerator::visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool) regs.take(regexp); Register temp = regs.takeAny(); - masm.computeEffectiveAddress(Address(StackPointer, sizeof(irregexp::InputOutputData)), temp); + masm.computeEffectiveAddress(Address(masm.getStackPointer(), + sizeof(irregexp::InputOutputData)), temp); pushArg(temp); pushArg(input); @@ -1952,7 +1954,7 @@ void CodeGenerator::visitCallee(LCallee* lir) { Register callee = ToRegister(lir->output()); - Address ptr(StackPointer, frameSize() + JitFrameLayout::offsetOfCalleeToken()); + Address ptr(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfCalleeToken()); masm.loadFunctionFromCalleeToken(ptr, callee); } @@ -1961,7 +1963,7 @@ void CodeGenerator::visitIsConstructing(LIsConstructing* lir) { Register output = ToRegister(lir->output()); - Address calleeToken(StackPointer, frameSize() + JitFrameLayout::offsetOfCalleeToken()); + Address calleeToken(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfCalleeToken()); masm.loadPtr(calleeToken, output); // We must be inside a function. @@ -2012,7 +2014,7 @@ CodeGenerator::visitOsrEntry(LOsrEntry* lir) // If profiling, save the current frame pointer to a per-thread global field. if (isProfilerInstrumentationEnabled()) - masm.profilerEnterFrame(StackPointer, temp); + masm.profilerEnterFrame(masm.getStackPointer(), temp); // Allocate the full frame for this function // Note we have a new entry here. So we reset MacroAssembler::framePushed() @@ -2085,7 +2087,7 @@ CodeGenerator::visitStackArgT(LStackArgT* lir) MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount()); int32_t stack_offset = StackOffsetOfPassedArg(argslot); - Address dest(StackPointer, stack_offset); + Address dest(masm.getStackPointer(), stack_offset); if (arg->isFloatReg()) masm.storeDouble(ToFloatRegister(arg), dest); @@ -2104,7 +2106,7 @@ CodeGenerator::visitStackArgV(LStackArgV* lir) int32_t stack_offset = StackOffsetOfPassedArg(argslot); - masm.storeValue(val, Address(StackPointer, stack_offset)); + masm.storeValue(val, Address(masm.getStackPointer(), stack_offset)); } void @@ -2256,7 +2258,7 @@ CodeGenerator::visitStoreSlotV(LStoreSlotV* lir) } static void -GuardReceiver(MacroAssembler& masm, const ReceiverGuard::StackGuard& guard, +GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard, Register obj, Register scratch, Label* miss, bool checkNullExpando) { if (guard.group) { @@ -2284,7 +2286,7 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis Label done; for (size_t i = 0; i < mir->numReceivers(); i++) { - ReceiverGuard::StackGuard receiver = mir->receiver(i); + ReceiverGuard receiver = mir->receiver(i); Label next; GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); @@ -2343,6 +2345,18 @@ CodeGenerator::visitGetPropertyPolymorphicT(LGetPropertyPolymorphicT* ins) emitGetPropertyPolymorphic(ins, obj, temp, output); } +template +static void +EmitUnboxedPreBarrier(MacroAssembler &masm, T address, JSValueType type) +{ + if (type == JSVAL_TYPE_OBJECT) + masm.patchableCallPreBarrier(address, MIRType_Object); + else if (type == JSVAL_TYPE_STRING) + masm.patchableCallPreBarrier(address, MIRType_String); + else + MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); +} + void CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Register scratch, const ConstantOrRegister& value) @@ -2351,7 +2365,7 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis Label done; for (size_t i = 0; i < mir->numReceivers(); i++) { - ReceiverGuard::StackGuard receiver = mir->receiver(i); + ReceiverGuard receiver = mir->receiver(i); Label next; GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); @@ -2381,13 +2395,7 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis receiver.group->unboxedLayout().lookup(mir->name()); Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); - if (property->type == JSVAL_TYPE_OBJECT) - masm.patchableCallPreBarrier(propertyAddr, MIRType_Object); - else if (property->type == JSVAL_TYPE_STRING) - masm.patchableCallPreBarrier(propertyAddr, MIRType_String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(property->type)); - + EmitUnboxedPreBarrier(masm, propertyAddr, property->type); masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr); } @@ -2429,7 +2437,9 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins) void CodeGenerator::visitElements(LElements* lir) { - Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); + Address elements(ToRegister(lir->object()), + lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements() + : NativeObject::offsetOfElements()); masm.loadPtr(elements, ToRegister(lir->output())); } @@ -2527,7 +2537,7 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir) Label done; for (size_t i = 0; i < mir->numReceivers(); i++) { - const ReceiverGuard::StackGuard& receiver = mir->receiver(i); + const ReceiverGuard& receiver = mir->receiver(i); Label next; GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true); @@ -2745,8 +2755,8 @@ CodeGenerator::visitCallNative(LCallNative* call) // Preload arguments into registers. masm.loadJSContext(argContextReg); - masm.move32(Imm32(call->numStackArgs()), argUintNReg); - masm.movePtr(StackPointer, argVpReg); + masm.move32(Imm32(call->numActualArgs()), argUintNReg); + masm.moveStackPtrTo(argVpReg); masm.Push(argUintNReg); @@ -2768,7 +2778,7 @@ CodeGenerator::visitCallNative(LCallNative* call) masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); // Load the outparam vp[0] into output register(s). - masm.loadValue(Address(StackPointer, NativeExitFrameLayout::offsetOfResult()), JSReturnOperand); + masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), JSReturnOperand); // The next instruction is removing the footer of the exit frame, so there // is no need for leaveFakeExitFrame. @@ -2838,7 +2848,7 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call) // &vp[1] masm.adjustStack(unusedStack); // argObj is filled with the extracted object, then returned. - Register obj = masm.extractObject(Address(StackPointer, 0), argObj); + Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj); MOZ_ASSERT(obj == argObj); // Push a Value containing the callee object: natives are allowed to access their callee before @@ -2851,23 +2861,23 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call) JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgv == 0); JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgc == IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv); - masm.computeEffectiveAddress(Address(StackPointer, 2 * sizeof(Value)), argArgs); + masm.computeEffectiveAddress(Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs); LoadDOMPrivate(masm, obj, argPrivate); // Push argc from the call instruction into what will become the IonExitFrame - masm.Push(Imm32(call->numStackArgs())); + masm.Push(Imm32(call->numActualArgs())); // Push our argv onto the stack masm.Push(argArgs); // And store our JSJitMethodCallArgs* in argArgs. - masm.movePtr(StackPointer, argArgs); + masm.moveStackPtrTo(argArgs); // Push |this| object for passing HandleObject. We push after argc to // maintain the same sp-relative location of the object pointer with other // DOMExitFrames. masm.Push(argObj); - masm.movePtr(StackPointer, argObj); + masm.moveStackPtrTo(argObj); // Construct native exit frame. uint32_t safepointOffset; @@ -2888,14 +2898,14 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative* call) masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->jitInfo()->method)); if (target->jitInfo()->isInfallible) { - masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()), + masm.loadValue(Address(masm.getStackPointer(), IonDOMMethodExitFrameLayout::offsetOfResult()), JSReturnOperand); } else { // Test for failure. masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); // Load the outparam vp[0] into output register(s). - masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()), + masm.loadValue(Address(masm.getStackPointer(), IonDOMMethodExitFrameLayout::offsetOfResult()), JSReturnOperand); } @@ -2929,9 +2939,9 @@ CodeGenerator::emitCallInvokeFunction(LInstruction* call, Register calleereg, // Each path must account for framePushed_ separately, for callVM to be valid. masm.freeStack(unusedStack); - pushArg(StackPointer); // argv. - pushArg(Imm32(argc)); // argc. - pushArg(calleereg); // JSFunction*. + pushArg(masm.getStackPointer()); // argv. + pushArg(Imm32(argc)); // argc. + pushArg(calleereg); // JSFunction*. callVM(InvokeFunctionInfo, call); @@ -2983,8 +2993,11 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call) masm.Push(Imm32(descriptor)); // Check whether the provided arguments satisfy target argc. + // We cannot have lowered to LCallGeneric with a known target. Assert that we didn't + // add any undefineds in IonBuilder. NB: MCall::numStackArgs includes |this|. + MOZ_ASSERT(call->numActualArgs() == call->mir()->numStackArgs() - 1); masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), nargsreg); - masm.branch32(Assembler::Above, nargsreg, Imm32(call->numStackArgs()), &thunk); + masm.branch32(Assembler::Above, nargsreg, Imm32(call->numActualArgs()), &thunk); masm.jump(&makeCall); // Argument fixed needed. Load the ArgumentsRectifier. @@ -2993,7 +3006,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call) MOZ_ASSERT(ArgumentsRectifierReg != objreg); masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking. masm.loadPtr(Address(objreg, JitCode::offsetOfCode()), objreg); - masm.move32(Imm32(call->numStackArgs()), ArgumentsRectifierReg); + masm.move32(Imm32(call->numActualArgs()), ArgumentsRectifierReg); } // Finally call the function in objreg. @@ -3018,7 +3031,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric* call) if (call->mir()->isConstructing()) { Label notPrimitive; masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, ¬Primitive); - masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand); + masm.loadValue(Address(masm.getStackPointer(), unusedStack), JSReturnOperand); masm.bind(¬Primitive); } } @@ -3035,7 +3048,7 @@ CodeGenerator::visitCallKnown(LCallKnown* call) // Native single targets are handled by LCallNative. MOZ_ASSERT(!target->isNative()); // Missing arguments must have been explicitly appended by the IonBuilder. - MOZ_ASSERT(target->nargs() <= call->numStackArgs()); + MOZ_ASSERT(target->nargs() <= call->mir()->numStackArgs() - 1); MOZ_ASSERT_IF(call->mir()->isConstructing(), target->isInterpretedConstructor()); @@ -3084,7 +3097,7 @@ CodeGenerator::visitCallKnown(LCallKnown* call) if (call->mir()->isConstructing()) { Label notPrimitive; masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, ¬Primitive); - masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand); + masm.loadValue(Address(masm.getStackPointer(), unusedStack), JSReturnOperand); masm.bind(¬Primitive); } } @@ -3096,7 +3109,7 @@ CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraSt MOZ_ASSERT(objreg != extraStackSize); // Push the space used by the arguments. - masm.movePtr(StackPointer, objreg); + masm.moveStackPtrTo(objreg); masm.Push(extraStackSize); pushArg(objreg); // argv. @@ -3136,7 +3149,7 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp // Reserve space for copying the arguments. NativeObject::elementsSizeMustNotOverflow(); masm.lshiftPtr(Imm32(ValueShift), extraStackSpace); - masm.subPtr(extraStackSpace, StackPointer); + masm.subFromStackPtr(extraStackSpace); #ifdef DEBUG // Put a magic value in the space reserved for padding. Note, this code @@ -3147,7 +3160,7 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp Label noPaddingNeeded; // if the number of arguments is odd, then we do not need any padding. masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); - BaseValueIndex dstPtr(StackPointer, argcreg); + BaseValueIndex dstPtr(masm.getStackPointer(), argcreg); masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr); masm.bind(&noPaddingNeeded); } @@ -3180,7 +3193,7 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp // srcPtr = (StackPointer + extraStackSpace) + argvSrcOffset // dstPtr = (StackPointer ) + argvDstOffset - masm.addPtr(StackPointer, argvSrcBase); + masm.addStackPtrTo(argvSrcBase); // Copy arguments. { @@ -3191,14 +3204,14 @@ CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSp // to loop back, we have to substract the size of the word which are // copied. BaseValueIndex srcPtr(argvSrcBase, argvIndex, argvSrcOffset - sizeof(void*)); - BaseValueIndex dstPtr(StackPointer, argvIndex, argvDstOffset - sizeof(void*)); + BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, argvDstOffset - sizeof(void*)); masm.loadPtr(srcPtr, copyreg); masm.storePtr(copyreg, dstPtr); // Handle 32 bits architectures. if (sizeof(Value) == 2 * sizeof(void*)) { BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, argvSrcOffset - 2 * sizeof(void*)); - BaseValueIndex dstPtrLow(StackPointer, argvIndex, argvDstOffset - 2 * sizeof(void*)); + BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, argvDstOffset - 2 * sizeof(void*)); masm.loadPtr(srcPtrLow, copyreg); masm.storePtr(copyreg, dstPtrLow); } @@ -3317,7 +3330,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) markSafepointAt(callOffset, apply); // Recover the number of arguments from the frame descriptor. - masm.loadPtr(Address(StackPointer, 0), stackSpace); + masm.loadPtr(Address(masm.getStackPointer(), 0), stackSpace); masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), stackSpace); masm.subPtr(Imm32(pushed), stackSpace); @@ -3382,7 +3395,7 @@ CodeGenerator::visitGetDynamicName(LGetDynamicName* lir) /* Make space for the outparam. */ masm.adjustStack(-int32_t(sizeof(Value))); - masm.movePtr(StackPointer, temp2); + masm.moveStackPtrTo(temp2); masm.setupUnalignedABICall(4, temp1); masm.passABIArg(temp3); @@ -3393,7 +3406,7 @@ CodeGenerator::visitGetDynamicName(LGetDynamicName* lir) const ValueOperand out = ToOutValue(lir); - masm.loadValue(Address(StackPointer, 0), out); + masm.loadValue(Address(masm.getStackPointer(), 0), out); masm.adjustStack(sizeof(Value)); Label undefined; @@ -3470,7 +3483,7 @@ CodeGenerator::generateArgumentsChecks(bool bailout) // ... * sizeof(Value) - Scale by value size. // ArgToStackOffset(...) - Compute displacement within arg vector. int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value)); - masm.guardTypeSet(Address(StackPointer, offset), types, BarrierKind::TypeSet, temp, &miss); + masm.guardTypeSet(Address(masm.getStackPointer(), offset), types, BarrierKind::TypeSet, temp, &miss); } if (miss.used()) { @@ -3529,7 +3542,7 @@ CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) addOutOfLineCode(ool, lir->mir()); // Conditional forward (unlikely) branch to failure. - masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), StackPointer, ool->entry()); + masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), ool->entry()); masm.bind(ool->rejoin()); } @@ -3816,7 +3829,7 @@ CodeGenerator::emitAssertResultV(const ValueOperand input, TemporaryTypeSet *typ saveVolatile(); masm.pushValue(input); - masm.movePtr(StackPointer, temp1); + masm.moveStackPtrTo(temp1); masm.setupUnalignedABICall(2, temp2); masm.loadJSContext(temp2); @@ -4006,28 +4019,40 @@ class OutOfLineNewArray : public OutOfLineCodeBase } }; -typedef ArrayObject *(*NewDenseArrayFn)(ExclusiveContext *, uint32_t, HandleObjectGroup, +typedef JSObject* (*NewArrayOperationFn)(JSContext*, HandleScript, jsbytecode*, uint32_t, + NewObjectKind); +static const VMFunction NewArrayOperationInfo = + FunctionInfo(NewArrayOperation); + +typedef ArrayObject* (*NewDenseArrayFn)(ExclusiveContext*, uint32_t, HandleObjectGroup, AllocatingBehaviour, bool); static const VMFunction NewDenseArrayInfo = FunctionInfo(NewDenseArray); void -CodeGenerator::visitNewArrayCallVM(LNewArray *lir) +CodeGenerator::visitNewArrayCallVM(LNewArray* lir) { Register objReg = ToRegister(lir->output()); MOZ_ASSERT(!lir->isCall()); saveLive(lir); - JSObject *templateObject = lir->mir()->templateObject(); - ObjectGroup *group = - templateObject->isSingleton() ? nullptr : templateObject->group(); + JSObject* templateObject = lir->mir()->templateObject(); - pushArg(Imm32(lir->mir()->convertDoubleElements())); - pushArg(Imm32(lir->mir()->allocatingBehaviour())); - pushArg(ImmGCPtr(group)); - pushArg(Imm32(lir->mir()->count())); + if (templateObject && !templateObject->is()) { + pushArg(Imm32(lir->mir()->convertDoubleElements())); + pushArg(Imm32(lir->mir()->allocatingBehaviour())); + pushArg(ImmGCPtr(templateObject->group())); + pushArg(Imm32(lir->mir()->count())); - callVM(NewDenseArrayInfo, lir); + callVM(NewDenseArrayInfo, lir); + } else { + pushArg(Imm32(GenericObject)); + pushArg(Imm32(lir->mir()->count())); + pushArg(ImmPtr(lir->mir()->pc())); + pushArg(ImmGCPtr(lir->mir()->block()->info().script())); + + callVM(NewArrayOperationInfo, lir); + } if (ReturnReg != objReg) masm.movePtr(ReturnReg, objReg); @@ -4093,11 +4118,11 @@ CodeGenerator::visitHypot(LHypot* lir) } void -CodeGenerator::visitNewArray(LNewArray *lir) +CodeGenerator::visitNewArray(LNewArray* lir) { Register objReg = ToRegister(lir->output()); Register tempReg = ToRegister(lir->temp()); - ArrayObject* templateObject = lir->mir()->templateObject(); + JSObject* templateObject = lir->mir()->templateObject(); DebugOnly count = lir->mir()->count(); MOZ_ASSERT(count < NativeObject::NELEMENTS_LIMIT); @@ -4107,7 +4132,7 @@ CodeGenerator::visitNewArray(LNewArray *lir) return; } - OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir); + OutOfLineNewArray* ool = new(alloc()) OutOfLineNewArray(lir); addOutOfLineCode(ool, lir->mir()); masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), @@ -4794,7 +4819,7 @@ CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) const LAllocation* callObj = lir->getCallObject(); Register temp = ToRegister(lir->getTemp(0)); - masm.movePtr(StackPointer, temp); + masm.moveStackPtrTo(temp); masm.addPtr(Imm32(frameSize()), temp); pushArg(ToRegister(callObj)); @@ -6121,7 +6146,7 @@ JitRuntime::generateLazyLinkStub(JSContext* cx) // The caller did not push an exit frame on the stack, it pushed a // JitFrameLayout. We modify the descriptor to be a valid exit frame and // restore it once the lazy link is complete. - Address descriptor(StackPointer, CommonFrameLayout::offsetOfDescriptor()); + Address descriptor(masm.getStackPointer(), CommonFrameLayout::offsetOfDescriptor()); size_t convertToExitFrame = JitFrameLayout::Size() - ExitFrameLayout::Size(); masm.addPtr(Imm32(convertToExitFrame << FRAMESIZE_SHIFT), descriptor); @@ -6225,6 +6250,30 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) masm.bumpKey(&index, -1); } +void +CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir) +{ + Register obj = ToRegister(lir->object()); + Register result = ToRegister(lir->output()); + masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result); +} + +void +CodeGenerator::visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir) +{ + Register obj = ToRegister(lir->object()); + Register result = ToRegister(lir->output()); + masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), result); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), result); +} + +void +CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir) +{ + Register obj = ToRegister(lir->object()); + masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); +} + void CodeGenerator::visitNotO(LNotO* lir) { @@ -6429,18 +6478,21 @@ CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation* index, bailoutFrom(&bail, snapshot); } +static ConstantOrRegister +ToConstantOrRegister(const LAllocation* value, MIRType valueType) +{ + if (value->isConstant()) + return ConstantOrRegister(*value->toConstant()); + return TypedOrValueRegister(valueType, ToAnyRegister(value)); +} + void CodeGenerator::emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType, Register elements, const LAllocation* index, int32_t offsetAdjustment) { - ConstantOrRegister v; - if (value->isConstant()) - v = ConstantOrRegister(*value->toConstant()); - else - v = TypedOrValueRegister(valueType, ToAnyRegister(value)); - + ConstantOrRegister v = ToConstantOrRegister(value, valueType); if (index->isConstant()) { Address dest(elements, ToInt32(index) * sizeof(js::Value) + offsetAdjustment); masm.storeUnboxedValue(v, valueType, dest, elementType); @@ -6497,19 +6549,45 @@ CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir); addOutOfLineCode(ool, lir->mir()); + Register obj = ToRegister(lir->object()); Register elements = ToRegister(lir->elements()); const LAllocation* index = lir->index(); - // OOL path if index >= initializedLength. - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); + JSValueType unboxedType = lir->mir()->unboxedType(); + if (unboxedType == JSVAL_TYPE_MAGIC) { + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index); - masm.bind(ool->rejoinStore()); - emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), - elements, index, 0); + masm.bind(ool->rejoinStore()); + emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), + elements, index, 0); + } else { + Register temp = ToRegister(lir->getTemp(0)); + Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + masm.load32(initLength, temp); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); + masm.branchKey(Assembler::BelowOrEqual, temp, ToInt32Key(index), ool->entry()); + + ConstantOrRegister v = ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); + + if (index->isConstant()) { + Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); + EmitUnboxedPreBarrier(masm, address, unboxedType); + + masm.bind(ool->rejoinStore()); + masm.storeUnboxedProperty(address, unboxedType, v, nullptr); + } else { + BaseIndex address(elements, ToRegister(index), + ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); + EmitUnboxedPreBarrier(masm, address, unboxedType); + + masm.bind(ool->rejoinStore()); + masm.storeUnboxedProperty(address, unboxedType, v, nullptr); + } + } masm.bind(ool->rejoin()); } @@ -6520,29 +6598,54 @@ CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir); addOutOfLineCode(ool, lir->mir()); + Register obj = ToRegister(lir->object()); Register elements = ToRegister(lir->elements()); const LAllocation* index = lir->index(); const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value); - // OOL path if index >= initializedLength. - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); + JSValueType unboxedType = lir->mir()->unboxedType(); + if (unboxedType == JSVAL_TYPE_MAGIC) { + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index); - masm.bind(ool->rejoinStore()); - if (lir->index()->isConstant()) - masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value))); - else - masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight)); + masm.bind(ool->rejoinStore()); + if (index->isConstant()) + masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); + else + masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); + } else { + Register temp = ToRegister(lir->getTemp(0)); + Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + masm.load32(initLength, temp); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); + masm.branchKey(Assembler::BelowOrEqual, temp, ToInt32Key(index), ool->entry()); + + if (index->isConstant()) { + Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); + EmitUnboxedPreBarrier(masm, address, unboxedType); + + masm.bind(ool->rejoinStore()); + masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); + } else { + BaseIndex address(elements, ToRegister(index), + ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); + EmitUnboxedPreBarrier(masm, address, unboxedType); + + masm.bind(ool->rejoinStore()); + masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); + } + } masm.bind(ool->rejoin()); } -typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue, - bool strict); -static const VMFunction SetDenseElementInfo = FunctionInfo(SetDenseElement); +typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t, + HandleValue, bool strict); +static const VMFunction SetDenseOrUnboxedArrayElementInfo = + FunctionInfo(SetDenseOrUnboxedArrayElement); void CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) @@ -6552,6 +6655,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) const LAllocation* index; MIRType valueType; ConstantOrRegister value; + JSValueType unboxedType; + LDefinition *temp = nullptr; if (ins->isStoreElementHoleV()) { LStoreElementHoleV* store = ins->toStoreElementHoleV(); @@ -6560,6 +6665,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value)); + unboxedType = store->mir()->unboxedType(); + temp = store->getTemp(0); } else { LStoreElementHoleT* store = ins->toStoreElementHoleT(); object = ToRegister(store->object()); @@ -6570,6 +6677,8 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) value = ConstantOrRegister(*store->value()->toConstant()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); + unboxedType = store->mir()->unboxedType(); + temp = store->getTemp(0); } // If index == initializedLength, try to bump the initialized length inline. @@ -6578,33 +6687,55 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) Label callStub; #ifdef JS_CODEGEN_MIPS // Had to reimplement for MIPS because there are no flags. - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub); + if (unboxedType == JSVAL_TYPE_MAGIC) { + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub); + } else { + Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + masm.load32(initLength, ToRegister(temp)); + masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp)); + masm.branchKey(Assembler::NotEqual, ToRegister(temp), ToInt32Key(index), &callStub); + } #else masm.j(Assembler::NotEqual, &callStub); #endif Int32Key key = ToInt32Key(index); - // Check array capacity. - masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), - key, &callStub); + if (unboxedType == JSVAL_TYPE_MAGIC) { + // Check array capacity. + masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), + key, &callStub); - // Update initialized length. The capacity guard above ensures this won't overflow, - // due to NELEMENTS_LIMIT. - masm.bumpKey(&key, 1); - masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength())); + // Update initialized length. The capacity guard above ensures this won't overflow, + // due to NELEMENTS_LIMIT. + masm.bumpKey(&key, 1); + masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength())); - // Update length if length < initializedLength. - Label dontUpdate; - masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), - key, &dontUpdate); - masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength())); - masm.bind(&dontUpdate); + // Update length if length < initializedLength. + Label dontUpdate; + masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), + key, &dontUpdate); + masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength())); + masm.bind(&dontUpdate); - masm.bumpKey(&key, -1); + masm.bumpKey(&key, -1); + } else { + // Check array capacity. + masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub); - if (ins->isStoreElementHoleT() && valueType != MIRType_Double) { + // Update initialized length. + masm.add32(Imm32(1), Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); + + // Update length if length < initializedLength. + Address lengthAddr(object, UnboxedArrayObject::offsetOfLength()); + Label dontUpdate; + masm.branchKey(Assembler::Above, lengthAddr, key, &dontUpdate); + masm.add32(Imm32(1), lengthAddr); + masm.bind(&dontUpdate); + } + + if (ins->isStoreElementHoleT() && unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType_Double) { // The inline path for StoreElementHoleT does not always store the type tag, // so we do the store on the OOL path. We use MIRType_None for the element type // so that storeElementTyped will always store the type tag. @@ -6626,7 +6757,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) else pushArg(ToRegister(index)); pushArg(object); - callVM(SetDenseElementInfo, ins); + callVM(SetDenseOrUnboxedArrayElementInfo, ins); restoreLive(ins); masm.jump(ool->rejoin()); @@ -6945,32 +7076,60 @@ CodeGenerator::visitIteratorStart(LIteratorStart* lir) masm.branchTest32(Assembler::NonZero, Address(niTemp, offsetof(NativeIterator, flags)), Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), ool->entry()); - // Load the iterator's shape array. - masm.loadPtr(Address(niTemp, offsetof(NativeIterator, shapes_array)), temp2); + // Load the iterator's receiver guard array. + masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2); - // Compare shape of object with the first shape. - masm.loadObjShape(obj, temp1); - masm.branchPtr(Assembler::NotEqual, Address(temp2, 0), temp1, ool->entry()); + // Compare object with the first receiver guard. The last iterator can only + // match for native objects and unboxed objects. + { + Address groupAddr(temp2, offsetof(ReceiverGuard, group)); + Address shapeAddr(temp2, offsetof(ReceiverGuard, shape)); + Label guardDone, shapeMismatch, noExpando; + masm.loadObjShape(obj, temp1); + masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch); - // Compare shape of object's prototype with the second shape. + // Ensure the object does not have any elements. The presence of dense + // elements is not captured by the shape tests above. + masm.branchPtr(Assembler::NotEqual, + Address(obj, NativeObject::offsetOfElements()), + ImmPtr(js::emptyObjectElements), + ool->entry()); + masm.jump(&guardDone); + + masm.bind(&shapeMismatch); + masm.loadObjGroup(obj, temp1); + masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry()); + masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1); + masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando); + masm.branchPtr(Assembler::NotEqual, + Address(temp1, NativeObject::offsetOfElements()), + ImmPtr(js::emptyObjectElements), + ool->entry()); + masm.loadObjShape(temp1, temp1); + masm.bind(&noExpando); + masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry()); + masm.bind(&guardDone); + } + + // Compare shape of object's prototype with the second shape. The prototype + // must be native, as unboxed objects cannot be prototypes (they cannot + // have the delegate flag set). Also check for the absence of dense elements. + Address prototypeShapeAddr(temp2, sizeof(ReceiverGuard) + offsetof(ReceiverGuard, shape)); masm.loadObjProto(obj, temp1); + masm.branchPtr(Assembler::NotEqual, + Address(temp1, NativeObject::offsetOfElements()), + ImmPtr(js::emptyObjectElements), + ool->entry()); masm.loadObjShape(temp1, temp1); - masm.branchPtr(Assembler::NotEqual, Address(temp2, sizeof(Shape*)), temp1, ool->entry()); + masm.branchPtr(Assembler::NotEqual, prototypeShapeAddr, temp1, ool->entry()); // Ensure the object's prototype's prototype is nullptr. The last native // iterator will always have a prototype chain length of one (i.e. it must - // be a plain ), so we do not need to generate a loop here. + // be a plain object), so we do not need to generate a loop here. masm.loadObjProto(obj, temp1); masm.loadObjProto(temp1, temp1); masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry()); - // Ensure the object does not have any elements. The presence of dense - // elements is not captured by the shape tests above. - masm.branchPtr(Assembler::NotEqual, - Address(obj, NativeObject::offsetOfElements()), - ImmPtr(js::emptyObjectElements), - ool->entry()); - // Write barrier for stores to the iterator. We only need to take a write // barrier if NativeIterator::obj is actually going to change. { @@ -7115,7 +7274,7 @@ CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) { // read number of actual arguments from the JS frame. Register argc = ToRegister(lir->output()); - Address ptr(StackPointer, frameSize() + JitFrameLayout::offsetOfNumActualArgs()); + Address ptr(masm.getStackPointer(), frameSize() + JitFrameLayout::offsetOfNumActualArgs()); masm.loadPtr(ptr, argc); } @@ -7129,11 +7288,11 @@ CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) if (index->isConstant()) { int32_t i = index->toConstant()->toInt32(); - Address argPtr(StackPointer, sizeof(Value) * i + argvOffset); + Address argPtr(masm.getStackPointer(), sizeof(Value) * i + argvOffset); masm.loadValue(argPtr, result); } else { Register i = ToRegister(index); - BaseValueIndex argPtr(StackPointer, i, argvOffset); + BaseValueIndex argPtr(masm.getStackPointer(), i, argvOffset); masm.loadValue(argPtr, result); } } @@ -7149,11 +7308,11 @@ CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT* lir) if (type == MIRType_Double) { // Store doubles directly. FloatRegister input = ToFloatRegister(lir->input()); - masm.storeDouble(input, Address(StackPointer, argOffset)); + masm.storeDouble(input, Address(masm.getStackPointer(), argOffset)); } else { Register input = ToRegister(lir->input()); - masm.storeValue(ValueTypeFromMIRType(type), input, Address(StackPointer, argOffset)); + masm.storeValue(ValueTypeFromMIRType(type), input, Address(masm.getStackPointer(), argOffset)); } } @@ -7162,7 +7321,7 @@ CodeGenerator:: visitSetFrameArgumentC(LSetFrameArgumentC* lir) { size_t argOffset = frameSize() + JitFrameLayout::offsetOfActualArgs() + (sizeof(Value) * lir->mir()->argno()); - masm.storeValue(lir->val(), Address(StackPointer, argOffset)); + masm.storeValue(lir->val(), Address(masm.getStackPointer(), argOffset)); } void @@ -7171,7 +7330,7 @@ CodeGenerator:: visitSetFrameArgumentV(LSetFrameArgumentV* lir) const ValueOperand val = ToValue(lir, LSetFrameArgumentV::Input); size_t argOffset = frameSize() + JitFrameLayout::offsetOfActualArgs() + (sizeof(Value) * lir->mir()->argno()); - masm.storeValue(val, Address(StackPointer, argOffset)); + masm.storeValue(val, Address(masm.getStackPointer(), argOffset)); } typedef bool (*RunOnceScriptPrologueFn)(JSContext*, HandleScript); @@ -7197,7 +7356,7 @@ CodeGenerator::emitRest(LInstruction* lir, Register array, Register numActuals, { // Compute actuals() + numFormals. size_t actualsOffset = frameSize() + JitFrameLayout::offsetOfActualArgs(); - masm.movePtr(StackPointer, temp1); + masm.moveStackPtrTo(temp1); masm.addPtr(Imm32(sizeof(Value) * numFormals + actualsOffset), temp1); // Compute numActuals - numFormals. @@ -8531,13 +8690,27 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) // If the index is out of bounds, load |undefined|. Otherwise, load the // value. Label undefined, done; - if (lir->index()->isConstant()) { + if (lir->index()->isConstant()) masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(ToInt32(lir->index())), &undefined); - NativeObject::elementsSizeMustNotOverflow(); - masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); - } else { + else masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined); - masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); + + if (mir->unboxedType() != JSVAL_TYPE_MAGIC) { + size_t width = UnboxedTypeSize(mir->unboxedType()); + if (lir->index()->isConstant()) { + Address addr(elements, ToInt32(lir->index()) * width); + masm.loadUnboxedProperty(addr, mir->unboxedType(), out); + } else { + BaseIndex addr(elements, ToRegister(lir->index()), ScaleFromElemWidth(width)); + masm.loadUnboxedProperty(addr, mir->unboxedType(), out); + } + } else { + if (lir->index()->isConstant()) { + NativeObject::elementsSizeMustNotOverflow(); + masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); + } else { + masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); + } } // If a hole check is needed, and the value wasn't a hole, we're done. @@ -9135,14 +9308,14 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) // We pass the pointer to our out param as an instance of // JSJitGetterCallArgs, since on the binary level it's the same thing. JS_STATIC_ASSERT(sizeof(JSJitGetterCallArgs) == sizeof(Value*)); - masm.movePtr(StackPointer, ValueReg); + masm.moveStackPtrTo(ValueReg); masm.Push(ObjectReg); LoadDOMPrivate(masm, ObjectReg, PrivateReg); // Rooting will happen at GC time. - masm.movePtr(StackPointer, ObjectReg); + masm.moveStackPtrTo(ObjectReg); uint32_t safepointOffset; masm.buildFakeExitFrame(JSContextReg, &safepointOffset); @@ -9161,12 +9334,12 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun())); if (ins->mir()->isInfallible()) { - masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()), + masm.loadValue(Address(masm.getStackPointer(), IonDOMExitFrameLayout::offsetOfResult()), JSReturnOperand); } else { masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); - masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()), + masm.loadValue(Address(masm.getStackPointer(), IonDOMExitFrameLayout::offsetOfResult()), JSReturnOperand); } masm.adjustStack(IonDOMExitFrameLayout::Size()); @@ -9225,14 +9398,14 @@ CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) // We pass the pointer to our out param as an instance of // JSJitGetterCallArgs, since on the binary level it's the same thing. JS_STATIC_ASSERT(sizeof(JSJitSetterCallArgs) == sizeof(Value*)); - masm.movePtr(StackPointer, ValueReg); + masm.moveStackPtrTo(ValueReg); masm.Push(ObjectReg); LoadDOMPrivate(masm, ObjectReg, PrivateReg); // Rooting will happen at GC time. - masm.movePtr(StackPointer, ObjectReg); + masm.moveStackPtrTo(ObjectReg); uint32_t safepointOffset; masm.buildFakeExitFrame(JSContextReg, &safepointOffset); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index f6ffa15f69..81ff8336c7 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -186,6 +186,9 @@ class CodeGenerator : public CodeGeneratorSpecific void visitSubstr(LSubstr* lir); void visitInitializedLength(LInitializedLength* lir); void visitSetInitializedLength(LSetInitializedLength* lir); + void visitUnboxedArrayLength(LUnboxedArrayLength* lir); + void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir); + void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir); void visitNotO(LNotO* ins); void visitNotV(LNotV* ins); void visitBoundsCheck(LBoundsCheck* lir); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 9c7020bac1..ddcdb553ae 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1707,17 +1707,16 @@ MIRGenerator::traceNurseryObjects(JSTracer* trc) class MarkOffThreadNurseryObjects : public gc::BufferableRef { public: - void mark(JSTracer* trc); + void trace(JSTracer* trc) override; }; void -MarkOffThreadNurseryObjects::mark(JSTracer* trc) +MarkOffThreadNurseryObjects::trace(JSTracer* trc) { JSRuntime* rt = trc->runtime(); if (trc->runtime()->isHeapMinorCollecting()) { - // Only reset hasIonNurseryObjects if we're doing an actual minor GC, - // not if we're, for instance, verifying post barriers. + // Only reset hasIonNurseryObjects if we're doing an actual minor GC. MOZ_ASSERT(rt->jitRuntime()->hasIonNurseryObjects()); rt->jitRuntime()->setHasIonNurseryObjects(false); } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 2bb19d5e89..1d179c7976 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -903,8 +903,7 @@ IonBuilder::build() if (!processIterators()) return false; - if (!abortedPreliminaryGroups().empty()) { - MOZ_ASSERT(!info().isAnalysis()); + if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) { abortReason_ = AbortReason_PreliminaryObjects; return false; } @@ -1061,8 +1060,7 @@ IonBuilder::buildInline(IonBuilder* callerBuilder, MResumePoint* callerResumePoi // Discard unreferenced & pre-allocated resume points. replaceMaybeFallbackFunctionGetter(nullptr); - if (!abortedPreliminaryGroups().empty()) { - MOZ_ASSERT(!info().isAnalysis()); + if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) { abortReason_ = AbortReason_PreliminaryObjects; return false; } @@ -6498,37 +6496,29 @@ bool IonBuilder::jsop_newarray(uint32_t count) { JSObject* templateObject = inspector->getTemplateObject(pc); - if (!templateObject) { - if (info().analysisMode() == Analysis_ArgumentsUsage) { - MUnknownValue* unknown = MUnknownValue::New(alloc()); - current->add(unknown); - current->push(unknown); - return true; - } - return abort("No template object for NEWARRAY"); - } + gc::InitialHeap heap; + MConstant* templateConst; - MOZ_ASSERT(templateObject->is()); - if (templateObject->group()->unknownProperties()) { - if (info().analysisMode() == Analysis_ArgumentsUsage) { - MUnknownValue* unknown = MUnknownValue::New(alloc()); - current->add(unknown); - current->push(unknown); - return true; - } - // We will get confused in jsop_initelem_array if we can't find the - // object group being initialized. - return abort("New array has unknown properties"); + if (templateObject) { + heap = templateObject->group()->initialHeap(constraints()); + templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); + } else { + heap = gc::DefaultHeap; + templateConst = MConstant::New(alloc(), NullValue()); } - - MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject); current->add(templateConst); MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, - templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating); + heap, NewArray_FullyAllocating, pc); current->add(ins); current->push(ins); + + ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc); + if (templateGroup) { + TemporaryTypeSet* types = MakeSingletonTypeSet(constraints(), templateGroup); + ins->setResultTypeSet(types); + } + return true; } @@ -6602,10 +6592,23 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; - if (obj->isUnknownValue()) { + JSValueType unboxedType = JSVAL_TYPE_MAGIC; + if (shouldAbortOnPreliminaryGroups(obj)) { + needStub = true; + } else if (!obj->resultTypeSet() || + obj->resultTypeSet()->unknownObject() || + obj->resultTypeSet()->getObjectCount() != 1) + { needStub = true; } else { + MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1); TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0); + if (initializer->clasp() == &UnboxedArrayObject::class_) { + if (initializer->group()->unboxedLayout().nativeGroup()) + needStub = true; + else + unboxedType = initializer->group()->unboxedLayout().elementType(); + } if (value->type() == MIRType_MagicHole) { if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; @@ -6618,9 +6621,6 @@ IonBuilder::jsop_initelem_array() } } - if (NeedsPostBarrier(info(), value)) - current->add(MPostWriteBarrier::New(alloc(), obj, value)); - if (needStub) { MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj, GET_UINT24(pc), value); current->add(store); @@ -6631,29 +6631,44 @@ IonBuilder::jsop_initelem_array() current->add(id); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj); + MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); - if (obj->toNewArray()->convertDoubleElements()) { - MInstruction* valueDouble = MToDouble::New(alloc(), value); - current->add(valueDouble); - value = valueDouble; + if (unboxedType != JSVAL_TYPE_MAGIC) { + // Note: storeUnboxedValue takes care of any post barriers on the value. + storeUnboxedValue(obj, elements, 0, id, unboxedType, value); + + MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); + current->add(increment); + + if (!resumeAfter(increment)) + return false; + } else { + if (NeedsPostBarrier(info(), value)) + current->add(MPostWriteBarrier::New(alloc(), obj, value)); + + if (obj->toNewArray()->convertDoubleElements()) { + MInstruction* valueDouble = MToDouble::New(alloc(), value); + current->add(valueDouble); + value = valueDouble; + } + + // Store the value. + MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, + /* needsHoleCheck = */ false); + current->add(store); + + // Update the initialized length. (The template object for this array has + // the array's ultimate length, so the length field is already correct: no + // updating needed.) + MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); + current->add(initLength); + + if (!resumeAfter(initLength)) + return false; } - // Store the value. - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, /* needsHoleCheck = */ false); - current->add(store); - - // Update the initialized length. (The template object for this array has - // the array's ultimate length, so the length field is already correct: no - // updating needed.) - MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); - current->add(initLength); - - if (!resumeAfter(initLength)) - return false; - - return true; + return true; } bool @@ -7123,18 +7138,8 @@ ObjectHasExtraOwnProperty(CompileCompartment* comp, TypeSet::ObjectKey* object, return name == comp->runtime()->names().length; // Resolve hooks can install new properties on objects on demand. - if (!clasp->resolve) - return false; - - if (clasp->resolve == str_resolve) { - // str_resolve only resolves integers, not names. - return false; - } - - if (clasp->resolve == fun_resolve) - return FunctionHasResolveHook(comp->runtime()->names(), NameToId(name)); - - return true; + JSObject* singleton = object->isSingleton() ? object->singleton() : nullptr; + return ClassMayResolveId(comp->runtime()->names(), clasp, NameToId(name), singleton); } void @@ -7729,7 +7734,7 @@ IonBuilder::jsop_getelem() // Always use a call if we are performing analysis and not actually // emitting code, to simplify later analysis. - if (info().isAnalysis()) { + if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) { MInstruction* ins = MCallGetElement::New(alloc(), obj, index); current->add(ins); @@ -8143,9 +8148,12 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); - if (!ElementAccessIsDenseNative(constraints(), obj, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; + JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index); + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!ElementAccessIsDenseNative(constraints(), obj, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; + } } // Don't generate a fast path if there have been bounds check failures @@ -8162,8 +8170,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) return true; } - // Emit dense getelem variant. - if (!jsop_getelem_dense(obj, index)) + if (!jsop_getelem_dense(obj, index, unboxedType)) return false; trackOptimizationSuccess(); @@ -8475,7 +8482,7 @@ IonBuilder::getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index) } bool -IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) +IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType) { TemporaryTypeSet* types = bytecodeTypes(pc); @@ -8499,7 +8506,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) !ElementAccessHasExtraIndexedProperty(constraints(), obj); MIRType knownType = MIRType_Value; - if (barrier == BarrierKind::NoBarrier) + if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier) knownType = GetElemKnownType(needsHoleCheck, types); // Ensure index is an integer. @@ -8508,19 +8515,24 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) index = idInt32; // Get the elements vector. - MInstruction* elements = MElements::New(alloc(), obj); + MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); // Note: to help GVN, use the original MElements instruction and not // MConvertElementsToDoubles as operand. This is fine because converting // elements to double does not change the initialized length. - MInitializedLength* initLength = MInitializedLength::New(alloc(), elements); + MInstruction* initLength; + if (unboxedType != JSVAL_TYPE_MAGIC) + initLength = MUnboxedArrayInitializedLength::New(alloc(), obj); + else + initLength = MInitializedLength::New(alloc(), elements); current->add(initLength); // If we can load the element as a definite double, make sure to check that // the array has been converted to homogenous doubles first. TemporaryTypeSet* objTypes = obj->resultTypeSet(); bool loadDouble = + unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier && loopDepth_ && !readOutOfBounds && @@ -8540,13 +8552,18 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) // hoisting. index = addBoundsCheck(index, initLength); - load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); - current->add(load); + if (unboxedType != JSVAL_TYPE_MAGIC) { + load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types); + } else { + load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); + current->add(load); + } } else { // This load may return undefined, so assume that we *can* read holes, // or that we can read out-of-bounds accesses. In this case, the bounds // check is part of the opcode. - load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck); + load = MLoadElementHole::New(alloc(), elements, index, initLength, + unboxedType, needsHoleCheck); current->add(load); // If maybeUndefined was true, the typeset must have undefined, and @@ -8754,6 +8771,13 @@ IonBuilder::jsop_setelem() trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet()); trackTypeInfo(TrackedTypeSite::Value, value->type(), value->resultTypeSet()); + if (shouldAbortOnPreliminaryGroups(object)) { + MInstruction* ins = MCallSetElement::New(alloc(), object, index, value, IsStrictSetPC(pc)); + current->add(ins); + current->push(value); + return resumeAfter(ins); + } + trackOptimizationAttempt(TrackedStrategy::SetElem_TypedObject); if (!setElemTryTypedObject(&emitted, object, index, value) || emitted) return emitted; @@ -8974,9 +8998,12 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, { MOZ_ASSERT(*emitted == false); - if (!ElementAccessIsDenseNative(constraints(), object, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; + JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index); + if (unboxedType == JSVAL_TYPE_MAGIC) { + if (!ElementAccessIsDenseNative(constraints(), object, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; + } } if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, @@ -9010,7 +9037,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, } // Emit dense setelem variant. - if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value)) + if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value, unboxedType)) return false; trackOptimizationSuccess(); @@ -9095,9 +9122,12 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, bool IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, SetElemSafety safety, - MDefinition* obj, MDefinition* id, MDefinition* value) + MDefinition* obj, MDefinition* id, MDefinition* value, + JSValueType unboxedType) { - MIRType elementType = DenseNativeElementType(constraints(), obj); + MIRType elementType = MIRType_None; + if (unboxedType == JSVAL_TYPE_MAGIC) + elementType = DenseNativeElementType(constraints(), obj); bool packed = ElementAccessIsPacked(constraints(), obj); // Writes which are on holes in the object do not have to bail out if they @@ -9116,7 +9146,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, obj = addMaybeCopyElementsForWrite(obj); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj); + MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); current->add(elements); // Ensure the value is a double, if double conversion might be needed. @@ -9154,20 +9184,23 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, // Use MStoreElementHole if this SETELEM has written to out-of-bounds // indexes in the past. Otherwise, use MStoreElement so that we can hoist // the initialized length and bounds check. - MStoreElementCommon* store; + MInstruction* store; + MStoreElementCommon *common = nullptr; if (writeHole && writeOutOfBounds) { MOZ_ASSERT(safety == SetElem_Normal); - MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue); + MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType); store = ins; + common = ins; current->add(ins); current->push(value); - - if (!resumeAfter(ins)) - return false; } else { - MInitializedLength* initLength = MInitializedLength::New(alloc(), elements); + MInstruction* initLength; + if (unboxedType != JSVAL_TYPE_MAGIC) + initLength = MUnboxedArrayInitializedLength::New(alloc(), obj); + else + initLength = MInitializedLength::New(alloc(), elements); current->add(initLength); bool needsHoleCheck; @@ -9178,24 +9211,31 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, needsHoleCheck = false; } - MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); - store = ins; + if (unboxedType != JSVAL_TYPE_MAGIC) { + store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue); + } else { + MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); + store = ins; + common = ins; - current->add(ins); + current->add(store); + } if (safety == SetElem_Normal) current->push(value); - - if (!resumeAfter(ins)) - return false; } - // Determine whether a write barrier is required. - if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID)) - store->setNeedsBarrier(); + if (!resumeAfter(store)) + return false; - if (elementType != MIRType_None && packed) - store->setElementType(elementType); + if (common) { + // Determine whether a write barrier is required. + if (obj->resultTypeSet()->propertyNeedsBarrier(constraints(), JSID_VOID)) + common->setNeedsBarrier(); + + if (elementType != MIRType_None && packed) + common->setElementType(elementType); + } return true; } @@ -9321,6 +9361,16 @@ IonBuilder::jsop_length_fastPath() return true; } + // Compute the length for unboxed array objects. + if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC) { + current->pop(); + + MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj); + current->add(length); + current->push(length); + return true; + } + // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { @@ -9400,7 +9450,7 @@ IonBuilder::jsop_rest() MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst, templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating); + NewArray_FullyAllocating, pc); current->add(array); if (numRest == 0) { @@ -9453,37 +9503,6 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ return UINT32_MAX; } - // Watch for groups which still have preliminary object information and - // have not had the new script properties other analyses performed on their - // preliminary objects. Normally this is done after a small number of the - // objects have been created, but if only a few have been created we can - // still perform the analysis with a smaller object population. The - // analysis can have side effects so abort the builder and retry later. - // - // We always check this, so that even if we aren't able to find a common - // slot we ensure that the new script analysis is performed on all accessed - // objects. Later, this will let us elide baseline IC stubs for preliminary - // objects, which often have a different number of fixed slots from - // subsequent objects. - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - - if (ObjectGroup* group = key->maybeGroup()) { - if (group->newScript() && !group->newScript()->analyzed()) { - addAbortedPreliminaryGroup(group); - trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo); - return UINT32_MAX; - } - if (group->maybePreliminaryObjects()) { - addAbortedPreliminaryGroup(group); - trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo); - return UINT32_MAX; - } - } - } - uint32_t slot = UINT32_MAX; for (size_t i = 0; i < types->getObjectCount(); i++) { @@ -9509,6 +9528,10 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ if (!convertUnboxedGroups.append(key->group())) CrashAtUnhandlableOOM("IonBuilder::getDefiniteSlot"); key = TypeSet::ObjectKey::get(nativeGroup); + if (key->unknownProperties()) { + trackOptimizationOutcome(TrackedOutcome::UnknownProperties); + return UINT32_MAX; + } } } @@ -9613,10 +9636,9 @@ IonBuilder::jsop_not() { MDefinition* value = current->pop(); - MNot* ins = MNot::New(alloc(), value); + MNot* ins = MNot::New(alloc(), value, constraints()); current->add(ins); current->push(ins); - ins->cacheOperandMightEmulateUndefined(constraints()); return true; } @@ -9954,6 +9976,37 @@ IonBuilder::storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool n return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType); } +bool +IonBuilder::shouldAbortOnPreliminaryGroups(MDefinition *obj) +{ + // Watch for groups which still have preliminary object information and + // have not had the new script properties or unboxed layout analyses + // performed. Normally this is done after a small number of the objects + // have been created, but if only a few have been created we can still + // perform the analysis with a smaller object population. The analysis can + // have side effects so we will end up aborting compilation after building + // finishes and retrying later. + TemporaryTypeSet *types = obj->resultTypeSet(); + if (!types || types->unknownObject()) + return false; + + bool preliminary = false; + for (size_t i = 0; i < types->getObjectCount(); i++) { + TypeSet::ObjectKey* key = types->getObject(i); + if (!key) + continue; + + if (ObjectGroup* group = key->maybeGroup()) { + if (group->hasUnanalyzedPreliminaryObjects()) { + addAbortedPreliminaryGroup(group); + preliminary = true; + } + } + } + + return preliminary; +} + bool IonBuilder::jsop_getprop(PropertyName* name) { @@ -9995,7 +10048,7 @@ IonBuilder::jsop_getprop(PropertyName* name) // not actually emitting code, to simplify later analysis. Also skip deeper // analysis if there are no known types for this operation, as it will // always invalidate when executing. - if (info().isAnalysis() || types->empty()) { + if (info().isAnalysis() || types->empty() || shouldAbortOnPreliminaryGroups(obj)) { if (types->empty()) { // Since no further optimizations will be tried, use the IC // strategy, which would have been the last one to be tried, as a @@ -10523,33 +10576,39 @@ IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unb MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); current->add(scaledOffset); + return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(), + scaledOffset, unboxedType, barrier, types); +} + +MInstruction* +IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, + MDefinition* scaledOffset, JSValueType unboxedType, + BarrierKind barrier, TemporaryTypeSet* types) +{ + MInstruction* load; switch (unboxedType) { case JSVAL_TYPE_BOOLEAN: - load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Uint8, - DoesNotRequireMemoryBarrier, - UnboxedPlainObject::offsetOfData()); + load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Uint8, + DoesNotRequireMemoryBarrier, elementsOffset); load->setResultType(MIRType_Boolean); break; case JSVAL_TYPE_INT32: - load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Int32, - DoesNotRequireMemoryBarrier, - UnboxedPlainObject::offsetOfData()); + load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Int32, + DoesNotRequireMemoryBarrier, elementsOffset); load->setResultType(MIRType_Int32); break; case JSVAL_TYPE_DOUBLE: - load = MLoadUnboxedScalar::New(alloc(), obj, scaledOffset, Scalar::Float64, - DoesNotRequireMemoryBarrier, - UnboxedPlainObject::offsetOfData(), + load = MLoadUnboxedScalar::New(alloc(), elements, scaledOffset, Scalar::Float64, + DoesNotRequireMemoryBarrier, elementsOffset, /* canonicalizeDoubles = */ false); load->setResultType(MIRType_Double); break; case JSVAL_TYPE_STRING: - load = MLoadUnboxedString::New(alloc(), obj, scaledOffset, - UnboxedPlainObject::offsetOfData()); + load = MLoadUnboxedString::New(alloc(), elements, scaledOffset, elementsOffset); break; case JSVAL_TYPE_OBJECT: { @@ -10558,8 +10617,8 @@ IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unb nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; else nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible; - load = MLoadUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, nullBehavior, - UnboxedPlainObject::offsetOfData()); + load = MLoadUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, nullBehavior, + elementsOffset); break; } @@ -11102,7 +11161,7 @@ IonBuilder::jsop_setprop(PropertyName* name) // Always use a call if we are doing the definite properties analysis and // not actually emitting code, to simplify later analysis. - if (info().isAnalysis()) { + if (info().isAnalysis() || shouldAbortOnPreliminaryGroups(obj)) { bool strict = IsStrictSetPC(pc); MInstruction* ins = MCallSetProperty::New(alloc(), obj, value, name, strict); current->add(ins); @@ -11442,34 +11501,39 @@ IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType un MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); current->add(scaledOffset); + return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(), + scaledOffset, unboxedType, value); +} + +MInstruction* +IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset, + MDefinition* scaledOffset, JSValueType unboxedType, + MDefinition* value) +{ MInstruction* store; switch (unboxedType) { case JSVAL_TYPE_BOOLEAN: - store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Uint8, - DoesNotRequireMemoryBarrier, - UnboxedPlainObject::offsetOfData()); + store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8, + DoesNotRequireMemoryBarrier, elementsOffset); break; case JSVAL_TYPE_INT32: - store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Int32, - DoesNotRequireMemoryBarrier, - UnboxedPlainObject::offsetOfData()); + store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32, + DoesNotRequireMemoryBarrier, elementsOffset); break; case JSVAL_TYPE_DOUBLE: - store = MStoreUnboxedScalar::New(alloc(), obj, scaledOffset, value, Scalar::Float64, - DoesNotRequireMemoryBarrier, - UnboxedPlainObject::offsetOfData()); + store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64, + DoesNotRequireMemoryBarrier, elementsOffset); break; case JSVAL_TYPE_STRING: - store = MStoreUnboxedString::New(alloc(), obj, scaledOffset, value, - UnboxedPlainObject::offsetOfData()); + store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, elementsOffset); break; case JSVAL_TYPE_OBJECT: - store = MStoreUnboxedObjectOrNull::New(alloc(), obj, scaledOffset, value, obj, - UnboxedPlainObject::offsetOfData()); + store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj, + elementsOffset); break; default: @@ -12241,7 +12305,8 @@ IonBuilder::jsop_in() MDefinition* obj = current->peek(-1); MDefinition* id = current->peek(-2); - if (ElementAccessIsDenseNative(constraints(), obj, id) && + if (!shouldAbortOnPreliminaryGroups(obj) && + ElementAccessIsDenseNative(constraints(), obj, id) && !ElementAccessHasExtraIndexedProperty(constraints(), obj)) { return jsop_in_dense(); @@ -12906,17 +12971,8 @@ IonBuilder::storeReferenceTypedObjectValue(MDefinition* typedObj, MConstant* IonBuilder::constant(const Value& v) { - // For performance reason (TLS) and error code handling (AtomizeString), we - // should prefer the specialized frunction constantMaybeAtomize instead of - // constant. MOZ_ASSERT(!v.isString() || v.toString()->isAtom(), - "To handle non-atomized strings, you should use constantMaybeAtomize instead of constant."); - if (v.isString() && MOZ_UNLIKELY(!v.toString()->isAtom())) { - MConstant *cst = constantMaybeAtomize(v); - if (!cst) - js::CrashAtUnhandlableOOM("Use constantMaybeAtomize."); - return cst; - } + "Handle non-atomized strings outside IonBuilder."); MConstant* c = MConstant::New(alloc(), v, constraints()); current->add(c); @@ -12929,19 +12985,6 @@ IonBuilder::constantInt(int32_t i) return constant(Int32Value(i)); } -MConstant* -IonBuilder::constantMaybeAtomize(const Value& v) -{ - if (!v.isString() || v.toString()->isAtom()) - return constant(v); - - JSContext* cx = GetJitContext()->cx; - JSAtom* atom = js::AtomizeString(cx, v.toString()); - if (!atom) - return nullptr; - return constant(StringValue(atom)); -} - MDefinition* IonBuilder::getCallee() { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index b94e66303c..0e4c56b984 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -353,9 +353,6 @@ class IonBuilder MConstant* constant(const Value& v); MConstant* constantInt(int32_t i); - // Note: This function might return nullptr in case of failure. - MConstant* constantMaybeAtomize(const Value& v); - // Improve the type information at tests bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test); bool improveTypesAtCompare(MCompare* ins, bool trueBranch, MTest* test); @@ -422,6 +419,7 @@ class IonBuilder MIRType slotType = MIRType_None); bool storeSlot(MDefinition* obj, Shape* shape, MDefinition* value, bool needsBarrier, MIRType slotType = MIRType_None); + bool shouldAbortOnPreliminaryGroups(MDefinition *obj); MDefinition* tryInnerizeWindow(MDefinition* obj); @@ -658,12 +656,13 @@ class IonBuilder bool jsop_intrinsic(PropertyName* name); bool jsop_bindname(PropertyName* name); bool jsop_getelem(); - bool jsop_getelem_dense(MDefinition* obj, MDefinition* index); + bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType); bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); bool jsop_setelem(); bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, SetElemSafety safety, - MDefinition* object, MDefinition* index, MDefinition* value); + MDefinition* object, MDefinition* index, MDefinition* value, + JSValueType unboxedType); bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType, SetElemSafety safety, MDefinition* object, MDefinition* index, MDefinition* value); @@ -848,6 +847,8 @@ class IonBuilder InliningStatus inlineSimdStore(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type, unsigned numElems); + InliningStatus inlineSimdBool(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type); + // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo& callInfo); InliningStatus inlineIsObject(CallInfo& callInfo); @@ -940,8 +941,16 @@ class IonBuilder JSValueType* punboxedType); MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, BarrierKind barrier, TemporaryTypeSet* types); + MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset, + MDefinition* scaledOffset, JSValueType unboxedType, + BarrierKind barrier, TemporaryTypeSet* types); MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, MDefinition* value); + MInstruction* storeUnboxedValue(MDefinition* obj, + MDefinition* elements, int32_t elementsOffset, + MDefinition* scaledOffset, JSValueType unboxedType, + MDefinition* value); + bool checkPreliminaryGroups(MDefinition *obj); bool freezePropTypeSets(TemporaryTypeSet* types, JSObject* foundProto, PropertyName* name); bool canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index fa3bc4fcc8..9b0dcd4000 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1011,7 +1011,7 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm, // Preload arguments into registers. masm.loadJSContext(argJSContextReg); masm.move32(Imm32(0), argUintNReg); - masm.movePtr(StackPointer, argVpReg); + masm.moveStackPtrTo(argVpReg); // Push marking data for later use. masm.Push(argUintNReg); @@ -1032,17 +1032,16 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm, masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); // Load the outparam vp[0] into output register(s). - Address outparam(StackPointer, IonOOLNativeExitFrameLayout::offsetOfResult()); + Address outparam(masm.getStackPointer(), IonOOLNativeExitFrameLayout::offsetOfResult()); masm.loadTypedOrValue(outparam, output); // masm.leaveExitFrame & pop locals masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0)); } else if (IsCacheableGetPropCallPropertyOp(obj, holder, shape)) { Register argJSContextReg = regSet.takeAnyGeneral(); - Register argUintNReg = regSet.takeAnyGeneral(); - Register argVpReg = regSet.takeAnyGeneral(); - Register argObjReg = argUintNReg; + Register argObjReg = regSet.takeAnyGeneral(); Register argIdReg = regSet.takeAnyGeneral(); + Register argVpReg = regSet.takeAnyGeneral(); GetterOp target = shape->getterOp(); MOZ_ASSERT(target); @@ -1054,14 +1053,25 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm, // Push args on stack first so we can take pointers to make handles. masm.Push(UndefinedValue()); - masm.movePtr(StackPointer, argVpReg); + masm.moveStackPtrTo(argVpReg); - // push canonical jsid from shape instead of propertyname. + // Push canonical jsid from shape instead of propertyname. masm.Push(shape->propid(), scratchReg); - masm.movePtr(StackPointer, argIdReg); + masm.moveStackPtrTo(argIdReg); - masm.Push(object); - masm.movePtr(StackPointer, argObjReg); + // Push the holder. + if (obj == holder) { + // When the holder is also the current receiver, we just have a shape guard, + // so we might end up with a random object which is also guaranteed to have + // this JSGetterOp. + masm.Push(object); + } else { + // If the holder is on the prototype chain, the prototype-guarding + // only allows objects with the same holder. + masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg); + masm.Push(scratchReg); + } + masm.moveStackPtrTo(argObjReg); masm.loadJSContext(argJSContextReg); @@ -1081,7 +1091,7 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm, masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); // Load the outparam vp[0] into output register(s). - Address outparam(StackPointer, IonOOLPropertyOpExitFrameLayout::offsetOfResult()); + Address outparam(masm.getStackPointer(), IonOOLPropertyOpExitFrameLayout::offsetOfResult()); masm.loadTypedOrValue(outparam, output); // masm.leaveExitFrame & pop locals. @@ -1561,17 +1571,17 @@ EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at // Push args on stack first so we can take pointers to make handles. masm.Push(UndefinedValue()); - masm.movePtr(StackPointer, argVpReg); + masm.moveStackPtrTo(argVpReg); RootedId propId(cx, AtomToId(name)); masm.Push(propId, scratch); - masm.movePtr(StackPointer, argIdReg); + masm.moveStackPtrTo(argIdReg); // Pushing object and receiver. Both are the same, so Handle to one is equivalent to // handle to other. masm.Push(object); masm.Push(object); - masm.movePtr(StackPointer, argProxyReg); + masm.moveStackPtrTo(argProxyReg); masm.loadJSContext(argJSContextReg); @@ -1592,7 +1602,7 @@ EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); // Load the outparam vp[0] into output register(s). - Address outparam(StackPointer, IonOOLProxyExitFrameLayout::offsetOfResult()); + Address outparam(masm.getStackPointer(), IonOOLProxyExitFrameLayout::offsetOfResult()); masm.loadTypedOrValue(outparam, output); // masm.leaveExitFrame & pop locals @@ -2206,7 +2216,7 @@ EmitObjectOpResultCheck(MacroAssembler &masm, Label *failure, bool strict, // if (!result) { Label noStrictError; masm.branch32(Assembler::Equal, - Address(StackPointer, + Address(masm.getStackPointer(), FrameLayout::offsetOfObjectOpResult()), Imm32(ObjectOpResult::OkCode), &noStrictError); @@ -2215,14 +2225,14 @@ EmitObjectOpResultCheck(MacroAssembler &masm, Label *failure, bool strict, // goto failure; masm.loadJSContext(argJSContextReg); masm.computeEffectiveAddress( - Address(StackPointer, FrameLayout::offsetOfObject()), + Address(masm.getStackPointer(), FrameLayout::offsetOfObject()), argObjReg); masm.computeEffectiveAddress( - Address(StackPointer, FrameLayout::offsetOfId()), + Address(masm.getStackPointer(), FrameLayout::offsetOfId()), argIdReg); masm.move32(Imm32(strict), argStrictReg); masm.computeEffectiveAddress( - Address(StackPointer, FrameLayout::offsetOfObjectOpResult()), + Address(masm.getStackPointer(), FrameLayout::offsetOfObjectOpResult()), argResultReg); masm.setupUnalignedABICall(5, scratchReg); masm.passABIArg(argJSContextReg); @@ -2279,18 +2289,18 @@ EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &at // Push args on stack so we can take pointers to make handles. // Push value before touching any other registers (see WARNING above). masm.Push(value); - masm.movePtr(StackPointer, argValueReg); + masm.moveStackPtrTo(argValueReg); masm.move32(Imm32(strict), argStrictReg); masm.Push(propId, scratch); - masm.movePtr(StackPointer, argIdReg); + masm.moveStackPtrTo(argIdReg); // Pushing object and receiver. Both are the same, so Handle to one is equivalent to // handle to other. masm.Push(object); masm.Push(object); - masm.movePtr(StackPointer, argProxyReg); + masm.moveStackPtrTo(argProxyReg); masm.loadJSContext(argJSContextReg); @@ -2491,7 +2501,7 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler& masm, masm.Push(value); masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object))); masm.Push(ObjectValue(*target)); - masm.movePtr(StackPointer, argVpReg); + masm.moveStackPtrTo(argVpReg); // Preload other regs masm.loadJSContext(argJSContextReg); @@ -2535,7 +2545,7 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler& masm, // the stubCode pointer in order to match the layout of // IonOOLSetterOpExitFrameLayout. PushObjectOpResult(masm); - masm.movePtr(StackPointer, argResultReg); + masm.moveStackPtrTo(argResultReg); attacher.pushStubCodePointer(masm); @@ -2554,14 +2564,14 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler& masm, // We can just reuse the "object" register for argObjReg Register argObjReg = object; Register argIdReg = regSet.takeAnyGeneral(); - masm.movePtr(StackPointer, argValueReg); + masm.moveStackPtrTo(argValueReg); // push canonical jsid from shape instead of propertyname. masm.Push(shape->propid(), argIdReg); - masm.movePtr(StackPointer, argIdReg); + masm.moveStackPtrTo(argIdReg); masm.Push(object); - masm.movePtr(StackPointer, argObjReg); + masm.moveStackPtrTo(argObjReg); masm.loadJSContext(argJSContextReg); @@ -2789,7 +2799,7 @@ GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &att // if a type barrier is required. if (checkTypeset) { CheckTypeSetForWrite(masm, obj, newShape->propid(), object, value, &failuresPopObject); - masm.loadPtr(Address(StackPointer, 0), object); + masm.loadPtr(Address(masm.getStackPointer(), 0), object); } // Guard shapes along prototype chain. diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 0f47a85ad9..77ed06a328 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -545,7 +545,19 @@ static inline bool IsSimdType(MIRType type) { return type == MIRType_Int32x4 || type == MIRType_Float32x4; -}; +} + +static inline bool +IsFloatingPointSimdType(MIRType type) +{ + return type == MIRType_Float32x4; +} + +static inline bool +IsIntegerSimdType(MIRType type) +{ + return type == MIRType_Int32x4; +} static inline bool IsMagicType(MIRType type) diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 8ae4abbf1b..7ff7b596c9 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -442,10 +442,7 @@ HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame, ResumeFromEx Debugger::handleUnrecoverableIonBailoutError(cx, rematFrame); } -#ifdef DEBUG - if (rematFrame) - Debugger::assertNotInFrameMaps(rematFrame); -#endif + MOZ_ASSERT_IF(rematFrame, !Debugger::inFrameMaps(rematFrame)); } if (!script->hasTrynotes()) @@ -587,7 +584,7 @@ HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFrom RootedValue exception(cx); if (cx->isExceptionPending() && cx->compartment()->isDebuggee() && - cx->getPendingException(&exception) && !exception.isMagic(JS_GENERATOR_CLOSING)) + !cx->isClosingGenerator()) { switch (Debugger::onExceptionUnwind(cx, frame.baselineFrame())) { case JSTRAP_ERROR: @@ -650,9 +647,7 @@ HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFrom if (cx->isExceptionPending()) { // If we're closing a legacy generator, we have to skip catch // blocks. - if (!cx->getPendingException(&exception)) - continue; - if (exception.isMagic(JS_GENERATOR_CLOSING)) + if (cx->isClosingGenerator()) continue; // Ion can compile try-catch, but bailing out to catch diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index d24f7c984a..e3c50967a2 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -172,7 +172,6 @@ JitcodeGlobalEntry::BaselineEntry::callStackAtAddr(JSRuntime* rt, void* ptr, uint32_t maxResults) const { MOZ_ASSERT(containsPointer(ptr)); - MOZ_ASSERT(script_->hasBaselineScript()); MOZ_ASSERT(maxResults >= 1); results[0] = str(); @@ -788,7 +787,7 @@ JitcodeGlobalTable::sweep(JSRuntime* rt) if (entry->baseEntry().isJitcodeAboutToBeFinalized()) e.removeFront(); else - entry->sweep(); + entry->sweep(rt); } } @@ -924,6 +923,22 @@ JitcodeGlobalEntry::IonEntry::isMarkedFromAnyThread() return true; } +bool +JitcodeGlobalEntry::IonCacheEntry::markIfUnmarked(JSTracer* trc) +{ + JitcodeGlobalEntry entry; + RejoinEntry(trc->runtime(), *this, nativeStartAddr(), &entry); + return entry.markIfUnmarked(trc); +} + +void +JitcodeGlobalEntry::IonCacheEntry::sweep(JSRuntime* rt) +{ + JitcodeGlobalEntry entry; + RejoinEntry(rt, *this, nativeStartAddr(), &entry); + entry.sweep(rt); +} + bool JitcodeGlobalEntry::IonCacheEntry::isMarkedFromAnyThread(JSRuntime* rt) { diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index 91b7a56391..47d68bed21 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -443,10 +443,12 @@ class JitcodeGlobalEntry uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, uint32_t maxResults) const; - void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr, - JSScript **script, jsbytecode **pc) const; + void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, + JSScript** script, jsbytecode** pc) const; - bool isMarkedFromAnyThread(JSRuntime *rt); + bool markIfUnmarked(JSTracer* trc); + void sweep(JSRuntime* rt); + bool isMarkedFromAnyThread(JSRuntime* rt); }; // Dummy entries are created for jitcode generated when profiling is not turned on, @@ -454,7 +456,7 @@ class JitcodeGlobalEntry // stack when profiling is enabled. struct DummyEntry : public BaseEntry { - void init(JitCode *code, void *nativeStartAddr, void *nativeEndAddr) { + void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) { BaseEntry::init(Dummy, code, nativeStartAddr, nativeEndAddr); } @@ -472,8 +474,8 @@ class JitcodeGlobalEntry return 0; } - void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr, - JSScript **script, jsbytecode **pc) const + void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, + JSScript** script, jsbytecode** pc) const { *script = nullptr; *pc = nullptr; @@ -495,7 +497,7 @@ class JitcodeGlobalEntry }; private: - JitcodeSkiplistTower *tower_; + JitcodeSkiplistTower* tower_; union { // Shadowing BaseEntry instance to allow access to base fields @@ -585,7 +587,7 @@ class JitcodeGlobalEntry } } - JitCode *jitcode() const { + JitCode* jitcode() const { return baseEntry().jitcode(); } void* nativeStartAddr() const { @@ -742,8 +744,8 @@ class JitcodeGlobalEntry } - void youngestFrameLocationAtAddr(JSRuntime *rt, void *ptr, - JSScript **script, jsbytecode **pc) const + void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, + JSScript** script, jsbytecode** pc) const { switch (kind()) { case Ion: @@ -786,7 +788,7 @@ class JitcodeGlobalEntry return false; } - mozilla::Maybe trackedOptimizationIndexAtAddr(void *addr, uint32_t *entryOffsetOut) { + mozilla::Maybe trackedOptimizationIndexAtAddr(void* addr, uint32_t* entryOffsetOut) { switch (kind()) { case Ion: return ionEntry().trackedOptimizationIndexAtAddr(addr, entryOffsetOut); @@ -812,11 +814,11 @@ class JitcodeGlobalEntry return ionEntry().allTrackedTypes(); } - Zone *zone() { + Zone* zone() { return baseEntry().jitcode()->zone(); } - bool markIfUnmarked(JSTracer *trc) { + bool markIfUnmarked(JSTracer* trc) { bool markedAny = baseEntry().markJitcodeIfUnmarked(trc); switch (kind()) { case Ion: @@ -826,6 +828,7 @@ class JitcodeGlobalEntry markedAny |= baselineEntry().markIfUnmarked(trc); break; case IonCache: + markedAny |= ionCacheEntry().markIfUnmarked(trc); case Dummy: break; default: @@ -834,7 +837,7 @@ class JitcodeGlobalEntry return markedAny; } - void sweep() { + void sweep(JSRuntime* rt) { switch (kind()) { case Ion: ionEntry().sweep(); @@ -843,6 +846,7 @@ class JitcodeGlobalEntry baselineEntry().sweep(); break; case IonCache: + ionCacheEntry().sweep(rt); case Dummy: break; default: @@ -850,7 +854,7 @@ class JitcodeGlobalEntry } } - bool isMarkedFromAnyThread(JSRuntime *rt) { + bool isMarkedFromAnyThread(JSRuntime* rt) { if (!baseEntry().isJitcodeMarkedFromAnyThread()) return false; switch (kind()) { @@ -875,23 +879,23 @@ class JitcodeGlobalEntry // |JitcodeSkiplistTower *|. // - void addToFreeList(JitcodeGlobalEntry **freeList) { + void addToFreeList(JitcodeGlobalEntry** freeList) { MOZ_ASSERT(!isValid()); - JitcodeGlobalEntry *nextFreeEntry = *freeList; + JitcodeGlobalEntry* nextFreeEntry = *freeList; MOZ_ASSERT_IF(nextFreeEntry, !nextFreeEntry->isValid()); - tower_ = (JitcodeSkiplistTower *) nextFreeEntry; + tower_ = (JitcodeSkiplistTower*) nextFreeEntry; *freeList = this; } - static JitcodeGlobalEntry *PopFromFreeList(JitcodeGlobalEntry **freeList) { + static JitcodeGlobalEntry* PopFromFreeList(JitcodeGlobalEntry** freeList) { if (!*freeList) return nullptr; - JitcodeGlobalEntry *entry = *freeList; + JitcodeGlobalEntry* entry = *freeList; MOZ_ASSERT(!entry->isValid()); - JitcodeGlobalEntry *nextFreeEntry = (JitcodeGlobalEntry *) entry->tower_; + JitcodeGlobalEntry* nextFreeEntry = (JitcodeGlobalEntry*) entry->tower_; entry->tower_ = nullptr; *freeList = nextFreeEntry; return entry; @@ -907,12 +911,12 @@ class JitcodeGlobalTable static const size_t LIFO_CHUNK_SIZE = 16 * 1024; LifoAlloc alloc_; - JitcodeGlobalEntry *freeEntries_; + JitcodeGlobalEntry* freeEntries_; uint32_t rand_; uint32_t skiplistSize_; - JitcodeGlobalEntry *startTower_[JitcodeSkiplistTower::MAX_HEIGHT]; - JitcodeSkiplistTower *freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT]; + JitcodeGlobalEntry* startTower_[JitcodeSkiplistTower::MAX_HEIGHT]; + JitcodeSkiplistTower* freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT]; public: JitcodeGlobalTable() @@ -930,10 +934,10 @@ class JitcodeGlobalTable } bool lookup(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt); - bool lookupForSampler(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt, + bool lookupForSampler(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt, uint32_t sampleBufferGen); - void lookupInfallible(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt) { + void lookupInfallible(void* ptr, JitcodeGlobalEntry* result, JSRuntime* rt) { mozilla::DebugOnly success = lookup(ptr, result, rt); MOZ_ASSERT(success); } @@ -951,16 +955,16 @@ class JitcodeGlobalTable return addEntry(JitcodeGlobalEntry(entry), rt); } - void removeEntry(JitcodeGlobalEntry &entry, JitcodeGlobalEntry **prevTower, JSRuntime *rt); - void releaseEntry(JitcodeGlobalEntry &entry, JitcodeGlobalEntry **prevTower, JSRuntime *rt); + void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); + void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); - bool markIteratively(JSTracer *trc); - void sweep(JSRuntime *rt); + bool markIteratively(JSTracer* trc); + void sweep(JSRuntime* rt); private: - bool addEntry(const JitcodeGlobalEntry &entry, JSRuntime *rt); + bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt); - JitcodeGlobalEntry *lookupInternal(void *ptr); + JitcodeGlobalEntry* lookupInternal(void* ptr); // Initialize towerOut such that towerOut[i] (for i in [0, MAX_HEIGHT-1]) // is a JitcodeGlobalEntry that is sorted to be MSimdShift::Operation operation() const { return mir_->toSimdShift()->operation(); } + const char* extraName() const { + return MSimdShift::OperationName(operation()); + } MSimdShift* mir() const { return mir_->toSimdShift(); } @@ -1533,14 +1536,6 @@ class LJSCallInstructionHelper : public LCallInstructionHelpergetSingleTarget(); } - // The number of stack arguments is the max between the number of formal - // arguments and the number of actual arguments. The number of stack - // argument includes the |undefined| padding added in case of underflow. - // Does not include |this|. - uint32_t numStackArgs() const { - MOZ_ASSERT(mir()->numStackArgs() >= 1); - return mir()->numStackArgs() - 1; // |this| is not a formal argument. - } // Does not include |this|. uint32_t numActualArgs() const { return mir()->numActualArgs(); @@ -3710,12 +3705,19 @@ class LInt32x4ToFloat32x4 : public LInstructionHelper<1, 1, 0> } }; -class LFloat32x4ToInt32x4 : public LInstructionHelper<1, 1, 0> +class LFloat32x4ToInt32x4 : public LInstructionHelper<1, 1, 1> { public: LIR_HEADER(Float32x4ToInt32x4); - explicit LFloat32x4ToInt32x4(const LAllocation& input) { + explicit LFloat32x4ToInt32x4(const LAllocation& input, const LDefinition& temp) { setOperand(0, input); + setTemp(0, temp); + } + const LDefinition* temp() { + return getTemp(0); + } + const MSimdConvert* mir() const { + return mir_->toSimdConvert(); } }; @@ -4039,6 +4041,10 @@ class LElements : public LInstructionHelper<1, 1, 0> const LAllocation* object() { return getOperand(0); } + + const MElements* mir() const { + return mir_->toElements(); + } }; // If necessary, convert any int32 elements in a vector into doubles. @@ -4136,6 +4142,48 @@ class LSetInitializedLength : public LInstructionHelper<0, 2, 0> } }; +class LUnboxedArrayLength : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(UnboxedArrayLength) + + explicit LUnboxedArrayLength(const LAllocation& object) { + setOperand(0, object); + } + + const LAllocation* object() { + return getOperand(0); + } +}; + +class LUnboxedArrayInitializedLength : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(UnboxedArrayInitializedLength) + + explicit LUnboxedArrayInitializedLength(const LAllocation& object) { + setOperand(0, object); + } + + const LAllocation* object() { + return getOperand(0); + } +}; + +class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, 0> +{ + public: + LIR_HEADER(IncrementUnboxedArrayInitializedLength) + + explicit LIncrementUnboxedArrayInitializedLength(const LAllocation& object) { + setOperand(0, object); + } + + const LAllocation* object() { + return getOperand(0); + } +}; + // Load the length from an elements header. class LArrayLength : public LInstructionHelper<1, 1, 0> { @@ -4565,16 +4613,17 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0> }; // Like LStoreElementV, but supports indexes >= initialized length. -class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> +class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> { public: LIR_HEADER(StoreElementHoleV) LStoreElementHoleV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index) { + const LAllocation& index, const LDefinition& temp) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); + setTemp(0, temp); } static const size_t Value = 3; @@ -4594,17 +4643,19 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> }; // Like LStoreElementT, but supports indexes >= initialized length. -class LStoreElementHoleT : public LInstructionHelper<0, 4, 0> +class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> { public: LIR_HEADER(StoreElementHoleT) LStoreElementHoleT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value) { + const LAllocation& index, const LAllocation& value, + const LDefinition& temp) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); + setTemp(0, temp); } const MStoreElementHole* mir() const { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 5522b3bfe8..473fa4fe80 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -213,6 +213,9 @@ _(PostWriteBarrierV) \ _(InitializedLength) \ _(SetInitializedLength) \ + _(UnboxedArrayLength) \ + _(UnboxedArrayInitializedLength) \ + _(IncrementUnboxedArrayInitializedLength) \ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 6fe8803862..35c56160fb 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2459,6 +2459,24 @@ LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins) useRegisterOrConstant(ins->index())), ins); } +void +LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins) +{ + define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins) +{ + define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins); +} + +void +LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins) +{ + add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins); +} + void LIRGenerator::visitNot(MNot* ins) { @@ -2701,17 +2719,22 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegisterOrConstant(ins->index()); + // Use a temp register when adding new elements to unboxed arrays. + LDefinition tempDef = LDefinition::BogusTemp(); + if (ins->unboxedType() != JSVAL_TYPE_MAGIC) + tempDef = temp(); + LInstruction* lir; switch (ins->value()->type()) { case MIRType_Value: - lir = new(alloc()) LStoreElementHoleV(object, elements, index); + lir = new(alloc()) LStoreElementHoleV(object, elements, index, tempDef); useBox(lir, LStoreElementHoleV::Value, ins->value()); break; default: { const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LStoreElementHoleT(object, elements, index, value); + lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef); break; } } @@ -3854,11 +3877,13 @@ LIRGenerator::visitSimdConvert(MSimdConvert* ins) { MOZ_ASSERT(IsSimdType(ins->type())); MDefinition* input = ins->input(); - LUse use = useRegisterAtStart(input); - + LUse use = useRegister(input); if (ins->type() == MIRType_Int32x4) { MOZ_ASSERT(input->type() == MIRType_Float32x4); - define(new(alloc()) LFloat32x4ToInt32x4(use), ins); + LFloat32x4ToInt32x4* lir = new(alloc()) LFloat32x4ToInt32x4(use, temp()); + if (!gen->conversionErrorLabel()) + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); } else if (ins->type() == MIRType_Float32x4) { MOZ_ASSERT(input->type() == MIRType_Int32x4); define(new(alloc()) LInt32x4ToFloat32x4(use), ins); @@ -4065,6 +4090,8 @@ void LIRGenerator::visitSimdShift(MSimdShift* ins) { MOZ_ASSERT(ins->type() == MIRType_Int32x4); + MOZ_ASSERT(ins->lhs()->type() == MIRType_Int32x4); + MOZ_ASSERT(ins->rhs()->type() == MIRType_Int32); LUse vector = useRegisterAtStart(ins->lhs()); LAllocation value = useRegisterOrConstant(ins->rhs()); diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index c0ac5ea9d4..755a8c274c 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -186,6 +186,9 @@ class LIRGenerator : public LIRGeneratorSpecific void visitTypedObjectDescr(MTypedObjectDescr* ins); void visitInitializedLength(MInitializedLength* ins); void visitSetInitializedLength(MSetInitializedLength* ins); + void visitUnboxedArrayLength(MUnboxedArrayLength* ins); + void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins); + void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins); void visitNot(MNot* ins); void visitBoundsCheck(MBoundsCheck* ins); void visitBoundsCheckLower(MBoundsCheckLower* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index d655043565..eeafa717e0 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -45,6 +45,13 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) // Default failure reason is observing an unsupported type. trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType); + if (shouldAbortOnPreliminaryGroups(callInfo.thisArg())) + return InliningStatus_NotInlined; + for (size_t i = 0; i < callInfo.argc(); i++) { + if (shouldAbortOnPreliminaryGroups(callInfo.getArg(i))) + return InliningStatus_NotInlined; + } + // Atomic natives. if (native == atomics_compareExchange) return inlineAtomicsCompareExchange(callInfo); @@ -286,6 +293,13 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_) #undef INLINE_SIMD_BITWISE_ + if (native == js::simd_int32x4_shiftLeftByScalar) + return inlineBinarySimd(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_shiftRightArithmeticByScalar) + return inlineBinarySimd(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4); + if (native == js::simd_int32x4_shiftRightLogicalByScalar) + return inlineBinarySimd(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4); + #define INLINE_SIMD_COMPARISON_(OP) \ if (native == js::simd_int32x4_##OP) \ return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4); \ @@ -571,7 +585,7 @@ IonBuilder::inlineArray(CallInfo &callInfo) MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, templateArray->group()->initialHeap(constraints()), - allocating); + allocating, pc); current->add(ins); current->push(ins); @@ -1530,14 +1544,11 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) if (templateObject->getDenseInitializedLength() != initLength) return InliningStatus_NotInlined; - JSContext *cx = GetJitContext()->cx; Vector arrayValues; for (uint32_t i = 0; i < initLength; i++) { - JSAtom* str = js::AtomizeString(cx, templateObject->getDenseElement(i).toString()); - if (!str) - return InliningStatus_Error; - - MConstant *value = MConstant::New(alloc(), StringValue(str), constraints()); + Value str = templateObject->getDenseElement(i); + MOZ_ASSERT(str.toString()->isAtom()); + MConstant* value = MConstant::New(alloc(), str, constraints()); if (!TypeSetIncludes(key.maybeTypes(), value->type(), value->resultTypeSet())) return InliningStatus_NotInlined; @@ -1556,7 +1567,7 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst, templateObject->group()->initialHeap(constraints()), - NewArray_FullyAllocating); + NewArray_FullyAllocating, pc); current->add(ins); current->push(ins); @@ -2101,7 +2112,7 @@ IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base) TemporaryTypeSet::DoubleConversion conversion = obj->resultTypeSet()->convertDoubleElements(constraints()); - if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem)) + if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem, JSVAL_TYPE_MAGIC)) return false; return true; } @@ -2187,11 +2198,9 @@ IonBuilder::inlineHasClass(CallInfo& callInfo, } // Convert to bool with the '!!' idiom - MNot* resultInverted = MNot::New(alloc(), last); - resultInverted->cacheOperandMightEmulateUndefined(constraints()); + MNot* resultInverted = MNot::New(alloc(), last, constraints()); current->add(resultInverted); - MNot* result = MNot::New(alloc(), resultInverted); - result->cacheOperandMightEmulateUndefined(constraints()); + MNot* result = MNot::New(alloc(), resultInverted, constraints()); current->add(result); current->push(result); } @@ -2698,18 +2707,13 @@ IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target) CallInfo callInfo(alloc(), nativeCallInfo.constructing()); callInfo.setFun(constant(ObjectValue(*scriptedTarget))); - MConstant* thisConst = constantMaybeAtomize(thisVal); - if (!thisConst) - return InliningStatus_Error; - callInfo.setThis(thisConst); + callInfo.setThis(constant(thisVal)); if (!callInfo.argv().reserve(argc)) return InliningStatus_Error; for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) { - MConstant* argConst = constantMaybeAtomize(target->getBoundFunctionArgument(i)); - if (!argConst) - return InliningStatus_Error; + MConstant* argConst = constant(target->getBoundFunctionArgument(i)); callInfo.argv().infallibleAppend(argConst); } for (size_t i = 0; i < nativeCallInfo.argc(); i++) @@ -3037,7 +3041,7 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) // Generic constructor of SIMD valuesX4. MIRType simdType = SimdTypeDescrToMIRType(descr->type()); - // TODO Happens for TYPE_FLOAT64 (Bug 1124205) + // TODO Happens for Float64x2 (Bug 1124205) if (simdType == MIRType_Undefined) return InliningStatus_NotInlined; @@ -3367,5 +3371,33 @@ IonBuilder::inlineSimdStore(CallInfo &callInfo, JSNative native, SimdTypeDescr:: return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineSimdBool(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type) +{ + InlineTypedObject* templateObj = nullptr; + if (!checkInlineSimd(callInfo, native, type, 4, &templateObj)) + return InliningStatus_NotInlined; + + MOZ_ASSERT(type == SimdTypeDescr::Int32x4, "at the moment, only int32x4.bool is inlined"); + + MInstruction* operands[4]; + for (unsigned i = 0; i < 4; i++) { + operands[i] = MNot::New(alloc(), callInfo.getArg(i), constraints()); + current->add(operands[i]); + } + + // Inline int32x4.bool(x, y, z, w) as int32x4(!x - 1, !y - 1, !z - 1, !w - 1); + MSimdValueX4* vector = MSimdValueX4::New(alloc(), MIRType_Int32x4, operands[0], operands[1], + operands[2], operands[3]); + current->add(vector); + + MSimdConstant* one = MSimdConstant::New(alloc(), SimdConstant::SplatX4(1), MIRType_Int32x4); + current->add(one); + + MSimdBinaryArith* result = MSimdBinaryArith::New(alloc(), vector, one, MSimdBinaryArith::Op_sub, + MIRType_Int32x4); + return boxSimd(callInfo, result, templateObj); +} + } // namespace jit } // namespace js diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 86cfaf0cbb..b17eb61628 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -651,19 +651,30 @@ MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v) return new(alloc) MConstant(v); } -TemporaryTypeSet* -jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj) +static TemporaryTypeSet* +MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key) { // Invalidate when this object's ObjectGroup gets unknown properties. This // happens for instance when we mutate an object's __proto__, in this case // we want to invalidate and mark this TypeSet as containing AnyObject // (because mutating __proto__ will change an object's ObjectGroup). MOZ_ASSERT(constraints); - TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj); key->hasStableClassAndProto(constraints); LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc(); - return alloc->new_(alloc, TypeSet::ObjectType(obj)); + return alloc->new_(alloc, TypeSet::ObjectType(key)); +} + +TemporaryTypeSet* +jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj) +{ + return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj)); +} + +TemporaryTypeSet* +jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj) +{ + return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj)); } static TemporaryTypeSet* @@ -1012,6 +1023,11 @@ MSimdBinaryComp::printOpcode(FILE *fp) const { PrintOpcodeOperation(this, fp); } +void +MSimdShift::printOpcode(FILE* fp) const +{ + PrintOpcodeOperation(this, fp); +} void MConstantElements::printOpcode(FILE* fp) const @@ -1247,13 +1263,19 @@ MCallDOMNative::getAliasSet() const // getArg(0) is "this", so skip it MDefinition* arg = getArg(argIndex+1); MIRType actualType = arg->type(); - // The only way to reliably avoid side-effects given the informtion we + // The only way to reliably avoid side-effects given the information we // have here is if we're passing in a known primitive value to an - // argument that expects a primitive value. XXXbz maybe we need to - // communicate better information. For example, a sequence argument - // will sort of unavoidably have side effects, while a typed array - // argument won't have any, but both are claimed to be - // JSJitInfo::Object. + // argument that expects a primitive value. + // + // XXXbz maybe we need to communicate better information. For example, + // a sequence argument will sort of unavoidably have side effects, while + // a typed array argument won't have any, but both are claimed to be + // JSJitInfo::Object. But if we do that, we need to watch out for our + // movability/DCE-ability bits: if we have an arg type that can reliably + // throw an exception on conversion, that might not affect our alias set + // per se, but it should prevent us being moved or DCE-ed, unless we + // know the incoming things match that arg type and won't throw. + // if ((actualType == MIRType_Value || actualType == MIRType_Object) || (*argType & JSJitInfo::Object)) { @@ -3977,18 +3999,18 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state) return res; } -MNewArray::MNewArray(CompilerConstraintList *constraints, uint32_t count, MConstant *templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating) +MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, + gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc) : MUnaryInstruction(templateConst), count_(count), initialHeap_(initialHeap), allocating_(allocating), - convertDoubleElements_(false) + convertDoubleElements_(false), + pc_(pc) { - ArrayObject *obj = templateObject(); setResultType(MIRType_Object); - if (!obj->isSingleton()) { - TemporaryTypeSet *types = MakeSingletonTypeSet(constraints, obj); + if (templateObject()) { + TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject()); setResultTypeSet(types); if (types->convertDoubleElements(constraints) == TemporaryTypeSet::AlwaysConvertToDoubles) convertDoubleElements_ = true; @@ -3998,16 +4020,25 @@ MNewArray::MNewArray(CompilerConstraintList *constraints, uint32_t count, MConst bool MNewArray::shouldUseVM() const { + if (!templateObject()) + return true; + + // Allocate space using the VMCall when mir hints it needs to get allocated + // immediately, but only when data doesn't fit the available array slots. + if (allocatingBehaviour() == NewArray_Unallocating) + return false; + + if (templateObject()->is()) { + MOZ_ASSERT(templateObject()->as().capacity() >= count()); + return !templateObject()->as().hasInlineElements(); + } + MOZ_ASSERT(count() < NativeObject::NELEMENTS_LIMIT); size_t arraySlots = gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER; - // Allocate space using the VMCall when mir hints it needs to get allocated - // immediately, but only when data doesn't fit the available array slots. - bool allocating = allocatingBehaviour() != NewArray_Unallocating && count() > arraySlots; - - return templateObject()->isSingleton() || allocating; + return count() > arraySlots; } bool @@ -4606,6 +4637,48 @@ jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, return clasp && clasp->isNative() && !IsAnyTypedArrayClass(clasp); } +JSValueType +jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, + MDefinition* id) +{ + if (obj->mightBeType(MIRType_String)) + return JSVAL_TYPE_MAGIC; + + if (id && id->type() != MIRType_Int32 && id->type() != MIRType_Double) + return JSVAL_TYPE_MAGIC; + + TemporaryTypeSet* types = obj->resultTypeSet(); + if (!types || types->unknownObject()) + return JSVAL_TYPE_MAGIC; + + JSValueType elementType = JSVAL_TYPE_MAGIC; + for (unsigned i = 0; i < types->getObjectCount(); i++) { + TypeSet::ObjectKey* key = types->getObject(i); + if (!key) + continue; + + if (key->unknownProperties() || !key->isGroup()) + return JSVAL_TYPE_MAGIC; + + if (key->clasp() != &UnboxedArrayObject::class_) + return JSVAL_TYPE_MAGIC; + + const UnboxedLayout &layout = key->group()->unboxedLayout(); + + if (layout.nativeGroup()) + return JSVAL_TYPE_MAGIC; + + if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC) + elementType = layout.elementType(); + else + return JSVAL_TYPE_MAGIC; + + key->watchStateChangeForUnboxedConvertedToNative(constraints); + } + + return elementType; +} + bool jit::ElementAccessIsAnyTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index b08034e32c..922b12a827 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1526,9 +1526,14 @@ class MSimdConvert : MUnaryInstruction(obj) { MOZ_ASSERT(IsSimdType(toType)); - setMovable(); setResultType(toType); specialization_ = fromType; // expects fromType as input + + setMovable(); + if (IsFloatingPointSimdType(fromType) && IsIntegerSimdType(toType)) { + // Does the extra range check => do not remove + setGuard(); + } } public: @@ -2266,7 +2271,7 @@ class MSimdBinaryBitwise class MSimdShift : public MBinaryInstruction, - public NoTypePolicy::Data + public MixPolicy, SimdScalarPolicy<1> >::Data { public: enum Operation { @@ -2281,7 +2286,6 @@ class MSimdShift MSimdShift(MDefinition* left, MDefinition* right, Operation op) : MBinaryInstruction(left, right), operation_(op) { - MOZ_ASSERT(left->type() == MIRType_Int32x4 && right->type() == MIRType_Int32); setResultType(MIRType_Int32x4); setMovable(); } @@ -2291,6 +2295,14 @@ class MSimdShift static MSimdShift* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, Operation op) { + MOZ_ASSERT(left->type() == MIRType_Int32x4 && right->type() == MIRType_Int32); + return new(alloc) MSimdShift(left, right, op); + } + + static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, + Operation op, MIRType type) + { + MOZ_ASSERT(type == MIRType_Int32x4); return new(alloc) MSimdShift(left, right, op); } @@ -2300,6 +2312,17 @@ class MSimdShift Operation operation() const { return operation_; } + static const char* OperationName(Operation op) { + switch (op) { + case lsh: return "lsh"; + case rsh: return "rsh-arithmetic"; + case ursh: return "rhs-logical"; + } + MOZ_CRASH("unexpected operation"); + } + + void printOpcode(FILE* fp) const override; + bool congruentTo(const MDefinition* ins) const override { if (!binaryCongruentTo(ins)) return false; @@ -2826,6 +2849,9 @@ class MThrow TemporaryTypeSet* MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj); +TemporaryTypeSet* +MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj); + bool MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet, MIRType newType, TemporaryTypeSet* newTypeSet); @@ -2890,25 +2916,29 @@ class MNewArray // Whether values written to this array should be converted to double first. bool convertDoubleElements_; + jsbytecode* pc_; + MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating); + gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc); public: INSTRUCTION_HEADER(NewArray) static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst, - gc::InitialHeap initialHeap, AllocatingBehaviour allocating) + gc::InitialHeap initialHeap, AllocatingBehaviour allocating, + jsbytecode* pc) { - return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, allocating); + return new(alloc) MNewArray(constraints, count, templateConst, + initialHeap, allocating, pc); } uint32_t count() const { return count_; } - ArrayObject* templateObject() const { - return &getOperand(0)->toConstant()->value().toObject().as(); + JSObject* templateObject() const { + return getOperand(0)->toConstant()->value().toObjectOrNull(); } gc::InitialHeap initialHeap() const { @@ -2919,6 +2949,10 @@ class MNewArray return allocating_; } + jsbytecode* pc() const { + return pc_; + } + bool convertDoubleElements() const { return convertDoubleElements_; } @@ -2941,7 +2975,7 @@ class MNewArray bool canRecoverOnBailout() const override { // The template object can safely be used in the recover instruction // because it can never be mutated by any other function execution. - return true; + return templateObject() != nullptr; } }; @@ -3725,13 +3759,13 @@ class MCallDOMNative : public MCall MCallDOMNative(JSFunction* target, uint32_t numActualArgs) : MCall(target, numActualArgs, false) { - // If our jitinfo is not marked movable, that means that our C++ + // If our jitinfo is not marked eliminatable, that means that our C++ // implementation is fallible or that it never wants to be eliminated or - // coalesced or that we have no hope of ever doing the sort of argument - // analysis that would allow us to detemine that we're side-effect-free. - // In the latter case we wouldn't get DCEd no matter what, but for the - // former two cases we have to explicitly say that we can't be DCEd. - if (!getJitInfo()->isMovable) + // that we have no hope of ever doing the sort of argument analysis that + // would allow us to detemine that we're side-effect-free. In the + // latter case we wouldn't get DCEd no matter what, but for the former + // two cases we have to explicitly say that we can't be DCEd. + if (!getJitInfo()->isEliminatable) setGuard(); } @@ -7524,8 +7558,10 @@ class MElements : public MUnaryInstruction, public SingleObjectPolicy::Data { - explicit MElements(MDefinition* object) - : MUnaryInstruction(object) + bool unboxed_; + + explicit MElements(MDefinition* object, bool unboxed) + : MUnaryInstruction(object), unboxed_(unboxed) { setResultType(MIRType_Elements); setMovable(); @@ -7534,15 +7570,19 @@ class MElements public: INSTRUCTION_HEADER(Elements) - static MElements* New(TempAllocator& alloc, MDefinition* object) { - return new(alloc) MElements(object); + static MElements* New(TempAllocator& alloc, MDefinition* object, bool unboxed = false) { + return new(alloc) MElements(object, unboxed); } MDefinition* object() const { return getOperand(0); } + bool unboxed() const { + return unboxed_; + } bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); + return congruentIfOperandsEqual(ins) && + ins->toElements()->unboxed() == unboxed(); } AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::ObjectFields); @@ -7773,6 +7813,96 @@ class MSetInitializedLength ALLOW_CLONE(MSetInitializedLength) }; +// Load the length from an unboxed array. +class MUnboxedArrayLength + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MUnboxedArrayLength(MDefinition* object) + : MUnaryInstruction(object) + { + setResultType(MIRType_Int32); + setMovable(); + } + + public: + INSTRUCTION_HEADER(UnboxedArrayLength) + + static MUnboxedArrayLength* New(TempAllocator& alloc, MDefinition* object) { + return new(alloc) MUnboxedArrayLength(object); + } + + MDefinition* object() const { + return getOperand(0); + } + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const override { + return AliasSet::Load(AliasSet::ObjectFields); + } + + ALLOW_CLONE(MUnboxedArrayLength) +}; + +// Load the initialized length from an unboxed array. +class MUnboxedArrayInitializedLength + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MUnboxedArrayInitializedLength(MDefinition* object) + : MUnaryInstruction(object) + { + setResultType(MIRType_Int32); + setMovable(); + } + + public: + INSTRUCTION_HEADER(UnboxedArrayInitializedLength) + + static MUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* object) { + return new(alloc) MUnboxedArrayInitializedLength(object); + } + + MDefinition* object() const { + return getOperand(0); + } + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const override { + return AliasSet::Load(AliasSet::ObjectFields); + } + + ALLOW_CLONE(MUnboxedArrayInitializedLength) +}; + +// Increment the initialized length of an unboxed array object. +class MIncrementUnboxedArrayInitializedLength + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MIncrementUnboxedArrayInitializedLength(MDefinition* obj) + : MUnaryInstruction(obj) + {} + + public: + INSTRUCTION_HEADER(IncrementUnboxedArrayInitializedLength) + + static MIncrementUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* obj) { + return new(alloc) MIncrementUnboxedArrayInitializedLength(obj); + } + + MDefinition* object() const { + return getOperand(0); + } + AliasSet getAliasSet() const override { + return AliasSet::Store(AliasSet::ObjectFields); + } + + ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength) +}; + // Load the array length from an elements header. class MArrayLength : public MUnaryInstruction, @@ -8015,18 +8145,24 @@ class MNot bool operandMightEmulateUndefined_; bool operandIsNeverNaN_; - explicit MNot(MDefinition* input) + explicit MNot(MDefinition* input, CompilerConstraintList* constraints = nullptr) : MUnaryInstruction(input), operandMightEmulateUndefined_(true), operandIsNeverNaN_(false) { setResultType(MIRType_Boolean); setMovable(); + if (constraints) + cacheOperandMightEmulateUndefined(constraints); } + void cacheOperandMightEmulateUndefined(CompilerConstraintList *constraints); + public: - static MNot* New(TempAllocator& alloc, MDefinition* elements) { - return new(alloc) MNot(elements); + static MNot* New(TempAllocator& alloc, MDefinition* elements, + CompilerConstraintList* constraints = nullptr) + { + return new(alloc) MNot(elements, constraints); } static MNot* NewAsmJS(TempAllocator& alloc, MDefinition* elements) { MNot* ins = new(alloc) MNot(elements); @@ -8036,7 +8172,6 @@ class MNot INSTRUCTION_HEADER(Not) - void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints); MDefinition* foldsTo(TempAllocator& alloc) override; void markOperandCantEmulateUndefined() { @@ -8267,18 +8402,23 @@ class MLoadElement ALLOW_CLONE(MLoadElement) }; -// Load a value from a dense array's element vector. If the index is -// out-of-bounds, or the indexed slot has a hole, undefined is returned -// instead. +// Load a value from the elements vector for a dense native or unboxed array. +// If the index is out-of-bounds, or the indexed slot has a hole, undefined is +// returned instead. class MLoadElementHole : public MTernaryInstruction, public SingleObjectPolicy::Data { + // Unboxed element type, JSVAL_TYPE_MAGIC for dense native elements. + JSValueType unboxedType_; + bool needsNegativeIntCheck_; bool needsHoleCheck_; - MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength, bool needsHoleCheck) + MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength, + JSValueType unboxedType, bool needsHoleCheck) : MTernaryInstruction(elements, index, initLength), + unboxedType_(unboxedType), needsNegativeIntCheck_(true), needsHoleCheck_(needsHoleCheck) { @@ -8299,8 +8439,10 @@ class MLoadElementHole INSTRUCTION_HEADER(LoadElementHole) static MLoadElementHole* New(TempAllocator& alloc, MDefinition* elements, MDefinition* index, - MDefinition* initLength, bool needsHoleCheck) { - return new(alloc) MLoadElementHole(elements, index, initLength, needsHoleCheck); + MDefinition* initLength, JSValueType unboxedType, + bool needsHoleCheck) { + return new(alloc) MLoadElementHole(elements, index, initLength, + unboxedType, needsHoleCheck); } MDefinition* elements() const { @@ -8312,6 +8454,9 @@ class MLoadElementHole MDefinition* initLength() const { return getOperand(2); } + JSValueType unboxedType() const { + return unboxedType_; + } bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } @@ -8322,6 +8467,8 @@ class MLoadElementHole if (!ins->isLoadElementHole()) return false; const MLoadElementHole* other = ins->toLoadElementHole(); + if (unboxedType() != other->unboxedType()) + return false; if (needsHoleCheck() != other->needsHoleCheck()) return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) @@ -8329,7 +8476,9 @@ class MLoadElementHole return congruentIfOperandsEqual(other); } AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::Element); + return AliasSet::Load(unboxedType() == JSVAL_TYPE_MAGIC + ? AliasSet::Element + : AliasSet::UnboxedElement); } void collectRangeInfoPreTrunc() override; @@ -8539,17 +8688,21 @@ class MStoreElement ALLOW_CLONE(MStoreElement) }; -// Like MStoreElement, but supports indexes >= initialized length. The downside -// is that we cannot hoist the elements vector and bounds check, since this -// instruction may update the (initialized) length and reallocate the elements -// vector. +// Like MStoreElement, but supports indexes >= initialized length, and can +// handle unboxed arrays. The downside is that we cannot hoist the elements +// vector and bounds check, since this instruction may update the (initialized) +// length and reallocate the elements vector. class MStoreElementHole : public MAryInstruction<4>, public MStoreElementCommon, public MixPolicy >::Data { + JSValueType unboxedType_; + MStoreElementHole(MDefinition* object, MDefinition* elements, - MDefinition* index, MDefinition* value) { + MDefinition* index, MDefinition* value, JSValueType unboxedType) + : unboxedType_(unboxedType) + { initOperand(0, object); initOperand(1, elements); initOperand(2, index); @@ -8562,8 +8715,8 @@ class MStoreElementHole INSTRUCTION_HEADER(StoreElementHole) static MStoreElementHole* New(TempAllocator& alloc, MDefinition* object, MDefinition* elements, - MDefinition* index, MDefinition* value) { - return new(alloc) MStoreElementHole(object, elements, index, value); + MDefinition* index, MDefinition* value, JSValueType unboxedType) { + return new(alloc) MStoreElementHole(object, elements, index, value, unboxedType); } MDefinition* object() const { @@ -8578,6 +8731,9 @@ class MStoreElementHole MDefinition* value() const { return getOperand(3); } + JSValueType unboxedType() const { + return unboxedType_; + } ALLOW_CLONE(MStoreElementHole) }; @@ -9739,7 +9895,7 @@ class MGetPropertyPolymorphic { struct Entry { // The group and/or shape to guard against. - ReceiverGuard::StackGuard receiver; + ReceiverGuard receiver; // The property to load, null for loads from unboxed properties. Shape* shape; @@ -9773,7 +9929,7 @@ class MGetPropertyPolymorphic return congruentIfOperandsEqual(ins); } - bool addReceiver(const ReceiverGuard::StackGuard receiver, Shape* shape) { + bool addReceiver(const ReceiverGuard& receiver, Shape* shape) { Entry entry; entry.receiver = receiver; entry.shape = shape; @@ -9782,7 +9938,7 @@ class MGetPropertyPolymorphic size_t numReceivers() const { return receivers_.length(); } - const ReceiverGuard::StackGuard receiver(size_t i) const { + const ReceiverGuard receiver(size_t i) const { return receivers_[i].receiver; } Shape* shape(size_t i) const { @@ -9819,7 +9975,7 @@ class MSetPropertyPolymorphic { struct Entry { // The group and/or shape to guard against. - ReceiverGuard::StackGuard receiver; + ReceiverGuard receiver; // The property to store, null for stores to unboxed properties. Shape* shape; @@ -9846,7 +10002,7 @@ class MSetPropertyPolymorphic return new(alloc) MSetPropertyPolymorphic(alloc, obj, value, name); } - bool addReceiver(const ReceiverGuard::StackGuard& receiver, Shape* shape) { + bool addReceiver(const ReceiverGuard& receiver, Shape* shape) { Entry entry; entry.receiver = receiver; entry.shape = shape; @@ -9855,7 +10011,7 @@ class MSetPropertyPolymorphic size_t numReceivers() const { return receivers_.length(); } - const ReceiverGuard::StackGuard& receiver(size_t i) const { + const ReceiverGuard& receiver(size_t i) const { return receivers_[i].receiver; } Shape* shape(size_t i) const { @@ -10182,7 +10338,7 @@ class MGuardReceiverPolymorphic : public MUnaryInstruction, public SingleObjectPolicy::Data { - Vector receivers_; + Vector receivers_; MGuardReceiverPolymorphic(TempAllocator& alloc, MDefinition* obj) : MUnaryInstruction(obj), @@ -10204,13 +10360,13 @@ class MGuardReceiverPolymorphic return getOperand(0); } - bool addReceiver(const ReceiverGuard::StackGuard& receiver) { + bool addReceiver(const ReceiverGuard& receiver) { return receivers_.append(receiver); } size_t numReceivers() const { return receivers_.length(); } - const ReceiverGuard::StackGuard& receiver(size_t i) const { + const ReceiverGuard& receiver(size_t i) const { return receivers_[i]; } @@ -13146,6 +13302,8 @@ MControlInstruction* MDefinition::toControlInstruction() { bool ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id); +JSValueType UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, + MDefinition* id); bool ElementAccessIsAnyTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, Scalar::Type* arrayType); diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index 76fcef8d1b..ffbaacad5e 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -37,8 +37,10 @@ class MIRGenerator public: MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options, TempAllocator* alloc, MIRGraph* graph, - CompileInfo *info, const OptimizationInfo *optimizationInfo, - Label *outOfBoundsLabel = nullptr, bool usesSignalHandlersForAsmJSOOB = false); + CompileInfo* info, const OptimizationInfo* optimizationInfo, + Label* outOfBoundsLabel = nullptr, + Label* conversionErrorLabel = nullptr, + bool usesSignalHandlersForAsmJSOOB = false); TempAllocator& alloc() { return *alloc_; @@ -200,13 +202,17 @@ class MIRGenerator // CodeGenerator::link). ObjectVector nurseryObjects_; - Label *outOfBoundsLabel_; + void addAbortedPreliminaryGroup(ObjectGroup* group); + + Label* outOfBoundsLabel_; + // Label where we should jump in asm.js mode, in the case where we have an + // invalid conversion or a loss of precision (when converting from a + // floating point SIMD type into an integer SIMD type). + Label* conversionErrorLabel_; #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) bool usesSignalHandlersForAsmJSOOB_; #endif - void addAbortedPreliminaryGroup(ObjectGroup *group); - void setForceAbort() { shouldForceAbort_ = true; } @@ -230,10 +236,15 @@ class MIRGenerator return nurseryObjects_; } - Label *outOfBoundsLabel() const { + Label* conversionErrorLabel() const { + MOZ_ASSERT((conversionErrorLabel_ != nullptr) == compilingAsmJS()); + return conversionErrorLabel_; + } + Label* outOfBoundsLabel() const { + MOZ_ASSERT(compilingAsmJS()); return outOfBoundsLabel_; } - bool needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess *access) const { + bool needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const { // A heap access needs a bounds-check branch if we're not relying on signal // handlers to catch errors, and if it's not proven to be within bounds. // We use signal-handlers on x64, but on x86 there isn't enough address diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 3c93802002..08bb421635 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -19,8 +19,10 @@ using mozilla::Swap; MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options, TempAllocator* alloc, MIRGraph* graph, CompileInfo* info, - const OptimizationInfo *optimizationInfo, - Label *outOfBoundsLabel, bool usesSignalHandlersForAsmJSOOB) + const OptimizationInfo* optimizationInfo, + Label* outOfBoundsLabel, + Label* conversionErrorLabel, + bool usesSignalHandlersForAsmJSOOB) : compartment(compartment), info_(info), optimizationInfo_(optimizationInfo), @@ -42,6 +44,7 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti instrumentedProfilingIsCached_(false), nurseryObjects_(*alloc), outOfBoundsLabel_(outOfBoundsLabel), + conversionErrorLabel_(conversionErrorLabel), #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB), #endif diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index b594e52c2f..57c076670f 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -177,6 +177,9 @@ namespace jit { _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \ + _(UnboxedArrayLength) \ + _(UnboxedArrayInitializedLength) \ + _(IncrementUnboxedArrayInitializedLength) \ _(Not) \ _(BoundsCheck) \ _(BoundsCheckLower) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 1f65a0d49d..9f8ffa2d1c 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -916,6 +916,31 @@ template void MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type, ConstantOrRegister value, Label* failure); +void +MacroAssembler::checkUnboxedArrayCapacity(Register obj, const Int32Key& index, Register temp, + Label* failure) +{ + Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); + + Label capacityIsIndex, done; + load32(initLengthAddr, temp); + branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex); + branchKey(Assembler::BelowOrEqual, lengthAddr, index, failure); + jump(&done); + bind(&capacityIsIndex); + + // Do a partial shift so that we can get an absolute offset from the base + // of CapacityArray to use. + JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4); + rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp); + and32(Imm32(~0x3), temp); + + addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp); + branchKey(Assembler::BelowOrEqual, Address(temp, 0), index, failure); + bind(&done); +} + // Inlined version of gc::CheckAllocatorState that checks the bare essentials // and bails for anything that cannot be handled with our jit allocators. void @@ -960,9 +985,10 @@ MacroAssembler::nurseryAllocate(Register result, Register temp, gc::AllocKind al MOZ_ASSERT(initialHeap != gc::TenuredHeap); // We still need to allocate in the nursery, per the comment in - // shouldNurseryAllocate; however, we need to insert into hugeSlots, so - // bail to do the nursery allocation in the interpreter. - if (nDynamicSlots >= Nursery::MaxNurserySlots) { + // shouldNurseryAllocate; however, we need to insert into the + // mallocedBuffers set, so bail to do the nursery allocation in the + // interpreter. + if (nDynamicSlots >= Nursery::MaxNurseryBufferSize / sizeof(Value)) { jump(fail); return; } @@ -1346,6 +1372,16 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj, storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando())); if (initContents) initUnboxedObjectContents(obj, &templateObj->as()); + } else if (templateObj->is()) { + MOZ_ASSERT(templateObj->as().hasInlineElements()); + int elementsOffset = UnboxedArrayObject::offsetOfInlineElements(); + computeEffectiveAddress(Address(obj, elementsOffset), temp); + storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements())); + store32(Imm32(templateObj->as().length()), + Address(obj, UnboxedArrayObject::offsetOfLength())); + uint32_t capacityIndex = templateObj->as().capacityIndex(); + store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift), + Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); } else { MOZ_CRASH("Unknown object"); } @@ -1464,7 +1500,7 @@ void MacroAssembler::linkExitFrame() { AbsoluteAddress jitTop(GetJitContext()->runtime->addressOfJitTop()); - storePtr(StackPointer, jitTop); + storeStackPtr(jitTop); } static void @@ -1577,7 +1613,7 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo) popValue(R0); // Discard exit frame. - addPtr(Imm32(ExitFrameLayout::SizeWithFooter()), StackPointer); + addToStackPtr(Imm32(ExitFrameLayout::SizeWithFooter())); #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) push(BaselineTailCallReg); @@ -1615,7 +1651,7 @@ MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo) popValue(R0); // Discard exit frame. - addPtr(Imm32(ExitFrameLayout::SizeWithFooter()), StackPointer); + addToStackPtr(Imm32(ExitFrameLayout::SizeWithFooter())); jump(jitcodeReg); } @@ -2405,15 +2441,15 @@ MacroAssembler::alignJitStackBasedOnNArgs(Register nargs) #endif assertStackAlignment(sizeof(Value), 0); branchTestPtr(Assembler::NonZero, nargs, Imm32(1), &odd); - branchTestPtr(Assembler::NonZero, StackPointer, Imm32(JitStackAlignment - 1), maybeAssert); - subPtr(Imm32(sizeof(Value)), StackPointer); + branchTestStackPtr(Assembler::NonZero, Imm32(JitStackAlignment - 1), maybeAssert); + subFromStackPtr(Imm32(sizeof(Value))); #ifdef DEBUG bind(&assert); #endif assertStackAlignment(JitStackAlignment, sizeof(Value)); jump(&end); bind(&odd); - andPtr(Imm32(~(JitStackAlignment - 1)), StackPointer); + andToStackPtr(Imm32(~(JitStackAlignment - 1))); bind(&end); } @@ -2441,12 +2477,12 @@ MacroAssembler::alignJitStackBasedOnNArgs(uint32_t nargs) assertStackAlignment(sizeof(Value), 0); if (nargs % 2 == 0) { Label end; - branchTestPtr(Assembler::NonZero, StackPointer, Imm32(JitStackAlignment - 1), &end); - subPtr(Imm32(sizeof(Value)), StackPointer); + branchTestStackPtr(Assembler::NonZero, Imm32(JitStackAlignment - 1), &end); + subFromStackPtr(Imm32(sizeof(Value))); bind(&end); assertStackAlignment(JitStackAlignment, sizeof(Value)); } else { - andPtr(Imm32(~(JitStackAlignment - 1)), StackPointer); + andToStackPtr(Imm32(~(JitStackAlignment - 1))); } } diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index d951ab74fe..1f5b0f8541 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -572,7 +572,7 @@ class MacroAssembler : public MacroAssemblerSpecific return extractObject(source, scratch); } - void branchIfFunctionHasNoScript(Register fun, Label *label) { + void branchIfFunctionHasNoScript(Register fun, Label* label) { // 16-bit loads are slow and unaligned 32-bit loads may be too so // perform an aligned 32-bit load and adjust the bitmask accordingly. MOZ_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0); @@ -672,7 +672,7 @@ class MacroAssembler : public MacroAssemblerSpecific } template - void loadFromTypedArray(Scalar::Type arrayType, const T &src, AnyRegister dest, Register temp, Label *fail, + void loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp, Label* fail, bool canonicalizeDoubles = true, unsigned numElems = 0); template @@ -708,12 +708,12 @@ class MacroAssembler : public MacroAssemblerSpecific void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S &value, const T &mem, Register temp1, Register temp2, AnyRegister output); - void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex &dest, + void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex& dest, unsigned numElems = 0); - void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address &dest, + void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest, unsigned numElems = 0); - // Load a property from an UnboxedPlainObject. + // Load a property from an UnboxedPlainObject or UnboxedArrayObject. template void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output); @@ -724,6 +724,9 @@ class MacroAssembler : public MacroAssemblerSpecific void storeUnboxedProperty(T address, JSValueType type, ConstantOrRegister value, Label* failure); + void checkUnboxedArrayCapacity(Register obj, const Int32Key& index, Register temp, + Label* failure); + Register extractString(const Address& address, Register scratch) { return extractObject(address, scratch); } @@ -931,6 +934,53 @@ class MacroAssembler : public MacroAssemblerSpecific branchTestClassIsProxy(proxy, scratch, label); } + public: +#ifndef JS_CODEGEN_ARM64 + // StackPointer manipulation functions. + // On ARM64, the StackPointer is implemented as two synchronized registers. + // Code shared across platforms must use these functions to be valid. + template + void addToStackPtr(T t) { addPtr(t, getStackPointer()); } + template + void addStackPtrTo(T t) { addPtr(getStackPointer(), t); } + + template + void subFromStackPtr(T t) { subPtr(t, getStackPointer()); } + template + void subStackPtrFrom(T t) { subPtr(getStackPointer(), t); } + + template + void andToStackPtr(T t) { andPtr(t, getStackPointer()); } + template + void andStackPtrTo(T t) { andPtr(getStackPointer(), t); } + + template + void moveToStackPtr(T t) { movePtr(t, getStackPointer()); } + template + void moveStackPtrTo(T t) { movePtr(getStackPointer(), t); } + + template + void loadStackPtr(T t) { loadPtr(t, getStackPointer()); } + template + void storeStackPtr(T t) { storePtr(getStackPointer(), t); } + + // StackPointer testing functions. + // On ARM64, sp can function as the zero register depending on context. + // Code shared across platforms must use these functions to be valid. + template + void branchTestStackPtr(Condition cond, T t, Label *label) { + branchTestPtr(cond, getStackPointer(), t, label); + } + template + void branchStackPtr(Condition cond, T rhs, Label *label) { + branchPtr(cond, getStackPointer(), rhs, label); + } + template + void branchStackPtrRhs(Condition cond, T lhs, Label *label) { + branchPtr(cond, lhs, getStackPointer(), label); + } +#endif // !JS_CODEGEN_ARM64 + private: // These two functions are helpers used around call sites throughout the // assembler. They are called from the above call wrappers to emit the @@ -1259,12 +1309,12 @@ class MacroAssembler : public MacroAssemblerSpecific uint32_t off = offset; while (off) { uint32_t lowestBit = 1 << mozilla::CountTrailingZeroes32(off); - branchTestPtr(Assembler::Zero, StackPointer, Imm32(lowestBit), &bad); + branchTestStackPtr(Assembler::Zero, Imm32(lowestBit), &bad); off ^= lowestBit; } // Check that all remaining bits are zero. - branchTestPtr(Assembler::Zero, StackPointer, Imm32((alignment - 1) ^ offset), &ok); + branchTestStackPtr(Assembler::Zero, Imm32((alignment - 1) ^ offset), &ok); bind(&bad); breakpoint(); diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp index 22478cce44..a7d1a19b84 100644 --- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -214,7 +214,7 @@ HashType(TypeSet::Type ty) } static HashNumber -HashTypeList(const TypeSet::TypeList& types) +HashTypeList(const TempTypeList& types) { HashNumber h = 0; for (uint32_t i = 0; i < types.length(); i++) @@ -445,11 +445,11 @@ IonTrackedOptimizationsRegion::RangeIterator::readNext(uint32_t* startOffset, ui } Maybe -JitcodeGlobalEntry::IonEntry::trackedOptimizationIndexAtAddr(void *ptr, uint32_t *entryOffsetOut) +JitcodeGlobalEntry::IonEntry::trackedOptimizationIndexAtAddr(void* ptr, uint32_t* entryOffsetOut) { MOZ_ASSERT(hasTrackedOptimizations()); MOZ_ASSERT(containsPointer(ptr)); - uint32_t ptrOffset = ((uint8_t *) ptr) - ((uint8_t *) nativeStartAddr()); + uint32_t ptrOffset = ((uint8_t*) ptr) - ((uint8_t*) nativeStartAddr()); Maybe region = optsRegionTable_->findRegion(ptrOffset); if (region.isNothing()) return Nothing(); @@ -491,7 +491,7 @@ IonTrackedOptimizationsTypeInfo::forEach(ForEachOp& op, const IonTrackedTypeVect } Maybe -IonTrackedOptimizationsRegion::findIndex(uint32_t offset, uint32_t *entryOffsetOut) const +IonTrackedOptimizationsRegion::findIndex(uint32_t offset, uint32_t* entryOffsetOut) const { if (offset <= startOffset_ || offset > endOffset_) return Nothing(); @@ -1040,6 +1040,11 @@ IonBuilder::startTrackingOptimizations() // OOMs are handled as if optimization tracking were turned off. if (!trackedOptimizationSites_.append(site)) site = nullptr; + } else { + // The same bytecode may be visited multiple times (see + // restartLoop). Only the last time matters, so clear any previous + // tracked optimizations. + site->optimizations()->clear(); } if (site) @@ -1053,7 +1058,7 @@ IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType, { BytecodeSite* site = current->trackedSite(); // OOMs are handled as if optimization tracking were turned off. - OptimizationTypeInfo typeInfo(kind, mirType); + OptimizationTypeInfo typeInfo(alloc(), kind, mirType); if (!typeInfo.trackTypeSet(typeSet)) { site->setOptimizations(nullptr); return; @@ -1067,7 +1072,7 @@ IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, JSObject* obj) { BytecodeSite* site = current->trackedSite(); // OOMs are handled as if optimization tracking were turned off. - OptimizationTypeInfo typeInfo(kind, MIRType_Object); + OptimizationTypeInfo typeInfo(alloc(), kind, MIRType_Object); if (!typeInfo.trackType(TypeSet::ObjectType(obj))) return; if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo))) @@ -1128,11 +1133,11 @@ IonBuilder::trackInlineSuccessUnchecked(InliningStatus status) } JS_PUBLIC_API(void) -JS::ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr, uint8_t index, - ForEachTrackedOptimizationAttemptOp &op, - JSScript **scriptOut, jsbytecode **pcOut) +JS::ForEachTrackedOptimizationAttempt(JSRuntime* rt, void* addr, uint8_t index, + ForEachTrackedOptimizationAttemptOp& op, + JSScript** scriptOut, jsbytecode** pcOut) { - JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable(); + JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); JitcodeGlobalEntry entry; table->lookupInfallible(addr, &entry, rt); entry.youngestFrameLocationAtAddr(rt, addr, scriptOut, pcOut); @@ -1170,7 +1175,7 @@ FunctionFromTrackedType(const IonTrackedTypeWithAddendum& tracked) } void -IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::readType(const IonTrackedTypeWithAddendum &tracked) +IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::readType(const IonTrackedTypeWithAddendum& tracked) { TypeSet::Type ty = tracked.type; @@ -1182,7 +1187,7 @@ IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::readType(const IonTrackedType char buf[512]; const uint32_t bufsize = mozilla::ArrayLength(buf); - if (JSFunction *fun = FunctionFromTrackedType(tracked)) { + if (JSFunction* fun = FunctionFromTrackedType(tracked)) { if (fun->isNative()) { // // Print out the absolute address of the function pointer. @@ -1196,7 +1201,7 @@ IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::readType(const IonTrackedType // for use with utilities like `addr2line` on Linux and `atos` on // OS X. Converting to an offset may be done via dladdr(): // - // void *addr = JS_FUNC_TO_DATA_PTR(void *, fun->native()); + // void* addr = JS_FUNC_TO_DATA_PTR(void*, fun->native()); // uintptr_t offset; // Dl_info info; // if (dladdr(addr, &info) != 0) @@ -1208,19 +1213,22 @@ IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::readType(const IonTrackedType return; } - PutEscapedString(buf, bufsize, fun->displayAtom(), 0); - const char *filename; + if (fun->displayAtom()) + PutEscapedString(buf, bufsize, fun->displayAtom(), 0); + const char* filename; unsigned lineno; InterpretedFunctionFilenameAndLineNumber(fun, &filename, &lineno); - op_.readType(tracked.constructor ? "constructor" : "function", buf, filename, lineno); + op_.readType(tracked.constructor ? "constructor" : "function", + fun->displayAtom() ? buf : nullptr, + filename, lineno); return; } - const char *className = ty.objectKey()->clasp()->name; + const char* className = ty.objectKey()->clasp()->name; JS_snprintf(buf, bufsize, "[object %s]", className); if (tracked.hasAllocationSite()) { - JSScript *script = tracked.script; + JSScript* script = tracked.script; op_.readType("alloc site", buf, script->maybeForwardedScriptSource()->filename(), PCToLineNumber(script, script->offsetToPC(tracked.offset))); @@ -1243,10 +1251,10 @@ IonTrackedOptimizationsTypeInfo::ForEachOpAdapter::operator()(JS::TrackedTypeSit } JS_PUBLIC_API(void) -JS::ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr, uint8_t index, - ForEachTrackedOptimizationTypeInfoOp &op) +JS::ForEachTrackedOptimizationTypeInfo(JSRuntime* rt, void* addr, uint8_t index, + ForEachTrackedOptimizationTypeInfoOp& op) { - JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable(); + JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); JitcodeGlobalEntry entry; table->lookupInfallible(addr, &entry, rt); IonTrackedOptimizationsTypeInfo::ForEachOpAdapter adapter(op); @@ -1254,9 +1262,9 @@ JS::ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr, uint8_t index, } JS_PUBLIC_API(Maybe) -JS::TrackedOptimizationIndexAtAddr(JSRuntime *rt, void *addr, void **entryAddr) +JS::TrackedOptimizationIndexAtAddr(JSRuntime* rt, void* addr, void** entryAddr) { - JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable(); + JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); JitcodeGlobalEntry entry; table->lookupInfallible(addr, &entry, rt); if (!entry.hasTrackedOptimizations()) @@ -1264,6 +1272,6 @@ JS::TrackedOptimizationIndexAtAddr(JSRuntime *rt, void *addr, void **entryAddr) uint32_t entryOffset = 0; Maybe index = entry.trackedOptimizationIndexAtAddr(addr, &entryOffset); if (index.isSome()) - *entryAddr = (void *)(((uint8_t *) entry.nativeStartAddr()) + entryOffset); + *entryAddr = (void*)(((uint8_t*) entry.nativeStartAddr()) + entryOffset); return index; } diff --git a/js/src/jit/OptimizationTracking.h b/js/src/jit/OptimizationTracking.h index 7f1e141740..47d73acbd4 100644 --- a/js/src/jit/OptimizationTracking.h +++ b/js/src/jit/OptimizationTracking.h @@ -52,6 +52,7 @@ class OptimizationAttempt }; typedef Vector TempOptimizationAttemptsVector; +typedef Vector TempTypeList; class UniqueTrackedTypes; @@ -59,7 +60,7 @@ class OptimizationTypeInfo { JS::TrackedTypeSite site_; MIRType mirType_; - TypeSet::TypeList types_; + TempTypeList types_; public: OptimizationTypeInfo(OptimizationTypeInfo&& other) @@ -68,9 +69,10 @@ class OptimizationTypeInfo types_(mozilla::Move(other.types_)) { } - OptimizationTypeInfo(JS::TrackedTypeSite site, MIRType mirType) + OptimizationTypeInfo(TempAllocator& alloc, JS::TrackedTypeSite site, MIRType mirType) : site_(site), - mirType_(mirType) + mirType_(mirType), + types_(alloc) { } bool trackTypeSet(TemporaryTypeSet* typeSet); @@ -78,7 +80,7 @@ class OptimizationTypeInfo JS::TrackedTypeSite site() const { return site_; } MIRType mirType() const { return mirType_; } - const TypeSet::TypeList& types() const { return types_; } + const TempTypeList& types() const { return types_; } bool operator ==(const OptimizationTypeInfo& other) const; bool operator !=(const OptimizationTypeInfo& other) const; @@ -105,6 +107,12 @@ class TrackedOptimizations : public TempObject currentAttempt_(UINT32_MAX) { } + void clear() { + types_.clear(); + attempts_.clear(); + currentAttempt_ = UINT32_MAX; + } + bool trackTypeInfo(OptimizationTypeInfo&& ty); bool trackAttempt(JS::TrackedStrategy strategy); diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index 929820a82c..bdfe101fe3 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1214,10 +1214,6 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const RootedValue result(cx); RootedObjectGroup group(cx); - // See CodeGenerator::visitNewArrayCallVM - if (!templateObject->isSingleton()) - group = templateObject->group(); - JSObject* resultObject = NewDenseArray(cx, count_, group, allocatingBehaviour_); if (!resultObject) return false; diff --git a/js/src/jit/RematerializedFrame.cpp b/js/src/jit/RematerializedFrame.cpp index c868448029..8a380c0cfa 100644 --- a/js/src/jit/RematerializedFrame.cpp +++ b/js/src/jit/RematerializedFrame.cpp @@ -98,7 +98,7 @@ RematerializedFrame::FreeInVector(Vector& frames) { for (size_t i = 0; i < frames.length(); i++) { RematerializedFrame* f = frames[i]; - Debugger::assertNotInFrameMaps(f); + MOZ_ASSERT(!Debugger::inFrameMaps(f)); f->RematerializedFrame::~RematerializedFrame(); js_free(f); } diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index b8715dd6b8..0aaf282125 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -575,6 +575,10 @@ IsArrayEscaped(MInstruction* ins) return true; } + JSObject* obj = ins->toNewArray()->templateObject(); + if (!obj || obj->is()) + return true; + if (count >= 16) { JitSpewDef(JitSpew_Escape, "Array has too many elements\n", ins); return true; diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 62f7aa8cf7..0a34b6ef31 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -977,8 +977,23 @@ PopBlockScope(JSContext* cx, BaselineFrame* frame) } bool -FreshenBlockScope(JSContext *cx, BaselineFrame *frame) +DebugLeaveThenPopBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc) { + MOZ_ALWAYS_TRUE(DebugLeaveBlock(cx, frame, pc)); + frame->popBlock(cx); + return true; +} + +bool +FreshenBlockScope(JSContext* cx, BaselineFrame* frame) +{ + return frame->freshenBlock(cx); +} + +bool +DebugLeaveThenFreshenBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc) +{ + MOZ_ALWAYS_TRUE(DebugLeaveBlock(cx, frame, pc)); return frame->freshenBlock(cx); } @@ -1086,22 +1101,22 @@ Recompile(JSContext* cx) } bool -SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, - bool strict) +SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, + HandleValue value, bool strict) { // This function is called from Ion code for StoreElementHole's OOL path. - // In this case we know the object is native and we can use setDenseElement - // instead of setDenseElementWithType. + // In this case we know the object is native or an unboxed array and we can + // use setDenseElement instead of setDenseElementWithType. NativeObject::EnsureDenseResult result = NativeObject::ED_SPARSE; do { - if (index < 0) + if (index < 0 || obj->is()) break; bool isArray = obj->is(); if (isArray && !obj->as().lengthIsWritable()) break; uint32_t idx = uint32_t(index); - result = obj->ensureDenseElements(cx, idx, 1); + result = obj->as().ensureDenseElements(cx, idx, 1); if (result != NativeObject::ED_OK) break; if (isArray) { @@ -1109,7 +1124,7 @@ SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValu if (idx >= arr.length()) arr.setLengthInt32(idx + 1); } - obj->setDenseElement(idx, value); + obj->as().setDenseElement(idx, value); return true; } while (false); @@ -1117,6 +1132,15 @@ SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValu return false; MOZ_ASSERT(result == NativeObject::ED_SPARSE); + if (index >= 0 && obj->is()) { + UnboxedArrayObject* nobj = &obj->as(); + if (uint32_t(index) == nobj->initializedLength() && + uint32_t(index) < UnboxedArrayObject::MaximumCapacity) + { + return nobj->appendElementNoTypeChange(cx, index, value); + } + } + RootedValue indexVal(cx, Int32Value(index)); return SetObjectElement(cx, obj, indexVal, value, strict); } diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 9eb6bbf17f..caa11d2a3e 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -731,7 +731,9 @@ bool LeaveWith(JSContext* cx, BaselineFrame* frame); bool PushBlockScope(JSContext* cx, BaselineFrame* frame, Handle block); bool PopBlockScope(JSContext* cx, BaselineFrame* frame); -bool FreshenBlockScope(JSContext *cx, BaselineFrame *frame); +bool DebugLeaveThenPopBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc); +bool FreshenBlockScope(JSContext* cx, BaselineFrame* frame); +bool DebugLeaveThenFreshenBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc); bool DebugLeaveBlock(JSContext* cx, BaselineFrame* frame, jsbytecode* pc); bool InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame, @@ -749,20 +751,20 @@ JSString* RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp, JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern, HandleString repl); -bool SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value, - bool strict); +bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, + HandleValue value, bool strict); -void AssertValidObjectPtr(JSContext *cx, JSObject *obj); -void AssertValidObjectOrNullPtr(JSContext *cx, JSObject *obj); -void AssertValidStringPtr(JSContext *cx, JSString *str); -void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym); -void AssertValidValue(JSContext *cx, Value *v); +void AssertValidObjectPtr(JSContext* cx, JSObject* obj); +void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj); +void AssertValidStringPtr(JSContext* cx, JSString* str); +void AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym); +void AssertValidValue(JSContext* cx, Value* v); -void MarkValueFromIon(JSRuntime *rt, Value *vp); -void MarkStringFromIon(JSRuntime *rt, JSString **stringp); -void MarkObjectFromIon(JSRuntime *rt, JSObject **objp); -void MarkShapeFromIon(JSRuntime *rt, Shape **shapep); -void MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp); +void MarkValueFromIon(JSRuntime* rt, Value* vp); +void MarkStringFromIon(JSRuntime* rt, JSString** stringp); +void MarkObjectFromIon(JSRuntime* rt, JSObject** objp); +void MarkShapeFromIon(JSRuntime* rt, Shape** shapep); +void MarkObjectGroupFromIon(JSRuntime* rt, ObjectGroup** groupp); // Helper for generatePreBarrier. inline void* diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 443ba141dd..66262de825 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1269,6 +1269,10 @@ class Assembler : public AssemblerShared void setPrinter(Sprinter* sp) { } + static const Register getStackPointer() { + return StackPointer; + } + private: bool isFinished; public: diff --git a/js/src/jit/mips/Assembler-mips.h b/js/src/jit/mips/Assembler-mips.h index 1e6959967b..320df5633d 100644 --- a/js/src/jit/mips/Assembler-mips.h +++ b/js/src/jit/mips/Assembler-mips.h @@ -786,6 +786,10 @@ class Assembler : public AssemblerShared void setPrinter(Sprinter* sp) { } + static const Register getStackPointer() { + return StackPointer; + } + private: bool isFinished; public: diff --git a/js/src/jit/none/MacroAssembler-none.h b/js/src/jit/none/MacroAssembler-none.h index 773586cdf5..5c6947c3ac 100644 --- a/js/src/jit/none/MacroAssembler-none.h +++ b/js/src/jit/none/MacroAssembler-none.h @@ -450,6 +450,8 @@ class MacroAssemblerNone : public Assembler void setPrinter(Sprinter*) { MOZ_CRASH(); } Operand ToPayload(Operand base) { MOZ_CRASH(); } + static const Register getStackPointer() { MOZ_CRASH(); } + // Instrumentation for entering and leaving the profiler. void profilerEnterFrame(Register , Register ) { MOZ_CRASH(); } void profilerExitFrame() { MOZ_CRASH(); } diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index a0c24ab2bc..4de0bc5092 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -886,12 +886,15 @@ class ReadTempAttemptsVectorOp : public JS::ForEachTrackedOptimizationAttemptOp struct ReadTempTypeInfoVectorOp : public IonTrackedOptimizationsTypeInfo::ForEachOp { + TempAllocator& alloc_; TempOptimizationTypeInfoVector* types_; - TypeSet::TypeList accTypes_; + TempTypeList accTypes_; public: - explicit ReadTempTypeInfoVectorOp(TempOptimizationTypeInfoVector* types) - : types_(types) + ReadTempTypeInfoVectorOp(TempAllocator& alloc, TempOptimizationTypeInfoVector* types) + : alloc_(alloc), + types_(types), + accTypes_(alloc) { } void readType(const IonTrackedTypeWithAddendum& tracked) override { @@ -899,7 +902,7 @@ struct ReadTempTypeInfoVectorOp : public IonTrackedOptimizationsTypeInfo::ForEac } void operator()(JS::TrackedTypeSite site, MIRType mirType) override { - OptimizationTypeInfo ty(site, mirType); + OptimizationTypeInfo ty(alloc_, site, mirType); for (uint32_t i = 0; i < accTypes_.length(); i++) MOZ_ALWAYS_TRUE(ty.trackType(accTypes_[i])); MOZ_ALWAYS_TRUE(types_->append(mozilla::Move(ty))); @@ -971,7 +974,7 @@ CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(JitCode* code, uint32_ // decoded. IonTrackedOptimizationsTypeInfo typeInfo = typesTable->entry(index); TempOptimizationTypeInfoVector tvec(alloc()); - ReadTempTypeInfoVectorOp top(&tvec); + ReadTempTypeInfoVectorOp top(alloc(), &tvec); typeInfo.forEach(top, allTypes); MOZ_ASSERT(entry.optimizations->matchTypes(tvec)); @@ -1057,7 +1060,7 @@ HandleRegisterDump(Op op, MacroAssembler &masm, LiveRegisterSet liveRegs, Regist // To use the original value of the activation register (that's // now on top of the stack), we need the scratch register. masm.push(scratch); - masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), scratch); + masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch); op(scratch, dump); masm.pop(scratch); } else { @@ -1435,7 +1438,7 @@ CodeGeneratorShared::emitAsmJSCall(LAsmJSCall* ins) AsmJSStackAlignment % ABIStackAlignment == 0, "The asm.js stack alignment should subsume the ABI-required alignment"); Label ok; - masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(AsmJSStackAlignment - 1), &ok); + masm.branchTestStackPtr(Assembler::Zero, Imm32(AsmJSStackAlignment - 1), &ok); masm.breakpoint(); masm.bind(&ok); #endif diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 13c88ddf8b..186ac88412 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -534,8 +534,13 @@ MacroAssemblerX64::branchValueIsNurseryObject(Condition cond, ValueOperand value { MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); - // 'Value' representing the start of the nursery tagged as a JSObject const Nursery& nursery = GetJitContext()->runtime->gcNursery(); + + // Avoid creating a bogus ObjectValue below. + if (!nursery.exists()) + return; + + // 'Value' representing the start of the nursery tagged as a JSObject Value start = ObjectValue(*reinterpret_cast(nursery.start())); movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), ScratchReg); diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index 3649e21e80..1d6235ab82 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -349,6 +349,10 @@ class AssemblerX86Shared : public AssemblerShared masm.setPrinter(sp); } + static const Register getStackPointer() { + return StackPointer; + } + void executableCopy(void* buffer); void processCodeLabels(uint8_t* rawCode); static int32_t ExtractCodeLabelOffset(uint8_t* code) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 8187ec4e87..4f492ae913 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2175,7 +2175,58 @@ CodeGeneratorX86Shared::visitFloat32x4ToInt32x4(LFloat32x4ToInt32x4* ins) { FloatRegister in = ToFloatRegister(ins->input()); FloatRegister out = ToFloatRegister(ins->output()); + Register temp = ToRegister(ins->temp()); + masm.convertFloat32x4ToInt32x4(in, out); + + OutOfLineSimdFloatToIntCheck *ool = new(alloc()) OutOfLineSimdFloatToIntCheck(temp, in, ins); + addOutOfLineCode(ool, ins->mir()); + + static const SimdConstant InvalidResult = SimdConstant::SplatX4(int32_t(-2147483648)); + + masm.loadConstantInt32x4(InvalidResult, ScratchSimdReg); + masm.packedEqualInt32x4(Operand(out), ScratchSimdReg); + // TODO (bug 1156228): If we have SSE4.1, we can use PTEST here instead of + // the two following instructions. + masm.vmovmskps(ScratchSimdReg, temp); + masm.cmp32(temp, Imm32(0)); + masm.j(Assembler::NotEqual, ool->entry()); + + masm.bind(ool->rejoin()); +} + +void +CodeGeneratorX86Shared::visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIntCheck *ool) +{ + static const SimdConstant Int32MaxX4 = SimdConstant::SplatX4(2147483647.f); + static const SimdConstant Int32MinX4 = SimdConstant::SplatX4(-2147483648.f); + + Label bail; + Label* onConversionError = gen->conversionErrorLabel(); + if (!onConversionError) + onConversionError = &bail; + + FloatRegister input = ool->input(); + Register temp = ool->temp(); + + masm.loadConstantFloat32x4(Int32MinX4, ScratchSimdReg); + masm.vcmpleps(Operand(input), ScratchSimdReg, ScratchSimdReg); + masm.vmovmskps(ScratchSimdReg, temp); + masm.cmp32(temp, Imm32(15)); + masm.j(Assembler::NotEqual, onConversionError); + + masm.loadConstantFloat32x4(Int32MaxX4, ScratchSimdReg); + masm.vcmpleps(Operand(input), ScratchSimdReg, ScratchSimdReg); + masm.vmovmskps(ScratchSimdReg, temp); + masm.cmp32(temp, Imm32(0)); + masm.j(Assembler::NotEqual, onConversionError); + + masm.jump(ool->rejoin()); + + if (bail.used()) { + masm.bind(&bail); + bailout(ool->ins()->snapshot()); + } } void @@ -3097,15 +3148,12 @@ CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins) FloatRegister out = ToFloatRegister(ins->output()); MOZ_ASSERT(ToFloatRegister(ins->vector()) == out); // defineReuseInput(0); - // TODO: If the shift count is greater than 31, this will just zero all - // lanes by default for lsh and ursh, and set the count to 32 for rsh - // (which will just extend the sign bit to all bits). Plain JS doesn't do - // this: instead it only keeps the five low bits of the mask. Spec isn't - // clear about that topic so this might need to be fixed. See also bug - // 1068028. + // If the shift count is greater than 31, this will just zero all lanes by + // default for lsh and ursh, and for rsh extend the sign bit to all bits, + // per the SIMD.js spec (as of March 19th 2015). const LAllocation* val = ins->value(); if (val->isConstant()) { - int32_t c = ToInt32(val); + uint32_t c = uint32_t(ToInt32(val)); if (c > 31) { switch (ins->operation()) { case MSimdShift::lsh: diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index a8ce8aeb9f..84afd816f4 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -53,27 +53,49 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared // Additional bounds checking for heap accesses with constant offsets. class OffsetBoundsCheck : public OutOfLineCodeBase { - Label *outOfBounds_; + Label* outOfBounds_; Register ptrReg_; int32_t offset_; public: - OffsetBoundsCheck(Label *outOfBounds, Register ptrReg, int32_t offset) + OffsetBoundsCheck(Label* outOfBounds, Register ptrReg, int32_t offset) : outOfBounds_(outOfBounds), ptrReg_(ptrReg), offset_(offset) {} - Label *outOfBounds() const { return outOfBounds_; } + Label* outOfBounds() const { return outOfBounds_; } Register ptrReg() const { return ptrReg_; } int32_t offset() const { return offset_; } - void accept(CodeGeneratorX86Shared *codegen) { + void accept(CodeGeneratorX86Shared* codegen) { codegen->visitOffsetBoundsCheck(this); } }; + // Additional bounds check for vector Float to Int conversion, when the + // undefined pattern is seen. Might imply a bailout. + class OutOfLineSimdFloatToIntCheck : public OutOfLineCodeBase + { + Register temp_; + FloatRegister input_; + LInstruction* ins_; + + public: + OutOfLineSimdFloatToIntCheck(Register temp, FloatRegister input, LInstruction *ins) + : temp_(temp), input_(input), ins_(ins) + {} + + Register temp() const { return temp_; } + FloatRegister input() const { return input_; } + LInstruction* ins() const { return ins_; } + + void accept(CodeGeneratorX86Shared* codegen) { + codegen->visitOutOfLineSimdFloatToIntCheck(this); + } + }; + // Functions for emitting bounds-checking code with branches. MOZ_WARN_UNUSED_RESULT - uint32_t emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess *mir, const MInstruction *ins, - Register ptr, Label *fail); - void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess *mir, Register ptr); + uint32_t emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins, + Register ptr, Label* fail); + void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, Register ptr); // Label for the common return path. NonAssertingLabel returnLabel_; @@ -282,6 +304,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitModOverflowCheck(ModOverflowCheck* ool); void visitReturnZero(ReturnZero* ool); void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool); + void visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIntCheck* ool); void generateInvalidateEpilogue(); }; diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index ce2b976878..a3dcc8c5cc 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -868,11 +868,10 @@ class MacroAssemblerX86Shared : public Assembler } void convertFloat32x4ToInt32x4(FloatRegister src, FloatRegister dest) { - // TODO: Note that if the conversion failed (because the converted + // Note that if the conversion failed (because the converted // result is larger than the maximum signed int32, or less than the // least signed int32, or NaN), this will return the undefined integer - // value (0x8000000). Spec should define what to do in such cases. See - // also bug 1068020. + // value (0x8000000). vcvttps2dq(src, dest); } void convertInt32x4ToFloat32x4(FloatRegister src, FloatRegister dest) { diff --git a/js/src/js.msg b/js/src/js.msg index 0c19be92b8..374c3dae2e 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -53,7 +53,6 @@ MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-co MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element") MSG_DEF(JSMSG_NOT_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a function") MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} is not a constructor") -MSG_DEF(JSMSG_CANT_CONVERT, 1, JSEXN_ERR, "can't convert {0} to an integer") MSG_DEF(JSMSG_CANT_CONVERT_TO, 2, JSEXN_TYPEERR, "can't convert {0} to {1}") MSG_DEF(JSMSG_NO_PROPERTIES, 1, JSEXN_TYPEERR, "{0} has no properties") MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") @@ -95,7 +94,6 @@ MSG_DEF(JSMSG_NOT_EXPECTED_TYPE, 3, JSEXN_TYPEERR, "{0}: expected {1}, got MSG_DEF(JSMSG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable") MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 2, JSEXN_NONE, "{0} is being assigned a {1}, but already has one") MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value") -MSG_DEF(JSMSG_WRONG_VALUE, 2, JSEXN_ERR, "expected {0} but found {1}") MSG_DEF(JSMSG_CANT_SET_PROTO, 0, JSEXN_TYPEERR, "can't set prototype of this object") MSG_DEF(JSMSG_CANT_SET_PROTO_OF, 1, JSEXN_TYPEERR, "can't set prototype of {0}") MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle") @@ -308,7 +306,7 @@ MSG_DEF(JSMSG_STMT_AFTER_SEMI_LESS, 0, JSEXN_SYNTAXERR, "unreachable expressi MSG_DEF(JSMSG_STRICT_CODE_WITH, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements") MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function") MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing } in template string") -MSG_DEF(JSMSG_SIMD_NOT_A_VECTOR, 0, JSEXN_TYPEERR, "value isn't a SIMD value object") +MSG_DEF(JSMSG_SIMD_NOT_A_VECTOR, 2, JSEXN_TYPEERR, "expecting a SIMD {0} object as argument {1}") MSG_DEF(JSMSG_TOO_MANY_CASES, 0, JSEXN_INTERNALERR, "too many switch cases") MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 0, JSEXN_SYNTAXERR, "too many catch variables") MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 0, JSEXN_SYNTAXERR, "too many constructor arguments") @@ -438,14 +436,14 @@ MSG_DEF(JSMSG_UNTERM_CLASS, 0, JSEXN_SYNTAXERR, "unterminated charact MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR, 0, JSEXN_ERR, "internal error getting the default locale") MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP,1, JSEXN_ERR, "No such property on self-hosted object: {0}") -// Typed object +// Typed object / SIMD MSG_DEF(JSMSG_INVALID_PROTOTYPE, 0, JSEXN_TYPEERR, "prototype field is not an object") -MSG_DEF(JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS, 0, JSEXN_ERR, "Invalid arguments") MSG_DEF(JSMSG_TYPEDOBJECT_BAD_ARGS, 0, JSEXN_TYPEERR, "invalid arguments") MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index") MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached") MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor") MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 0, JSEXN_ERR, "Type is too large to allocate") +MSG_DEF(JSMSG_SIMD_FAILED_CONVERSION, 0, JSEXN_RANGEERR, "SIMD conversion loses precision") // Typed array MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index e2f8fe7398..a9ac6fbf47 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -40,7 +40,6 @@ UNIFIED_SOURCES += [ 'testGCFinalizeCallback.cpp', 'testGCHeapPostBarriers.cpp', 'testGCMarking.cpp', - 'testGCNursery.cpp', 'testGCOutOfMemory.cpp', 'testGCStoreBufferRemoval.cpp', 'testGetPropertyDescriptor.cpp', diff --git a/js/src/jsapi-tests/testAddPropertyPropcache.cpp b/js/src/jsapi-tests/testAddPropertyPropcache.cpp index c177de4e7f..d3212fdcd5 100644 --- a/js/src/jsapi-tests/testAddPropertyPropcache.cpp +++ b/js/src/jsapi-tests/testAddPropertyPropcache.cpp @@ -10,7 +10,7 @@ static int callCount = 0; static bool -AddProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp) +AddProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v) { callCount++; return true; diff --git a/js/src/jsapi-tests/testChromeBuffer.cpp b/js/src/jsapi-tests/testChromeBuffer.cpp index 5cec22bb96..99bb6e9c68 100644 --- a/js/src/jsapi-tests/testChromeBuffer.cpp +++ b/js/src/jsapi-tests/testChromeBuffer.cpp @@ -22,6 +22,7 @@ static const JSClass global_class = { nullptr, nullptr, nullptr, + nullptr, JS_GlobalObjectTraceHook }; diff --git a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp index 59f33c9d31..0646de6a84 100644 --- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp +++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp @@ -25,7 +25,7 @@ BEGIN_TEST(testRedefineGlobalEval) "global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, GlobalEnumerate, GlobalResolve, nullptr, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; diff --git a/js/src/jsapi-tests/testGCNursery.cpp b/js/src/jsapi-tests/testGCNursery.cpp index d7d2cea1fb..db7276ac81 100644 --- a/js/src/jsapi-tests/testGCNursery.cpp +++ b/js/src/jsapi-tests/testGCNursery.cpp @@ -27,6 +27,7 @@ static const js::Class TenuredClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* ??? */ _finalize, nullptr, /* call */ @@ -47,6 +48,7 @@ static const js::Class NurseryClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* ??? */ _finalize, nullptr, /* call */ diff --git a/js/src/jsapi-tests/testNewObject.cpp b/js/src/jsapi-tests/testNewObject.cpp index f4da9bdfc1..335c3736b8 100644 --- a/js/src/jsapi-tests/testNewObject.cpp +++ b/js/src/jsapi-tests/testNewObject.cpp @@ -98,7 +98,7 @@ BEGIN_TEST(testNewObject_1) 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, constructHook + nullptr, nullptr, nullptr, constructHook }; JS::RootedObject ctor(cx, JS_NewObject(cx, &cls)); CHECK(ctor); diff --git a/js/src/jsapi-tests/testOps.cpp b/js/src/jsapi-tests/testOps.cpp index f0072c2850..c3030f7c61 100644 --- a/js/src/jsapi-tests/testOps.cpp +++ b/js/src/jsapi-tests/testOps.cpp @@ -23,7 +23,7 @@ static const JSClass myClass = { "MyClass", 0, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, my_convert + nullptr, nullptr, nullptr, my_convert }; static bool diff --git a/js/src/jsapi-tests/testPersistentRooted.cpp b/js/src/jsapi-tests/testPersistentRooted.cpp index 3273ab8963..9b86e8dba0 100644 --- a/js/src/jsapi-tests/testPersistentRooted.cpp +++ b/js/src/jsapi-tests/testPersistentRooted.cpp @@ -29,6 +29,7 @@ const JSClass BarkWhenTracedClass::class_ = { nullptr, nullptr, nullptr, + nullptr, finalize, nullptr, nullptr, diff --git a/js/src/jsapi-tests/testPropCache.cpp b/js/src/jsapi-tests/testPropCache.cpp index fc8112e30c..8c802f96b6 100644 --- a/js/src/jsapi-tests/testPropCache.cpp +++ b/js/src/jsapi-tests/testPropCache.cpp @@ -10,7 +10,7 @@ static int g_counter; static bool -CounterAdd(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp) +CounterAdd(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v) { g_counter++; return true; diff --git a/js/src/jsapi-tests/testUbiNode.cpp b/js/src/jsapi-tests/testUbiNode.cpp index 79925aae14..566efc80fa 100644 --- a/js/src/jsapi-tests/testUbiNode.cpp +++ b/js/src/jsapi-tests/testUbiNode.cpp @@ -88,3 +88,18 @@ BEGIN_TEST(test_ubiNodeCompartment) return true; } END_TEST(test_ubiNodeCompartment) + +BEGIN_TEST(test_ubiNodeJSObjectConstructorName) +{ + JS::RootedValue val(cx); + EVAL("this.Ctor = function Ctor() {}; new Ctor", &val); + CHECK(val.isObject()); + + mozilla::UniquePtr ctorName; + CHECK(JS::ubi::Node(&val.toObject()).jsObjectConstructorName(cx, ctorName)); + CHECK(ctorName); + CHECK(js_strcmp(ctorName.get(), MOZ_UTF16("Ctor")) == 0); + + return true; +} +END_TEST(test_ubiNodeJSObjectConstructorName) diff --git a/js/src/jsapi-tests/testWeakMap.cpp b/js/src/jsapi-tests/testWeakMap.cpp index 24861e59ec..acb3b9487b 100644 --- a/js/src/jsapi-tests/testWeakMap.cpp +++ b/js/src/jsapi-tests/testWeakMap.cpp @@ -148,6 +148,7 @@ JSObject* newKey() nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -204,6 +205,7 @@ JSObject* newDelegate() nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 08fb4dff5c..6285755a72 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -226,7 +226,7 @@ class JSAPITest "global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; return &c; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 286517f168..4d90854f49 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1139,13 +1139,12 @@ typedef struct JSStdName { } JSStdName; static const JSStdName* -LookupStdName(JSRuntime* rt, HandleString name, const JSStdName* table) +LookupStdName(const JSAtomState& names, JSAtom* name, const JSStdName* table) { - MOZ_ASSERT(name->isAtom()); for (unsigned i = 0; !table[i].isSentinel(); i++) { if (table[i].isDummy()) continue; - JSAtom* atom = AtomStateOffsetToName(*rt->commonNames, table[i].atomOffset); + JSAtom* atom = AtomStateOffsetToName(names, table[i].atomOffset); MOZ_ASSERT(atom); if (name == atom) return &table[i]; @@ -1221,11 +1220,10 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso if (!rt->hasContexts() || !JSID_IS_ATOM(id)) return true; - RootedString idstr(cx, JSID_TO_STRING(id)); - /* Check whether we're resolving 'undefined', and define it if so. */ + JSAtom* idAtom = JSID_TO_ATOM(id); JSAtom* undefinedAtom = cx->names().undefined; - if (idstr == undefinedAtom) { + if (idAtom == undefinedAtom) { *resolved = true; return DefineProperty(cx, obj, undefinedAtom->asPropertyName(), UndefinedHandleValue, nullptr, nullptr, @@ -1233,11 +1231,11 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso } /* Try for class constructors/prototypes named by well-known atoms. */ - stdnm = LookupStdName(rt, idstr, standard_class_names); + stdnm = LookupStdName(cx->names(), idAtom, standard_class_names); /* Try less frequently used top-level functions and constants. */ if (!stdnm) - stdnm = LookupStdName(rt, idstr, builtin_property_names); + stdnm = LookupStdName(cx->names(), idAtom, builtin_property_names); // If this class is anonymous, then it doesn't exist as a global // property, so we won't resolve anything. @@ -1261,6 +1259,27 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso return true; } +JS_PUBLIC_API(bool) +JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj) +{ + MOZ_ASSERT_IF(maybeObj, maybeObj->is()); + + // The global object's resolve hook is special: JS_ResolveStandardClass + // initializes the prototype chain lazily. Only attempt to optimize here + // if we know the prototype chain has been initialized. + if (!maybeObj || !maybeObj->getProto()) + return true; + + if (!JSID_IS_ATOM(id)) + return false; + + JSAtom* atom = JSID_TO_ATOM(id); + + return atom == names.undefined || + LookupStdName(names, atom, standard_class_names) || + LookupStdName(names, atom, builtin_property_names); +} + JS_PUBLIC_API(bool) JS_EnumerateStandardClasses(JSContext* cx, HandleObject obj) { @@ -1306,8 +1325,9 @@ JS_IdToProtoKey(JSContext* cx, HandleId id) if (!JSID_IS_ATOM(id)) return JSProto_Null; - RootedString idstr(cx, JSID_TO_STRING(id)); - const JSStdName* stdnm = LookupStdName(cx->runtime(), idstr, standard_class_names); + + JSAtom* atom = JSID_TO_ATOM(id); + const JSStdName* stdnm = LookupStdName(cx->names(), atom, standard_class_names); if (!stdnm) return JSProto_Null; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index d2f6d9a1c6..5270926476 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -94,20 +94,20 @@ class AutoValueArray : public AutoGCRooter }; template -class AutoVectorRooter : protected AutoGCRooter +class AutoVectorRooterBase : protected AutoGCRooter { typedef js::Vector VectorImpl; VectorImpl vector; public: - explicit AutoVectorRooter(JSContext* cx, ptrdiff_t tag + explicit AutoVectorRooterBase(JSContext* cx, ptrdiff_t tag MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, tag), vector(cx) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - explicit AutoVectorRooter(js::ContextFriendFields* cx, ptrdiff_t tag + explicit AutoVectorRooterBase(js::ContextFriendFields* cx, ptrdiff_t tag MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : AutoGCRooter(cx, tag), vector(cx) { @@ -120,10 +120,10 @@ class AutoVectorRooter : protected AutoGCRooter size_t length() const { return vector.length(); } bool empty() const { return vector.empty(); } - bool append(const T &v) { return vector.append(v); } - bool appendN(const T &v, size_t len) { return vector.appendN(v, len); } - bool append(const T *ptr, size_t len) { return vector.append(ptr, len); } - bool appendAll(const AutoVectorRooter &other) { + bool append(const T& v) { return vector.append(v); } + bool appendN(const T& v, size_t len) { return vector.appendN(v, len); } + bool append(const T* ptr, size_t len) { return vector.append(ptr, len); } + bool appendAll(const AutoVectorRooterBase& other) { return vector.appendAll(other.vector); } @@ -190,6 +190,33 @@ class AutoVectorRooter : protected AutoGCRooter MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +template +class MOZ_STACK_CLASS AutoVectorRooter : public AutoVectorRooterBase +{ + public: + explicit AutoVectorRooter(JSContext* cx + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoVectorRooterBase(cx, this->GetTag(T())) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + explicit AutoVectorRooter(js::ContextFriendFields* cx + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoVectorRooterBase(cx, this->GetTag(T())) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +typedef AutoVectorRooter AutoValueVector; +typedef AutoVectorRooter AutoIdVector; +typedef AutoVectorRooter AutoObjectVector; +typedef AutoVectorRooter AutoFunctionVector; +typedef AutoVectorRooter AutoScriptVector; + template class AutoHashMapRooter : protected AutoGCRooter { @@ -415,78 +442,6 @@ class AutoHashSetRooter : protected AutoGCRooter MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; -class MOZ_STACK_CLASS AutoValueVector : public AutoVectorRooter -{ - public: - explicit AutoValueVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, VALVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoIdVector : public AutoVectorRooter -{ - public: - explicit AutoIdVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, IDVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoObjectVector : public AutoVectorRooter -{ - public: - explicit AutoObjectVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, OBJVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoFunctionVector : public AutoVectorRooter -{ - public: - explicit AutoFunctionVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, FUNVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - explicit AutoFunctionVector(js::ContextFriendFields* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, FUNVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoScriptVector : public AutoVectorRooter -{ - public: - explicit AutoScriptVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, SCRIPTVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - /* * Custom rooting behavior for internal and external clients. */ @@ -1175,6 +1130,7 @@ class JS_PUBLIC_API(RuntimeOptions) { asmJS_(true), nativeRegExp_(true), unboxedObjects_(false), // Not enabled by default yet + unboxedArrays_(false), // Ditto werror_(false), strictMode_(false), extraWarnings_(false), @@ -1224,6 +1180,12 @@ class JS_PUBLIC_API(RuntimeOptions) { return *this; } + bool unboxedArrays() const { return unboxedArrays_; } + RuntimeOptions& setUnboxedArrays(bool flag) { + unboxedArrays_ = flag; + return *this; + } + bool werror() const { return werror_; } RuntimeOptions& setWerror(bool flag) { werror_ = flag; @@ -1270,6 +1232,7 @@ class JS_PUBLIC_API(RuntimeOptions) { bool asmJS_ : 1; bool nativeRegExp_ : 1; bool unboxedObjects_ : 1; + bool unboxedArrays_ : 1; bool werror_ : 1; bool strictMode_ : 1; bool extraWarnings_ : 1; @@ -1499,6 +1462,9 @@ JS_InitStandardClasses(JSContext* cx, JS::Handle obj); extern JS_PUBLIC_API(bool) JS_ResolveStandardClass(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved); +extern JS_PUBLIC_API(bool) +JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj); + extern JS_PUBLIC_API(bool) JS_EnumerateStandardClasses(JSContext* cx, JS::HandleObject obj); @@ -2594,7 +2560,7 @@ class PropertyDescriptorOperations MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY)); MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE)); MOZ_ASSERT(!has(SHADOWABLE)); - MOZ_ASSERT(desc()->value.isUndefined()); + MOZ_ASSERT(value().isUndefined()); MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter()); MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter()); } else { @@ -2692,7 +2658,19 @@ class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations< value().set(v); } - void setEnumerable() { desc()->attrs |= JSPROP_ENUMERATE; } + void setConfigurable(bool configurable) { + setAttributes((desc()->attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) | + (configurable ? 0 : JSPROP_PERMANENT)); + } + void setEnumerable(bool enumerable) { + setAttributes((desc()->attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) | + (enumerable ? JSPROP_ENUMERATE : 0)); + } + void setWritable(bool writable) { + MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER))); + setAttributes((desc()->attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) | + (writable ? 0 : JSPROP_READONLY)); + } void setAttributes(unsigned attrs) { desc()->attrs = attrs; } void setGetter(JSGetterOp op) { @@ -2703,8 +2681,16 @@ class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations< MOZ_ASSERT(op != JS_StrictPropertyStub); desc()->setter = op; } - void setGetterObject(JSObject *obj) { desc()->getter = reinterpret_cast(obj); } - void setSetterObject(JSObject *obj) { desc()->setter = reinterpret_cast(obj); } + void setGetterObject(JSObject* obj) { + desc()->getter = reinterpret_cast(obj); + desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY); + desc()->attrs |= JSPROP_GETTER | JSPROP_SHARED; + } + void setSetterObject(JSObject* obj) { + desc()->setter = reinterpret_cast(obj); + desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY); + desc()->attrs |= JSPROP_SETTER | JSPROP_SHARED; + } JS::MutableHandleObject getterObject() { MOZ_ASSERT(this->hasGetterObject()); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index e14f8f94b9..a08440aaf3 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -380,7 +380,7 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v) * non-configurable, but proxies may implement different semantics.) */ static bool -DeleteArrayElement(JSContext *cx, HandleObject obj, double index, ObjectOpResult &result) +DeleteArrayElement(JSContext* cx, HandleObject obj, double index, ObjectOpResult& result) { MOZ_ASSERT(index >= 0); MOZ_ASSERT(floor(index) == index); @@ -436,31 +436,16 @@ js::SetLengthProperty(JSContext* cx, HandleObject obj, double length) return SetProperty(cx, obj, cx->names().length, v); } -/* - * Since SpiderMonkey supports cross-class prototype-based delegation, we have - * to be careful about the length getter and setter being called on an object - * not of Array class. For the getter, we search obj's prototype chain for the - * array that caused this getter to be invoked. In the setter case to overcome - * the JSPROP_SHARED attribute, we must define a shadowing length property. - */ static bool -array_length_getter(JSContext* cx, HandleObject obj_, HandleId id, MutableHandleValue vp) +array_length_getter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { - RootedObject obj(cx, obj_); - do { - if (obj->is()) { - vp.setNumber(obj->as().length()); - return true; - } - if (!GetPrototype(cx, obj, &obj)) - return false; - } while (obj); + vp.setNumber(obj->as().length()); return true; } static bool -array_length_setter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, - ObjectOpResult &result) +array_length_setter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp, + ObjectOpResult& result) { if (!obj->is()) { // This array .length property was found on the prototype @@ -505,36 +490,36 @@ js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen) return false; } -/* ES6 20130308 draft 8.4.2.4 ArraySetLength */ +/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */ bool js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, - unsigned attrs, HandleValue value, ObjectOpResult &result) + unsigned attrs, HandleValue value, ObjectOpResult& result) { MOZ_ASSERT(id == NameToId(cx->names().length)); if (!arr->maybeCopyElementsForWrite(cx)) return false; - /* Steps 1-2 are irrelevant in our implementation. */ - - /* Steps 3-5. */ + // Step 1. uint32_t newLen; - if (!CanonicalizeArrayLengthValue(cx, value, &newLen)) - return false; + if (attrs & JSPROP_IGNORE_VALUE) { + // The spec has us calling OrdinaryDefineOwnProperty if + // Desc.[[Value]] is absent, but our implementation is so different that + // this is impossible. Instead, set newLen to the current length and + // proceed to step 9. + newLen = arr->length(); + } else { + // Step 2 is irrelevant in our implementation. - // Abort if we're being asked to change enumerability or configurability. - // (The length property of arrays is non-configurable, so such attempts - // must fail.) This behavior is spread throughout the ArraySetLength spec - // algorithm, but we only need check it once as our array implementation - // is internally so different from the spec algorithm. (ES5 and ES6 define - // behavior by delegating to the default define-own-property algorithm -- - // OrdinaryDefineOwnProperty in ES6, the default [[DefineOwnProperty]] in - // ES5 -- but we reimplement all the conflict-detection bits ourselves here - // so that we can use a customized length representation.) - if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) - return result.fail(JSMSG_CANT_REDEFINE_PROP); + // Steps 3-7. + MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined()); + if (!CanonicalizeArrayLengthValue(cx, value, &newLen)) + return false; - /* Steps 6-7. */ + // Step 8 is irrelevant in our implementation. + } + + // Steps 9-11. bool lengthIsWritable = arr->lengthIsWritable(); #ifdef DEBUG { @@ -543,10 +528,21 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, MOZ_ASSERT(lengthShape->writable() == lengthIsWritable); } #endif - uint32_t oldLen = arr->length(); - /* Steps 8-9 for arrays with non-writable length. */ + // Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change + // enumerability or configurability, or otherwise break the object + // invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but + // in SM, the array length property is hardly ordinary.) + if ((attrs & (JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT)) == 0 || + (attrs & (JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE)) == JSPROP_ENUMERATE || + (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0 || + (!lengthIsWritable && (attrs & (JSPROP_READONLY | JSPROP_IGNORE_READONLY)) == 0)) + { + return result.fail(JSMSG_CANT_REDEFINE_PROP); + } + + // Steps 12-13 for arrays with non-writable length. if (!lengthIsWritable) { if (newLen == oldLen) return result.succeed(); @@ -554,7 +550,7 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH); } - /* Step 8. */ + // Step 19. bool succeeded = true; do { // The initialized length and capacity of an array only need updating @@ -616,10 +612,10 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, // If we're removing a relatively small number of elements, just do // it exactly by the spec. while (newLen < oldLen) { - /* Step 15a. */ + // Step 15a. oldLen--; - /* Steps 15b-d. */ + // Steps 15b-d. ObjectOpResult deleteSucceeded; if (!DeleteElement(cx, arr, oldLen, deleteSucceeded)) return false; @@ -679,7 +675,7 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, MOZ_ASSERT(indexes[i] < index, "indexes should never repeat"); index = indexes[i]; - /* Steps 15b-d. */ + // Steps 15b-d. ObjectOpResult deleteSucceeded; if (!DeleteElement(cx, arr, index, deleteSucceeded)) return false; @@ -692,23 +688,25 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, } } while (false); - /* Steps 12, 16. */ - - // Yes, we totally drop a non-stub getter/setter from a defineProperty - // API call on the floor here. Given that getter/setter will go away in - // the long run, with accessors replacing them both internally and at the - // API level, just run with this. - RootedShape lengthShape(cx, arr->lookup(cx, id)); - if (!NativeObject::changeProperty(cx, arr, lengthShape, - attrs | JSPROP_PERMANENT | JSPROP_SHARED | - (lengthShape->attributes() & JSPROP_READONLY), - array_length_getter, array_length_setter)) - { - return false; - } - + // Update array length. Technically we should have been doing this + // throughout the loop, in step 19.d.iii. arr->setLength(cx, newLen); + // Step 20. + if (attrs & JSPROP_READONLY) { + // Yes, we totally drop a non-stub getter/setter from a defineProperty + // API call on the floor here. Given that getter/setter will go away in + // the long run, with accessors replacing them both internally and at the + // API level, just run with this. + RootedShape lengthShape(cx, arr->lookup(cx, id)); + if (!NativeObject::changeProperty(cx, arr, lengthShape, + lengthShape->attributes() | JSPROP_READONLY, + array_length_getter, array_length_setter)) + { + return false; + } + } + // All operations past here until the |!succeeded| code must be infallible, // so that all element fields remain properly synchronized. @@ -745,12 +743,12 @@ js::WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index) if (!obj->is()) return false; - ArrayObject *arr = &obj->as(); + ArrayObject* arr = &obj->as(); return !arr->lengthIsWritable() && index >= arr->length(); } static bool -array_addProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) +array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) { Rooted arr(cx, &obj->as()); @@ -819,8 +817,9 @@ AddLengthProperty(ExclusiveContext* cx, HandleArrayObject obj) MOZ_ASSERT(!obj->lookup(cx, lengthId)); return NativeObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter, - SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, - /* allowDictionary = */ false); + SHAPE_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_SHADOWABLE, + 0, /* allowDictionary = */ false); } #if JS_HAS_TOSOURCE @@ -1174,13 +1173,13 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp) } static inline bool -InitArrayTypes(JSContext *cx, ObjectGroup *group, JSObject *obj, - const Value *vector, unsigned count) +InitArrayTypes(JSContext* cx, ObjectGroup* group, JSObject* obj, + const Value* vector, unsigned count) { if (!group->unknownProperties()) { AutoEnterAnalysis enter(cx); - HeapTypeSet *types = group->getProperty(cx, obj, JSID_VOID); + HeapTypeSet* types = group->getProperty(cx, obj, JSID_VOID); if (!types) return false; @@ -1201,14 +1200,14 @@ enum ShouldUpdateTypes /* vector must point to rooted memory. */ static bool -InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes) +InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, uint32_t count, const Value* vector, ShouldUpdateTypes updateTypes) { MOZ_ASSERT(count <= MAX_ARRAY_INDEX); if (count == 0) return true; - ObjectGroup *group = obj->getGroup(cx); + ObjectGroup* group = obj->getGroup(cx); if (!group) return false; if (updateTypes && !InitArrayTypes(cx, group, obj, vector, count)) @@ -1251,7 +1250,7 @@ InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t coun return true; } while (false); - const Value *end = vector + count; + const Value* end = vector + count; while (vector < end && start <= MAX_ARRAY_INDEX) { if (!CheckForInterrupt(cx) || !SetArrayElement(cx, obj, start++, HandleValue::fromMarkedLocation(vector++))) { @@ -2295,8 +2294,13 @@ TryReuseArrayGroup(JSObject* obj, ArrayObject* narr) */ MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(), &ArrayObject::class_, narr->group())); - if (obj->is() && !obj->isSingleton() && obj->getProto() == narr->getProto()) + if (obj->is() && + !obj->isSingleton() && + !obj->group()->hasUnanalyzedPreliminaryObjects() && + obj->getProto() == narr->getProto()) + { narr->setGroup(obj->group()); + } } /* @@ -3028,11 +3032,11 @@ IsArrayConstructor(const Value& v) } static bool -ArrayFromCallArgs(JSContext *cx, HandleObjectGroup group, CallArgs &args) +ArrayFromCallArgs(JSContext* cx, HandleObjectGroup group, CallArgs& args) { if (!InitArrayTypes(cx, group, nullptr, args.array(), args.length())) return false; - JSObject *obj = (args.length() == 0) + JSObject* obj = (args.length() == 0) ? NewDenseEmptyArray(cx) : NewDenseCopiedArray(cx, args.length(), args.array()); if (!obj) @@ -3283,6 +3287,7 @@ const Class ArrayObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -3322,7 +3327,7 @@ EnsureNewArrayElements(ExclusiveContext* cx, ArrayObject* obj, uint32_t length) } static bool -NewArrayIsCachable(ExclusiveContext *cxArg, NewObjectKind newKind) +NewArrayIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind) { return cxArg->isJSContext() && newKind == GenericObject; } @@ -3441,7 +3446,7 @@ js::NewDenseUnallocatedArray(ExclusiveContext* cx, uint32_t length, } ArrayObject * -js::NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleObjectGroup group, +js::NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group, AllocatingBehaviour allocating, bool convertDoubleElements) { NewObjectKind newKind = !group ? SingletonObject : GenericObject; @@ -3539,12 +3544,12 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb return arr; } -JSObject * -js::NewDenseCopyOnWriteArray(JSContext *cx, HandleArrayObject templateObject, gc::InitialHeap heap) +JSObject* +js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap) { MOZ_ASSERT(!gc::IsInsideNursery(templateObject)); - ArrayObject *arr = ArrayObject::createCopyOnWriteArray(cx, heap, templateObject); + ArrayObject* arr = ArrayObject::createCopyOnWriteArray(cx, heap, templateObject); if (!arr) return nullptr; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 3c92af7c4b..dfd8d1b15d 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -86,7 +86,6 @@ js::TraceCycleDetectionSet(JSTracer* trc, js::ObjectSet& set) { for (js::ObjectSet::Enum e(set); !e.empty(); e.popFront()) { JSObject* key = e.front(); - JS::AutoOriginalTraceLocation reloc(trc, &e.front()); TraceRoot(trc, &key, "cycle detector table entry"); if (key != e.front()) e.rekeyFront(key); @@ -992,6 +991,12 @@ JSContext::isThrowingOutOfMemory() return throwing && unwrappedException_ == StringValue(names().outOfMemory); } +bool +JSContext::isClosingGenerator() +{ + return throwing && unwrappedException_.isMagic(JS_GENERATOR_CLOSING); +} + bool JSContext::saveFrameChain() { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 3c1997a113..04c9e0d5f2 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -425,6 +425,7 @@ struct JSContext : public js::ExclusiveContext, bool getPendingException(JS::MutableHandleValue rval); bool isThrowingOutOfMemory(); + bool isClosingGenerator(); void setPendingException(js::Value v); @@ -658,44 +659,9 @@ CheckForInterrupt(JSContext* cx) /************************************************************************/ -class AutoStringVector : public AutoVectorRooter -{ - public: - explicit AutoStringVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, STRINGVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoPropertyNameVector : public AutoVectorRooter -{ - public: - explicit AutoPropertyNameVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, STRINGVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -class AutoShapeVector : public AutoVectorRooter -{ - public: - explicit AutoShapeVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, SHAPEVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; +typedef JS::AutoVectorRooter AutoStringVector; +typedef JS::AutoVectorRooter AutoPropertyNameVector; +typedef JS::AutoVectorRooter AutoShapeVector; class AutoObjectObjectHashMap : public AutoHashMapRooter { @@ -819,7 +785,8 @@ bool intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp); -bool intrinsic_ThrowError(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp); +bool intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp); bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index b33969c42a..f48615e3f6 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -303,13 +303,13 @@ CallJSNativeConstructor(JSContext* cx, Native native, const CallArgs& args) } MOZ_ALWAYS_INLINE bool -CallJSGetterOp(JSContext *cx, GetterOp op, HandleObject receiver, HandleId id, +CallJSGetterOp(JSContext* cx, GetterOp op, HandleObject obj, HandleId id, MutableHandleValue vp) { JS_CHECK_RECURSION(cx, return false); - assertSameCompartment(cx, receiver, id, vp); - bool ok = op(cx, receiver, id, vp); + assertSameCompartment(cx, obj, id, vp); + bool ok = op(cx, obj, id, vp); if (ok) assertSameCompartment(cx, vp); return ok; @@ -325,6 +325,16 @@ CallJSSetterOp(JSContext *cx, SetterOp op, HandleObject obj, HandleId id, return op(cx, obj, id, vp, result); } +inline bool +CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op, HandleObject obj, HandleId id, + HandleValue v) +{ + JS_CHECK_RECURSION(cx, return false); + + assertSameCompartment(cx, obj, id, v); + return op(cx, obj, id, v); +} + inline bool CallJSDeletePropertyOp(JSContext *cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id, ObjectOpResult &result) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index effaa298ac..7d76b250da 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -193,7 +193,7 @@ class WrapperMapRef : public BufferableRef WrapperMapRef(WrapperMap* map, const CrossCompartmentKey& key) : map(map), key(key) {} - void mark(JSTracer* trc) { + void trace(JSTracer* trc) override { CrossCompartmentKey prior = key; if (key.debugger) TraceManuallyBarrieredEdge(trc, &key.debugger, "CCW debugger"); @@ -698,11 +698,11 @@ JSCompartment::clearObjectMetadata() } void -JSCompartment::setNewObjectMetadata(JSContext *cx, JSObject *obj) +JSCompartment::setNewObjectMetadata(JSContext* cx, JSObject* obj) { MOZ_ASSERT(this == cx->compartment()); - if (JSObject *metadata = objectMetadataCallback(cx)) { + if (JSObject* metadata = objectMetadataCallback(cx, obj)) { if (!objectMetadataTable) { objectMetadataTable = cx->new_(cx); if (!objectMetadataTable) @@ -714,7 +714,7 @@ JSCompartment::setNewObjectMetadata(JSContext *cx, JSObject *obj) } static bool -AddInnerLazyFunctionsFromScript(JSScript *script, AutoObjectVector& lazyFunctions) +AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions) { if (!script->hasObjects()) return true; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index ab5fd033ea..d4bb165086 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -588,6 +588,7 @@ struct JSCompartment // No longer using 5 (was: let expressions) DeprecatedNoSuchMethod = 6, // JS 1.7+ DeprecatedFlagsArgument = 7, // JS 1.3 or older + RegExpSourceProperty = 8, // ES5 DeprecatedLanguageExtensionCount }; }; @@ -720,12 +721,12 @@ struct WrapperValue Value value; }; -class AutoWrapperVector : public AutoVectorRooter +class AutoWrapperVector : public JS::AutoVectorRooterBase { public: explicit AutoWrapperVector(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, WRAPVECTOR) + : AutoVectorRooterBase(cx, WRAPVECTOR) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 4e5ab35ee6..368dffd104 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3043,6 +3043,7 @@ const Class DateObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ date_convert, nullptr, /* finalize */ nullptr, /* call */ diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index a2ba2102ac..fbb7006ca8 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -77,6 +77,7 @@ static const JSFunctionSpec exception_methods[] = { nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ nullptr, /* convert */ \ exn_finalize, \ nullptr, /* call */ \ @@ -108,6 +109,7 @@ ErrorObject::classes[JSEXN_LIMIT] = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ exn_finalize, nullptr, /* call */ diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index fafaca12cb..59d6d56f6b 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -293,6 +293,7 @@ namespace js { nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ js::proxy_Convert, \ js::proxy_Finalize, /* finalize */ \ nullptr, /* call */ \ @@ -595,7 +596,7 @@ struct String extern JS_FRIEND_DATA(const js::Class* const) ObjectClassPtr; inline const js::Class* -GetObjectClass(JSObject* obj) +GetObjectClass(const JSObject* obj) { return reinterpret_cast(obj)->group->clasp; } @@ -2296,6 +2297,7 @@ struct JSJitInfo { #define JITINFO_OP_TYPE_BITS 4 #define JITINFO_ALIAS_SET_BITS 4 #define JITINFO_RETURN_TYPE_BITS 8 +#define JITINFO_SLOT_INDEX_BITS 10 // The OpType that says what sort of function we are. uint32_t type_ : JITINFO_OP_TYPE_BITS; @@ -2324,7 +2326,10 @@ struct JSJitInfo { uint32_t isMovable : 1; /* Is op movable? To be movable the op must not AliasEverything, but even that might not be enough (e.g. in cases when it can - throw). */ + throw or is explicitly not movable). */ + uint32_t isEliminatable : 1; /* Can op be dead-code eliminated? Again, this + depends on whether the op can throw, in + addition to the alias set. */ // XXXbz should we have a JSValueType for the type of the member? uint32_t isAlwaysInSlot : 1; /* True if this is a getter that can always get the value from a slot of the "this" @@ -2335,9 +2340,15 @@ struct JSJitInfo { slot of the "this" object. */ uint32_t isTypedMethod : 1; /* True if this is an instance of JSTypedMethodJitInfo. */ - uint32_t slotIndex : 11; /* If isAlwaysInSlot or isSometimesInSlot is - true, the index of the slot to get the value - from. Otherwise 0. */ + uint32_t slotIndex : JITINFO_SLOT_INDEX_BITS; /* If isAlwaysInSlot or + isSometimesInSlot is true, + the index of the slot to + get the value from. + Otherwise 0. */ + + static const size_t maxSlotIndex = (1 << JITINFO_SLOT_INDEX_BITS) - 1; + +#undef JITINFO_SLOT_INDEX_BITS }; static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)), @@ -2562,7 +2573,7 @@ class JS_FRIEND_API(AutoCTypesActivityCallback) { }; typedef JSObject* -(* ObjectMetadataCallback)(JSContext* cx); +(* ObjectMetadataCallback)(JSContext* cx, JSObject* obj); /* * Specify a callback to invoke when creating each JS object in the current diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index cb16dbc5e2..215c82cac9 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -411,18 +411,18 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleObject obj) return proto; } -bool -js::FunctionHasResolveHook(const JSAtomState& atomState, jsid id) +static bool +fun_mayResolve(const JSAtomState& names, jsid id, JSObject*) { if (!JSID_IS_ATOM(id)) return false; JSAtom* atom = JSID_TO_ATOM(id); - return atom == atomState.prototype || atom == atomState.length || atom == atomState.name; + return atom == names.prototype || atom == names.length || atom == names.name; } -bool -js::fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) +static bool +fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) { if (!JSID_IS_ATOM(id)) return true; @@ -970,7 +970,8 @@ const Class JSFunction::class_ = { nullptr, /* getProperty */ nullptr, /* setProperty */ fun_enumerate, - js::fun_resolve, + fun_resolve, + fun_mayResolve, nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 7bedb24f21..1cfb903807 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -53,8 +53,8 @@ class JSFunction : public js::NativeObject must be constructible but not decompilable. */ HAS_REST = 0x0200, /* function has a rest (...) parameter */ INTERPRETED_LAZY = 0x0400, /* function is interpreted but doesn't have a script yet */ - RESOLVED_LENGTH = 0x0800, /* f.length has been resolved (see js::fun_resolve). */ - RESOLVED_NAME = 0x1000, /* f.name has been resolved (see js::fun_resolve). */ + RESOLVED_LENGTH = 0x0800, /* f.length has been resolved (see fun_resolve). */ + RESOLVED_NAME = 0x1000, /* f.name has been resolved (see fun_resolve). */ FUNCTION_KIND_SHIFT = 13, FUNCTION_KIND_MASK = 0x3 << FUNCTION_KIND_SHIFT, @@ -590,9 +590,6 @@ DefineFunction(JSContext* cx, HandleObject obj, HandleId id, JSNative native, bool FunctionHasResolveHook(const JSAtomState& atomState, jsid id); -extern bool -fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp); - extern bool fun_toString(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index d521d01c72..4cc307e0f2 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1055,7 +1055,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) : maxMallocBytes(0), numArenasFreeCommitted(0), verifyPreData(nullptr), - verifyPostData(nullptr), chunkAllocationSinceLastGC(false), nextFullGCTime(0), lastGCTime(PRMJ_Now()), @@ -1151,8 +1150,8 @@ const char* gc::ZealModeHelpText = " 8: Incremental GC in two slices: 1) mark roots 2) finish collection\n" " 9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n" " 10: Incremental GC in multiple slices\n" - " 11: Verify post write barriers between instructions\n" - " 12: Verify post write barriers between paints\n" + " 11: unused\n" + " 12: unused\n" " 13: Check internal hashtables on minor GC\n" " 14: Perform a shrinking collection every N allocations\n"; @@ -1161,8 +1160,6 @@ GCRuntime::setZeal(uint8_t zeal, uint32_t frequency) { if (verifyPreData) VerifyBarriers(rt, PreBarrierVerifier); - if (verifyPostData) - VerifyBarriers(rt, PostBarrierVerifier); if (zealMode == ZealGenerationalGCValue) { evictNursery(JS::gcreason::DEBUG_GC); @@ -6208,11 +6205,6 @@ GCRuntime::notifyDidPaint() return; } - if (zealMode == ZealFrameVerifierPostValue) { - verifyPostBarriers(); - return; - } - if (zealMode == ZealFrameGCValue) { JS::PrepareForFullGC(rt); gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME); @@ -7050,40 +7042,19 @@ JS::IsIncrementalBarrierNeeded(JSContext* cx) return IsIncrementalBarrierNeeded(cx->runtime()); } +struct IncrementalReferenceBarrierFunctor { + template void operator()(gc::Cell* cell) { + T::writeBarrierPre(static_cast(cell)); + } +}; + JS_PUBLIC_API(void) JS::IncrementalReferenceBarrier(GCCellPtr thing) { if (!thing) return; - if (thing.isString() && thing.toString()->isPermanentAtom()) - return; - -#ifdef DEBUG - Zone* zone = thing.isObject() - ? thing.toObject()->zone() - : thing.asCell()->asTenured().zone(); - MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMajorCollecting()); -#endif - - switch(thing.kind()) { - case JSTRACE_OBJECT: return JSObject::writeBarrierPre(thing.toObject()); - case JSTRACE_STRING: return JSString::writeBarrierPre(thing.toString()); - case JSTRACE_SCRIPT: return JSScript::writeBarrierPre(thing.toScript()); - case JSTRACE_SYMBOL: return JS::Symbol::writeBarrierPre(thing.toSymbol()); - case JSTRACE_LAZY_SCRIPT: - return LazyScript::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_JITCODE: - return jit::JitCode::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_SHAPE: - return Shape::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_BASE_SHAPE: - return BaseShape::writeBarrierPre(static_cast(thing.asCell())); - case JSTRACE_OBJECT_GROUP: - return ObjectGroup::writeBarrierPre(static_cast(thing.asCell())); - default: - MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier."); - } + CallTyped(IncrementalReferenceBarrierFunctor(), thing.kind(), thing.asCell()); } JS_PUBLIC_API(void) @@ -7111,25 +7082,13 @@ JS::WasIncrementalGC(JSRuntime* rt) JS::AutoDisableGenerationalGC::AutoDisableGenerationalGC(JSRuntime* rt) : gc(&rt->gc) -#ifdef JS_GC_ZEAL - , restartVerifier(false) -#endif { -#ifdef JS_GC_ZEAL - restartVerifier = gc->endVerifyPostBarriers(); -#endif gc->disableGenerationalGC(); } JS::AutoDisableGenerationalGC::~AutoDisableGenerationalGC() { gc->enableGenerationalGC(); -#ifdef JS_GC_ZEAL - if (restartVerifier) { - MOZ_ASSERT(gc->isGenerationalGCEnabled()); - gc->startVerifyPostBarriers(); - } -#endif } JS_PUBLIC_API(bool) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 7b55146622..53266c5123 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -331,6 +331,12 @@ GetGCKindSlots(AllocKind thingKind, const Class* clasp) return nslots; } +static inline size_t +GetGCKindBytes(AllocKind thingKind) +{ + return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value); +} + // Class to assist in triggering background chunk allocation. This cannot be done // while holding the GC or worker thread state lock due to lock ordering issues. // As a result, the triggering is delayed using this class until neither of the @@ -902,6 +908,7 @@ class ArenaLists friend class GCRuntime; friend class js::Nursery; + friend class js::TenuringTracer; }; /* The number of GC cycles an empty chunk can survive before been released. */ @@ -1177,6 +1184,7 @@ MergeCompartments(JSCompartment* source, JSCompartment* target); class RelocationOverlay { friend class MinorCollectionTracer; + friend class js::TenuringTracer; /* The low bit is set so this should never equal a normal pointer. */ static const uintptr_t Relocated = uintptr_t(0xbad0bad1); @@ -1330,15 +1338,12 @@ const int ZealGenerationalGCValue = 7; const int ZealIncrementalRootsThenFinish = 8; const int ZealIncrementalMarkAllThenFinish = 9; const int ZealIncrementalMultipleSlices = 10; -const int ZealVerifierPostValue = 11; -const int ZealFrameVerifierPostValue = 12; const int ZealCheckHashTablesOnMinorGC = 13; const int ZealCompactValue = 14; const int ZealLimit = 14; enum VerifierType { - PreBarrierVerifier, - PostBarrierVerifier + PreBarrierVerifier }; #ifdef JS_GC_ZEAL diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 2f9fc2dca8..760f54783c 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -60,6 +60,9 @@ NativeIterator::mark(JSTracer* trc) if (obj) TraceEdge(trc, &obj, "obj"); + for (size_t i = 0; i < guard_length; i++) + guard_array[i].trace(trc); + // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the // GC removes any elements from the list, it won't remove this one. if (iterObj_) @@ -338,8 +341,20 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) if (!enumerate(cx, pobj, properties)) return false; + RootedId id(cx); for (size_t n = 0; n < properties.length(); n++) { - if (!Enumerate(cx, pobj, properties[n], true, flags, ht, props)) + id = properties[n]; + bool enumerable = true; + + // The enumerate hook does not indicate whether the properties + // it returns are enumerable or not. There is no non-effectful + // way to determine this from the object, so carve out + // exceptions here for places where the property is not + // enumerable. + if (pobj->is() && id == NameToId(cx->names().length)) + enumerable = false; + + if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props)) return false; } @@ -576,10 +591,12 @@ NewPropertyIteratorObject(JSContext *cx, unsigned flags) } NativeIterator* -NativeIterator::allocateIterator(JSContext* cx, uint32_t slength, const AutoIdVector& props) +NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, const AutoIdVector& props) { + JS_STATIC_ASSERT(sizeof(ReceiverGuard) == 2 * sizeof(void*)); + size_t plength = props.length(); - NativeIterator* ni = cx->zone()->pod_malloc_with_extra(plength + slength); + NativeIterator* ni = cx->zone()->pod_malloc_with_extra(plength + numGuards * 2); if (!ni) return nullptr; @@ -614,14 +631,14 @@ NativeIterator::allocateSentinel(JSContext* cx) } inline void -NativeIterator::init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t slength, uint32_t key) +NativeIterator::init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t numGuards, uint32_t key) { this->obj.init(obj); this->iterObj_ = iterObj; this->flags = flags; - this->shapes_array = (Shape**) this->props_end; - this->shapes_length = slength; - this->shapes_key = key; + this->guard_array = (HeapReceiverGuard*) this->props_end; + this->guard_length = numGuards; + this->guard_key = key; } static inline void @@ -637,8 +654,8 @@ RegisterEnumerator(JSContext* cx, PropertyIteratorObject* iterobj, NativeIterato } static inline bool -VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector& keys, - uint32_t slength, uint32_t key, MutableHandleObject objp) +VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector& keys, + uint32_t numGuards, uint32_t key, MutableHandleObject objp) { MOZ_ASSERT(!(flags & JSITER_FOREACH)); @@ -650,26 +667,20 @@ VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVecto if (!iterobj) return false; - NativeIterator* ni = NativeIterator::allocateIterator(cx, slength, keys); + NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys); if (!ni) return false; - ni->init(obj, iterobj, flags, slength, key); + ni->init(obj, iterobj, flags, numGuards, key); - if (slength) { - /* - * Fill in the shape array from scratch. We can't use the array that was - * computed for the cache lookup earlier, as constructing iterobj could - * have triggered a shape-regenerating GC. Don't bother with regenerating - * the shape key; if such a GC *does* occur, we can only get hits through - * the one-slot lastNativeIterator cache. - */ - JSObject *pobj = obj; + if (numGuards) { + // Fill in the guard array from scratch. + JSObject* pobj = obj; size_t ind = 0; do { - ni->shapes_array[ind++] = pobj->as().lastProperty(); + ni->guard_array[ind++].init(ReceiverGuard(pobj)); pobj = pobj->getProto(); } while (pobj); - MOZ_ASSERT(ind == slength); + MOZ_ASSERT(ind == numGuards); } iterobj->setNativeIterator(ni); @@ -744,8 +755,39 @@ UpdateNativeIterator(NativeIterator* ni, JSObject* obj) ni->obj = obj; } +static inline bool +CanCompareIterableObjectToCache(JSObject* obj) +{ + if (obj->isNative()) + return obj->as().hasEmptyElements(); + if (obj->is()) { + if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) + return expando->hasEmptyElements(); + return true; + } + return false; +} + +static inline bool +CanCacheIterableObject(JSContext* cx, JSObject* obj) +{ + if (!CanCompareIterableObjectToCache(obj)) + return false; + if (obj->isNative()) { + if (IsAnyTypedArray(obj) || + obj->hasUncacheableProto() || + obj->getOps()->enumerate || + obj->getClass()->enumerate || + obj->as().containsPure(cx->names().iteratorIntrinsic)) + { + return false; + } + } + return true; +} + bool -js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleObject objp) +js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleObject objp) { if (obj->is() || obj->is()) { objp.set(obj); @@ -762,27 +804,21 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleOb return Proxy::enumerate(cx, obj, objp); } - Vector shapes(cx); + Vector guards(cx); uint32_t key = 0; if (flags == JSITER_ENUMERATE) { - /* - * Check to see if this is the same as the most recent object which - * was iterated over. We don't explicitly check for shapeless - * objects here, as they are not inserted into the cache and - * will result in a miss. - */ + // Check to see if this is the same as the most recent object which was + // iterated over. PropertyIteratorObject* last = cx->runtime()->nativeIterCache.last; if (last) { - NativeIterator *lastni = last->getNativeIterator(); + NativeIterator* lastni = last->getNativeIterator(); if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && - obj->isNative() && - obj->as().hasEmptyElements() && - obj->as().lastProperty() == lastni->shapes_array[0]) + CanCompareIterableObjectToCache(obj) && + ReceiverGuard(obj) == lastni->guard_array[0]) { - JSObject *proto = obj->getProto(); - if (proto->isNative() && - proto->as().hasEmptyElements() && - proto->as().lastProperty() == lastni->shapes_array[1] && + JSObject* proto = obj->getProto(); + if (CanCompareIterableObjectToCache(proto) && + ReceiverGuard(proto) == lastni->guard_array[1] && !proto->getProto()) { objp.set(last); @@ -800,39 +836,34 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleOb * currently active. */ { - JSObject *pobj = obj; + JSObject* pobj = obj; do { - if (!pobj->isNative() || - !pobj->as().hasEmptyElements() || - IsAnyTypedArray(pobj) || - pobj->hasUncacheableProto() || - pobj->getOps()->enumerate || - pobj->getClass()->enumerate || - pobj->as().containsPure(cx->names().iteratorIntrinsic)) - { - shapes.clear(); + if (!CanCacheIterableObject(cx, pobj)) { + guards.clear(); goto miss; } - Shape *shape = pobj->as().lastProperty(); - key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3); - if (!shapes.append(shape)) + ReceiverGuard guard(pobj); + key = (key + (key << 16)) ^ guard.hash(); + if (!guards.append(guard)) return false; pobj = pobj->getProto(); } while (pobj); } - PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key); + PropertyIteratorObject* iterobj = cx->runtime()->nativeIterCache.get(key); if (iterobj) { NativeIterator* ni = iterobj->getNativeIterator(); if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && - ni->shapes_key == key && - ni->shapes_length == shapes.length() && - Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) { + ni->guard_key == key && + ni->guard_length == guards.length() && + Compare(reinterpret_cast(ni->guard_array), + guards.begin(), ni->guard_length)) + { objp.set(iterobj); UpdateNativeIterator(ni, obj); RegisterEnumerator(cx, iterobj, ni); - if (shapes.length() == 2) + if (guards.length() == 2) cx->runtime()->nativeIterCache.last = iterobj; return true; } @@ -847,7 +878,7 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleOb AutoIdVector keys(cx); if (flags & JSITER_FOREACH) { - MOZ_ASSERT(shapes.empty()); + MOZ_ASSERT(guards.empty()); if (!Snapshot(cx, obj, flags, &keys)) return false; @@ -856,17 +887,17 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleOb } else { if (!Snapshot(cx, obj, flags, &keys)) return false; - if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, objp)) + if (!VectorToKeyIterator(cx, obj, flags, keys, guards.length(), key, objp)) return false; } PropertyIteratorObject* iterobj = &objp->as(); /* Cache the iterator object if possible. */ - if (shapes.length()) + if (guards.length()) cx->runtime()->nativeIterCache.set(key, iterobj); - if (shapes.length() == 2) + if (guards.length() == 2) cx->runtime()->nativeIterCache.last = iterobj; return true; } @@ -1045,6 +1076,7 @@ const Class PropertyIteratorObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ finalize, nullptr, /* call */ @@ -1430,6 +1462,7 @@ const Class StopIterationObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ diff --git a/js/src/jsiter.h b/js/src/jsiter.h index a9adeaf0bb..1c6289d317 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -16,6 +16,7 @@ #include "jscntxt.h" #include "gc/Barrier.h" +#include "vm/ReceiverGuard.h" #include "vm/Stack.h" /* @@ -34,9 +35,9 @@ struct NativeIterator HeapPtrFlatString* props_array; HeapPtrFlatString* props_cursor; HeapPtrFlatString* props_end; - Shape** shapes_array; - uint32_t shapes_length; - uint32_t shapes_key; + HeapReceiverGuard* guard_array; + uint32_t guard_length; + uint32_t guard_key; uint32_t flags; private: diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index f0ae1e3b7d..157bdc04db 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -46,6 +46,7 @@ #include "jit/BaselineJIT.h" #include "js/MemoryMetrics.h" #include "js/Proxy.h" +#include "js/UbiNode.h" #include "vm/ArgumentsObject.h" #include "vm/Interpreter.h" #include "vm/ProxyObject.h" @@ -126,56 +127,78 @@ js::InformalValueTypeName(const Value& v) return "value"; } +// ES6 draft rev37 6.2.4.4 FromPropertyDescriptor bool -js::FromPropertyDescriptor(JSContext *cx, Handle desc, - MutableHandleValue vp) +js::FromPropertyDescriptor(JSContext* cx, Handle desc, MutableHandleValue vp) { + // Step 1. if (!desc.object()) { vp.setUndefined(); return true; } + return FromPropertyDescriptorToObject(cx, desc, vp); +} + +bool +js::FromPropertyDescriptorToObject(JSContext* cx, Handle desc, + MutableHandleValue vp) +{ + // Step 2-3. RootedObject obj(cx, NewBuiltinClassInstance(cx)); if (!obj) return false; - const JSAtomState &names = cx->names(); - RootedValue v(cx); - if (desc.hasConfigurable()) { - v.setBoolean(desc.configurable()); - if (!DefineProperty(cx, obj, names.configurable, v)) - return false; - } - if (desc.hasEnumerable()) { - v.setBoolean(desc.enumerable()); - if (!DefineProperty(cx, obj, names.enumerable, v)) - return false; - } + const JSAtomState& names = cx->names(); + + // Step 4. if (desc.hasValue()) { if (!DefineProperty(cx, obj, names.value, desc.value())) return false; } + + // Step 5. + RootedValue v(cx); if (desc.hasWritable()) { v.setBoolean(desc.writable()); if (!DefineProperty(cx, obj, names.writable, v)) return false; } + + // Step 6. if (desc.hasGetterObject()) { - if (JSObject *get = desc.getterObject()) + if (JSObject* get = desc.getterObject()) v.setObject(*get); else v.setUndefined(); if (!DefineProperty(cx, obj, names.get, v)) return false; } + + // Step 7. if (desc.hasSetterObject()) { - if (JSObject *set = desc.setterObject()) + if (JSObject* set = desc.setterObject()) v.setObject(*set); else v.setUndefined(); if (!DefineProperty(cx, obj, names.set, v)) return false; } + + // Step 8. + if (desc.hasEnumerable()) { + v.setBoolean(desc.enumerable()); + if (!DefineProperty(cx, obj, names.enumerable, v)) + return false; + } + + // Step 9. + if (desc.hasConfigurable()) { + v.setBoolean(desc.configurable()); + if (!DefineProperty(cx, obj, names.configurable, v)) + return false; + } + vp.setObject(*obj); return true; } @@ -220,7 +243,7 @@ GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id, MutableHandle } bool -js::Throw(JSContext *cx, jsid id, unsigned errorNumber) +js::Throw(JSContext* cx, jsid id, unsigned errorNumber) { MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1); @@ -236,7 +259,7 @@ js::Throw(JSContext *cx, jsid id, unsigned errorNumber) } bool -js::Throw(JSContext *cx, JSObject *obj, unsigned errorNumber) +js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber) { if (js_ErrorFormatString[errorNumber].argCount == 1) { RootedValue val(cx, ObjectValue(*obj)); @@ -255,8 +278,8 @@ js::Throw(JSContext *cx, JSObject *obj, unsigned errorNumber) /*** Standard-compliant property definition (used by Object.defineProperty) **********************/ static bool -DefinePropertyOnObject(JSContext *cx, HandleNativeObject obj, HandleId id, - Handle desc, ObjectOpResult &result) +DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, + Handle desc, ObjectOpResult& result) { /* 8.12.9 step 1. */ RootedShape shape(cx); @@ -554,8 +577,8 @@ DefinePropertyOnObject(JSContext *cx, HandleNativeObject obj, HandleId id, /* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */ static bool -DefinePropertyOnArray(JSContext *cx, Handle arr, HandleId id, - Handle desc, ObjectOpResult &result) +DefinePropertyOnArray(JSContext* cx, Handle arr, HandleId id, + Handle desc, ObjectOpResult& result) { /* Step 2. */ if (id == NameToId(cx->names().length)) { @@ -616,58 +639,22 @@ DefinePropertyOnArray(JSContext *cx, Handle arr, HandleId id, // ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]] static bool -DefinePropertyOnTypedArray(JSContext *cx, HandleObject obj, HandleId id, - Handle desc, ObjectOpResult &result) +DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id, + Handle desc, ObjectOpResult& result) { MOZ_ASSERT(IsAnyTypedArray(obj)); // Steps 3.a-c. uint64_t index; - if (IsTypedArrayIndex(id, &index)) { - // These are all substeps of 3.c. - // Steps i-vi. - // We (wrongly) ignore out of range defines with a value. - if (index >= AnyTypedArrayLength(obj)) - return result.succeed(); - - // Step vii. - if (desc.isAccessorDescriptor()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - - // Step viii. - if (desc.hasConfigurable() && desc.configurable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - - // Step ix. - if (desc.hasEnumerable() && !desc.enumerable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - - // Step x. - if (desc.hasWritable() && !desc.writable()) - return result.fail(JSMSG_CANT_REDEFINE_PROP); - - // Step xi. - if (desc.hasValue()) { - double d; - if (!ToNumber(cx, desc.value(), &d)) - return false; - - if (obj->is()) - TypedArrayObject::setElement(obj->as(), index, d); - else - SharedTypedArrayObject::setElement(obj->as(), index, d); - } - - // Step xii. - return result.succeed(); - } + if (IsTypedArrayIndex(id, &index)) + return DefineTypedArrayElement(cx, obj, index, desc, result); // Step 4. return DefinePropertyOnObject(cx, obj.as(), id, desc, result); } bool -js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, - Handle desc, ObjectOpResult &result) +js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle desc, ObjectOpResult& result) { if (obj->is()) { Rooted arr(cx, &obj->as()); @@ -693,7 +680,7 @@ js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, } bool -js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, +js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc) { ObjectOpResult success; @@ -702,7 +689,7 @@ js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, } bool -CheckCallable(JSContext *cx, JSObject *obj, const char *fieldName) +CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName) { if (obj && !obj->isCallable()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD, @@ -713,7 +700,7 @@ CheckCallable(JSContext *cx, JSObject *obj, const char *fieldName) } bool -js::ToPropertyDescriptor(JSContext *cx, HandleValue descval, bool checkAccessors, +js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors, MutableHandle desc) { // step 2 @@ -829,7 +816,7 @@ js::ToPropertyDescriptor(JSContext *cx, HandleValue descval, bool checkAccessors } bool -js::CheckPropertyDescriptorAccessors(JSContext *cx, Handle desc) +js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle desc) { if (desc.hasGetterObject()) { if (!CheckCallable(cx, desc.getterObject(), js_getter_str)) @@ -845,9 +832,9 @@ js::CheckPropertyDescriptorAccessors(JSContext *cx, Handle d void js::CompletePropertyDescriptor(MutableHandle desc) { + desc.assertValid(); + if (desc.isGenericDescriptor() || desc.isDataDescriptor()) { - if (!desc.hasValue()) - desc.value().setUndefined(); if (!desc.hasWritable()) desc.attributesRef() |= JSPROP_READONLY; desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE); @@ -858,16 +845,16 @@ js::CompletePropertyDescriptor(MutableHandle desc) desc.setSetterObject(nullptr); desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; } - if (!desc.hasEnumerable()) - desc.attributesRef() &= ~JSPROP_ENUMERATE; if (!desc.hasConfigurable()) desc.attributesRef() |= JSPROP_PERMANENT; desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE); + + desc.assertComplete(); } bool -js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors, - AutoIdVector *ids, AutoPropertyDescriptorVector *descs) +js::ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors, + AutoIdVector* ids, AutoPropertyDescriptorVector* descs) { if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids)) return false; @@ -888,7 +875,7 @@ js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccesso } bool -js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props) +js::DefineProperties(JSContext* cx, HandleObject obj, HandleObject props) { AutoIdVector ids(cx); AutoPropertyDescriptorVector descs(cx); @@ -1004,8 +991,6 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level) desc.setAttributes(AllowConfigureAndWritable | JSPROP_PERMANENT | JSPROP_READONLY); } - desc.object().set(obj); - // 8.a.i-ii. / 9.a.iii.3-4 if (!StandardDefineProperty(cx, obj, id, desc)) return false; @@ -1084,7 +1069,7 @@ js::TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bo * FIXME bug 547327: estimate the size from the allocation site. */ static inline gc::AllocKind -NewObjectGCKind(const js::Class *clasp) +NewObjectGCKind(const js::Class* clasp) { if (clasp == &ArrayObject::class_) return gc::AllocKind::OBJECT8; @@ -1093,11 +1078,11 @@ NewObjectGCKind(const js::Class *clasp) return gc::AllocKind::OBJECT4; } -static inline JSObject * -NewObject(ExclusiveContext *cx, HandleObjectGroup group, gc::AllocKind kind, +static inline JSObject* +NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind, NewObjectKind newKind) { - const Class *clasp = group->clasp(); + const Class* clasp = group->clasp(); MOZ_ASSERT(clasp != &ArrayObject::class_); MOZ_ASSERT_IF(clasp == &JSFunction::class_, @@ -1137,7 +1122,7 @@ NewObject(ExclusiveContext *cx, HandleObjectGroup group, gc::AllocKind kind, void NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto, - gc::AllocKind kind, NativeObject *obj) + gc::AllocKind kind, NativeObject* obj) { MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is()); MOZ_ASSERT(obj->getTaggedProto() == proto); @@ -1145,8 +1130,8 @@ NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto } static bool -NewObjectWithTaggedProtoIsCachable(ExclusiveContext *cxArg, Handle proto, - NewObjectKind newKind, const Class *clasp) +NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle proto, + NewObjectKind newKind, const Class* clasp) { return cxArg->isJSContext() && proto.isObject() && @@ -1155,8 +1140,8 @@ NewObjectWithTaggedProtoIsCachable(ExclusiveContext *cxArg, Handle !proto.toObject()->is(); } -JSObject * -js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp, +JSObject* +js::NewObjectWithGivenTaggedProto(ExclusiveContext* cxArg, const Class* clasp, Handle proto, gc::AllocKind allocKind, NewObjectKind newKind) { @@ -1194,112 +1179,16 @@ js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp, return obj; } -static JSProtoKey -ClassProtoKeyOrAnonymousOrNull(const js::Class* clasp) -{ - JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null) - return key; - if (clasp->flags & JSCLASS_IS_ANONYMOUS) - return JSProto_Object; - return JSProto_Null; -} - -static inline bool -NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp) -{ - if (shape->hasSlot()) { - *vp = pobj->getSlot(shape->slot()); - MOZ_ASSERT(!vp->isMagic()); - } else { - vp->setUndefined(); - } - - /* Fail if we have a custom getter. */ - return shape->hasDefaultGetter(); -} - static bool -FindClassPrototype(ExclusiveContext* cx, MutableHandleObject protop, const Class* clasp) -{ - protop.set(nullptr); - - JSAtom* atom = Atomize(cx, clasp->name, strlen(clasp->name)); - if (!atom) - return false; - RootedId id(cx, AtomToId(atom)); - - RootedObject pobj(cx); - RootedShape shape(cx); - if (!NativeLookupProperty(cx, cx->global(), id, &pobj, &shape)) - return false; - - RootedObject ctor(cx); - if (shape && pobj->isNative()) { - if (shape->hasSlot()) { - RootedValue v(cx, pobj->as().getSlot(shape->slot())); - if (v.isObject()) - ctor = &v.toObject(); - } - } - - if (ctor && ctor->is()) { - JSFunction* nctor = &ctor->as(); - RootedValue v(cx); - if (cx->isJSContext()) { - if (!GetProperty(cx->asJSContext(), ctor, ctor, cx->names().prototype, &v)) - return false; - } else { - Shape* shape = nctor->lookup(cx, cx->names().prototype); - if (!shape || !NativeGetPureInline(nctor, shape, v.address())) - return false; - } - if (v.isObject()) - protop.set(&v.toObject()); - } - return true; -} - -// Find the appropriate proto for a class. There are three different ways to achieve this: -// 1. Built-in classes have a cached proto and anonymous classes get Object.prototype. -// 2. Lookup global[clasp->name].prototype -// 3. Fallback to Object.prototype -// -// Step 2 is in some circumstances an observable operation, which is probably wrong -// as a matter of specifications. It's legacy garbage that we're working to remove eventually. -static bool -FindProto(ExclusiveContext* cx, const js::Class* clasp, MutableHandleObject proto) -{ - JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp); - if (protoKey != JSProto_Null) - return GetBuiltinPrototype(cx, protoKey, proto); - - if (!FindClassPrototype(cx, proto, clasp)) - return false; - - if (!proto) { - // We're looking for the prototype of a class that is currently being - // resolved; the global object's resolve hook is on the - // stack. js::FindClassPrototype detects this goofy case and returns - // true with proto null. Fall back on Object.prototype. - MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == JSProto_Null); - return GetBuiltinPrototype(cx, JSProto_Object, proto); - } - return true; -} - -static bool -NewObjectWithClassProtoIsCachable(ExclusiveContext *cxArg, - JSProtoKey protoKey, NewObjectKind newKind, const Class *clasp) +NewObjectIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind, const Class* clasp) { return cxArg->isJSContext() && - protoKey != JSProto_Null && newKind == GenericObject && clasp->isNative(); } -JSObject * -js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp, +JSObject* +js::NewObjectWithClassProtoCommon(ExclusiveContext* cxArg, const Class* clasp, HandleObject protoArg, gc::AllocKind allocKind, NewObjectKind newKind) { @@ -1313,32 +1202,29 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp, Handle global = cxArg->global(); - /* - * Use the object cache, except for classes without a cached proto key. - * On these objects, FindProto will do a dynamic property lookup to get - * global[className].prototype, where changes to either the className or - * prototype property would render the cached lookup incorrect. For classes - * with a proto key, the prototype created during class initialization is - * stored in an immutable slot on the global (except for ClearScope, which - * will flush the new object cache). - */ - JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp); - - bool isCachable = NewObjectWithClassProtoIsCachable(cxArg, protoKey, newKind, clasp); + bool isCachable = NewObjectIsCachable(cxArg, newKind, clasp); if (isCachable) { - JSContext *cx = cxArg->asJSContext(); - JSRuntime *rt = cx->runtime(); - NewObjectCache &cache = rt->newObjectCache; + JSContext* cx = cxArg->asJSContext(); + JSRuntime* rt = cx->runtime(); + NewObjectCache& cache = rt->newObjectCache; NewObjectCache::EntryIndex entry = -1; if (cache.lookupGlobal(clasp, global, allocKind, &entry)) { - JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp)); + JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp)); if (obj) return obj; } } + /* + * Find the appropriate proto for clasp. Built-in classes have a cached + * proto on cx->global(); all others get %ObjectPrototype%. + */ + JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); + if (protoKey == JSProto_Null) + protoKey = JSProto_Object; + RootedObject proto(cxArg, protoArg); - if (!FindProto(cxArg, clasp, &proto)) + if (!GetBuiltinPrototype(cxArg, protoKey, &proto)) return nullptr; Rooted taggedProto(cxArg, TaggedProto(proto)); @@ -1346,12 +1232,12 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp, if (!group) return nullptr; - JSObject *obj = NewObject(cxArg, group, allocKind, newKind); + JSObject* obj = NewObject(cxArg, group, allocKind, newKind); if (!obj) return nullptr; if (isCachable && !obj->as().hasDynamicSlots()) { - NewObjectCache &cache = cxArg->asJSContext()->runtime()->newObjectCache; + NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache; NewObjectCache::EntryIndex entry = -1; cache.lookupGlobal(clasp, global, allocKind, &entry); cache.fillGlobal(entry, clasp, global, allocKind, @@ -1362,7 +1248,7 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp, } static bool -NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group, +NewObjectWithGroupIsCachable(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind) { return group->proto().isObject() && @@ -1376,8 +1262,8 @@ NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group, * Create a plain object with the specified group. This bypasses getNewGroup to * avoid losing creation site information for objects made by scripted 'new'. */ -JSObject * -js::NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, +JSObject* +js::NewObjectWithGroupCommon(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind allocKind, NewObjectKind newKind) { MOZ_ASSERT(gc::IsObjectAllocKind(allocKind)); @@ -1386,17 +1272,17 @@ js::NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, bool isCachable = NewObjectWithGroupIsCachable(cx, group, newKind); if (isCachable) { - NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache; + NewObjectCache& cache = cx->asJSContext()->runtime()->newObjectCache; NewObjectCache::EntryIndex entry = -1; if (cache.lookupGroup(group, allocKind, &entry)) { - JSObject *obj = cache.newObjectFromHit(cx->asJSContext(), entry, + JSObject* obj = cache.newObjectFromHit(cx->asJSContext(), entry, GetInitialHeap(newKind, group->clasp())); if (obj) return obj; } } - JSObject *obj = NewObject(cx, group, allocKind, newKind); + JSObject* obj = NewObject(cx, group, allocKind, newKind); if (!obj) return nullptr; @@ -1433,8 +1319,8 @@ js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) return true; } -JSObject * -js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee) +JSObject* +js::CreateThis(JSContext* cx, const Class* newclasp, HandleObject callee) { RootedValue protov(cx); if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov)) @@ -1445,14 +1331,14 @@ js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee) return NewObjectWithClassProto(cx, newclasp, proto, kind); } -static inline JSObject * -CreateThisForFunctionWithGroup(JSContext *cx, HandleObjectGroup group, +static inline JSObject* +CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group, NewObjectKind newKind) { if (group->maybeUnboxedLayout() && newKind != SingletonObject) return UnboxedPlainObject::create(cx, group, newKind); - if (TypeNewScript *newScript = group->newScript()) { + if (TypeNewScript* newScript = group->newScript()) { if (newScript->analyzed()) { // The definite properties analysis has been performed for this // group, so get the shape and alloc kind to use from the @@ -1483,7 +1369,7 @@ CreateThisForFunctionWithGroup(JSContext *cx, HandleObjectGroup group, // plain object and register it with the group. Use the maximum number // of fixed slots, as is also required by the TypeNewScript. gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS); - PlainObject *res = NewObjectWithGroup(cx, group, allocKind, newKind); + PlainObject* res = NewObjectWithGroup(cx, group, allocKind, newKind); if (!res) return nullptr; @@ -1503,8 +1389,8 @@ CreateThisForFunctionWithGroup(JSContext *cx, HandleObjectGroup group, return NewObjectWithGroup(cx, group, allocKind, newKind); } -JSObject * -js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, HandleObject proto, +JSObject* +js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObject proto, NewObjectKind newKind /* = GenericObject */) { RootedObject res(cx); @@ -1571,12 +1457,12 @@ js::CreateThisForFunction(JSContext* cx, HandleObject callee, NewObjectKind newK } /* static */ bool -JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult &result) +JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) { RootedValue value(cx, v); if (MOZ_UNLIKELY(obj->watched())) { - WatchpointMap *wpmap = cx->compartment()->watchpointMap; + WatchpointMap* wpmap = cx->compartment()->watchpointMap; if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &value)) return false; } @@ -1584,8 +1470,8 @@ JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj, HandleId id, Han } /* static */ bool -JSObject::nonNativeSetElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v, - HandleValue receiver, ObjectOpResult &result) +JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v, + HandleValue receiver, ObjectOpResult& result) { RootedId id(cx); if (!IndexToId(cx, index, &id)) @@ -1708,7 +1594,7 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle proto) } static bool -GetScriptArrayObjectElements(JSContext *cx, HandleArrayObject obj, AutoValueVector &values) +GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject obj, AutoValueVector &values) { MOZ_ASSERT(!obj->isSingleton()); @@ -1725,7 +1611,7 @@ GetScriptArrayObjectElements(JSContext *cx, HandleArrayObject obj, AutoValueVect // converts their dense elements into data properties. for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) { - Shape &shape = r.front(); + Shape& shape = r.front(); if (shape.propid() == NameToId(cx->names().length)) continue; MOZ_ASSERT(shape.isDataDescriptor()); @@ -1751,16 +1637,16 @@ GetScriptArrayObjectElements(JSContext *cx, HandleArrayObject obj, AutoValueVect } static bool -GetScriptPlainObjectProperties(JSContext *cx, HandleObject obj, AutoIdValueVector &properties) +GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj, AutoIdValueVector& properties) { if (obj->is()) { - PlainObject *nobj = &obj->as(); + PlainObject* nobj = &obj->as(); if (!properties.appendN(IdValuePair(), nobj->slotSpan())) return false; for (Shape::Range r(nobj->lastProperty()); !r.empty(); r.popFront()) { - Shape &shape = r.front(); + Shape& shape = r.front(); MOZ_ASSERT(shape.isDataDescriptor()); uint32_t slot = shape.slot(); properties[slot].get().id = shape.propid(); @@ -1777,14 +1663,14 @@ GetScriptPlainObjectProperties(JSContext *cx, HandleObject obj, AutoIdValueVecto } if (obj->is()) { - UnboxedPlainObject *nobj = &obj->as(); + UnboxedPlainObject* nobj = &obj->as(); - const UnboxedLayout &layout = nobj->layout(); + const UnboxedLayout& layout = nobj->layout(); if (!properties.appendN(IdValuePair(), layout.properties().length())) return false; for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property &property = layout.properties()[i]; + const UnboxedLayout::Property& property = layout.properties()[i]; properties[i].get().id = NameToId(property.name); properties[i].get().value = nobj->getValue(property); } @@ -1796,7 +1682,7 @@ GetScriptPlainObjectProperties(JSContext *cx, HandleObject obj, AutoIdValueVecto } static bool -DeepCloneValue(JSContext *cx, Value *vp, NewObjectKind newKind) +DeepCloneValue(JSContext* cx, Value* vp, NewObjectKind newKind) { if (vp->isObject()) { RootedObject obj(cx, &vp->toObject()); @@ -1808,8 +1694,8 @@ DeepCloneValue(JSContext *cx, Value *vp, NewObjectKind newKind) return true; } -JSObject * -js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind) +JSObject* +js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind) { /* NB: Keep this in sync with XDRObjectLiteral. */ MOZ_ASSERT_IF(obj->isSingleton(), @@ -1866,7 +1752,7 @@ js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKin } static bool -InitializePropertiesFromCompatibleNativeObject(JSContext *cx, +InitializePropertiesFromCompatibleNativeObject(JSContext* cx, HandleNativeObject dst, HandleNativeObject src) { @@ -1924,7 +1810,7 @@ InitializePropertiesFromCompatibleNativeObject(JSContext *cx, } JS_FRIEND_API(bool) -JS_InitializePropertiesFromCompatibleNativeObject(JSContext *cx, +JS_InitializePropertiesFromCompatibleNativeObject(JSContext* cx, HandleObject dst, HandleObject src) { @@ -1935,11 +1821,11 @@ JS_InitializePropertiesFromCompatibleNativeObject(JSContext *cx, template bool -js::XDRObjectLiteral(XDRState *xdr, MutableHandleObject obj) +js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj) { /* NB: Keep this in sync with DeepCloneObjectLiteral. */ - JSContext *cx = xdr->cx(); + JSContext* cx = xdr->cx(); MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(), JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates()); @@ -2076,13 +1962,13 @@ js::XDRObjectLiteral(XDRState *xdr, MutableHandleObject obj) } template bool -js::XDRObjectLiteral(XDRState *xdr, MutableHandleObject obj); +js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj); template bool -js::XDRObjectLiteral(XDRState *xdr, MutableHandleObject obj); +js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj); -JSObject * -js::CloneObjectLiteral(JSContext *cx, HandleObject srcObj) +JSObject* +js::CloneObjectLiteral(JSContext* cx, HandleObject srcObj) { if (srcObj->is()) { AllocKind kind = GetBackgroundAllocKind(gc::GetGCObjectKind(srcObj->as().numFixedSlots())); @@ -2482,16 +2368,15 @@ bad: } NativeObject* -js::InitClass(JSContext *cx, HandleObject obj, HandleObject protoProto_, +js::InitClass(JSContext* cx, HandleObject obj, HandleObject protoProto_, const Class* clasp, Native constructor, unsigned nargs, - const JSPropertySpec *ps, const JSFunctionSpec *fs, - const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs, - NativeObject **ctorp, AllocKind ctorKind) + const JSPropertySpec* ps, const JSFunctionSpec* fs, + const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs, + NativeObject** ctorp, AllocKind ctorKind) { RootedObject protoProto(cx, protoProto_); /* Check function pointer members. */ - MOZ_ASSERT(clasp->addProperty != JS_PropertyStub); MOZ_ASSERT(clasp->getProperty != JS_PropertyStub); MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub); @@ -2767,7 +2652,7 @@ js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id, */ if (LookupPropertyOp op = obj->getOps()->lookupProperty) return op(cx, obj, id, objp, propp); - return NativeLookupProperty(cx, obj.as(), id, objp, propp); + return LookupPropertyInline(cx, obj.as(), id, objp, propp); } bool @@ -2917,19 +2802,10 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** return true; } - // Fail if there's a resolve hook. We allow the JSFunction resolve hook - // if we know it will never add a property with this name or str_resolve - // with a non-integer property. - do { - const Class* clasp = obj->getClass(); - if (!clasp->resolve) - break; - if (clasp->resolve == fun_resolve && !FunctionHasResolveHook(cx->names(), id)) - break; - if (clasp->resolve == str_resolve && !JSID_IS_INT(id)) - break; + // Fail if there's a resolve hook, unless the mayResolve hook tells + // us the resolve hook won't define a property with this id. + if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) return false; - } while (0); } else if (obj->is()) { if (obj->as().containsUnboxedOrExpandoProperty(cx, id)) { *objp = obj; @@ -2954,6 +2830,20 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** return true; } +static inline bool +NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp) +{ + if (shape->hasSlot()) { + *vp = pobj->getSlot(shape->slot()); + MOZ_ASSERT(!vp->isMagic()); + } else { + vp->setUndefined(); + } + + /* Fail if we have a custom getter. */ + return shape->hasDefaultGetter(); +} + bool js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp) { @@ -2995,7 +2885,7 @@ JSObject::reportNotExtensible(JSContext* cx, unsigned report) /*** ES6 standard internal methods ***************************************************************/ bool -js::SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto, JS::ObjectOpResult &result) +js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::ObjectOpResult& result) { /* * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's @@ -3073,14 +2963,14 @@ js::SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto, JS::Object } bool -js::SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto) +js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto) { ObjectOpResult result; return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj); } bool -js::PreventExtensions(JSContext *cx, HandleObject obj, ObjectOpResult &result) +js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result) { if (obj->is()) return js::Proxy::preventExtensions(cx, obj, result); @@ -3108,7 +2998,7 @@ js::PreventExtensions(JSContext *cx, HandleObject obj, ObjectOpResult &result) } bool -js::PreventExtensions(JSContext *cx, HandleObject obj) +js::PreventExtensions(JSContext* cx, HandleObject obj) { ObjectOpResult result; return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj); @@ -3125,19 +3015,18 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, return ok; } + RootedNativeObject nobj(cx, obj.as()); RootedShape shape(cx); - if (!NativeLookupOwnProperty(cx, obj.as(), id, &shape)) + if (!NativeLookupOwnProperty(cx, nobj, id, &shape)) return false; if (!shape) { desc.object().set(nullptr); return true; } - bool doGet = true; desc.setAttributes(GetShapeAttributes(obj, shape)); if (desc.isAccessorDescriptor()) { MOZ_ASSERT(desc.isShared()); - doGet = false; // The result of GetOwnPropertyDescriptor() must be either undefined or // a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2) @@ -3159,27 +3048,31 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, desc.setSetterObject(nullptr); desc.attributesRef() |= JSPROP_SETTER; } + + desc.value().setUndefined(); } else { // This is either a straight-up data property or (rarely) a // property with a JSGetterOp/JSSetterOp. The latter must be // reported to the caller as a plain data property, so don't // populate desc.getter/setter, and mask away the SHARED bit. desc.attributesRef() &= ~JSPROP_SHARED; + + if (IsImplicitDenseOrTypedArrayElement(shape)) { + desc.value().set(nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id))); + } else { + if (!NativeGetExistingProperty(cx, nobj, nobj, shape, desc.value())) + return false; + } } - RootedValue value(cx); - if (doGet && !GetProperty(cx, obj, obj, id, &value)) - return false; - - desc.value().set(value); - desc.object().set(obj); + desc.object().set(nobj); desc.assertComplete(); return true; } bool -js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, Handle desc, - ObjectOpResult &result) +js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc, + ObjectOpResult& result) { desc.assertValid(); if (DefinePropertyOp op = obj->getOps()->defineProperty) @@ -3188,14 +3081,14 @@ js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, Handle desc(cx); - desc.initFields(obj, value, attrs, getter, setter); + desc.initFields(NullPtr(), value, attrs, getter, setter); if (DefinePropertyOp op = obj->getOps()->defineProperty) { if (!cx->shouldBeJSContext()) return false; @@ -3205,18 +3098,18 @@ js::DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleVa } bool -js::DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, +js::DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value, JSGetterOp getter, JSSetterOp setter, unsigned attrs, - ObjectOpResult &result) + ObjectOpResult& result) { RootedId id(cx, NameToId(name)); return DefineProperty(cx, obj, id, value, getter, setter, attrs, result); } bool -js::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, +js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value, JSGetterOp getter, JSSetterOp setter, unsigned attrs, - ObjectOpResult &result) + ObjectOpResult& result) { MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); @@ -3228,7 +3121,7 @@ js::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, Handle } bool -js::DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, +js::DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value, JSGetterOp getter, JSSetterOp setter, unsigned attrs) { ObjectOpResult result; @@ -3244,7 +3137,7 @@ js::DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleVa } bool -js::DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, +js::DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value, JSGetterOp getter, JSSetterOp setter, unsigned attrs) { RootedId id(cx, NameToId(name)); @@ -3252,7 +3145,7 @@ js::DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, H } bool -js::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, +js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value, JSGetterOp getter, JSSetterOp setter, unsigned attrs) { MOZ_ASSERT(getter != JS_PropertyStub); @@ -3903,7 +3796,7 @@ JSObject::dump() if (obj->isNative()) { fprintf(stderr, "properties:\n"); - Vector props; + Vector props; for (Shape::Range r(obj->as().lastProperty()); !r.empty(); r.popFront()) if (!props.append(&r.front())) { fprintf(stderr, "(OOM while appending properties)\n"); @@ -3917,7 +3810,7 @@ JSObject::dump() } static void -MaybeDumpObject(const char *name, JSObject *obj) +MaybeDumpObject(const char* name, JSObject* obj) { if (obj) { fprintf(stderr, " %s: ", name); @@ -3937,7 +3830,7 @@ MaybeDumpValue(const char* name, const Value& v) } JS_FRIEND_API(void) -js::DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start) +js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start) { /* This should only called during live debugging. */ ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED); @@ -4007,7 +3900,7 @@ js::DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start) #endif /* DEBUG */ JS_FRIEND_API(void) -js::DumpBacktrace(JSContext *cx) +js::DumpBacktrace(JSContext* cx) { Sprinter sprinter(cx); sprinter.init(); @@ -4031,6 +3924,78 @@ js::DumpBacktrace(JSContext *cx) /* * */ +js::gc::AllocKind +JSObject::allocKindForTenure(const js::Nursery& nursery) const +{ + if (is()) { + const ArrayObject& aobj = as(); + MOZ_ASSERT(aobj.numFixedSlots() == 0); + + /* Use minimal size object if we are just going to copy the pointer. */ + if (!nursery.isInside(aobj.getElementsHeader())) + return AllocKind::OBJECT0_BACKGROUND; + + size_t nelements = aobj.getDenseCapacity(); + return GetBackgroundAllocKind(GetGCArrayKind(nelements)); + } + + if (is()) + return as().getAllocKind(); + + /* + * Typed arrays in the nursery may have a lazily allocated buffer, make + * sure there is room for the array's fixed data when moving the array. + */ + if (is() && !as().buffer()) { + size_t nbytes = as().byteLength(); + return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes)); + } + + // Proxies have finalizers and are not nursery allocated. + MOZ_ASSERT(!IsProxy(this)); + + // Unboxed plain objects are sized according to the data they store. + if (is()) { + size_t nbytes = as().layoutDontCheckGeneration().size(); + return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes); + } + + // Unboxed arrays use inline data if their size is small enough. + if (is()) { + const UnboxedArrayObject* nobj = &as(); + size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() + + nobj->capacity() * nobj->elementSize(); + if (nbytes <= JSObject::MAX_BYTE_SIZE) + return GetGCObjectKindForBytes(nbytes); + return AllocKind::OBJECT0; + } + + // Inlined typed objects are followed by their data, so make sure we copy + // it all over to the new object. + if (is()) { + // Figure out the size of this object, from the prototype's TypeDescr. + // The objects we are traversing here are all tenured, so we don't need + // to check forwarding pointers. + TypeDescr& descr = as().typeDescr(); + MOZ_ASSERT(!IsInsideNursery(&descr)); + return InlineTypedObject::allocKindForTypeDescriptor(&descr); + } + + // Outline typed objects use the minimum allocation kind. + if (is()) + return AllocKind::OBJECT0; + + // All nursery allocatable non-native objects are handled above. + MOZ_ASSERT(isNative()); + + AllocKind kind = GetGCObjectFixedSlotsKind(as().numFixedSlots()); + MOZ_ASSERT(!IsBackgroundFinalized(kind)); + if (!CanBeFinalizedInBackground(kind, getClass())) + return kind; + return GetBackgroundAllocKind(kind); +} + + void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info) { @@ -4083,6 +4048,49 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassIn } } +size_t +JSObject::sizeOfIncludingThisInNursery() const +{ + // This function doesn't concern itself yet with typed objects (bug 1133593) + // nor unboxed objects (bug 1133592). + + MOZ_ASSERT(!isTenured()); + + const Nursery &nursery = compartment()->runtimeFromAnyThread()->gc.nursery; + size_t size = Arena::thingSize(allocKindForTenure(nursery)); + + if (is()) { + const NativeObject &native = as(); + + size += native.numFixedSlots() * sizeof(Value); + size += native.numDynamicSlots() * sizeof(Value); + + if (native.hasDynamicElements()) { + js::ObjectElements &elements = *native.getElementsHeader(); + if (!elements.isCopyOnWrite() || elements.ownerObject() == this) + size += elements.capacity * sizeof(HeapSlot); + } + } + + return size; +} + +size_t +JS::ubi::Concrete::size(mozilla::MallocSizeOf mallocSizeOf) const +{ + JSObject &obj = get(); + + if (!obj.isTenured()) + return obj.sizeOfIncludingThisInNursery(); + + JS::ClassInfo info; + obj.addSizeOfExcludingThis(mallocSizeOf, &info); + return obj.tenuredSizeOfThis() + info.sizeOfAllThings(); +} + +template<> const char16_t JS::ubi::TracerConcrete::concreteTypeName[] = + MOZ_UTF16("JSObject"); + void JSObject::traceChildren(JSTracer* trc) { @@ -4093,7 +4101,7 @@ JSObject::traceChildren(JSTracer* trc) clasp->trace(trc, this); if (clasp->isNative()) { - NativeObject *nobj = &as(); + NativeObject* nobj = &as(); TraceEdge(trc, &nobj->shape_, "shape"); @@ -4119,3 +4127,32 @@ JSObject::traceChildren(JSTracer* trc) } while (false); } } + +static JSAtom* +displayAtomFromObjectGroup(ObjectGroup& group) +{ + TypeNewScript* script = group.newScript(); + if (!script) + return nullptr; + + return script->function()->displayAtom(); +} + +bool +JSObject::constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name) +{ + ObjectGroup *g = getGroup(cx); + if (!g) + return false; + + name.set(displayAtomFromObjectGroup(*g)); + return true; +} + +JSAtom* +JSObject::maybeConstructorDisplayAtom() const +{ + if (hasLazyGroup()) + return nullptr; + return displayAtomFromObjectGroup(*group()); +} diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 72bb67497d..4db0a37ef5 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -33,7 +33,7 @@ struct ClassInfo; namespace js { -class AutoPropertyDescriptorVector; +typedef AutoVectorRooter AutoPropertyDescriptorVector; class GCMarker; class Nursery; @@ -105,9 +105,9 @@ class JSObject : public js::gc::Cell friend class js::NewObjectCache; friend class js::Nursery; friend class js::gc::RelocationOverlay; - friend bool js::PreventExtensions(JSContext *cx, JS::HandleObject obj, JS::ObjectOpResult &result); - friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, - bool *succeeded); + friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result); + friend bool js::SetImmutablePrototype(js::ExclusiveContext* cx, JS::HandleObject obj, + bool* succeeded); // Make a new group to use for a singleton object. static js::ObjectGroup* makeLazyGroup(JSContext* cx, js::HandleObject obj); @@ -176,8 +176,8 @@ class JSObject : public js::gc::Cell // some non-native objects. After creating an object, tobjects for which // the shape pointer is invalid need to overwrite this pointer before a GC // can occur. - inline void setInitialShapeMaybeNonNative(js::Shape *shape); - inline void setShapeMaybeNonNative(js::Shape *shape); + inline void setInitialShapeMaybeNonNative(js::Shape* shape); + inline void setShapeMaybeNonNative(js::Shape* shape); // Set the initial slots and elements of an object. These pointers are only // valid for native objects, but during initialization are set for all @@ -191,7 +191,7 @@ class JSObject : public js::gc::Cell GENERATE_SHAPE }; - bool setFlags(js::ExclusiveContext *cx, js::BaseShape::Flag flags, + bool setFlags(js::ExclusiveContext* cx, js::BaseShape::Flag flags, GenerateShape generateShape = GENERATE_NONE); inline bool hasAllFlags(js::BaseShape::Flag flags) const; @@ -254,6 +254,20 @@ class JSObject : public js::gc::Cell */ inline bool isIndexed() const; + /* + * If this object was instantiated with `new Ctor`, return the constructor's + * display atom. Otherwise, return nullptr. + */ + bool constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name); + + /* + * The same as constructorDisplayAtom above, however if this object has a + * lazy group, nullptr is returned. This allows for use in situations that + * cannot GC and where having some information, even if it is inconsistently + * available, is better than no information. + */ + JSAtom* maybeConstructorDisplayAtom() const; + /* GC support. */ void traceChildren(JSTracer* trc); @@ -282,6 +296,9 @@ class JSObject : public js::gc::Cell static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(JSObject* obj, void* cellp); static MOZ_ALWAYS_INLINE void writeBarrierPostRemove(JSObject* obj, void* cellp); + /* Return the allocKind we would use if we were to tenure this object. */ + js::gc::AllocKind allocKindForTenure(const js::Nursery &nursery) const; + size_t tenuredSizeOfThis() const { MOZ_ASSERT(isTenured()); return js::gc::Arena::thingSize(asTenured().getAllocKind()); @@ -289,6 +306,12 @@ class JSObject : public js::gc::Cell void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info); + // We can only use addSizeOfExcludingThis on tenured objects: it assumes it + // can apply mallocSizeOf to bits and pieces of the object, whereas objects + // in the nursery may have those bits and pieces allocated in the nursery + // along with them, and are not each their own malloc blocks. + size_t sizeOfIncludingThisInNursery() const; + /* * Marks this object as having a singleton type, and leave the group lazy. * Constructs a new, unique shape for the object. @@ -376,18 +399,18 @@ class JSObject : public js::gc::Cell // Mark an object as having its 'new' script information cleared. inline bool wasNewScriptCleared() const; - bool setNewScriptCleared(js::ExclusiveContext *cx) { + bool setNewScriptCleared(js::ExclusiveContext* cx) { return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED); } /* Set a new prototype for an object with a singleton type. */ - bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle proto); + bool splicePrototype(JSContext* cx, const js::Class* clasp, js::Handle proto); /* * For bootstrapping, whether to splice a prototype for Function.prototype * or the global object. */ - bool shouldSplicePrototype(JSContext *cx); + bool shouldSplicePrototype(JSContext* cx); /* * Scope chains. @@ -565,22 +588,22 @@ operator!=(const JSObject& lhs, const JSObject& rhs) } // Size of the various GC thing allocation sizes used for objects. -struct JSObject_Slots0 : JSObject { void *data[3]; }; -struct JSObject_Slots2 : JSObject { void *data[3]; js::Value fslots[2]; }; -struct JSObject_Slots4 : JSObject { void *data[3]; js::Value fslots[4]; }; -struct JSObject_Slots8 : JSObject { void *data[3]; js::Value fslots[8]; }; -struct JSObject_Slots12 : JSObject { void *data[3]; js::Value fslots[12]; }; -struct JSObject_Slots16 : JSObject { void *data[3]; js::Value fslots[16]; }; +struct JSObject_Slots0 : JSObject { void* data[3]; }; +struct JSObject_Slots2 : JSObject { void* data[3]; js::Value fslots[2]; }; +struct JSObject_Slots4 : JSObject { void* data[3]; js::Value fslots[4]; }; +struct JSObject_Slots8 : JSObject { void* data[3]; js::Value fslots[8]; }; +struct JSObject_Slots12 : JSObject { void* data[3]; js::Value fslots[12]; }; +struct JSObject_Slots16 : JSObject { void* data[3]; js::Value fslots[16]; }; /* static */ MOZ_ALWAYS_INLINE void -JSObject::readBarrier(JSObject *obj) +JSObject::readBarrier(JSObject* obj) { if (!isNullLike(obj) && obj->isTenured()) obj->asTenured().readBarrier(&obj->asTenured()); } /* static */ MOZ_ALWAYS_INLINE void -JSObject::writeBarrierPre(JSObject *obj) +JSObject::writeBarrierPre(JSObject* obj) { if (!isNullLike(obj) && obj->isTenured()) obj->asTenured().writeBarrierPre(&obj->asTenured()); @@ -737,51 +760,51 @@ GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, * the DefineProperty functions do not enforce some invariants mandated by ES6. */ extern bool -StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, - Handle descriptor, ObjectOpResult &result); +StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle descriptor, ObjectOpResult& result); /* * Same as above except without the ObjectOpResult out-parameter. Throws a * TypeError on failure. */ extern bool -StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, +StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc); extern bool -DefineProperty(JSContext *cx, HandleObject obj, HandleId id, - Handle desc, ObjectOpResult &result); +DefineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle desc, ObjectOpResult& result); extern bool -DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, - JSGetterOp getter, JSSetterOp, unsigned attrs, ObjectOpResult &result); +DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value, + JSGetterOp getter, JSSetterOp, unsigned attrs, ObjectOpResult& result); extern bool -DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, - JSGetterOp getter, JSSetterOp, unsigned attrs, ObjectOpResult &result); +DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value, + JSGetterOp getter, JSSetterOp, unsigned attrs, ObjectOpResult& result); extern bool -DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, - JSGetterOp getter, JSSetterOp, unsigned attrs, ObjectOpResult &result); +DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value, + JSGetterOp getter, JSSetterOp, unsigned attrs, ObjectOpResult& result); /* * When the 'result' out-param is omitted, the behavior is the same as above, except * that any failure results in a TypeError. */ extern bool -DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, +DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value, JSGetterOp getter = nullptr, JSSetterOp setter = nullptr, unsigned attrs = JSPROP_ENUMERATE); extern bool -DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, +DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value, JSGetterOp getter = nullptr, JSSetterOp setter = nullptr, unsigned attrs = JSPROP_ENUMERATE); extern bool -DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, +DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value, JSGetterOp getter = nullptr, JSSetterOp setter = nullptr, unsigned attrs = JSPROP_ENUMERATE); @@ -1092,7 +1115,7 @@ GetInitialHeap(NewObjectKind newKind, const Class* clasp) { if (newKind != GenericObject) return gc::TenuredHeap; - if (clasp->finalize && !(clasp->flags & JSCLASS_FINALIZE_FROM_NURSERY)) + if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE)) return gc::TenuredHeap; return gc::DefaultHeap; } @@ -1104,37 +1127,37 @@ CreateThisForFunctionWithProto(JSContext* cx, js::HandleObject callee, HandleObj NewObjectKind newKind = GenericObject); // Specialized call for constructing |this| with a known function callee. -extern JSObject * +extern JSObject* CreateThisForFunction(JSContext* cx, js::HandleObject callee, NewObjectKind newKind); // Generic call for constructing |this|. -extern JSObject * -CreateThis(JSContext *cx, const js::Class* clasp, js::HandleObject callee); +extern JSObject* +CreateThis(JSContext* cx, const js::Class* clasp, js::HandleObject callee); -extern JSObject * -CloneObject(JSContext *cx, HandleObject obj, Handle proto); +extern JSObject* +CloneObject(JSContext* cx, HandleObject obj, Handle proto); -extern JSObject * -DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject); +extern JSObject* +DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind = GenericObject); extern bool -DefineProperties(JSContext *cx, HandleObject obj, HandleObject props); +DefineProperties(JSContext* cx, HandleObject obj, HandleObject props); inline JSGetterOp -CastAsGetterOp(JSObject *object) +CastAsGetterOp(JSObject* object) { return JS_DATA_TO_FUNC_PTR(JSGetterOp, object); } inline JSSetterOp -CastAsSetterOp(JSObject *object) +CastAsSetterOp(JSObject* object) { return JS_DATA_TO_FUNC_PTR(JSSetterOp, object); } /* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */ bool -ToPropertyDescriptor(JSContext *cx, HandleValue descval, bool checkAccessors, +ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors, MutableHandle desc); /* @@ -1153,8 +1176,8 @@ CompletePropertyDescriptor(MutableHandle desc); * ES5 15.2.3.7 steps 3-5. */ extern bool -ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors, - AutoIdVector *ids, AutoPropertyDescriptorVector *descs); +ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors, + AutoIdVector* ids, AutoPropertyDescriptorVector* descs); /* Read the name using a dynamic lookup on the scopeChain. */ extern bool @@ -1193,34 +1216,50 @@ LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject scope namespace js { -extern JSObject * +extern JSObject* FindVariableScope(JSContext* cx, JSFunction** funp); bool -LookupPropertyPure(ExclusiveContext* cx, JSObject *obj, jsid id, JSObject **objp, +LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp, Shape** propp); bool -GetPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, Value *vp); +GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp); bool -GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, +GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc); bool -GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); +GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); -/* ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc) */ -bool -FromPropertyDescriptor(JSContext *cx, Handle desc, MutableHandleValue vp); +/* + * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc). + * + * If desc.object() is null, then vp is set to undefined. + */ +extern bool +FromPropertyDescriptor(JSContext* cx, Handle desc, MutableHandleValue vp); + +/* + * Like FromPropertyDescriptor, but ignore desc.object() and always set vp + * to an object on success. + * + * Use FromPropertyDescriptor for getOwnPropertyDescriptor, since desc.object() + * is used to indicate whether a result was found or not. Use this instead for + * defineProperty: it would be senseless to define a "missing" property. + */ +extern bool +FromPropertyDescriptorToObject(JSContext* cx, Handle desc, + MutableHandleValue vp); extern bool -IsDelegate(JSContext *cx, HandleObject obj, const Value &v, bool *result); +IsDelegate(JSContext* cx, HandleObject obj, const Value& v, bool* result); // obj is a JSObject*, but we root it immediately up front. We do it // that way because we need a Rooted temporary in this method anyway. extern bool -IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject *obj, bool *result); +IsDelegateOfObject(JSContext* cx, HandleObject protoObj, JSObject* obj, bool* result); /* Wrap boolean, number or string as Boolean, Number or String object. */ extern JSObject* diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index ebd28443b3..691adfe5bf 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -26,20 +26,20 @@ #include "vm/TypeInference-inl.h" -inline js::Shape * +inline js::Shape* JSObject::maybeShape() const { - if (is()) + if (is() || is()) return nullptr; - return *reinterpret_cast(uintptr_t(this) + offsetOfShape()); + return *reinterpret_cast(uintptr_t(this) + offsetOfShape()); } -inline js::Shape * -JSObject::ensureShape(js::ExclusiveContext *cx) +inline js::Shape* +JSObject::ensureShape(js::ExclusiveContext* cx) { if (is() && !js::UnboxedPlainObject::convertToNative(cx->asJSContext(), this)) return nullptr; - js::Shape *shape = maybeShape(); + js::Shape* shape = maybeShape(); MOZ_ASSERT(shape); return shape; } @@ -246,7 +246,8 @@ SetNewObjectMetadata(ExclusiveContext* cxArg, JSObject* obj) // callback, and any reentering of JS via Invoke() etc. AutoEnterAnalysis enter(cx); - cx->compartment()->setNewObjectMetadata(cx, obj); + RootedObject hobj(cx, obj); + cx->compartment()->setNewObjectMetadata(cx, hobj); } } } @@ -266,7 +267,9 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi IsBackgroundFinalized(kind)); MOZ_ASSERT_IF(group->clasp()->finalize, heap == js::gc::TenuredHeap || - (group->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY)); + (group->clasp()->flags & JSCLASS_SKIP_NURSERY_FINALIZE)); + MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), + heap == js::gc::TenuredHeap); // Non-native classes cannot have reserved slots or private data, and the // objects can't have any fixed slots, for compatibility with @@ -570,18 +573,7 @@ IsInternalFunctionObject(JSObject* funobj) return fun->isLambda() && fun->isInterpreted() && !fun->environment(); } -class AutoPropertyDescriptorVector : public AutoVectorRooter -{ - public: - explicit AutoPropertyDescriptorVector(JSContext* cx - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, DESCVECTOR) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - } - - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; +typedef AutoVectorRooter AutoPropertyDescriptorVector; /* * Make an object with the specified prototype. If parent is null, it will @@ -641,23 +633,8 @@ NewObjectWithGivenProto(ExclusiveContext* cx, HandleObject proto, return obj ? &obj->as() : nullptr; } -/* - * Make an object with the prototype set according to the specified prototype or class: - * - * if proto is non-null: - * use the specified proto - * for a built-in class: - * use the memoized original value of the class constructor .prototype - * property object - * else if available - * the current value of .prototype - * else - * Object.prototype. - * - * The class prototype will be fetched from the parent's global. If global is - * null, the context's active global will be used, and the resulting object's - * parent will be that global. - */ +// Make an object with the prototype set according to the cached prototype or +// Object.prototype. JSObject* NewObjectWithClassProtoCommon(ExclusiveContext* cx, const Class* clasp, HandleObject proto, gc::AllocKind allocKind, NewObjectKind newKind); @@ -783,11 +760,11 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) return Proxy::objectClassIs(obj, classValue, cx); switch (classValue) { - case ESClass_Object: return obj->is(); + case ESClass_Object: return obj->is() || obj->is(); case ESClass_Array: case ESClass_IsArray: // There difference between those is only relevant for proxies. - return obj->is(); + return obj->is() || obj->is(); case ESClass_Number: return obj->is(); case ESClass_String: return obj->is(); case ESClass_Boolean: return obj->is(); @@ -814,7 +791,7 @@ IsObjectWithClass(const Value& v, ESClassValue classValue, JSContext* cx) inline bool IsArray(HandleObject obj, JSContext* cx) { - if (obj->is()) + if (obj->is() || obj->is()) return true; return ObjectClassIs(obj, ESClass_IsArray, cx); diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index ad1cdf0f99..ce3a87925d 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -26,7 +26,9 @@ namespace JS { -class AutoIdVector; +template +class AutoVectorRooter; +typedef AutoVectorRooter AutoIdVector; class CallArgs; template @@ -37,12 +39,14 @@ class JS_FRIEND_API(ReadOnlyCompileOptions); class JS_FRIEND_API(OwningCompileOptions); class JS_PUBLIC_API(CompartmentOptions); +class Value; struct Zone; } /* namespace JS */ namespace js { struct ContextFriendFields; +class Shape; } // namespace js /* @@ -265,10 +269,17 @@ class JS_PUBLIC_API(AutoGCRooter) OBJU32HASHMAP=-23, /* js::AutoObjectUnsigned32HashMap */ OBJHASHSET = -24, /* js::AutoObjectHashSet */ JSONPARSER = -25, /* js::JSONParser */ - CUSTOM = -26, /* js::CustomAutoRooter */ - FUNVECTOR = -27 /* js::AutoFunctionVector */ + CUSTOM = -26 /* js::CustomAutoRooter */ }; + static ptrdiff_t GetTag(const Value& value) { return VALVECTOR; } + static ptrdiff_t GetTag(const jsid& id) { return IDVECTOR; } + static ptrdiff_t GetTag(JSObject* obj) { return OBJVECTOR; } + static ptrdiff_t GetTag(JSScript* script) { return SCRIPTVECTOR; } + static ptrdiff_t GetTag(JSString* string) { return STRINGVECTOR; } + static ptrdiff_t GetTag(js::Shape* shape) { return SHAPEVECTOR; } + static ptrdiff_t GetTag(const JSPropertyDescriptor& pd) { return DESCVECTOR; } + private: AutoGCRooter ** const stackTop; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index fb4ae482c7..a7606d2c36 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1364,6 +1364,7 @@ const Class ScriptSourceObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ finalize, nullptr, /* call */ diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 5f877ca846..1f289b3549 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1076,6 +1076,12 @@ class JSScript : public js::gc::TenuredCell jsbytecode* codeEnd() const { return code() + length(); } + jsbytecode* lastPC() const { + jsbytecode* pc = codeEnd() - js::JSOP_RETRVAL_LENGTH; + MOZ_ASSERT(*pc == JSOP_RETRVAL); + return pc; + } + bool containsPC(const jsbytecode* pc) const { return pc >= code() && pc < codeEnd(); } @@ -1634,6 +1640,9 @@ class JSScript : public js::gc::TenuredCell return arr->vector[index]; } + // The following 3 functions find the static scope just before the + // execution of the instruction pointed to by pc. + js::NestedScopeObject* getStaticBlockScope(jsbytecode* pc); // Returns the innermost static scope at pc if it falls within the extent diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 821cc875fe..0676706d05 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -394,8 +394,15 @@ str_enumerate(JSContext* cx, HandleObject obj) return true; } -bool -js::str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) +static bool +str_mayResolve(const JSAtomState&, jsid id, JSObject*) +{ + // str_resolve ignores non-integer ids. + return JSID_IS_INT(id); +} + +static bool +str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) { if (!JSID_IS_INT(id)) return true; @@ -424,7 +431,8 @@ const Class StringObject::class_ = { nullptr, /* getProperty */ nullptr, /* setProperty */ str_enumerate, - str_resolve + str_resolve, + str_mayResolve }; /* diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 8e7ef2e2d9..cd11b7f914 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -409,9 +409,6 @@ str_split(JSContext* cx, unsigned argc, Value* vp); JSObject* str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep); -bool -str_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp); - bool str_replace_regexp_raw(JSContext* cx, HandleString string, HandleObject regexp, HandleString replacement, MutableHandleValue rval); diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index c8471f958a..f94083352a 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -616,6 +616,7 @@ const Class WeakMapObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ WeakMap_finalize, nullptr, /* call */ diff --git a/js/src/moz.build b/js/src/moz.build index ffbb31db61..8f78c025b3 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -255,6 +255,7 @@ UNIFIED_SOURCES += [ 'vm/PIC.cpp', 'vm/Probes.cpp', 'vm/ProxyObject.cpp', + 'vm/ReceiverGuard.cpp', 'vm/RegExpObject.cpp', 'vm/RegExpStatics.cpp', 'vm/Runtime.cpp', diff --git a/js/src/perf/jsperf.cpp b/js/src/perf/jsperf.cpp index d113db0cce..143ea2a468 100644 --- a/js/src/perf/jsperf.cpp +++ b/js/src/perf/jsperf.cpp @@ -163,7 +163,7 @@ static void pm_finalize(JSFreeOp* fop, JSObject* obj); static const JSClass pm_class = { "PerfMeasurement", JSCLASS_HAS_PRIVATE, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, pm_finalize }; diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index a468c5b6dc..317c184893 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -68,7 +68,9 @@ BaseProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver, else vp.setUndefined(); - return CallJSGetterOp(cx, desc.getter(), receiver, id, vp); + // A proxy object should never have own JSGetterOps. + MOZ_ASSERT(desc.object() != proxy); + return CallJSGetterOp(cx, desc.getter(), desc.object(), id, vp); } bool diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 55dadeba74..a0a954a3e0 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -574,7 +574,7 @@ ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, Ha // step 9 RootedValue descObj(cx); - if (!FromPropertyDescriptor(cx, desc, &descObj)) + if (!FromPropertyDescriptorToObject(cx, desc, &descObj)) return false; // steps 10-11 diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.cpp b/js/src/proxy/ScriptedIndirectProxyHandler.cpp index dcd21adb7b..192c30494d 100644 --- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp @@ -203,7 +203,7 @@ ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); RootedValue fval(cx), value(cx); return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) && - FromPropertyDescriptor(cx, desc, &value) && + FromPropertyDescriptorToObject(cx, desc, &value) && Trap2(cx, handler, fval, id, value, &value) && result.succeed(); } @@ -407,7 +407,6 @@ ScriptedIndirectProxyHandler::derivedSet(JSContext *cx, HandleObject proxy, Hand if (!receiver.isObject()) return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER); RootedObject receiverObj(cx, &receiver.toObject()); - desc.object().set(receiverObj); return DefineProperty(cx, receiverObj, id, desc, result); } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 545a9f8a53..76c8b28277 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -142,7 +142,10 @@ static bool enableIon = false; static bool enableAsmJS = false; static bool enableNativeRegExp = false; static bool enableUnboxedObjects = false; +static bool enableUnboxedArrays = false; +#ifdef JS_GC_ZEAL static char gZealStr[128]; +#endif static bool printTiming = false; static const char* jsCacheDir = nullptr; @@ -2631,7 +2634,7 @@ static const JSClass sandbox_class = { JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, sandbox_enumerate, sandbox_resolve, - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; @@ -5073,10 +5076,16 @@ global_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) return true; } +static bool +global_mayResolve(const JSAtomState& names, jsid id, JSObject* maybeObj) +{ + return JS_MayResolveStandardClass(names, id, maybeObj); +} + static const JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, - global_enumerate, global_resolve, + global_enumerate, global_resolve, global_mayResolve, nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook @@ -5139,6 +5148,7 @@ static const JSJitInfo dom_x_getterinfo = { JSVAL_TYPE_UNKNOWN, /* returnType */ true, /* isInfallible. False in setters. */ true, /* isMovable */ + true, /* isEliminatable */ false, /* isAlwaysInSlot */ false, /* isLazilyCachedInSlot */ false, /* isTypedMethod */ @@ -5154,6 +5164,7 @@ static const JSJitInfo dom_x_setterinfo = { JSVAL_TYPE_UNKNOWN, /* returnType */ false, /* isInfallible. False in setters. */ false, /* isMovable. */ + false, /* isEliminatable. */ false, /* isAlwaysInSlot */ false, /* isLazilyCachedInSlot */ false, /* isTypedMethod */ @@ -5169,6 +5180,7 @@ static const JSJitInfo doFoo_methodinfo = { JSVAL_TYPE_UNKNOWN, /* returnType */ false, /* isInfallible. False in setters. */ false, /* isMovable */ + false, /* isEliminatable */ false, /* isAlwaysInSlot */ false, /* isLazilyCachedInSlot */ false, /* isTypedMethod */ @@ -5740,12 +5752,14 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op) enableAsmJS = !op.getBoolOption("no-asmjs"); enableNativeRegExp = !op.getBoolOption("no-native-regexp"); enableUnboxedObjects = op.getBoolOption("unboxed-objects"); + enableUnboxedArrays = op.getBoolOption("unboxed-arrays"); JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline) .setIon(enableIon) .setAsmJS(enableAsmJS) .setNativeRegExp(enableNativeRegExp) - .setUnboxedObjects(enableUnboxedObjects); + .setUnboxedObjects(enableUnboxedObjects) + .setUnboxedArrays(enableUnboxedArrays); if (const char* str = op.getStringOption("ion-scalar-replacement")) { if (strcmp(str, "on") == 0) @@ -5950,7 +5964,8 @@ SetWorkerRuntimeOptions(JSRuntime *rt) .setIon(enableIon) .setAsmJS(enableAsmJS) .setNativeRegExp(enableNativeRegExp) - .setUnboxedObjects(enableUnboxedObjects); + .setUnboxedObjects(enableUnboxedObjects) + .setUnboxedArrays(enableUnboxedArrays); rt->setOffthreadIonCompilationEnabled(offthreadCompilation); rt->profilingScripts = enableDisassemblyDumps; @@ -6126,7 +6141,8 @@ main(int argc, char** argv, char** envp) || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") - || !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed objects") + || !op.addBoolOption('\0', "unboxed-objects", "Allow creating unboxed plain objects") + || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays") || !op.addStringOption('\0', "ion-scalar-replacement", "on/off", "Scalar Replacement (default: on, off to disable)") || !op.addStringOption('\0', "ion-gvn", "[mode]", diff --git a/js/src/tests/ecma_6/Class/classPrototype.js b/js/src/tests/ecma_6/Class/classPrototype.js index 3268470bf8..8f23b18c92 100644 --- a/js/src/tests/ecma_6/Class/classPrototype.js +++ b/js/src/tests/ecma_6/Class/classPrototype.js @@ -22,16 +22,6 @@ for (let test of [a,b]) { assertDeepEq(prototype, desiredPrototype); } -try { - eval(\`class a { - constructor() { }; - static [\"prototype\"]() { }; - }\`); -} catch (e if e instanceof TypeError) { - throw new Error("Congrats on making initprop respect non-writable " + - "non-configurable properties. Uncomment the test below " + - "for bonus points."); -/* // As such, it should by a TypeError to try and overwrite "prototype" with a // static member. The only way to try is with a computed property name; the rest // are early errors. @@ -72,8 +62,6 @@ assertThrowsInstanceOf(() => eval(\`( static set ["prototype"](x) { } } )\`), TypeError); -*/ -} `; if (classesEnabled()) diff --git a/js/src/tests/ecma_6/Class/superPropHomeObject.js b/js/src/tests/ecma_6/Class/superPropHomeObject.js index 514d347b37..514f388db4 100644 --- a/js/src/tests/ecma_6/Class/superPropHomeObject.js +++ b/js/src/tests/ecma_6/Class/superPropHomeObject.js @@ -1,4 +1,3 @@ -print("foo"); var test = ` // This is super weird. A super property reference in the spec contains two @@ -18,10 +17,12 @@ class derived extends base { constructor() { } test(expected) { super.test(expected); } testArrow() { return (() => super.test(this)); } + ["testCPN"](expected) { super.test(expected); } } let derivedInstance = new derived(); derivedInstance.test(derivedInstance); +derivedInstance.testCPN(derivedInstance); let obj = { test: derivedInstance.test }; obj.test(obj); diff --git a/js/src/tests/ecma_6/Object/getOwnPropertyDescriptor.js b/js/src/tests/ecma_6/Object/getOwnPropertyDescriptor.js index 52d5762c3b..7aaba7b5ee 100644 --- a/js/src/tests/ecma_6/Object/getOwnPropertyDescriptor.js +++ b/js/src/tests/ecma_6/Object/getOwnPropertyDescriptor.js @@ -18,17 +18,17 @@ if (typeof Symbol === "function") { } assertDeepEq(Object.getOwnPropertyDescriptor("foo", "length"), { - configurable: false, - enumerable: false, value: 3, - writable: false + writable: false, + enumerable: false, + configurable: false }); assertDeepEq(Object.getOwnPropertyDescriptor("foo", 0), { - configurable: false, - enumerable: true, value: "f", - writable: false + writable: false, + enumerable: true, + configurable: false }); if (typeof reportCompare === "function") diff --git a/js/src/tests/ecma_6/Object/property-descriptor-order.js b/js/src/tests/ecma_6/Object/property-descriptor-order.js new file mode 100644 index 0000000000..337d247dae --- /dev/null +++ b/js/src/tests/ecma_6/Object/property-descriptor-order.js @@ -0,0 +1,17 @@ +var names = Object.getOwnPropertyNames(Object.getOwnPropertyDescriptor({foo: 0}, "foo")); +assertDeepEq(names, ["value", "writable", "enumerable", "configurable"]); + +names = Object.getOwnPropertyNames(Object.getOwnPropertyDescriptor({get foo(){}}, "foo")); +assertDeepEq(names, ["get", "set", "enumerable", "configurable"]); + +var proxy = new Proxy({}, { + defineProperty(target, key, desc) { + var names = Object.getOwnPropertyNames(desc); + assertDeepEq(names, ["set", "configurable"]); + return true; + } +}); + +Object.defineProperty(proxy, "foo", {configurable: true, set: function() {}}); + +reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Symbol/property-basics.js b/js/src/tests/ecma_6/Symbol/property-basics.js index a38fa717ab..63bcb3867c 100644 --- a/js/src/tests/ecma_6/Symbol/property-basics.js +++ b/js/src/tests/ecma_6/Symbol/property-basics.js @@ -25,10 +25,10 @@ for (var sym of symbols) { assertEq(obj.hasOwnProperty(sym), true); assertEq(obj[sym], "ok"); assertDeepEq(Object.getOwnPropertyDescriptor(obj, sym), { - configurable: true, - enumerable: true, value: "ok", - writable: true + writable: true, + enumerable: true, + configurable: true }); // assign again, observe value is overwritten diff --git a/js/src/tests/ecma_6/Symbol/property-reflection.js b/js/src/tests/ecma_6/Symbol/property-reflection.js index a70a43fb98..5bdecf6632 100644 --- a/js/src/tests/ecma_6/Symbol/property-reflection.js +++ b/js/src/tests/ecma_6/Symbol/property-reflection.js @@ -21,17 +21,17 @@ var descs = new D; var s1 = Symbol("s1"); var hits = 0; descs[s1] = { - configurable: true, - enumerable: true, get: () => hits++, - set: undefined + set: undefined, + enumerable: true, + configurable: true }; var s2 = Symbol("s2"); descs[s2] = { - configurable: true, - enumerable: false, value: {}, - writable: true + writable: true, + enumerable: false, + configurable: true }; var s3 = Symbol("s3"); D.prototype[s3] = {value: "FAIL"}; diff --git a/js/src/tests/ecma_6/TypedArray/of.js b/js/src/tests/ecma_6/TypedArray/of.js index 9304f624e5..8fdbe45568 100644 --- a/js/src/tests/ecma_6/TypedArray/of.js +++ b/js/src/tests/ecma_6/TypedArray/of.js @@ -14,10 +14,10 @@ for (var constructor of constructors) { assertEq(constructor.of.length, 0); assertDeepEq(Object.getOwnPropertyDescriptor(constructor.__proto__, "of"), { - configurable: true, - enumerable: false, value: constructor.of, - writable: true + writable: true, + enumerable: false, + configurable: true }); // Basic tests. diff --git a/js/src/tests/ecma_6/TypedArray/slice.js b/js/src/tests/ecma_6/TypedArray/slice.js index 54e8992b2b..2f3333959b 100644 --- a/js/src/tests/ecma_6/TypedArray/slice.js +++ b/js/src/tests/ecma_6/TypedArray/slice.js @@ -61,12 +61,15 @@ for (var constructor of constructors) { strConstructor.constructor = "not a constructor"; strConstructor.slice(123); }, TypeError, "Assert that we have an invalid constructor"); - assertThrowsInstanceOf(() => { - var mathConstructor = new constructor; - mathConstructor.constructor = Math.sin; - mathConstructor.slice(123); - }, TypeError, "Assert that we have an invalid constructor"); + // If obj.constructor[@@species] is undefined or null -- which it has to be + // if we don't implement @@species -- then the default constructor is used. + var mathConstructor = new constructor(8); + mathConstructor.constructor = Math.sin; + assertDeepEq(mathConstructor.slice(4), new constructor(4)); + + assertEq("species" in Symbol, false, + "you've implemented @@species -- add real tests here!"); } if (typeof reportCompare === "function") diff --git a/js/src/tests/ecma_7/SIMD/conversions.js b/js/src/tests/ecma_7/SIMD/conversions.js new file mode 100644 index 0000000000..14a486a1f5 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/conversions.js @@ -0,0 +1,309 @@ +// |reftest| skip-if(!this.hasOwnProperty("SIMD")) +var float32x4 = SIMD.float32x4; +var float64x2 = SIMD.float64x2; +var int32x4 = SIMD.int32x4; + +function testFloat32x4FromFloat64x2() { + function expected(v) { + return [...(v.map(Math.fround)), 0, 0]; + } + + var vals = [ + [1, 2], + [-0, NaN], + [Infinity, -Infinity], + [Math.pow(2, 25) - 1, -Math.pow(25)], + [Math.pow(2, 1000), Math.pow(2, -1000)] + ]; + + for (var v of vals) { + assertEqX4(float32x4.fromFloat64x2(float64x2(...v)), expected(v)); + } + + // Test rounding to nearest, break tie with even + var f1 = makeFloat(0, 127, 0); + assertEq(f1, 1); + + var f2 = makeFloat(0, 127, 1); + var d = makeDouble(0, 1023, 0x0000020000000); + assertEq(f2, d); + + var mid = makeDouble(0, 1023, 0x0000010000000); + assertEq((1 + d) / 2, mid); + + var nextMid = makeDouble(0, 1023, 0x0000010000001); + + // mid is halfway between f1 and f2 => tie to even, which is f1 + var v = float64x2(mid, nextMid); + assertEqX4(float32x4.fromFloat64x2(v), [f1, f2, 0, 0]); + + var f3 = makeFloat(0, 127, 2); + var d = makeDouble(0, 1023, 0x0000040000000); + assertEq(f3, d); + + mid = makeDouble(0, 1023, 0x0000030000000); + assertEq((f2 + f3) / 2, mid); + + // same here. tie to even, which is f3 here + nextMid = makeDouble(0, 1023, 0x0000030000001); + var v = float64x2(mid, nextMid); + assertEqX4(float32x4.fromFloat64x2(v), [f3, f3, 0, 0]); + + // Test boundaries + var biggestFloat = makeFloat(0, 127 + 127, 0x7fffff); + assertEq(makeDouble(0, 1023 + 127, 0xfffffe0000000), biggestFloat); + + var lowestFloat = makeFloat(1, 127 + 127, 0x7fffff); + assertEq(makeDouble(1, 1023 + 127, 0xfffffe0000000), lowestFloat); + + var v = float64x2(lowestFloat, biggestFloat); + assertEqX4(float32x4.fromFloat64x2(v), [lowestFloat, biggestFloat, 0, 0]); + + var v = float64x2(makeDouble(0, 1023 + 127, 0xfffffe0000001), makeDouble(0, 1023 + 127, 0xffffff0000000)); + assertEqX4(float32x4.fromFloat64x2(v), [biggestFloat, Infinity, 0, 0]); +} + +function testFloat32x4FromFloat64x2Bits() { + var valsExp = [ + [[2.000000473111868, 512.0001225471497], [1.0, 2.0, 3.0, 4.0]], + [[-0, NaN], [0, -0, 0, NaN]], + [[Infinity, -Infinity], [0, NaN, 0, NaN]] + ]; + + for (var [v,w] of valsExp) { + assertEqX4(float32x4.fromFloat64x2Bits(float64x2(...v)), w); + } +} + +function testFloat32x4FromInt32x4() { + function expected(v) { + return v.map(Math.fround); + } + var vals = [ + [1, 2, 3, 4], + [INT32_MIN, INT32_MAX, Math.pow(2, 30) - 1, -Math.pow(2, 30)] + ]; + + for (var v of vals) { + assertEqX4(float32x4.fromInt32x4(int32x4(...v)), expected(v)); + } + + // Check that rounding to nearest, even is applied. + { + var num = makeFloat(0, 150 + 2, 0); + var next = makeFloat(0, 150 + 2, 1); + assertEq(num + 4, next); + + v = float32x4.fromInt32x4(int32x4(num, num + 1, num + 2, num + 3)); + assertEqX4(v, [num, num, /* even */ num, next]); + } + + { + var num = makeFloat(0, 150 + 2, 1); + var next = makeFloat(0, 150 + 2, 2); + assertEq(num + 4, next); + + v = float32x4.fromInt32x4(int32x4(num, num + 1, num + 2, num + 3)); + assertEqX4(v, [num, num, /* even */ next, next]); + } + + { + var last = makeFloat(0, 157, 0x7fffff); + + assertEq(last, Math.fround(last), "float"); + assertEq(last < Math.pow(2, 31), true, "less than 2**31"); + assertEq(last | 0, last, "it should be an integer, as exponent >= 150"); + + var diff = (Math.pow(2, 31) - 1) - last; + v = float32x4.fromInt32x4(int32x4(Math.pow(2, 31) - 1, + Math.pow(2, 30) + 1, + last + (diff / 2) | 0, // nearest is last + last + (diff / 2) + 1 | 0 // nearest is Math.pow(2, 31) + )); + assertEqX4(v, [Math.pow(2, 31), + Math.pow(2, 30), + last, + Math.pow(2, 31) + ]); + } +} + +function testFloat32x4FromInt32x4Bits() { + var valsExp = [ + [[100, 200, 300, 400], [1.401298464324817e-43, 2.802596928649634e-43, 4.203895392974451e-43, 5.605193857299268e-43]], + [[INT32_MIN, INT32_MAX, 0, 0], [-0, NaN, 0, 0]] + ]; + + for (var [v,w] of valsExp) { + assertEqX4(float32x4.fromInt32x4Bits(int32x4(...v)), w); + } +} + +function testFloat64x2FromFloat32x4() { + function expected(v) { + return v.slice(0, 2).map(Math.fround); + } + + var vals = [ + [100, 200, 300, 400], + [NaN, -0, NaN, -0], + [Infinity, -Infinity, Infinity, -Infinity], + [13.37, 12.853, 49.97, 53.124] + ]; + + for (var v of vals) { + assertEqX2(float64x2.fromFloat32x4(float32x4(...v)), expected(v)); + } +} + +function testFloat64x2FromFloat32x4Bits() { + var valsExp = [ + [[0, 1.875, 0, 2], [1.0, 2.0]], + [[NaN, -0, Infinity, -Infinity], [-1.058925634e-314, -1.404448428688076e+306]] + ]; + + for (var [v,w] of valsExp) { + assertEqX2(float64x2.fromFloat32x4Bits(float32x4(...v)), w); + } +} + +function testFloat64x2FromInt32x4() { + function expected(v) { + return v.slice(0, 2); + } + + var vals = [ + [1, 2, 3, 4], + [INT32_MAX, INT32_MIN, 0, 0] + ]; + + for (var v of vals) { + assertEqX2(float64x2.fromInt32x4(int32x4(...v)), expected(v)); + } +} + +function testFloat64x2FromInt32x4Bits() { + var valsExp = [ + [[0x00000000, 0x3ff00000, 0x0000000, 0x40000000], [1.0, 2.0]], + [[0xabcdef12, 0x3ff00000, 0x21fedcba, 0x40000000], [1.0000006400213732, 2.0000002532866263]] + ]; + + for (var [v,w] of valsExp) { + assertEqX2(float64x2.fromInt32x4Bits(int32x4(...v)), w); + } +} + +function testInt32x4FromFloat32x4() { + var d = float32x4(1.1, 2.2, 3.3, 4.6); + assertEqX4(int32x4.fromFloat32x4(d), [1, 2, 3, 4]); + + var d = float32x4(NaN, 0, 0, 0); + assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError); + + var d = float32x4(Infinity, 0, 0, 0); + assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError); + + var d = float32x4(-Infinity, 0, 0, 0); + assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError); + + // Test high boundaries: float(0, 157, 0x7fffff) < INT32_MAX < float(0, 158, 0) + var d = float32x4(makeFloat(0, 127 + 31, 0), 0, 0, 0); + assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError); + + var lastFloat = makeFloat(0, 127 + 30, 0x7FFFFF); + var d = float32x4(lastFloat, 0, 0, 0); + var e = SIMD.int32x4.fromFloat32x4(d); + assertEqX4(e, [lastFloat, 0, 0, 0]); + + // Test low boundaries + assertEq(makeFloat(1, 127 + 31, 0), INT32_MIN); + var d = float32x4(makeFloat(1, 127 + 31, 0), 0, 0, 0); + var e = SIMD.int32x4.fromFloat32x4(d); + assertEqX4(e, [INT32_MIN, 0, 0, 0]); + + var d = float32x4(makeFloat(1, 127 + 31, 1), 0, 0, 0); + assertThrowsInstanceOf(() => SIMD.int32x4.fromFloat32x4(d), RangeError); +} + +function testInt32x4FromFloat32x4Bits() { + var valsExp = [ + [[1, 2, 3, 4], [0x3f800000 | 0, 0x40000000 | 0, 0x40400000 | 0, 0x40800000 | 0]], + [[NaN, -0, Infinity, -Infinity], [0x7fc00000 | 0, 0x80000000 | 0, 0x7f800000 | 0, 0xff800000 | 0]] + ]; + + for (var [v,w] of valsExp) { + assertEqX4(int32x4.fromFloat32x4Bits(float32x4(...v)), w); + } +} + +function testInt32x4FromFloat64x2() { + assertEqX4(int32x4.fromFloat64x2(float64x2(1, 2.2)), [1, 2, 0, 0]); + + var g = float64x2(Infinity, 0); + assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError); + + var g = float64x2(-Infinity, 0); + assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError); + + var g = float64x2(NaN, 0); + assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError); + + // Testing high boundaries + // double(0, 1023 + 30, 0) < INT32_MAX < double(0, 1023 + 31, 0), so the + // lowest exactly representable quantity at this scale is 2**(-52 + 30) == + // 2**-22. + assertEq(makeDouble(0, 1023 + 30, 0) + Math.pow(2, -22), makeDouble(0, 1023 + 30, 1)); + assertEq(makeDouble(0, 1023 + 30, 0) + Math.pow(2, -23), makeDouble(0, 1023 + 30, 0)); + + var g = float64x2(INT32_MAX, 0); + assertEqX4(int32x4.fromFloat64x2(g), [INT32_MAX, 0, 0, 0]); + + var g = float64x2(INT32_MAX + Math.pow(2, -22), 0); + assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError); + + // Testing low boundaries + assertEq(makeDouble(1, 1023 + 31, 0), INT32_MIN); + + var g = float64x2(makeDouble(1, 1023 + 31, 0), 0); + assertEqX4(int32x4.fromFloat64x2(g), [INT32_MIN, 0, 0, 0]); + + var g = float64x2(makeDouble(1, 1023 + 31, 1), 0); + assertThrowsInstanceOf(() => int32x4.fromFloat64x2(g), RangeError); + +} + +function testInt32x4FromFloat64x2Bits() { + var valsExp = [ + [[1.0, 2.0], [0x00000000, 0x3FF00000, 0x00000000, 0x40000000]], + [[+Infinity, -Infinity], [0x00000000, 0x7ff00000, 0x00000000, -0x100000]], + [[-0, NaN], [0x00000000, -0x80000000, 0x00000000, 0x7ff80000]], + [[1.0000006400213732, 2.0000002532866263], [-0x543210ee, 0x3ff00000, 0x21fedcba, 0x40000000]] + ]; + + for (var [v,w] of valsExp) { + assertEqX4(int32x4.fromFloat64x2Bits(float64x2(...v)), w); + } +} + +function test() { + testFloat32x4FromFloat64x2(); + testFloat32x4FromFloat64x2Bits(); + testFloat32x4FromInt32x4(); + testFloat32x4FromInt32x4Bits(); + + testFloat64x2FromFloat32x4(); + testFloat64x2FromFloat32x4Bits(); + testFloat64x2FromInt32x4(); + testFloat64x2FromInt32x4Bits(); + + testInt32x4FromFloat32x4(); + testInt32x4FromFloat32x4Bits(); + testInt32x4FromFloat64x2(); + testInt32x4FromFloat64x2Bits(); + + if (typeof reportCompare === "function") { + reportCompare(true, true); + } +} + +test(); diff --git a/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js deleted file mode 100644 index a4b2ef94f5..0000000000 --- a/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js +++ /dev/null @@ -1,60 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float32x4 = SIMD.float32x4; -var float64x2 = SIMD.float64x2; - -var summary = 'float32x4 fromFloat64x2'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float64x2(1, 2); - var c = float32x4.fromFloat64x2(a); - assertEq(c.x, 1); - assertEq(c.y, 2); - assertEq(c.z, 0); - assertEq(c.w, 0); - - var d = float64x2(-0, NaN); - var f = float32x4.fromFloat64x2(d); - assertEq(f.x, -0); - assertEq(f.y, NaN); - assertEq(f.z, 0); - assertEq(f.w, 0); - - var g = float64x2(Infinity, -Infinity); - var i = float32x4.fromFloat64x2(g); - assertEq(i.x, Infinity); - assertEq(i.y, -Infinity); - assertEq(i.z, 0); - assertEq(i.w, 0); - - var j = Math.pow(2, 25) - 1; - var k = -Math.pow(2, 25); - var l = float64x2(j, k); - var m = float32x4.fromFloat64x2(l); - assertEq(m.x, Math.fround(j)); - assertEq(m.y, Math.fround(k)); - assertEq(m.z, 0); - assertEq(m.w, 0); - - var o = Math.pow(2, 1000); - var p = Math.pow(2, -1000); - var q = float64x2(o, p); - var r = float32x4.fromFloat64x2(q); - assertEq(r.x, Math.fround(o)); - assertEq(r.y, Math.fround(p)); - assertEq(r.z, 0); - assertEq(r.w, 0); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js deleted file mode 100644 index d3bb892f48..0000000000 --- a/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js +++ /dev/null @@ -1,43 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float32x4 = SIMD.float32x4; -var float64x2 = SIMD.float64x2; -var int32x4 = SIMD.int32x4; - -var summary = 'float32x4 fromFloat64x2Bits'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float64x2(2.000000473111868, 512.0001225471497); - var b = float32x4.fromFloat64x2Bits(a); - assertEq(b.x, 1.0); - assertEq(b.y, 2.0); - assertEq(b.z, 3.0); - assertEq(b.w, 4.0); - - var c = float64x2(-0, NaN); - var d = float32x4.fromFloat64x2Bits(c); - assertEq(d.x, 0); - assertEq(d.y, -0); - assertEq(d.z, 0); - assertEq(d.w, NaN); - - var e = float64x2(Infinity, -Infinity); - var f = float32x4.fromFloat64x2Bits(e); - assertEq(f.x, 0); - assertEq(f.y, NaN); - assertEq(f.z, 0); - assertEq(f.w, NaN); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float32x4fromint32x4.js b/js/src/tests/ecma_7/SIMD/float32x4fromint32x4.js deleted file mode 100644 index c6c50fa2a8..0000000000 --- a/js/src/tests/ecma_7/SIMD/float32x4fromint32x4.js +++ /dev/null @@ -1,35 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 946042; -var float32x4 = SIMD.float32x4; -var int32x4 = SIMD.int32x4; - -var summary = 'float32x4 fromInt32x4'; - -function test() { - print(BUGNUMBER + ": " + summary); - - var INT32_MAX = Math.pow(2, 31) - 1; - var INT32_MIN = -Math.pow(2, 31); - - var a = int32x4(1, 2, 3, 4); - var c = SIMD.float32x4.fromInt32x4(a); - assertEq(c.x, 1); - assertEq(c.y, 2); - assertEq(c.z, 3); - assertEq(c.w, 4); - - var value1 = Math.pow(2, 30) - 1; - var value2 = -Math.pow(2, 30); - var d = int32x4(INT32_MIN, INT32_MAX, value1, value2); - var f = float32x4.fromInt32x4(d); - assertEq(f.x, Math.fround(INT32_MIN)); - assertEq(f.y, Math.fround(INT32_MAX)); - assertEq(f.z, Math.fround(value1)); - assertEq(f.w, Math.fround(value2)); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float32x4fromint32x4bits.js b/js/src/tests/ecma_7/SIMD/float32x4fromint32x4bits.js deleted file mode 100644 index 64a9fb1b2c..0000000000 --- a/js/src/tests/ecma_7/SIMD/float32x4fromint32x4bits.js +++ /dev/null @@ -1,33 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 946042; -var float32x4 = SIMD.float32x4; -var int32x4 = SIMD.int32x4; - -var summary = 'float32x4 fromInt32x4Bits'; - -function test() { - print(BUGNUMBER + ": " + summary); - - var INT32_MAX = Math.pow(2, 31) - 1; - var INT32_MIN = -Math.pow(2, 31); - - var a = int32x4(100, 200, 300, 400); - var c = SIMD.float32x4.fromInt32x4Bits(a); - assertEq(c.x, 1.401298464324817e-43); - assertEq(c.y, 2.802596928649634e-43); - assertEq(c.z, 4.203895392974451e-43); - assertEq(c.w, 5.605193857299268e-43); - - var d = int32x4(INT32_MIN, INT32_MAX, 0, 0); - var f = float32x4.fromInt32x4Bits(d); - assertEq(f.x, -0); - assertEq(f.y, NaN); - assertEq(f.z, 0); - assertEq(f.w, 0); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js deleted file mode 100644 index b746f1efa7..0000000000 --- a/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js +++ /dev/null @@ -1,41 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float32x4 = SIMD.float32x4; -var float64x2 = SIMD.float64x2; - -var summary = 'float64x2 fromFloat32x4'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float32x4(100, 200, 300, 400); - var c = float64x2.fromFloat32x4(a); - assertEq(c.x, 100); - assertEq(c.y, 200); - - var d = float32x4(NaN, -0, NaN, -0); - var f = float64x2.fromFloat32x4(d); - assertEq(f.x, NaN); - assertEq(f.y, -0); - - var g = float32x4(Infinity, -Infinity, Infinity, -Infinity); - var i = float64x2.fromFloat32x4(g); - assertEq(i.x, Infinity); - assertEq(i.y, -Infinity); - - var j = float32x4(13.37, 12.853, 49.97, 53.124); - var l = float64x2.fromFloat32x4(j); - assertEq(l.x, Math.fround(13.37)); - assertEq(l.y, Math.fround(12.853)); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js deleted file mode 100644 index 6a36df124e..0000000000 --- a/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js +++ /dev/null @@ -1,32 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float32x4 = SIMD.float32x4; -var float64x2 = SIMD.float64x2; -var int32x4 = SIMD.int32x4; - -var summary = 'float64x2 fromFloat32x4Bits'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float32x4(0, 1.875, 0, 2); - var c = float64x2.fromFloat32x4Bits(a); - assertEq(c.x, 1.0); - assertEq(c.y, 2.0); - - var d = float32x4(NaN, -0, Infinity, -Infinity); - var f = float64x2.fromFloat32x4Bits(d); - assertEq(f.x, -1.058925634e-314); - assertEq(f.y, -1.404448428688076e+306); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js deleted file mode 100644 index 629d3ec8af..0000000000 --- a/js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js +++ /dev/null @@ -1,31 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float64x2 = SIMD.float64x2; -var int32x4 = SIMD.int32x4; - -var summary = 'float64x2 fromInt32x4'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = int32x4(1, 2, 3, 4); - var c = float64x2.fromInt32x4(a); - assertEq(c.x, 1); - assertEq(c.y, 2); - - var d = int32x4(INT32_MAX, INT32_MIN, 0, 0); - var f = float64x2.fromInt32x4(d); - assertEq(f.x, INT32_MAX); - assertEq(f.y, INT32_MIN); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js deleted file mode 100644 index e4525ac059..0000000000 --- a/js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js +++ /dev/null @@ -1,31 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float64x2 = SIMD.float64x2; -var int32x4 = SIMD.int32x4; - -var summary = 'float64x2 fromInt32x4Bits'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = int32x4(0x00000000, 0x3ff00000, 0x0000000, 0x40000000); - var c = float64x2.fromInt32x4Bits(a); - assertEq(c.x, 1.0); - assertEq(c.y, 2.0); - - var d = int32x4(0xabcdef12, 0x3ff00000, 0x21fedcba, 0x40000000); - var f = float64x2.fromInt32x4Bits(d); - assertEq(f.x, 1.0000006400213732); - assertEq(f.y, 2.0000002532866263); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4.js b/js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4.js deleted file mode 100644 index 0028a441ca..0000000000 --- a/js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4.js +++ /dev/null @@ -1,30 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 946042; -var float32x4 = SIMD.float32x4; -var int32x4 = SIMD.int32x4; - -var summary = 'int32x4 fromFloat32x4'; - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float32x4(1.1, 2.2, 3.3, 4.6); - var c = SIMD.int32x4.fromFloat32x4(a); - assertEq(c.x, 1); - assertEq(c.y, 2); - assertEq(c.z, 3); - assertEq(c.w, 4); - - var d = float32x4(NaN, -0, Infinity, -Infinity); - var f = SIMD.int32x4.fromFloat32x4(d); - assertEq(f.x, 0); - assertEq(f.y, 0); - assertEq(f.z, 0); - assertEq(f.w, 0); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4bits.js b/js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4bits.js deleted file mode 100644 index da1b7b37bd..0000000000 --- a/js/src/tests/ecma_7/SIMD/int32x4fromfloat32x4bits.js +++ /dev/null @@ -1,30 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 946042; -var float32x4 = SIMD.float32x4; -var int32x4 = SIMD.int32x4; - -var summary = 'int32x4 fromFloat32x4Bits'; - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float32x4(1, 2, 3, 4); - var c = SIMD.int32x4.fromFloat32x4Bits(a); - assertEq(c.x, 0x3f800000 | 0); - assertEq(c.y, 0x40000000 | 0); - assertEq(c.z, 0x40400000 | 0); - assertEq(c.w, 0x40800000 | 0); - - var d = float32x4(NaN, -0, Infinity, -Infinity); - var f = SIMD.int32x4.fromFloat32x4Bits(d); - assertEq(f.x, 0x7fc00000 | 0); - assertEq(f.y, 0x80000000 | 0); - assertEq(f.z, 0x7f800000 | 0); - assertEq(f.w, 0xff800000 | 0); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js deleted file mode 100644 index 84ab8def2b..0000000000 --- a/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js +++ /dev/null @@ -1,51 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float64x2 = SIMD.float64x2; -var int32x4 = SIMD.int32x4; - -var summary = 'int32x4 fromFloat64x2'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float64x2(1, 2.2); - var c = int32x4.fromFloat64x2(a); - assertEq(c.x, 1); - assertEq(c.y, 2); - assertEq(c.z, 0); - assertEq(c.w, 0); - - var d = float64x2(NaN, -0); - var f = int32x4.fromFloat64x2(d); - assertEq(f.x, 0); - assertEq(f.y, 0); - assertEq(f.z, 0); - assertEq(f.w, 0); - - var g = float64x2(Infinity, -Infinity); - var i = int32x4.fromFloat64x2(g); - assertEq(i.x, 0); - assertEq(i.y, 0); - assertEq(i.z, 0); - assertEq(i.w, 0); - - var j = Math.pow(2, 31); - var k = -Math.pow(2, 31) - 1; - var m = float64x2(j, k); - var l = int32x4.fromFloat64x2(m); - assertEq(l.x, INT32_MIN); - assertEq(l.y, INT32_MAX); - assertEq(l.z, 0); - assertEq(l.w, 0); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js deleted file mode 100644 index 52939cecfb..0000000000 --- a/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js +++ /dev/null @@ -1,49 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1031203; -var float64x2 = SIMD.float64x2; -var int32x4 = SIMD.int32x4; - -var summary = 'int32x4 fromFloat64x2Bits'; - -/* - * Any copyright is dedicated to the Public Domain. - * https://creativecommons.org/publicdomain/zero/1.0/ - */ - -function test() { - print(BUGNUMBER + ": " + summary); - - var a = float64x2(1.0, 2.0); - var c = int32x4.fromFloat64x2Bits(a); - assertEq(c.x, 0x00000000); - assertEq(c.y, 0x3FF00000); - assertEq(c.z, 0x00000000); - assertEq(c.w, 0x40000000); - - var d = float64x2(+Infinity, -Infinity); - var f = int32x4.fromFloat64x2Bits(d); - assertEq(f.x, 0x00000000); - assertEq(f.y, 0x7ff00000); - assertEq(f.z, 0x00000000); - assertEq(f.w, -0x100000); - - var g = float64x2(-0, NaN); - var i = int32x4.fromFloat64x2Bits(g); - assertEq(i.x, 0x00000000); - assertEq(i.y, -0x80000000); - assertEq(i.z, 0x00000000); - assertEq(i.w, 0x7ff80000); - - var j = float64x2(1.0000006400213732, 2.0000002532866263); - var l = int32x4.fromFloat64x2Bits(j); - assertEq(l.x, -0x543210ee); - assertEq(l.y, 0x3ff00000); - assertEq(l.z, 0x21fedcba); - assertEq(l.w, 0x40000000); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test(); - diff --git a/js/src/tests/ecma_7/SIMD/shell.js b/js/src/tests/ecma_7/SIMD/shell.js index 55ab7ff387..50433db843 100644 --- a/js/src/tests/ecma_7/SIMD/shell.js +++ b/js/src/tests/ecma_7/SIMD/shell.js @@ -1,3 +1,34 @@ +function makeFloat(sign, exp, mantissa) { + assertEq(sign, sign & 0x1); + assertEq(exp, exp & 0xFF); + assertEq(mantissa, mantissa & 0x7FFFFF); + + var i32 = new Int32Array(1); + var f32 = new Float32Array(i32.buffer); + + i32[0] = (sign << 31) | (exp << 23) | mantissa; + return f32[0]; +} + +function makeDouble(sign, exp, mantissa) { + assertEq(sign, sign & 0x1); + assertEq(exp, exp & 0x7FF); + + // Can't use bitwise operations on mantissa, as it might be a double + assertEq(mantissa <= 0xfffffffffffff, true); + var highBits = (mantissa / Math.pow(2, 32)) | 0; + var lowBits = mantissa - highBits * Math.pow(2, 32); + + var i32 = new Int32Array(2); + var f64 = new Float64Array(i32.buffer); + + // Note that this assumes little-endian order, which is the case on tier-1 + // platforms. + i32[0] = lowBits; + i32[1] = (sign << 31) | (exp << 20) | highBits; + return f64[0]; +} + function assertEqX2(v, arr) { try { assertEq(v.x, arr[0]); diff --git a/js/src/tests/ecma_7/SIMD/shifts.js b/js/src/tests/ecma_7/SIMD/shifts.js index e4d1925e3e..0555153bb2 100644 --- a/js/src/tests/ecma_7/SIMD/shifts.js +++ b/js/src/tests/ecma_7/SIMD/shifts.js @@ -8,13 +8,13 @@ var int32x4 = SIMD.int32x4; function lsh(a, b) { - return (a << b) | 0; + return (b >>> 0) >= 32 ? 0 : (a << b) | 0; } function rsh(a, b) { - return (a >> b) | 0; + return (a >> Math.min(b >>> 0, 31)) | 0; } function ursh(a, b) { - return (a >>> b) | 0; + return (b >>> 0) >= 32 ? 0 : (a >>> b) | 0; } function test() { @@ -28,7 +28,7 @@ function test() { int32x4(INT32_MAX, INT32_MIN, INT32_MAX - 1, INT32_MIN + 1) ]) { - for (var bits = 0; bits < 32; bits++) { + for (var bits = -2; bits < 36; bits++) { testBinaryScalarFunc(v, bits, int32x4.shiftLeftByScalar, lsh); testBinaryScalarFunc(v, bits, int32x4.shiftRightArithmeticByScalar, rsh); testBinaryScalarFunc(v, bits, int32x4.shiftRightLogicalByScalar, ursh); diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 54d4369198..eda09dff5e 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -288,9 +288,6 @@ args_delProperty(JSContext *cx, HandleObject obj, HandleId id, ObjectOpResult &r static bool ArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { - if (!obj->is()) - return true; - NormalArgumentsObject& argsobj = obj->as(); if (JSID_IS_INT(id)) { /* @@ -412,9 +409,6 @@ args_enumerate(JSContext* cx, HandleObject obj) static bool StrictArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) { - if (!obj->is()) - return true; - StrictArgumentsObject& argsobj = obj->as(); if (JSID_IS_INT(id)) { @@ -566,6 +560,7 @@ const Class NormalArgumentsObject::class_ = { nullptr, /* setProperty */ args_enumerate, args_resolve, + nullptr, /* mayResolve */ nullptr, /* convert */ ArgumentsObject::finalize, nullptr, /* call */ @@ -590,6 +585,7 @@ const Class StrictArgumentsObject::class_ = { nullptr, /* setProperty */ strictargs_enumerate, strictargs_resolve, + nullptr, /* mayResolve */ nullptr, /* convert */ ArgumentsObject::finalize, nullptr, /* call */ diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 46c57701e4..7b74d195d4 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -108,6 +108,7 @@ const Class ArrayBufferObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ ArrayBufferObject::finalize, nullptr, /* call */ diff --git a/js/src/vm/ArrayObject-inl.h b/js/src/vm/ArrayObject-inl.h index 07355d7df9..1ad8402891 100644 --- a/js/src/vm/ArrayObject-inl.h +++ b/js/src/vm/ArrayObject-inl.h @@ -38,6 +38,8 @@ ArrayObject::createArrayInternal(ExclusiveContext *cx, gc::AllocKind kind, gc::I MOZ_ASSERT(group->clasp() == shape->getObjectClass()); MOZ_ASSERT(group->clasp() == &ArrayObject::class_); MOZ_ASSERT_IF(group->clasp()->finalize, heap == gc::TenuredHeap); + MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), + heap == js::gc::TenuredHeap); // Arrays can use their fixed slots to store elements, so can't have shapes // which allow named properties to be stored in the fixed slots. diff --git a/js/src/vm/Debugger-inl.h b/js/src/vm/Debugger-inl.h index de46e23a4e..007267b3be 100644 --- a/js/src/vm/Debugger-inl.h +++ b/js/src/vm/Debugger-inl.h @@ -22,7 +22,7 @@ js::Debugger::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok) MOZ_ASSERT_IF(evalTraps, frame.isDebuggee()); if (frame.isDebuggee()) ok = slowPathOnLeaveFrame(cx, frame, ok); - assertNotInFrameMaps(frame); + MOZ_ASSERT(!inFrameMaps(frame)); return ok; } diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 69a82aa553..3428e4c2c0 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -606,6 +606,13 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool frame { Handle global = cx->global(); + // The onPop handler and associated clean up logic should not run multiple + // times on the same frame. If slowPathOnLeaveFrame has already been + // called, the frame will not be present in the Debugger frame maps. + FrameRange frameRange(frame, global); + if (frameRange.empty()) + return frameOk; + /* Save the frame's completion value. */ JSTrapStatus status; RootedValue value(cx); @@ -618,8 +625,8 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool frame if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) { /* Build a list of the recipients. */ AutoObjectVector frames(cx); - for (FrameRange r(frame, global); !r.empty(); r.popFront()) { - if (!frames.append(r.frontFrame())) { + for (; !frameRange.empty(); frameRange.popFront()) { + if (!frames.append(frameRange.frontFrame())) { cx->clearPendingException(); return false; } @@ -1688,8 +1695,8 @@ Debugger::slowPathOnNewGlobalObject(JSContext* cx, Handle global) } /* static */ bool -Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when, - GlobalObject::DebuggerVector& dbgs) +Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, + int64_t when, GlobalObject::DebuggerVector& dbgs) { MOZ_ASSERT(!dbgs.empty()); mozilla::DebugOnly begin = dbgs.begin(); @@ -1701,7 +1708,7 @@ Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int if ((*dbgp)->trackingAllocationSites && (*dbgp)->enabled && - !(*dbgp)->appendAllocationSite(cx, frame, when)) + !(*dbgp)->appendAllocationSite(cx, obj, frame, when)) { return false; } @@ -1717,15 +1724,38 @@ Debugger::isDebuggee(const JSCompartment* compartment) const return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal()); } +/* static */ Debugger::AllocationSite* +Debugger::AllocationSite::create(JSContext* cx, HandleObject frame, int64_t when, HandleObject obj) +{ + assertSameCompartment(cx, frame); + + RootedAtom ctorName(cx); + { + AutoCompartment ac(cx, obj); + if (!obj->constructorDisplayAtom(cx, &ctorName)) + return nullptr; + } + + AllocationSite* allocSite = cx->new_(frame, when); + if (!allocSite) + return nullptr; + + allocSite->className = obj->getClass()->name; + allocSite->ctorName = ctorName.get(); + return allocSite; +} + + bool -Debugger::appendAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when) +Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, + int64_t when) { AutoCompartment ac(cx, object); - RootedObject wrapped(cx, frame); - if (!cx->compartment()->wrap(cx, &wrapped)) + RootedObject wrappedFrame(cx, frame); + if (!cx->compartment()->wrap(cx, &wrappedFrame)) return false; - AllocationSite* allocSite = cx->new_(wrapped, when); + AllocationSite* allocSite = AllocationSite::create(cx, wrappedFrame, when, obj); if (!allocSite) return false; @@ -2386,6 +2416,8 @@ Debugger::trace(JSTracer* trc) for (AllocationSite* s = allocationsLog.getFirst(); s; s = s->getNext()) { if (s->frame) TraceEdge(trc, &s->frame, "allocation log SavedFrame"); + if (s->ctorName) + TraceEdge(trc, &s->ctorName, "allocation log constructor name"); } /* Trace the weak map from JSScript instances to Debugger.Script objects. */ @@ -2467,7 +2499,7 @@ const Class Debugger::jsclass = { "Debugger", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Debugger::finalize, nullptr, /* call */ nullptr, /* hasInstance */ @@ -4266,7 +4298,7 @@ const Class DebuggerScript_class = { "Script", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, /* call */ nullptr, /* hasInstance */ @@ -4999,13 +5031,11 @@ Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePt return true; } -/* static */ void -Debugger::assertNotInFrameMaps(AbstractFramePtr frame) +/* static */ bool +Debugger::inFrameMaps(AbstractFramePtr frame) { -#ifdef DEBUG FrameRange r(frame); - MOZ_ASSERT(r.empty()); -#endif + return !r.empty(); } /* static */ void @@ -5289,7 +5319,7 @@ const Class DebuggerSource_class = { "Source", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, /* call */ nullptr, /* hasInstance */ @@ -5644,7 +5674,7 @@ DebuggerFrame_finalize(FreeOp* fop, JSObject* obj) const Class DebuggerFrame_class = { "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, DebuggerFrame_finalize }; @@ -6401,7 +6431,7 @@ const Class DebuggerObject_class = { "Object", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, /* call */ nullptr, /* hasInstance */ @@ -7311,7 +7341,7 @@ const Class DebuggerEnv_class = { "Environment", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, /* call */ nullptr, /* hasInstance */ diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 884f9240c7..1a2b222382 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -255,11 +255,22 @@ class Debugger : private mozilla::LinkedListElement struct AllocationSite : public mozilla::LinkedListElement { - AllocationSite(HandleObject frame, int64_t when) : frame(frame), when(when) { + AllocationSite(HandleObject frame, int64_t when) + : frame(frame), + when(when), + className(nullptr), + ctorName(nullptr) + { MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is()); }; + + static AllocationSite* create(JSContext* cx, HandleObject frame, int64_t when, + HandleObject obj); + RelocatablePtrObject frame; int64_t when; + const char* className; + RelocatablePtrAtom ctorName; }; typedef mozilla::LinkedList AllocationSiteList; @@ -305,7 +316,8 @@ class Debugger : private mozilla::LinkedListElement static const size_t DEFAULT_MAX_ALLOCATIONS_LOG_LENGTH = 5000; - bool appendAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when); + bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, + int64_t when); void emptyAllocationsLog(); /* @@ -515,7 +527,7 @@ class Debugger : private mozilla::LinkedListElement static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame); static void slowPathOnNewScript(JSContext* cx, HandleScript script); static void slowPathOnNewGlobalObject(JSContext* cx, Handle global); - static bool slowPathOnLogAllocationSite(JSContext* cx, HandleSavedFrame frame, + static bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, int64_t when, GlobalObject::DebuggerVector& dbgs); static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise); static JSTrapStatus dispatchHook(JSContext* cx, MutableHandleValue vp, Hook which, @@ -666,8 +678,9 @@ class Debugger : private mozilla::LinkedListElement static inline void onNewScript(JSContext* cx, HandleScript script); static inline void onNewGlobalObject(JSContext* cx, Handle global); - static inline bool onLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when); - static inline void onGarbageCollection(JSRuntime *rt, const gcstats::Statistics &stats); + static inline bool onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, + int64_t when); + static inline void onGarbageCollection(JSRuntime* rt, const gcstats::Statistics& stats); static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp); static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp); static bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from, jit::BaselineFrame* to); @@ -675,7 +688,7 @@ class Debugger : private mozilla::LinkedListElement static void handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame); static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval); static bool hasLiveHook(GlobalObject* global, Hook which); - static void assertNotInFrameMaps(AbstractFramePtr frame); + static bool inFrameMaps(AbstractFramePtr frame); /************************************* Functions for use by Debugger.cpp. */ @@ -975,16 +988,17 @@ Debugger::onNewGlobalObject(JSContext* cx, Handle global) } /* static */ bool -Debugger::onLogAllocationSite(JSContext* cx, HandleSavedFrame frame, int64_t when) +Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, int64_t when) { GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers(); if (!dbgs || dbgs->empty()) return true; - return Debugger::slowPathOnLogAllocationSite(cx, frame, when, *dbgs); + RootedObject hobj(cx, obj); + return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs); } /* static */ void -Debugger::onGarbageCollection(JSRuntime *rt, const gcstats::Statistics &stats) +Debugger::onGarbageCollection(JSRuntime* rt, const gcstats::Statistics &stats) { for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) { if (dbg->debuggeeWasCollected && dbg->getHook(OnGarbageCollection)) { diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index 9ff61b0d4d..3ee35abd35 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -209,6 +209,19 @@ DebuggerMemory::drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp) if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) return false; + RootedString className(cx, Atomize(cx, allocSite->className, strlen(allocSite->className))); + if (!className) + return false; + RootedValue classNameValue(cx, StringValue(className)); + if (!DefineProperty(cx, obj, cx->names().class_, classNameValue)) + return false; + + RootedValue ctorName(cx, NullValue()); + if (allocSite->ctorName) + ctorName.setString(allocSite->ctorName); + if (!DefineProperty(cx, obj, cx->names().constructor, ctorName)) + return false; + result->setDenseElement(i, ObjectValue(*obj)); // Pop the front queue entry, and delete it immediately, so that diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 0975ddb7b0..cadeebde2a 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -514,7 +514,7 @@ GlobalDebuggees_finalize(FreeOp* fop, JSObject* obj) static const Class GlobalDebuggees_class = { "GlobalDebuggee", JSCLASS_HAS_PRIVATE, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, GlobalDebuggees_finalize }; diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index a81123a4c2..55d8e2c1be 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -192,7 +192,7 @@ static const JSClass parseTaskGlobalClass = { "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index c11f86c7fc..32646a49c4 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -626,7 +626,7 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t JSOp op = JSOp(*pc); MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); - MOZ_ASSERT(obj->is()); + MOZ_ASSERT(obj->is() || obj->is()); /* * If val is a hole, do not call DefineElement. diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index c81e60bd3e..ed0f14d9ec 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -56,8 +56,8 @@ #if defined(XP_UNIX) #include #elif defined(XP_WIN) -#include -#include +#include +#include #endif // defined(XP_UNIX) || defined(XP_WIN) using namespace js; @@ -174,7 +174,7 @@ js::OnUnknownMethod(JSContext* cx, HandleObject obj, Value idval_, MutableHandle return false; if (value.isObject()) { - NativeObject *obj = NewNativeObjectWithClassProto(cx, &js_NoSuchMethodClass, NullPtr()); + NativeObject* obj = NewNativeObjectWithClassProto(cx, &js_NoSuchMethodClass, NullPtr()); if (!obj) return false; @@ -186,7 +186,7 @@ js::OnUnknownMethod(JSContext* cx, HandleObject obj, Value idval_, MutableHandle } static bool -NoSuchMethod(JSContext *cx, unsigned argc, Value *vp) +NoSuchMethod(JSContext* cx, unsigned argc, Value* vp) { if (!cx->compartment()->warnedAboutNoSuchMethod) { if (!JS_ReportWarning(cx, "__noSuchMethod__ is deprecated")) @@ -200,13 +200,13 @@ NoSuchMethod(JSContext *cx, unsigned argc, Value *vp) MOZ_ASSERT(vp[0].isObject()); MOZ_ASSERT(vp[1].isObject()); - NativeObject *obj = &vp[0].toObject().as(); + NativeObject* obj = &vp[0].toObject().as(); MOZ_ASSERT(obj->getClass() == &js_NoSuchMethodClass); args.setCallee(obj->getReservedSlot(JSSLOT_FOUND_FUNCTION)); args.setThis(vp[1]); args[0].set(obj->getReservedSlot(JSSLOT_SAVED_ID)); - JSObject *argsobj = NewDenseCopiedArray(cx, argc, vp + 2); + JSObject* argsobj = NewDenseCopiedArray(cx, argc, vp + 2); if (!argsobj) return false; args[1].setObject(*argsobj); @@ -278,7 +278,7 @@ GetPropertyOperation(JSContext* cx, InterpreterFrame* fp, HandleScript script, j } static inline bool -GetNameOperation(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc, MutableHandleValue vp) +GetNameOperation(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc, MutableHandleValue vp) { JSObject* obj = fp->scopeChain(); PropertyName* name = fp->script()->getName(pc); @@ -318,7 +318,7 @@ GetNameOperation(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc, MutableHan } static bool -SetPropertyOperation(JSContext *cx, JSOp op, HandleValue lval, HandleId id, HandleValue rval) +SetPropertyOperation(JSContext* cx, JSOp op, HandleValue lval, HandleId id, HandleValue rval) { MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP); @@ -811,7 +811,7 @@ js::InvokeConstructor(JSContext* cx, Value fval, unsigned argc, const Value* arg } bool -js::InvokeGetter(JSContext *cx, JSObject *obj, Value fval, MutableHandleValue rval) +js::InvokeGetter(JSContext* cx, JSObject* obj, Value fval, MutableHandleValue rval) { /* * Invoke could result in another try to get or set the same id again, see @@ -823,7 +823,7 @@ js::InvokeGetter(JSContext *cx, JSObject *obj, Value fval, MutableHandleValue rv } bool -js::InvokeSetter(JSContext *cx, const Value &thisv, Value fval, HandleValue v) +js::InvokeSetter(JSContext* cx, const Value& thisv, Value fval, HandleValue v) { JS_CHECK_RECURSION(cx, return false); @@ -961,7 +961,7 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp) // XXX RM FIXME 2018-12-10 try to update this RootedValue val(cx, ObjectValue(*obj)); ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, - JSDVG_SEARCH_STACK, val, js::NullPtr()); + JSDVG_SEARCH_STACK, val, NullPtr()); return false; } @@ -1242,28 +1242,68 @@ js::UnwindAllScopesInFrame(JSContext* cx, ScopeIter& si) jsbytecode* js::UnwindScopeToTryPc(JSScript* script, JSTryNote* tn) { - return script->main() + tn->start - js_CodeSpec[JSOP_TRY].length; + jsbytecode* pc = script->main() + tn->start; + if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY) { + pc -= JSOP_TRY_LENGTH; + MOZ_ASSERT(*pc == JSOP_TRY); + } + return pc; } -static void -ForcedReturn(JSContext* cx, ScopeIter& si, InterpreterRegs& regs) +static bool +ForcedReturn(JSContext* cx, ScopeIter& si, InterpreterRegs& regs, bool frameOk = true) { + bool ok = Debugger::onLeaveFrame(cx, regs.fp(), frameOk); UnwindAllScopesInFrame(cx, si); + // Point the frame to the end of the script, regardless of error. The + // caller must jump to the correct continuation depending on 'ok'. regs.setToEndOfScript(); + return ok; } -static void +static bool ForcedReturn(JSContext* cx, InterpreterRegs& regs) { ScopeIter si(cx, regs.fp(), regs.pc); - ForcedReturn(cx, si, regs); + return ForcedReturn(cx, si, regs); } -void -js::UnwindForUncatchableException(JSContext* cx, const InterpreterRegs& regs) +static void +SettleOnTryNote(JSContext* cx, JSTryNote* tn, ScopeIter& si, InterpreterRegs& regs) { - /* c.f. the regular (catchable) TryNoteIter loop in HandleError. */ - for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) { + // Unwind the scope to the beginning of the JSOP_TRY. + UnwindScope(cx, si, UnwindScopeToTryPc(regs.fp()->script(), tn)); + + // Set pc to the first bytecode after the the try note to point + // to the beginning of catch or finally. + regs.pc = regs.fp()->script()->main() + tn->start + tn->length; + regs.sp = regs.spForStackDepth(tn->stackDepth); +} + +class InterpreterFrameStackDepthOp +{ + const InterpreterRegs& regs_; + public: + explicit InterpreterFrameStackDepthOp(const InterpreterRegs& regs) + : regs_(regs) + { } + uint32_t operator()() { return regs_.stackDepth(); } +}; + +class TryNoteIterInterpreter : public TryNoteIter +{ + public: + TryNoteIterInterpreter(JSContext* cx, const InterpreterRegs& regs) + : TryNoteIter(cx, regs.fp()->script(), regs.pc, InterpreterFrameStackDepthOp(regs)) + { } +}; + +static void +UnwindIteratorsForUncatchableException(JSContext* cx, const InterpreterRegs& regs) +{ + // c.f. the regular (catchable) TryNoteIterInterpreter loop in + // ProcessTryNotes. + for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) { JSTryNote* tn = *tni; if (tn->kind == JSTRY_FOR_IN) { Value* sp = regs.spForStackDepth(tn->stackDepth); @@ -1272,65 +1312,6 @@ js::UnwindForUncatchableException(JSContext* cx, const InterpreterRegs& regs) } } -TryNoteIter::TryNoteIter(JSContext* cx, const InterpreterRegs& regs) - : regs(regs), - script(cx, regs.fp()->script()), - pcOffset(regs.pc - script->main()) -{ - if (script->hasTrynotes()) { - tn = script->trynotes()->vector; - tnEnd = tn + script->trynotes()->length; - } else { - tn = tnEnd = nullptr; - } - settle(); -} - -void -TryNoteIter::operator++() -{ - ++tn; - settle(); -} - -bool -TryNoteIter::done() const -{ - return tn == tnEnd; -} - -void -TryNoteIter::settle() -{ - for (; tn != tnEnd; ++tn) { - /* If pc is out of range, try the next one. */ - if (pcOffset - tn->start >= tn->length) - continue; - - /* - * We have a note that covers the exception pc but we must check - * whether the interpreter has already executed the corresponding - * handler. This is possible when the executed bytecode implements - * break or return from inside a for-in loop. - * - * In this case the emitter generates additional [enditer] and [gosub] - * opcodes to close all outstanding iterators and execute the finally - * blocks. If such an [enditer] throws an exception, its pc can still - * be inside several nested for-in loops and try-finally statements - * even if we have already closed the corresponding iterators and - * invoked the finally blocks. - * - * To address this, we make [enditer] always decrease the stack even - * when its implementation throws an exception. Thus already executed - * [enditer] and [gosub] opcodes will have try notes with the stack - * depth exceeding the current one and this condition is what we use to - * filter them out. - */ - if (tn->stackDepth <= regs.stackDepth()) - break; - } -} - enum HandleErrorContinuation { SuccessfulReturnContinuation, @@ -1339,6 +1320,69 @@ enum HandleErrorContinuation FinallyContinuation }; +static HandleErrorContinuation +ProcessTryNotes(JSContext* cx, ScopeIter& si, InterpreterRegs& regs) +{ + for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) { + JSTryNote* tn = *tni; + + switch (tn->kind) { + case JSTRY_CATCH: + /* Catch cannot intercept the closing of a generator. */ + if (cx->isClosingGenerator()) + break; + SettleOnTryNote(cx, tn, si, regs); + return CatchContinuation; + + case JSTRY_FINALLY: + SettleOnTryNote(cx, tn, si, regs); + return FinallyContinuation; + + case JSTRY_FOR_IN: { + /* This is similar to JSOP_ENDITER in the interpreter loop. */ + DebugOnly pc = regs.fp()->script()->main() + tn->start + tn->length; + MOZ_ASSERT(JSOp(*pc) == JSOP_ENDITER); + Value* sp = regs.spForStackDepth(tn->stackDepth); + RootedObject obj(cx, &sp[-1].toObject()); + if (!UnwindIteratorForException(cx, obj)) { + // We should only settle on the note only if + // UnwindIteratorForException itself threw, as + // onExceptionUnwind should be called anew with the new + // location of the throw (the iterator). Indeed, we must + // settle to avoid infinitely handling the same exception. + SettleOnTryNote(cx, tn, si, regs); + return ErrorReturnContinuation; + } + break; + } + + case JSTRY_FOR_OF: + case JSTRY_LOOP: + break; + + default: + MOZ_CRASH("Invalid try note"); + } + } + + return SuccessfulReturnContinuation; +} + +bool +js::HandleClosingGeneratorReturn(JSContext* cx, AbstractFramePtr frame, bool ok) +{ + /* + * Propagate the exception or error to the caller unless the exception + * is an asynchronous return from a generator. + */ + if (cx->isClosingGenerator()) { + cx->clearPendingException(); + ok = true; + SetReturnValueForClosingGenerator(cx, frame); + } + return ok; +} + static HandleErrorContinuation HandleError(JSContext* cx, InterpreterRegs& regs) { @@ -1350,11 +1394,7 @@ HandleError(JSContext* cx, InterpreterRegs& regs) again: if (cx->isExceptionPending()) { /* Call debugger throw hooks. */ - RootedValue exception(cx); - if (!cx->getPendingException(&exception)) - goto again; - - if (!exception.isMagic(JS_GENERATOR_CLOSING)) { + if (!cx->isClosingGenerator()) { JSTrapStatus status = Debugger::onExceptionUnwind(cx, regs.fp()); switch (status) { case JSTRAP_ERROR: @@ -1365,7 +1405,9 @@ HandleError(JSContext* cx, InterpreterRegs& regs) break; case JSTRAP_RETURN: - ForcedReturn(cx, si, regs); + UnwindIteratorsForUncatchableException(cx, regs); + if (!ForcedReturn(cx, si, regs)) + return ErrorReturnContinuation; return SuccessfulReturnContinuation; default: @@ -1373,77 +1415,37 @@ HandleError(JSContext* cx, InterpreterRegs& regs) } } - for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) { - JSTryNote* tn = *tni; - - // Unwind the scope to the beginning of the JSOP_TRY. - UnwindScope(cx, si, UnwindScopeToTryPc(regs.fp()->script(), tn)); - - /* - * Set pc to the first bytecode after the the try note to point - * to the beginning of catch or finally or to [enditer] closing - * the for-in loop. - */ - regs.pc = regs.fp()->script()->main() + tn->start + tn->length; - regs.sp = regs.spForStackDepth(tn->stackDepth); - - switch (tn->kind) { - case JSTRY_CATCH: - /* Catch cannot intercept the closing of a generator. */ - if (!cx->getPendingException(&exception)) - return ErrorReturnContinuation; - if (exception.isMagic(JS_GENERATOR_CLOSING)) - break; - return CatchContinuation; - - case JSTRY_FINALLY: - return FinallyContinuation; - - case JSTRY_FOR_IN: { - /* This is similar to JSOP_ENDITER in the interpreter loop. */ - MOZ_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER); - RootedObject obj(cx, ®s.sp[-1].toObject()); - bool ok = UnwindIteratorForException(cx, obj); - regs.sp -= 1; - if (!ok) - goto again; - break; - } - - case JSTRY_FOR_OF: - case JSTRY_LOOP: - break; - } + switch (ProcessTryNotes(cx, si, regs)) { + case SuccessfulReturnContinuation: + break; + case ErrorReturnContinuation: + goto again; + case CatchContinuation: + return CatchContinuation; + case FinallyContinuation: + return FinallyContinuation; } - /* - * Propagate the exception or error to the caller unless the exception - * is an asynchronous return from a generator. - */ - if (cx->isExceptionPending()) { - RootedValue exception(cx); - if (!cx->getPendingException(&exception)) - return ErrorReturnContinuation; - - if (exception.isMagic(JS_GENERATOR_CLOSING)) { - cx->clearPendingException(); - ok = true; - SetReturnValueForClosingGenerator(cx, regs.fp()); - } - } + ok = HandleClosingGeneratorReturn(cx, regs.fp(), ok); + ok = Debugger::onLeaveFrame(cx, regs.fp(), ok); } else { // We may be propagating a forced return from the interrupt // callback, which cannot easily force a return. if (MOZ_UNLIKELY(cx->isPropagatingForcedReturn())) { cx->clearPropagatingForcedReturn(); - ForcedReturn(cx, si, regs); + if (!ForcedReturn(cx, si, regs)) + return ErrorReturnContinuation; return SuccessfulReturnContinuation; } - UnwindForUncatchableException(cx, regs); + UnwindIteratorsForUncatchableException(cx, regs); } - ForcedReturn(cx, si, regs); + // After this point, we will pop the frame regardless. Settle the frame on + // the end of the script. + UnwindAllScopesInFrame(cx, si); + regs.setToEndOfScript(); + return ok ? SuccessfulReturnContinuation : ErrorReturnContinuation; } @@ -1826,7 +1828,8 @@ Interpret(JSContext* cx, RunState& state) case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: - ForcedReturn(cx, REGS); + if (!ForcedReturn(cx, REGS)) + goto error; goto successful_return_continuation; case JSTRAP_THROW: case JSTRAP_ERROR: @@ -1872,7 +1875,8 @@ CASE(EnableInterruptsPseudoOpcode) break; case JSTRAP_RETURN: REGS.fp()->setReturnValue(rval); - ForcedReturn(cx, REGS); + if (!ForcedReturn(cx, REGS)) + goto error; goto successful_return_continuation; case JSTRAP_THROW: cx->setPendingException(rval); @@ -1893,7 +1897,8 @@ CASE(EnableInterruptsPseudoOpcode) goto error; case JSTRAP_RETURN: REGS.fp()->setReturnValue(rval); - ForcedReturn(cx, REGS); + if (!ForcedReturn(cx, REGS)) + goto error; goto successful_return_continuation; case JSTRAP_THROW: cx->setPendingException(rval); @@ -1918,7 +1923,6 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_UNUSED2) -CASE(JSOP_UNUSED126) CASE(JSOP_UNUSED148) CASE(JSOP_BACKPATCH) CASE(JSOP_UNUSED161) @@ -2098,8 +2102,6 @@ CASE(JSOP_RETRVAL) ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH); } - /* Increment pc so that |sp - fp->slots == ReconstructStackDepth(pc)|. */ - REGS.pc += JSOP_CALL_LENGTH; goto error; } else { MOZ_ASSERT(REGS.stackDepth() == 0); @@ -2293,14 +2295,14 @@ CASE(JSOP_BINDNAME) { JSOp op = JSOp(*REGS.pc); if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) { - RootedObject &scopeChain = rootObject0; + RootedObject& scopeChain = rootObject0; scopeChain = REGS.fp()->scopeChain(); - RootedPropertyName &name = rootName0; + RootedPropertyName& name = rootName0; name = script->getName(REGS.pc); /* Assigning to an undeclared name adds a property to the global object. */ - RootedObject &scope = rootObject1; + RootedObject& scope = rootObject1; if (!LookupNameUnqualified(cx, name, scopeChain, &scope)) goto error; @@ -2754,7 +2756,7 @@ CASE(JSOP_STRICTSETNAME) static_assert(JSOP_SETNAME_LENGTH == JSOP_SETGNAME_LENGTH, "We're sharing the END_CASE so the lengths better match"); - RootedObject &scope = rootObject0; + RootedObject& scope = rootObject0; scope = ®S.sp[-2].toObject(); HandleValue value = REGS.stackHandleAt(-1); @@ -3043,7 +3045,8 @@ CASE(JSOP_FUNCALL) case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: - ForcedReturn(cx, REGS); + if (!ForcedReturn(cx, REGS)) + goto error; goto successful_return_continuation; case JSTRAP_THROW: case JSTRAP_ERROR: @@ -3068,17 +3071,17 @@ CASE(JSOP_GIMPLICITTHIS) { JSOp op = JSOp(*REGS.pc); if (op == JSOP_IMPLICITTHIS || script->hasPollutedGlobalScope()) { - RootedPropertyName &name = rootName0; + RootedPropertyName& name = rootName0; name = script->getName(REGS.pc); - RootedObject &scopeObj = rootObject0; + RootedObject& scopeObj = rootObject0; scopeObj = REGS.fp()->scopeChain(); - RootedObject &scope = rootObject1; + RootedObject& scope = rootObject1; if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope)) goto error; - RootedValue &v = rootValue0; + RootedValue& v = rootValue0; if (!ComputeImplicitThis(cx, scope, &v)) goto error; PUSH_COPY(v); @@ -3108,7 +3111,7 @@ END_CASE(JSOP_GETNAME) CASE(JSOP_GETINTRINSIC) { - RootedValue &rval = rootValue0; + RootedValue& rval = rootValue0; if (!GetIntrinsicOperation(cx, REGS.pc, &rval)) goto error; @@ -3463,6 +3466,7 @@ CASE(JSOP_LAMBDA_ARROW) REGS.sp[-1].setObject(*obj); } END_CASE(JSOP_LAMBDA_ARROW) + CASE(JSOP_CALLEE) MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame()); PUSH_COPY(REGS.fp()->calleev()); @@ -3514,41 +3518,25 @@ CASE(JSOP_NEWINIT) { uint8_t i = GET_UINT8(REGS.pc); MOZ_ASSERT(i == JSProto_Array || i == JSProto_Object); - RootedObject& obj = rootObject0; - if (i == JSProto_Array) { - NewObjectKind newKind = GenericObject; - if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) - newKind = SingletonObject; - obj = NewDenseEmptyArray(cx, NullPtr(), newKind); - if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, - newKind == SingletonObject)) - { - goto error; - } - } else { + JSObject* obj; + if (i == JSProto_Array) + obj = NewArrayOperation(cx, script, REGS.pc, 0); + else obj = NewObjectOperation(cx, script, REGS.pc); - if (!obj) - goto error; - } + + if (!obj) + goto error; PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWINIT) CASE(JSOP_NEWARRAY) +CASE(JSOP_SPREADCALLARRAY) { - unsigned count = GET_UINT24(REGS.pc); - RootedObject& obj = rootObject0; - NewObjectKind newKind = GenericObject; - if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) - newKind = SingletonObject; - obj = NewDenseFullyAllocatedArray(cx, count, NullPtr(), newKind); - if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, - newKind == SingletonObject)) - { + JSObject* obj = NewArrayOperation(cx, script, REGS.pc, GET_UINT24(REGS.pc)); + if (!obj) goto error; - } - PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWARRAY) @@ -3571,7 +3559,7 @@ END_CASE(JSOP_NEWARRAY_COPYONWRITE) CASE(JSOP_NEWOBJECT) { - JSObject *obj = NewObjectOperation(cx, script, REGS.pc); + JSObject* obj = NewObjectOperation(cx, script, REGS.pc); if (!obj) goto error; PUSH_OBJECT(*obj); @@ -3612,7 +3600,7 @@ CASE(JSOP_INITHIDDENPROP) rval = REGS.sp[-1]; /* Load the object being initialized into lval/obj. */ - RootedObject &obj = rootObject0; + RootedObject& obj = rootObject0; obj = ®S.sp[-2].toObject(); PropertyName* name = script->getName(REGS.pc); @@ -3651,8 +3639,6 @@ CASE(JSOP_INITELEM_ARRAY) RootedObject& obj = rootObject0; obj = ®S.sp[-2].toObject(); - MOZ_ASSERT(obj->is()); - uint32_t index = GET_UINT24(REGS.pc); if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val)) goto error; @@ -3769,7 +3755,8 @@ CASE(JSOP_DEBUGGER) case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: - ForcedReturn(cx, REGS); + if (!ForcedReturn(cx, REGS)) + goto error; goto successful_return_continuation; case JSTRAP_THROW: goto error; @@ -3793,13 +3780,15 @@ CASE(JSOP_POPBLOCKSCOPE) { #ifdef DEBUG // Pop block from scope chain. - MOZ_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK); - NestedScopeObject* scope = script->getStaticBlockScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH); + NestedScopeObject* scope = script->getStaticBlockScope(REGS.pc); MOZ_ASSERT(scope && scope->is()); StaticBlockObject& blockObj = scope->as(); MOZ_ASSERT(blockObj.needsClone()); #endif + if (MOZ_UNLIKELY(cx->compartment()->isDebuggee())) + DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc); + // Pop block from scope chain. REGS.fp()->popBlock(cx); } @@ -3809,6 +3798,7 @@ CASE(JSOP_DEBUGLEAVEBLOCK) { MOZ_ASSERT(script->getStaticBlockScope(REGS.pc)); MOZ_ASSERT(script->getStaticBlockScope(REGS.pc)->is()); + MOZ_ASSERT(!script->getStaticBlockScope(REGS.pc)->as().needsClone()); // FIXME: This opcode should not be necessary. The debugger shouldn't need // help from bytecode to do its job. See bug 927782. @@ -3820,6 +3810,9 @@ END_CASE(JSOP_DEBUGLEAVEBLOCK) CASE(JSOP_FRESHENBLOCKSCOPE) { + if (MOZ_UNLIKELY(cx->compartment()->isDebuggee())) + DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc); + if (!REGS.fp()->freshenBlock(cx)) goto error; } @@ -3829,7 +3822,7 @@ CASE(JSOP_GENERATOR) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.stackDepth() == 0); - JSObject *obj = GeneratorObject::create(cx, REGS.fp()); + JSObject* obj = GeneratorObject::create(cx, REGS.fp()); if (!obj) goto error; PUSH_OBJECT(*obj); @@ -3919,11 +3912,11 @@ END_CASE(JSOP_ARRAYPUSH) CASE(JSOP_CLASSHERITAGE) { - RootedValue &val = rootValue0; + RootedValue& val = rootValue0; val = REGS.sp[-1]; - RootedValue &objProto = rootValue1; - RootedObject &funcProto = rootObject0; + RootedValue& objProto = rootValue1; + RootedObject& funcProto = rootObject0; if (val.isNull()) { objProto.setNull(); if (!GetBuiltinPrototype(cx, JSProto_Function, &funcProto)) @@ -3952,14 +3945,14 @@ END_CASE(JSOP_CLASSHERITAGE) CASE(JSOP_FUNWITHPROTO) { - RootedObject &proto = rootObject1; + RootedObject& proto = rootObject1; proto = ®S.sp[-1].toObject(); /* Load the specified function object literal. */ - RootedFunction &fun = rootFunction0; + RootedFunction& fun = rootFunction0; fun = script->getFunction(GET_UINT32_INDEX(REGS.pc)); - JSObject *obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->scopeChain(), + JSObject* obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->scopeChain(), proto, GenericObject); if (!obj) goto error; @@ -3970,10 +3963,10 @@ END_CASE(JSOP_FUNWITHPROTO) CASE(JSOP_OBJWITHPROTO) { - RootedObject &proto = rootObject0; + RootedObject& proto = rootObject0; proto = REGS.sp[-1].toObjectOrNull(); - JSObject *obj = NewObjectWithGivenProto(cx, proto); + JSObject* obj = NewObjectWithGivenProto(cx, proto); if (!obj) goto error; @@ -3983,7 +3976,8 @@ END_CASE(JSOP_OBJWITHPROTO) CASE(JSOP_INITHOMEOBJECT) { - MOZ_ASSERT(REGS.stackDepth() >= 2); + unsigned skipOver = GET_UINT8(REGS.pc); + MOZ_ASSERT(REGS.stackDepth() >= skipOver + 2); /* Load the function to be initialized */ RootedFunction& func = rootFunction0; @@ -3992,7 +3986,7 @@ CASE(JSOP_INITHOMEOBJECT) /* Load the home object */ RootedNativeObject& obj = rootNativeObject0; - obj = ®S.sp[-2].toObject().as(); + obj = ®S.sp[int(-2 - skipOver)].toObject().as(); MOZ_ASSERT(obj->is() || obj->is()); func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj)); @@ -4353,10 +4347,10 @@ js::DeletePropertyJit(JSContext* cx, HandleValue v, HandlePropertyName name, boo return true; } -template bool js::DeletePropertyJit (JSContext *cx, HandleValue val, HandlePropertyName name, - bool *bp); -template bool js::DeletePropertyJit(JSContext *cx, HandleValue val, HandlePropertyName name, - bool *bp); +template bool js::DeletePropertyJit (JSContext* cx, HandleValue val, HandlePropertyName name, + bool* bp); +template bool js::DeletePropertyJit(JSContext* cx, HandleValue val, HandlePropertyName name, + bool* bp); template bool @@ -4647,8 +4641,8 @@ js::SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, Hand return true; } -JSObject * -js::NewObjectOperation(JSContext *cx, HandleScript script, jsbytecode *pc, +JSObject* +js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, NewObjectKind newKind /* = GenericObject */) { MOZ_ASSERT(newKind != SingletonObject); @@ -4690,7 +4684,7 @@ js::NewObjectOperation(JSContext *cx, HandleScript script, jsbytecode *pc, } else { obj->setGroup(group); - if (PreliminaryObjectArray *preliminaryObjects = group->maybePreliminaryObjects()) + if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) preliminaryObjects->registerNewObject(obj); } @@ -4698,7 +4692,7 @@ js::NewObjectOperation(JSContext *cx, HandleScript script, jsbytecode *pc, } JSObject* -js::NewObjectOperationWithTemplate(JSContext *cx, HandleObject templateObject) +js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject) { // This is an optimized version of NewObjectOperation for use when the // object is not a singleton and has had its preliminary objects analyzed, @@ -4709,13 +4703,10 @@ js::NewObjectOperationWithTemplate(JSContext *cx, HandleObject templateObject) if (templateObject->group()->maybeUnboxedLayout()) { RootedObjectGroup group(cx, templateObject->group()); - JSObject *obj = UnboxedPlainObject::create(cx, group, newKind); - if (!obj) - return nullptr; - return obj; + return UnboxedPlainObject::create(cx, group, newKind); } - JSObject *obj = CopyInitializerObject(cx, templateObject.as(), newKind); + JSObject* obj = CopyInitializerObject(cx, templateObject.as(), newKind); if (!obj) return nullptr; @@ -4723,6 +4714,68 @@ js::NewObjectOperationWithTemplate(JSContext *cx, HandleObject templateObject) return obj; } +JSObject* +js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32_t length, + NewObjectKind newKind /* = GenericObject */) +{ + MOZ_ASSERT(newKind != SingletonObject); + + RootedObjectGroup group(cx); + if (ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) { + newKind = SingletonObject; + } else { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); + if (!group) + return nullptr; + if (group->maybePreliminaryObjects()) + group->maybePreliminaryObjects()->maybeAnalyze(cx, group); + + if (group->shouldPreTenure() || group->maybePreliminaryObjects()) + newKind = TenuredObject; + + if (group->maybeUnboxedLayout()) + return UnboxedArrayObject::create(cx, group, length, newKind); + } + + ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, NullPtr(), newKind); + if (!obj) + return nullptr; + + if (newKind == SingletonObject) { + MOZ_ASSERT(obj->isSingleton()); + } else { + obj->setGroup(group); + + if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) + preliminaryObjects->registerNewObject(obj); + } + + return obj; +} + +JSObject* +js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject) +{ + MOZ_ASSERT(!templateObject->isSingleton()); + + NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject; + + if (templateObject->is()) { + uint32_t length = templateObject->as().length(); + RootedObjectGroup group(cx, templateObject->group()); + return UnboxedArrayObject::create(cx, group, length, newKind); + } + + ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as().length(), + NullPtr(), newKind); + if (!obj) + return nullptr; + + MOZ_ASSERT(obj->lastProperty() == templateObject->as().lastProperty()); + obj->setGroup(templateObject->group()); + return obj; +} + void js::ReportUninitializedLexical(JSContext* cx, HandlePropertyName name) { diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index b35e523343..4b95d2a8dc 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -258,33 +258,76 @@ UnwindAllScopesInFrame(JSContext* cx, ScopeIter& si); extern jsbytecode* UnwindScopeToTryPc(JSScript* script, JSTryNote* tn); -/* - * Unwind for an uncatchable exception. This means not running finalizers, etc; - * just preserving the basic engine stack invariants. - */ -extern void -UnwindForUncatchableException(JSContext* cx, const InterpreterRegs& regs); - extern bool OnUnknownMethod(JSContext* cx, HandleObject obj, Value idval, MutableHandleValue vp); -class TryNoteIter +template +class MOZ_STACK_CLASS TryNoteIter { - const InterpreterRegs& regs; - RootedScript script; /* TryNotIter is always stack allocated. */ - uint32_t pcOffset; - JSTryNote* tn; - JSTryNote* tnEnd; + RootedScript script_; + uint32_t pcOffset_; + JSTryNote* tn_; + JSTryNote* tnEnd_; + StackDepthOp getStackDepth_; - void settle(); + void settle() { + for (; tn_ != tnEnd_; ++tn_) { + /* If pc is out of range, try the next one. */ + if (pcOffset_ - tn_->start >= tn_->length) + continue; + + /* + * We have a note that covers the exception pc but we must check + * whether the interpreter has already executed the corresponding + * handler. This is possible when the executed bytecode implements + * break or return from inside a for-in loop. + * + * In this case the emitter generates additional [enditer] and [gosub] + * opcodes to close all outstanding iterators and execute the finally + * blocks. If such an [enditer] throws an exception, its pc can still + * be inside several nested for-in loops and try-finally statements + * even if we have already closed the corresponding iterators and + * invoked the finally blocks. + * + * To address this, we make [enditer] always decrease the stack even + * when its implementation throws an exception. Thus already executed + * [enditer] and [gosub] opcodes will have try notes with the stack + * depth exceeding the current one and this condition is what we use to + * filter them out. + */ + if (tn_->stackDepth <= getStackDepth_()) + break; + } + } public: - explicit TryNoteIter(JSContext* cx, const InterpreterRegs& regs); - bool done() const; - void operator++(); - JSTryNote* operator*() const { return tn; } + TryNoteIter(JSContext* cx, JSScript* script, jsbytecode* pc, + StackDepthOp getStackDepth) + : script_(cx, script), + pcOffset_(pc - script->main()), + getStackDepth_(getStackDepth) + { + if (script->hasTrynotes()) { + tn_ = script->trynotes()->vector; + tnEnd_ = tn_ + script->trynotes()->length; + } else { + tn_ = tnEnd_ = nullptr; + } + settle(); + } + + void operator++() { + ++tn_; + settle(); + } + + bool done() const { return tn_ == tnEnd_; } + JSTryNote* operator*() const { return tn_; } }; +bool +HandleClosingGeneratorReturn(JSContext *cx, AbstractFramePtr frame, bool ok); + /************************************************************************/ bool @@ -398,12 +441,19 @@ bool SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv, HandleValue callee, HandleValue arr, MutableHandleValue res); -JSObject * -NewObjectOperation(JSContext *cx, HandleScript script, jsbytecode *pc, +JSObject* +NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, NewObjectKind newKind = GenericObject); -JSObject * -NewObjectOperationWithTemplate(JSContext *cx, HandleObject templateObject); +JSObject* +NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject); + +JSObject* +NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32_t length, + NewObjectKind newKind = GenericObject); + +JSObject* +NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject); inline bool SetConstOperation(JSContext* cx, HandleObject varobj, HandlePropertyName name, HandleValue rval) diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index f893c3617e..6e6f952ddd 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -414,6 +414,11 @@ CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleS if (!resolved) return true; + // Assert the mayResolve hook, if there is one, returns true for this + // property. + MOZ_ASSERT_IF(obj->getClass()->mayResolve, + obj->getClass()->mayResolve(cx->names(), id, obj)); + if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) { MarkDenseOrTypedArrayElementFound(propp); return true; @@ -425,6 +430,28 @@ CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleS return true; } +static MOZ_ALWAYS_INLINE bool +ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj) +{ + MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp); + + if (!clasp->resolve) { + // Sanity check: we should only have a mayResolve hook if we have a + // resolve hook. + MOZ_ASSERT(!clasp->mayResolve, "Class with mayResolve hook but no resolve hook"); + return false; + } + + if (clasp->mayResolve) { + // Tell the analysis our mayResolve hooks won't trigger GC. + JS::AutoSuppressGCAnalysis nogc; + if (!clasp->mayResolve(names, id, maybeObj)) + return false; + } + + return true; +} + template static MOZ_ALWAYS_INLINE bool LookupOwnPropertyInline(ExclusiveContext* cx, @@ -576,14 +603,6 @@ LookupPropertyInline(ExclusiveContext* cx, return true; } -inline bool -NativeLookupProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx, NameToId(name)); - return NativeLookupProperty(cx, obj, id, objp, propp); -} - inline bool WarnIfNotConstructing(JSContext* cx, const CallArgs& args, const char* builtinName) { diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 301c48bd97..aa9d0bf3e7 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -17,6 +17,7 @@ #include "jsobjinlines.h" +#include "gc/Nursery-inl.h" #include "vm/ArrayObject-inl.h" #include "vm/Shape-inl.h" @@ -24,6 +25,7 @@ using namespace js; using JS::GenericNaN; using mozilla::DebugOnly; +using mozilla::PodCopy; using mozilla::RoundUpPow2; static const ObjectElements emptyElementsHeader(0, 0); @@ -328,8 +330,11 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape) MOZ_ASSERT(shape->compartment() == compartment()); MOZ_ASSERT(shape->slotSpan() == 0); MOZ_ASSERT(shape->numFixedSlots() == 0); - MOZ_ASSERT(!hasDynamicElements()); - MOZ_ASSERT(!hasDynamicSlots()); + + if (hasDynamicElements()) + js_free(getElementsHeader()); + if (hasDynamicSlots()) + js_free(slots_); shape_ = shape; } @@ -374,31 +379,6 @@ NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span) return true; } -// This will not run the garbage collector. If a nursery cannot accomodate the slot array -// an attempt will be made to place the array in the tenured area. -static HeapSlot* -AllocateSlots(ExclusiveContext* cx, JSObject* obj, uint32_t nslots) -{ - if (cx->isJSContext()) - return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots); - return obj->zone()->pod_malloc(nslots); -} - -// This will not run the garbage collector. If a nursery cannot accomodate the slot array -// an attempt will be made to place the array in the tenured area. -// -// If this returns null then the old slots will be left alone. -static HeapSlot* -ReallocateSlots(ExclusiveContext* cx, JSObject* obj, HeapSlot* oldSlots, - uint32_t oldCount, uint32_t newCount) -{ - if (cx->isJSContext()) { - return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots, - oldCount, newCount); - } - return obj->zone()->pod_realloc(oldSlots, oldCount, newCount); -} - bool NativeObject::growSlots(ExclusiveContext* cx, uint32_t oldCount, uint32_t newCount) { @@ -414,14 +394,14 @@ NativeObject::growSlots(ExclusiveContext* cx, uint32_t oldCount, uint32_t newCou MOZ_ASSERT(newCount < NELEMENTS_LIMIT); if (!oldCount) { - slots_ = AllocateSlots(cx, this, newCount); + slots_ = AllocateObjectBuffer(cx, this, newCount); if (!slots_) return false; Debug_SetSlotRangeToCrashOnTouch(slots_, newCount); return true; } - HeapSlot* newslots = ReallocateSlots(cx, this, slots_, oldCount, newCount); + HeapSlot* newslots = ReallocateObjectBuffer(cx, this, slots_, oldCount, newCount); if (!newslots) return false; /* Leave slots at its old size. */ @@ -437,7 +417,7 @@ FreeSlots(ExclusiveContext* cx, HeapSlot* slots) { // Note: threads without a JSContext do not have access to GGC nursery allocated things. if (cx->isJSContext()) - return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots); + return cx->asJSContext()->runtime()->gc.nursery.freeBuffer(slots); js_free(slots); } @@ -454,7 +434,7 @@ NativeObject::shrinkSlots(ExclusiveContext* cx, uint32_t oldCount, uint32_t newC MOZ_ASSERT_IF(!is(), newCount >= SLOT_CAPACITY_MIN); - HeapSlot* newslots = ReallocateSlots(cx, this, slots_, oldCount, newCount); + HeapSlot* newslots = ReallocateObjectBuffer(cx, this, slots_, oldCount, newCount); if (!newslots) return; /* Leave slots at its old size. */ @@ -661,31 +641,6 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO return ED_OK; } -// This will not run the garbage collector. If a nursery cannot accomodate the element array -// an attempt will be made to place the array in the tenured area. -static ObjectElements* -AllocateElements(ExclusiveContext* cx, JSObject* obj, uint32_t nelems) -{ - if (cx->isJSContext()) - return cx->asJSContext()->runtime()->gc.nursery.allocateElements(obj, nelems); - return reinterpret_cast(obj->zone()->pod_malloc(nelems)); -} - -// This will not run the garbage collector. If a nursery cannot accomodate the element array -// an attempt will be made to place the array in the tenured area. -static ObjectElements* -ReallocateElements(ExclusiveContext* cx, JSObject* obj, ObjectElements* oldHeader, - uint32_t oldCount, uint32_t newCount) -{ - if (cx->isJSContext()) { - return cx->asJSContext()->runtime()->gc.nursery.reallocateElements(obj, oldHeader, - oldCount, newCount); - } - return reinterpret_cast( - obj->zone()->pod_realloc(reinterpret_cast(oldHeader), - oldCount, newCount)); -} - // Round up |reqAllocated| to a good size. Up to 1 Mebi (i.e. 1,048,576) the // slot count is usually a power-of-two: // @@ -710,6 +665,9 @@ ReallocateElements(ExclusiveContext* cx, JSObject* obj, ObjectElements* oldHeade // exceptional resizings will at most triple the capacity, as opposed to the // usual doubling. // +// Note: the structure and behavior of this method follow along with +// UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy +// in one should generally be matched by the other. /* static */ uint32_t NativeObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0) { @@ -814,19 +772,20 @@ NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity) uint32_t initlen = getDenseInitializedLength(); - ObjectElements* newheader; + HeapSlot* oldHeaderSlots = reinterpret_cast(getElementsHeader()); + HeapSlot* newHeaderSlots; if (hasDynamicElements()) { - newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated); - if (!newheader) + newHeaderSlots = ReallocateObjectBuffer(cx, this, oldHeaderSlots, oldAllocated, newAllocated); + if (!newHeaderSlots) return false; // Leave elements at its old size. } else { - newheader = AllocateElements(cx, this, newAllocated); - if (!newheader) + newHeaderSlots = AllocateObjectBuffer(cx, this, newAllocated); + if (!newHeaderSlots) return false; // Leave elements at its old size. - js_memcpy(newheader, getElementsHeader(), - (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value)); + PodCopy(newHeaderSlots, oldHeaderSlots, ObjectElements::VALUES_PER_HEADER + initlen); } + ObjectElements* newheader = reinterpret_cast(newHeaderSlots); newheader->capacity = newCapacity; elements_ = newheader->elements(); @@ -857,13 +816,15 @@ NativeObject::shrinkElements(ExclusiveContext* cx, uint32_t reqCapacity) MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER); uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER; - ObjectElements* newheader = ReallocateElements(cx, this, getElementsHeader(), - oldAllocated, newAllocated); - if (!newheader) { + HeapSlot* oldHeaderSlots = reinterpret_cast(getElementsHeader()); + HeapSlot* newHeaderSlots = ReallocateObjectBuffer(cx, this, oldHeaderSlots, + oldAllocated, newAllocated); + if (!newHeaderSlots) { cx->recoverFromOutOfMemory(); return; // Leave elements at its old size. } + ObjectElements* newheader = reinterpret_cast(newHeaderSlots); newheader->capacity = newCapacity; elements_ = newheader->elements(); } @@ -887,9 +848,10 @@ NativeObject::CopyElementsForWrite(ExclusiveContext* cx, NativeObject* obj) JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject()); - ObjectElements* newheader = AllocateElements(cx, obj, newAllocated); - if (!newheader) + HeapSlot* newHeaderSlots = AllocateObjectBuffer(cx, obj, newAllocated); + if (!newHeaderSlots) return false; + ObjectElements* newheader = reinterpret_cast(newHeaderSlots); js_memcpy(newheader, obj->getElementsHeader(), (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value)); @@ -1008,81 +970,32 @@ template bool js::NativeLookupOwnProperty(ExclusiveContext *cx, NativeObject *obj, jsid id, FakeMutableHandle propp); -template -bool -js::NativeLookupProperty(ExclusiveContext *cx, - typename MaybeRooted::HandleType obj, - typename MaybeRooted::HandleType id, - typename MaybeRooted::MutableHandleType objp, - typename MaybeRooted::MutableHandleType propp) -{ - return LookupPropertyInline(cx, obj, id, objp, propp); -} - -template bool -js::NativeLookupProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, - MutableHandleObject objp, MutableHandleShape propp); - -template bool -js::NativeLookupProperty(ExclusiveContext *cx, NativeObject *obj, jsid id, - FakeMutableHandle objp, - FakeMutableHandle propp); - -bool -js::NativeLookupElement(JSContext *cx, HandleNativeObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp) -{ - RootedId id(cx); - if (!IndexToId(cx, index, &id)) - return false; - - return LookupPropertyInline(cx, obj, id, objp, propp); -} - - /*** [[DefineOwnProperty]] ***********************************************************************/ -/* - * Backward compatibility requires allowing addProperty hooks to mutate the - * nominal initial value of a slotful property, while GC safety wants that - * value to be stored before the call-out through the hook. Optimize to do - * both while saving cycles for classes that stub their addProperty hook. - */ static inline bool -CallAddPropertyHook(ExclusiveContext *cx, HandleNativeObject obj, HandleShape shape, - HandleValue nominal) +CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape, + HandleValue value) { if (JSAddPropertyOp addProperty = obj->getClass()->addProperty) { - MOZ_ASSERT(addProperty != JS_PropertyStub); - if (!cx->shouldBeJSContext()) return false; - // Make a local copy of value so addProperty can mutate its inout parameter. - RootedValue value(cx, nominal); - - // Use CallJSGetterOp, since JSGetterOp and JSAddPropertyOp happen to - // have all the same argument types. - Rooted id(cx, shape->propid()); - if (!CallJSGetterOp(cx->asJSContext(), addProperty, obj, id, &value)) { + RootedId id(cx, shape->propid()); + if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) { obj->removeProperty(cx, shape->propid()); return false; } - if (value.get() != nominal) { - if (shape->hasSlot()) - obj->setSlotWithType(cx, shape, value); - } } return true; } static inline bool -CallAddPropertyHookDense(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index, - HandleValue nominal) +CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t index, + HandleValue value) { // Inline addProperty for array objects. if (obj->is()) { - ArrayObject *arr = &obj->as(); + ArrayObject* arr = &obj->as(); uint32_t length = arr->length(); if (index >= length) arr->setLength(cx, index + 1); @@ -1090,28 +1003,18 @@ CallAddPropertyHookDense(ExclusiveContext *cx, HandleNativeObject obj, uint32_t } if (JSAddPropertyOp addProperty = obj->getClass()->addProperty) { - MOZ_ASSERT(addProperty != JS_PropertyStub); - if (!cx->shouldBeJSContext()) return false; if (!obj->maybeCopyElementsForWrite(cx)) return false; - // Make a local copy of value so addProperty can mutate its inout parameter. - RootedValue value(cx, nominal); - - // Use CallJSGetterOp, since JSGetterOp and JSAddPropertyOp happen to - // have all the same argument types. - Rooted id(cx, INT_TO_JSID(index)); - if (!CallJSGetterOp(cx->asJSContext(), addProperty, obj, id, &value)) { + RootedId id(cx, INT_TO_JSID(index)); + if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) { obj->setDenseElementHole(cx, index); return false; } - if (value.get() != nominal) - obj->setDenseElementWithType(cx, index, value); } - return true; } @@ -1137,110 +1040,6 @@ UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, c return true; } -static bool -NativeSetExistingDataProperty(JSContext *cx, HandleNativeObject obj, HandleShape shape, - HandleValue v, HandleValue receiver, ObjectOpResult &result); - -static inline bool -DefinePropertyOrElement(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, - GetterOp getter, SetterOp setter, - unsigned attrs, HandleValue value, - bool callSetterAfterwards, ObjectOpResult &result) -{ - MOZ_ASSERT(getter != JS_PropertyStub); - MOZ_ASSERT(setter != JS_StrictPropertyStub); - - /* Use dense storage for new indexed properties where possible. */ - if (JSID_IS_INT(id) && - !getter && - !setter && - attrs == JSPROP_ENUMERATE && - (!obj->isIndexed() || !obj->containsPure(id)) && - !IsAnyTypedArray(obj)) - { - uint32_t index = JSID_TO_INT(id); - if (WouldDefinePastNonwritableLength(obj, index)) - return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); - - NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1); - if (edResult == NativeObject::ED_FAILED) - return false; - if (edResult == NativeObject::ED_OK) { - obj->setDenseElementWithType(cx, index, value); - if (!CallAddPropertyHookDense(cx, obj, index, value)) - return false; - return result.succeed(); - } - } - - if (obj->is()) { - Rooted arr(cx, &obj->as()); - if (id == NameToId(cx->names().length)) { - if (!cx->shouldBeJSContext()) - return false; - return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, result); - } - - uint32_t index; - if (IdIsIndex(id, &index)) { - if (WouldDefinePastNonwritableLength(obj, index)) - return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); - } - } - - // Don't define new indexed properties on typed arrays. - if (IsAnyTypedArray(obj)) { - uint64_t index; - if (IsTypedArrayIndex(id, &index)) - return result.succeed(); - } - - AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); - RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, getter, setter, - SHAPE_INVALID_SLOT, attrs, 0)); - if (!shape) - return false; - - if (!UpdateShapeTypeAndValue(cx, obj, shape, value)) - return false; - - /* - * Clear any existing dense index after adding a sparse indexed property, - * and investigate converting the object to dense indexes. - */ - if (JSID_IS_INT(id)) { - if (!obj->maybeCopyElementsForWrite(cx)) - return false; - - uint32_t index = JSID_TO_INT(id); - NativeObject::removeDenseElementForSparseIndex(cx, obj, index); - NativeObject::EnsureDenseResult edResult = NativeObject::maybeDensifySparseElements(cx, obj); - if (edResult == NativeObject::ED_FAILED) - return false; - if (edResult == NativeObject::ED_OK) { - MOZ_ASSERT(!setter); - if (!CallAddPropertyHookDense(cx, obj, index, value)) - return false; - return result.succeed(); - } - } - - if (!CallAddPropertyHook(cx, obj, shape, value)) - return false; - - if (callSetterAfterwards && setter) { - MOZ_ASSERT(!(attrs & JSPROP_GETTER)); - MOZ_ASSERT(!(attrs & JSPROP_SETTER)); - if (!cx->shouldBeJSContext()) - return false; - RootedValue receiver(cx, ObjectValue(*obj)); - return NativeSetExistingDataProperty(cx->asJSContext(), obj, shape, value, receiver, - result); - } - - return result.succeed(); -} - static unsigned ApplyOrDefaultAttributes(unsigned attrs, const Shape* shape = nullptr) { @@ -1318,102 +1117,152 @@ PurgeScopeChain(ExclusiveContext* cx, HandleObject obj, HandleId id) return true; } -/* - * Check whether we're redefining away a non-configurable getter, and - * throw if so. - */ -static inline bool -CheckAccessorRedefinition(ExclusiveContext *cx, HandleObject obj, HandleShape shape, - GetterOp getter, SetterOp setter, HandleId id, unsigned attrs) +static bool +AddOrChangeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, + Handle desc) { - MOZ_ASSERT(shape->isAccessorDescriptor()); - if (shape->configurable() || (getter == shape->getter() && setter == shape->setter())) - return true; + desc.assertComplete(); - /* - * Only allow redefining if JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ - * the object is a non-DOM global. The idea is that a DOM object can - * never have such a thing on its proto chain directly on the web, so we - * should be OK optimizing access to accessors found on such an object. - */ - if ((attrs & JSPROP_REDEFINE_NONCONFIGURABLE) && - obj->is() && - !obj->getClass()->isDOMClass()) - { - return true; - } - - if (!cx->isJSContext()) + if (!PurgeScopeChain(cx, obj, id)) return false; - return Throw(cx->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP); + // Use dense storage for new indexed properties where possible. + if (JSID_IS_INT(id) && + !desc.getter() && + !desc.setter() && + desc.attributes() == JSPROP_ENUMERATE && + (!obj->isIndexed() || !obj->containsPure(id)) && + !IsAnyTypedArray(obj)) + { + uint32_t index = JSID_TO_INT(id); + NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1); + if (edResult == NativeObject::ED_FAILED) + return false; + if (edResult == NativeObject::ED_OK) { + obj->setDenseElementWithType(cx, index, desc.value()); + if (!CallAddPropertyHookDense(cx, obj, index, desc.value())) + return false; + return true; + } + } + + RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(), + SHAPE_INVALID_SLOT, desc.attributes(), 0)); + if (!shape) + return false; + + if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value())) + return false; + + // Clear any existing dense index after adding a sparse indexed property, + // and investigate converting the object to dense indexes. + if (JSID_IS_INT(id)) { + if (!obj->maybeCopyElementsForWrite(cx)) + return false; + + uint32_t index = JSID_TO_INT(id); + NativeObject::removeDenseElementForSparseIndex(cx, obj, index); + NativeObject::EnsureDenseResult edResult = + NativeObject::maybeDensifySparseElements(cx, obj); + if (edResult == NativeObject::ED_FAILED) + return false; + if (edResult == NativeObject::ED_OK) { + MOZ_ASSERT(!desc.setter()); + return CallAddPropertyHookDense(cx, obj, index, desc.value()); + } + } + + return CallAddPropertyHook(cx, obj, shape, desc.value()); +} + +static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; } +static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; } +static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; } + +static bool IsAccessorDescriptor(unsigned attrs) { + return (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; +} + +static bool IsDataDescriptor(unsigned attrs) { + MOZ_ASSERT((attrs & (JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY)) == 0); + return !IsAccessorDescriptor(attrs); +} + +template +static MOZ_ALWAYS_INLINE bool +GetExistingProperty(JSContext* cx, + typename MaybeRooted::HandleType receiver, + typename MaybeRooted::HandleType obj, + typename MaybeRooted::HandleType shape, + typename MaybeRooted::MutableHandleType vp); + +static bool +GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, + HandleShape shape, MutableHandleValue vp) +{ + if (IsImplicitDenseOrTypedArrayElement(shape)) { + vp.set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id))); + return true; + } + if (!cx->shouldBeJSContext()) + return false; + return GetExistingProperty(cx->asJSContext(), obj, obj, shape, vp); } bool js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, - Handle desc, + Handle desc_, ObjectOpResult& result) { - desc.assertValid(); + desc_.assertValid(); - GetterOp getter = desc.getter(); - SetterOp setter = desc.setter(); - unsigned attrs = desc.attributes(); + // Section numbers and step numbers below refer to ES6 draft rev 36 + // (17 March 2015). + // + // This function aims to implement 9.1.6 [[DefineOwnProperty]] as well as + // the [[DefineOwnProperty]] methods described in 9.4.2.1 (arrays), 9.4.4.2 + // (arguments), and 9.4.5.3 (typed array views). - MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS)); - - AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); - - RootedShape shape(cx); - RootedValue updateValue(cx, desc.value()); - bool shouldDefine = true; - - /* - * If defining a getter or setter, we must check for its counterpart and - * update the attributes and property ops. A getter or setter is really - * only half of a property. - */ - if (desc.isAccessorDescriptor()) { - if (!NativeLookupOwnProperty(cx, obj, id, &shape)) - return false; - if (shape) { - /* - * If we are defining a getter whose setter was already defined, or - * vice versa, finish the job via obj->changeProperty. - */ - if (IsImplicitDenseOrTypedArrayElement(shape)) { - if (IsAnyTypedArray(obj)) { - /* Ignore getter/setter properties added to typed arrays. */ - return result.succeed(); - } - if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id))) - return false; - shape = obj->lookup(cx, id); - } - if (shape->isAccessorDescriptor()) { - if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs)) - return false; - attrs = ApplyOrDefaultAttributes(attrs, shape); - shape = NativeObject::changeProperty(cx, obj, shape, - attrs | JSPROP_GETTER | JSPROP_SETTER, - (attrs & JSPROP_GETTER) - ? getter - : shape->getter(), - (attrs & JSPROP_SETTER) - ? setter - : shape->setter()); - if (!shape) - return false; - shouldDefine = false; - } + // Dispense with custom behavior of exotic native objects first. + if (obj->is()) { + // 9.4.2.1 step 2. Redefining an array's length is very special. + Rooted arr(cx, &obj->as()); + if (id == NameToId(cx->names().length)) { + if (!cx->shouldBeJSContext()) + return false; + return ArraySetLength(cx->asJSContext(), arr, id, desc_.attributes(), desc_.value(), + result); } - // Either we are converting a data property to an accessor property, or - // creating a new accessor property; either way [[Get]] and [[Set]] - // must both be filled in. - if (shouldDefine) - attrs |= JSPROP_GETTER | JSPROP_SETTER; - } else if (desc.hasValue()) { + // 9.4.2.1 step 3. Don't extend a fixed-length array. + uint32_t index; + if (IdIsIndex(id, &index)) { + if (WouldDefinePastNonwritableLength(obj, index)) + return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); + } + } else if (IsAnyTypedArray(obj)) { + // 9.4.5.3 step 3. Indexed properties of typed arrays are special. + uint64_t index; + if (IsTypedArrayIndex(id, &index)) { + if (!cx->shouldBeJSContext()) + return false; + return DefineTypedArrayElement(cx->asJSContext(), obj, index, desc_, result); + } + } else if (obj->is()) { + if (id == NameToId(cx->names().length)) { + // Either we are resolving the .length property on this object, or + // redefining it. In the latter case only, we must set a bit. To + // distinguish the two cases, we note that when resolving, the + // property won't already exist; whereas the first time it is + // redefined, it will. + if (obj->containsPure(id)) + obj->as().markLengthOverridden(); + } + } + + // 9.1.6.1 OrdinaryDefineOwnProperty steps 1-2. + RootedShape shape(cx); + if (desc_.hasValue()) { // If we did a normal lookup here, it would cause resolve hook recursion in // the following case. Suppose the first script we run in a lazy global is // |parseInt()|. @@ -1427,118 +1276,200 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId // // Therefore we do a special lookup that does not call the resolve hook. NativeLookupOwnPropertyNoResolve(cx, obj, id, &shape); - - if (shape) { - // If any other JSPROP_IGNORE_* attributes are present, copy the - // corresponding JSPROP_* attributes from the existing property. - if (IsImplicitDenseOrTypedArrayElement(shape)) { - attrs = ApplyAttributes(attrs, true, true, !IsAnyTypedArray(obj)); - } else { - attrs = ApplyOrDefaultAttributes(attrs, shape); - - // Do not redefine a nonconfigurable accessor property. - if (shape->isAccessorDescriptor()) { - if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs)) - return false; - } - } - } } else { - // We have been asked merely to update JSPROP_PERMANENT and/or JSPROP_ENUMERATE. if (!NativeLookupOwnProperty(cx, obj, id, &shape)) return false; + } - if (shape) { - // Don't forget about arrays. + // From this point, the step numbers refer to + // 9.1.6.3, ValidateAndApplyPropertyDescriptor. + // Step 1 is a redundant assertion. + + // Filling in desc: Here we make a copy of the desc_ argument. We will turn + // it into a complete descriptor before updating obj. The spec algorithm + // does not explicitly do this, but the end result is the same. Search for + // "fill in" below for places where the filling-in actually occurs. + Rooted desc(cx, desc_); + + // Step 2. + if (!shape) { + if (!obj->nonProxyIsExtensible()) + return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); + + // Fill in missing desc fields with defaults. + CompletePropertyDescriptor(&desc); + + if (!AddOrChangeProperty(cx, obj, id, desc)) + return false; + return result.succeed(); + } + + MOZ_ASSERT(shape); + + // Non-standard hack: Allow redefining non-configurable properties if + // JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM + // global. The idea is that a DOM object can never have such a thing on + // its proto chain directly on the web, so we should be OK optimizing + // access to accessors found on such an object. Bug 1105518 contemplates + // removing this hack. + bool skipRedefineChecks = (desc.attributes() & JSPROP_REDEFINE_NONCONFIGURABLE) && + obj->is() && + !obj->getClass()->isDOMClass(); + + // Steps 3-4 are redundant. + + // Step 5. We use shapeAttrs as a stand-in for shape in many places below + // since shape might not be a pointer to a real Shape (see + // IsImplicitDenseOrTypedArrayElement). + unsigned shapeAttrs = GetShapeAttributes(obj, shape); + if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks) { + if (desc.hasConfigurable() && desc.configurable()) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + if (desc.hasEnumerable() && desc.enumerable() != IsEnumerable(shapeAttrs)) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + } + + // Fill in desc.[[Configurable]] and desc.[[Enumerable]] if missing. + if (!desc.hasConfigurable()) + desc.setConfigurable(IsConfigurable(shapeAttrs)); + if (!desc.hasEnumerable()) + desc.setEnumerable(IsEnumerable(shapeAttrs)); + + // Steps 6-9. + if (desc.isGenericDescriptor()) { + // Step 6. No further validation is required. + + // Fill in desc. A generic descriptor has none of these fields, so copy + // everything from shape. + MOZ_ASSERT(!desc.hasValue()); + MOZ_ASSERT(!desc.hasWritable()); + MOZ_ASSERT(!desc.hasGetterObject()); + MOZ_ASSERT(!desc.hasSetterObject()); + if (IsDataDescriptor(shapeAttrs)) { + RootedValue currentValue(cx); + if (!GetExistingPropertyValue(cx, obj, id, shape, ¤tValue)) + return false; + desc.setValue(currentValue); + desc.setWritable(IsWritable(shapeAttrs)); + } else { + desc.setGetterObject(shape->getterObject()); + desc.setSetterObject(shape->setterObject()); + } + } else if (desc.isDataDescriptor() != IsDataDescriptor(shapeAttrs)) { + // Step 7. + if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + + if (IsImplicitDenseOrTypedArrayElement(shape)) { + MOZ_ASSERT(!IsAnyTypedArray(obj)); + if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id))) + return false; + shape = obj->lookup(cx, id); + } + + // Fill in desc fields with default values (steps 7.b.i and 7.c.i). + CompletePropertyDescriptor(&desc); + } else if (desc.isDataDescriptor()) { + // Step 8. + bool frozen = !IsConfigurable(shapeAttrs) && !IsWritable(shapeAttrs); + if (frozen && desc.hasWritable() && desc.writable() && !skipRedefineChecks) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + + if (frozen || !desc.hasValue()) { if (IsImplicitDenseOrTypedArrayElement(shape)) { - if (IsAnyTypedArray(obj)) { - /* - * Silently ignore attempts to change individual index attributes. - * FIXME: Uses the same broken behavior as for accessors. This should - * fail. - */ - return result.succeed(); - } + MOZ_ASSERT(!IsAnyTypedArray(obj)); if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id))) return false; shape = obj->lookup(cx, id); } - if (shape->isAccessorDescriptor() && - !CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs)) - { + RootedValue currentValue(cx); + if (!GetExistingPropertyValue(cx, obj, id, shape, ¤tValue)) return false; - } - attrs = ApplyOrDefaultAttributes(attrs, shape); - - if (shape->isAccessorDescriptor() && !(attrs & JSPROP_IGNORE_READONLY)) { - // ES6 draft 2014-10-14 9.1.6.3 step 7.c: Since [[Writable]] - // is present, change the existing accessor property to a data - // property. - updateValue = UndefinedValue(); + if (!desc.hasValue()) { + // Fill in desc.[[Value]]. + desc.setValue(currentValue); } else { - // We are at most changing some attributes, and cannot convert - // from data descriptor to accessor, or vice versa. Take - // everything from the shape that we aren't changing. - uint32_t propMask = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT; - attrs = (shape->attributes() & ~propMask) | (attrs & propMask); - getter = shape->getter(); - setter = shape->setter(); - if (shape->hasSlot()) - updateValue = obj->getSlot(shape->slot()); + // Step 8.a.ii.1. + bool same; + if (!cx->shouldBeJSContext()) + return false; + if (!SameValue(cx->asJSContext(), desc.value(), currentValue, &same)) + return false; + if (!same && !skipRedefineChecks) + return result.fail(JSMSG_CANT_REDEFINE_PROP); } } + + if (!desc.hasWritable()) + desc.setWritable(IsWritable(shapeAttrs)); + } else { + // Step 9. + MOZ_ASSERT(shape->isAccessorDescriptor()); + MOZ_ASSERT(desc.isAccessorDescriptor()); + + // The spec says to use SameValue, but since the values in + // question are objects, we can just compare pointers. + if (desc.hasSetterObject()) { + if (!IsConfigurable(shapeAttrs) && + desc.setterObject() != shape->setterObject() && + !skipRedefineChecks) + { + return result.fail(JSMSG_CANT_REDEFINE_PROP); + } + } else { + // Fill in desc.[[Set]] from shape. + desc.setSetterObject(shape->setterObject()); + } + if (desc.hasGetterObject()) { + if (!IsConfigurable(shapeAttrs) && + desc.getterObject() != shape->getterObject() && + !skipRedefineChecks) + { + return result.fail(JSMSG_CANT_REDEFINE_PROP); + } + } else { + // Fill in desc.[[Get]] from shape. + desc.setGetterObject(shape->getterObject()); + } } - /* - * Purge the property cache of any properties named by id that are about - * to be shadowed in obj's scope chain. - */ - if (!PurgeScopeChain(cx, obj, id)) - return false; + // Dispense with any remaining JSPROP_IGNORE_* attributes. Any bits that + // needed to be copied from an existing property have been copied by + // now. Since we can never get here with JSPROP_IGNORE_VALUE relevant, just + // clear it. + desc.setAttributes(ApplyOrDefaultAttributes(desc.attributes()) & ~JSPROP_IGNORE_VALUE); - if (shouldDefine) { - // Handle the default cases here. Anyone that wanted to set non-default attributes has - // cleared the IGNORE flags by now. Since we can never get here with JSPROP_IGNORE_VALUE - // relevant, just clear it. - attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE; - return DefinePropertyOrElement(cx, obj, id, getter, setter, - attrs, updateValue, false, result); - } - - MOZ_ASSERT(shape); - - JS_ALWAYS_TRUE(UpdateShapeTypeAndValue(cx, obj, shape, updateValue)); - - if (!CallAddPropertyHook(cx, obj, shape, updateValue)) + // Step 10. + if (!AddOrChangeProperty(cx, obj, id, desc)) return false; return result.succeed(); } bool -js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, +js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs, - ObjectOpResult &result) + ObjectOpResult& result) { Rooted desc(cx); - desc.initFields(obj, value, attrs, getter, setter); + desc.initFields(NullPtr(), value, attrs, getter, setter); return NativeDefineProperty(cx, obj, id, desc, result); } bool -js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name, +js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name, HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs, - ObjectOpResult &result) + ObjectOpResult& result) { RootedId id(cx, NameToId(name)); return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result); } bool -js::NativeDefineElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index, +js::NativeDefineElement(ExclusiveContext* cx, HandleNativeObject obj, uint32_t index, HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs, - ObjectOpResult &result) + ObjectOpResult& result) { RootedId id(cx); if (index <= JSID_INT_MAX) { @@ -1555,7 +1486,7 @@ js::NativeDefineElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t i } bool -js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, +js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs) { @@ -1575,7 +1506,7 @@ js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId } bool -js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name, +js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name, HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs) { @@ -1640,7 +1571,8 @@ js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* /*** [[Get]] *************************************************************************************/ static inline bool -CallGetter(JSContext* cx, HandleObject receiver, HandleShape shape, MutableHandleValue vp) +CallGetter(JSContext* cx, HandleObject obj, HandleObject receiver, HandleShape shape, + MutableHandleValue vp) { MOZ_ASSERT(!shape->hasDefaultGetter()); @@ -1649,8 +1581,9 @@ CallGetter(JSContext* cx, HandleObject receiver, HandleShape shape, MutableHandl return InvokeGetter(cx, receiver, fval, vp); } + // In contrast to normal getters JSGetterOps always want the holder. RootedId id(cx, shape->propid()); - return CallJSGetterOp(cx, shape->getterOp(), receiver, id, vp); + return CallJSGetterOp(cx, shape->getterOp(), obj, id, vp); } template @@ -1694,6 +1627,7 @@ GetExistingProperty(JSContext* cx, return false; if (!CallGetter(cx, + MaybeRooted::toHandle(obj), MaybeRooted::toHandle(receiver), MaybeRooted::toHandle(shape), MaybeRooted::toMutableHandle(vp))) @@ -1849,7 +1783,7 @@ GetNonexistentProperty(JSContext* cx, NativeObject* obj, jsid id, JSObject* rece } static inline bool -GeneralizedGetProperty(JSContext *cx, HandleObject obj, HandleId id, HandleObject receiver, +GeneralizedGetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject receiver, IsNameLookup nameLookup, MutableHandleValue vp) { JS_CHECK_RECURSION(cx, return false); @@ -1874,7 +1808,7 @@ GeneralizedGetProperty(JSContext *cx, HandleObject obj, HandleId id, HandleObjec } static inline bool -GeneralizedGetProperty(JSContext *cx, JSObject *obj, jsid id, JSObject *receiver, +GeneralizedGetProperty(JSContext* cx, JSObject* obj, jsid id, JSObject* receiver, IsNameLookup nameLookup, FakeMutableHandle vp) { JS_CHECK_RECURSION_DONT_REPORT(cx, return false); @@ -1989,160 +1923,13 @@ MaybeReportUndeclaredVarAssignment(JSContext* cx, JSString* propname) JSMSG_UNDECLARED_VAR, bytes.ptr()); } -/* - * When a [[Set]] operation finds no existing property with the given id - * or finds a writable data property on the prototype chain, we end up here. - * Finish the [[Set]] by defining a new property on receiver. - * - * This implements ES6 draft rev 28, 9.1.9 [[Set]] steps 5.b-f, but it - * is really old code and there are a few barnacles. - */ -bool -js::SetPropertyByDefining(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiverValue, bool objHasOwn, ObjectOpResult &result) -{ - // Step 5.b. - if (!receiverValue.isObject()) - return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER); - RootedObject receiver(cx, &receiverValue.toObject()); - - // Step 5.c-d: Test whether receiver has an existing own property - // receiver[id]. The spec calls [[GetOwnProperty]]; js::HasOwnProperty is - // the same thing except faster in the non-proxy case. Sometimes we can - // even optimize away the HasOwnProperty call. - bool existing; - if (receiver == obj) { - // The common case. The caller has necessarily done a property lookup - // on obj and passed us the answer as objHasOwn. -#ifdef DEBUG - // Check that objHasOwn is correct. This could fail if receiver or a - // native object on its prototype chain has a nondeterministic resolve - // hook. We shouldn't have any that are quite that badly behaved. - if (!HasOwnProperty(cx, receiver, id, &existing)) - return false; - MOZ_ASSERT(existing == objHasOwn); -#endif - existing = objHasOwn; - } else { - if (!HasOwnProperty(cx, receiver, id, &existing)) - return false; - } - - // If the property doesn't already exist, check for an inextensible - // receiver. (According to the specification, this is supposed to be - // enforced by [[DefineOwnProperty]], but we haven't implemented that yet.) - if (!existing) { - bool extensible; - if (!IsExtensible(cx, receiver, &extensible)) - return false; - if (!extensible) - return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); - } - - // Invalidate SpiderMonkey-specific caches or bail. - const Class* clasp = receiver->getClass(); - - // Purge the property cache of now-shadowed id in receiver's scope chain. - if (!PurgeScopeChain(cx, receiver, id)) - return false; - - // Step 5.e-f. Define the new data property. - unsigned attrs = - existing - ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT - : JSPROP_ENUMERATE; - JSGetterOp getter = clasp->getProperty; - JSSetterOp setter = clasp->setProperty; - MOZ_ASSERT(getter != JS_PropertyStub); - MOZ_ASSERT(setter != JS_StrictPropertyStub); - if (!receiver->is()) - return DefineProperty(cx, receiver, id, v, getter, setter, attrs, result); - - // If the receiver is native, there is one more legacy wrinkle: the class - // JSSetterOp is called after defining the new property. - Rooted nativeReceiver(cx, &receiver->as()); - return DefinePropertyOrElement(cx, nativeReceiver, id, getter, setter, attrs, v, true, result); -} - -// When setting |id| for |receiver| and |obj| has no property for id, continue -// the search up the prototype chain. -bool -js::SetPropertyOnProto(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult &result) -{ - MOZ_ASSERT(!obj->is()); - - RootedObject proto(cx, obj->getProto()); - if (proto) - return SetProperty(cx, proto, id, v, receiver, result); - return SetPropertyByDefining(cx, obj, id, v, receiver, false, result); -} - -/* - * Implement "the rest of" assignment to a property when no property receiver[id] - * was found anywhere on the prototype chain. - * - * FIXME: This should be updated to follow ES6 draft rev 28, section 9.1.9, - * steps 4.d.i and 5. - */ -static bool -SetNonexistentProperty(JSContext *cx, HandleNativeObject obj, HandleId id, HandleValue v, - HandleValue receiver, QualifiedBool qualified, ObjectOpResult &result) -{ - // We should never add properties to lexical blocks. - MOZ_ASSERT_IF(receiver.isObject(), !receiver.toObject().is()); - - if (!qualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) { - if (!MaybeReportUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) - return false; - } - - return SetPropertyByDefining(cx, obj, id, v, receiver, false, result); -} - -/* - * Set an existing own property obj[index] that's a dense element or typed - * array element. - */ -static bool -SetDenseOrTypedArrayElement(JSContext *cx, HandleNativeObject obj, uint32_t index, HandleValue v, - ObjectOpResult &result) -{ - if (IsAnyTypedArray(obj)) { - double d; - if (!ToNumber(cx, v, &d)) - return false; - - // Silently do nothing for out-of-bounds sets, for consistency with - // current behavior. (ES6 currently says to throw for this in - // strict mode code, so we may eventually need to change.) - uint32_t len = AnyTypedArrayLength(obj); - if (index < len) { - if (obj->is()) - TypedArrayObject::setElement(obj->as(), index, d); - else - SharedTypedArrayObject::setElement(obj->as(), index, d); - } - return result.succeed(); - } - - if (WouldDefinePastNonwritableLength(obj, index)) - return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); - - if (!obj->maybeCopyElementsForWrite(cx)) - return false; - - obj->setDenseElementWithType(cx, index, v); - return result.succeed(); -} - /* * Finish assignment to a shapeful data property of a native object obj. This * conforms to no standard and there is a lot of legacy baggage here. */ static bool -NativeSetExistingDataProperty(JSContext *cx, HandleNativeObject obj, HandleShape shape, - HandleValue v, HandleValue receiver, ObjectOpResult &result) +NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape, + HandleValue v, HandleValue receiver, ObjectOpResult& result) { MOZ_ASSERT(obj->isNative()); MOZ_ASSERT(shape->isDataDescriptor()); @@ -2185,6 +1972,161 @@ NativeSetExistingDataProperty(JSContext *cx, HandleNativeObject obj, HandleShape return true; // result is populated by CallJSSetterOp above. } +/* + * When a [[Set]] operation finds no existing property with the given id + * or finds a writable data property on the prototype chain, we end up here. + * Finish the [[Set]] by defining a new property on receiver. + * + * This implements ES6 draft rev 28, 9.1.9 [[Set]] steps 5.b-f, but it + * is really old code and there are a few barnacles. + */ +bool +js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiverValue, bool objHasOwn, ObjectOpResult& result) +{ + // Step 5.b. + if (!receiverValue.isObject()) + return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER); + RootedObject receiver(cx, &receiverValue.toObject()); + + // Step 5.c-d: Test whether receiver has an existing own property + // receiver[id]. The spec calls [[GetOwnProperty]]; js::HasOwnProperty is + // the same thing except faster in the non-proxy case. Sometimes we can + // even optimize away the HasOwnProperty call. + bool existing; + if (receiver == obj) { + // The common case. The caller has necessarily done a property lookup + // on obj and passed us the answer as objHasOwn. +#ifdef DEBUG + // Check that objHasOwn is correct. This could fail if receiver or a + // native object on its prototype chain has a nondeterministic resolve + // hook. We shouldn't have any that are quite that badly behaved. + if (!HasOwnProperty(cx, receiver, id, &existing)) + return false; + MOZ_ASSERT(existing == objHasOwn); +#endif + existing = objHasOwn; + } else { + if (!HasOwnProperty(cx, receiver, id, &existing)) + return false; + } + + // Invalidate SpiderMonkey-specific caches or bail. + const Class* clasp = receiver->getClass(); + + // Purge the property cache of now-shadowed id in receiver's scope chain. + if (!PurgeScopeChain(cx, receiver, id)) + return false; + + // Step 5.e-f. Define the new data property. + unsigned attrs = + existing + ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT + : JSPROP_ENUMERATE; + JSGetterOp getter = clasp->getProperty; + JSSetterOp setter = clasp->setProperty; + MOZ_ASSERT(getter != JS_PropertyStub); + MOZ_ASSERT(setter != JS_StrictPropertyStub); + if (!DefineProperty(cx, receiver, id, v, getter, setter, attrs, result)) + return false; + + // If the receiver is native, there is one more legacy wrinkle: the class + // JSSetterOp is called after defining the new property. + if (setter && receiver->is()) { + if (!result) + return true; + + Rooted nativeReceiver(cx, &receiver->as()); + if (!cx->shouldBeJSContext()) + return false; + RootedValue receiverValue(cx, ObjectValue(*receiver)); + + // This lookup is a bit unfortunate, but not nearly the most + // unfortunate thing about Class getters and setters. Since the above + // DefineProperty call succeeded, receiver is native, and the property + // has a setter (and thus can't be a dense element), this lookup is + // guaranteed to succeed. + RootedShape shape(cx, nativeReceiver->lookup(cx, id)); + MOZ_ASSERT(shape); + return NativeSetExistingDataProperty(cx->asJSContext(), nativeReceiver, shape, v, + receiverValue, result); + } + + return true; +} + +// When setting |id| for |receiver| and |obj| has no property for id, continue +// the search up the prototype chain. +bool +js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) +{ + MOZ_ASSERT(!obj->is()); + + RootedObject proto(cx, obj->getProto()); + if (proto) + return SetProperty(cx, proto, id, v, receiver, result); + return SetPropertyByDefining(cx, obj, id, v, receiver, false, result); +} + +/* + * Implement "the rest of" assignment to a property when no property receiver[id] + * was found anywhere on the prototype chain. + * + * FIXME: This should be updated to follow ES6 draft rev 28, section 9.1.9, + * steps 4.d.i and 5. + */ +static bool +SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v, + HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result) +{ + // We should never add properties to lexical blocks. + MOZ_ASSERT_IF(receiver.isObject(), !receiver.toObject().is()); + + if (!qualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) { + if (!MaybeReportUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) + return false; + } + + return SetPropertyByDefining(cx, obj, id, v, receiver, false, result); +} + +/* + * Set an existing own property obj[index] that's a dense element or typed + * array element. + */ +static bool +SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v, + ObjectOpResult& result) +{ + if (IsAnyTypedArray(obj)) { + double d; + if (!ToNumber(cx, v, &d)) + return false; + + // Silently do nothing for out-of-bounds sets, for consistency with + // current behavior. (ES6 currently says to throw for this in + // strict mode code, so we may eventually need to change.) + uint32_t len = AnyTypedArrayLength(obj); + if (index < len) { + if (obj->is()) + TypedArrayObject::setElement(obj->as(), index, d); + else + SharedTypedArrayObject::setElement(obj->as(), index, d); + } + return result.succeed(); + } + + if (WouldDefinePastNonwritableLength(obj, index)) + return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); + + if (!obj->maybeCopyElementsForWrite(cx)) + return false; + + obj->setDenseElementWithType(cx, index, v); + return result.succeed(); +} + /* * Finish the assignment `receiver[id] = v` when an existing property (shape) * has been found on a native object (pobj). This implements ES6 draft rev 32 @@ -2194,9 +2136,9 @@ NativeSetExistingDataProperty(JSContext *cx, HandleNativeObject obj, HandleShape * dense or typed array element (i.e. not actually a pointer to a Shape). */ static bool -SetExistingProperty(JSContext *cx, HandleNativeObject obj, HandleId id, HandleValue v, +SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v, HandleValue receiver, HandleNativeObject pobj, HandleShape shape, - ObjectOpResult &result) + ObjectOpResult& result) { // Step 5 for dense elements. if (IsImplicitDenseOrTypedArrayElement(shape)) { @@ -2232,12 +2174,8 @@ SetExistingProperty(JSContext *cx, HandleNativeObject obj, HandleId id, HandleVa // SpiderMonkey special case: assigning to an inherited slotless // property causes the setter to be called, instead of shadowing, - // unless the existing property is JSPROP_SHADOWABLE (see bug 552432) - // or it's the array length property. - if (!shape->hasSlot() && - !shape->hasShadowable() && - !(pobj->is() && id == NameToId(cx->names().length))) - { + // unless the existing property is JSPROP_SHADOWABLE (see bug 552432). + if (!shape->hasSlot() && !shape->hasShadowable()) { // Even weirder sub-special-case: inherited slotless data property // with default setter. Wut. if (shape->hasDefaultSetter()) @@ -2264,13 +2202,13 @@ SetExistingProperty(JSContext *cx, HandleNativeObject obj, HandleId id, HandleVa } bool -js::NativeSetProperty(JSContext *cx, HandleNativeObject obj, HandleId id, HandleValue value, - HandleValue receiver, QualifiedBool qualified, ObjectOpResult &result) +js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, + HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result) { // Fire watchpoints, if any. RootedValue v(cx, value); if (MOZ_UNLIKELY(obj->watched())) { - WatchpointMap *wpmap = cx->compartment()->watchpointMap; + WatchpointMap* wpmap = cx->compartment()->watchpointMap; if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v)) return false; } @@ -2333,8 +2271,8 @@ js::NativeSetProperty(JSContext *cx, HandleNativeObject obj, HandleId id, Handle } bool -js::NativeSetElement(JSContext *cx, HandleNativeObject obj, uint32_t index, HandleValue v, - HandleValue receiver, ObjectOpResult &result) +js::NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v, + HandleValue receiver, ObjectOpResult& result) { RootedId id(cx); if (!IndexToId(cx, index, &id)) @@ -2346,8 +2284,8 @@ js::NativeSetElement(JSContext *cx, HandleNativeObject obj, uint32_t index, Hand // ES6 draft rev31 9.1.10 [[Delete]] bool -js::NativeDeleteProperty(JSContext *cx, HandleNativeObject obj, HandleId id, - ObjectOpResult &result) +js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id, + ObjectOpResult& result) { // Steps 2-3. RootedShape shape(cx); diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 07930ec60a..986ba52fcd 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -26,8 +26,8 @@ namespace js { -class Nursery; class Shape; +class TenuringTracer; /* * To really poison a set of values, using 'magic' or 'undefined' isn't good @@ -114,9 +114,9 @@ ArraySetLength(JSContext* cx, Handle obj, HandleId id, * - The length property as a uint32_t, accessible for array objects with * ArrayObject::{length,setLength}(). This is unused for non-arrays. * - The number of element slots (capacity), gettable with - * getDenseElementsCapacity(). + * getDenseCapacity(). * - The array's initialized length, accessible with - * getDenseElementsInitializedLength(). + * getDenseInitializedLength(). * * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values. * These indicate indexes which are not dense properties of the array. The @@ -180,9 +180,9 @@ class ObjectElements private: friend class ::JSObject; - friend class NativeObject; friend class ArrayObject; - friend class Nursery; + friend class NativeObject; + friend class TenuringTracer; friend bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level); @@ -404,7 +404,7 @@ class NativeObject : public JSObject uint32_t getDenseInitializedLength() { return getElementsHeader()->initializedLength; } - uint32_t getDenseCapacity() { + uint32_t getDenseCapacity() const { return getElementsHeader()->capacity; } @@ -457,7 +457,7 @@ class NativeObject : public JSObject bool toDictionaryMode(ExclusiveContext* cx); private: - friend class Nursery; + friend class TenuringTracer; /* * Get internal pointers to the range of values starting at start and @@ -1346,27 +1346,6 @@ NativeLookupOwnProperty(ExclusiveContext* cx, typename MaybeRooted::HandleType id, typename MaybeRooted::MutableHandleType propp); -/* - * On success, and if id was found, return true with *objp non-null and with a - * property of *objp stored in *propp. If successful but id was not found, - * return true with both *objp and *propp null. - */ -template -extern bool -NativeLookupProperty(ExclusiveContext* cx, - typename MaybeRooted::HandleType obj, - typename MaybeRooted::HandleType id, - typename MaybeRooted::MutableHandleType objp, - typename MaybeRooted::MutableHandleType propp); - -inline bool -NativeLookupProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name, - MutableHandleObject objp, MutableHandleShape propp); - -extern bool -NativeLookupElement(JSContext* cx, HandleNativeObject obj, uint32_t index, - MutableHandleObject objp, MutableHandleShape propp); - /* * Get a property from `receiver`, after having already done a lookup and found * the property on a native object `obj`. @@ -1376,7 +1355,7 @@ NativeLookupElement(JSContext* cx, HandleNativeObject obj, uint32_t index, */ extern bool NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObject obj, - HandleShape shape, MutableHandle vp); + HandleShape shape, MutableHandleValue vp); /* * */ diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 3cca4b56f0..e0fcadd19b 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -407,9 +407,8 @@ class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef : table(table), clasp(clasp), proto(proto), associated(associated) {} - void mark(JSTracer* trc) { + void trace(JSTracer* trc) override { JSObject* prior = proto; - JS::AutoOriginalTraceLocation reloc(trc, &proto); TraceManuallyBarrieredEdge(trc, &proto, "newObjectGroups set prototype"); if (prior == proto) return; @@ -459,6 +458,8 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, // unboxed plain object. MOZ_ASSERT(!clasp == (associated && associated->is())); + AutoEnterAnalysis enter(cx); + ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.defaultNewTable; if (!table) { @@ -485,6 +486,9 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, clasp = &PlainObject::class_; } + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) + return nullptr; + ObjectGroupCompartment::NewTable::AddPtr p = table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated)); if (p) { @@ -496,11 +500,6 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, return group; } - AutoEnterAnalysis enter(cx); - - if (proto.isObject() && !proto.toObject()->setDelegate(cx)) - return nullptr; - ObjectGroupFlags initialFlags = 0; if (!proto.isObject() || proto.toObject()->isNewGroupUnknown()) initialFlags = OBJECT_FLAG_DYNAMIC_MASK; @@ -1148,6 +1147,15 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc } } + if (JSOp(*pc) == JSOP_NEWARRAY && cx->runtime()->options().unboxedArrays()) { + PreliminaryObjectArrayWithTemplate* preliminaryObjects = + cx->new_(nullptr); + if (preliminaryObjects) + res->setPreliminaryObjects(preliminaryObjects); + else + cx->recoverFromOutOfMemory(); + } + if (!table->add(p, key, res)) return nullptr; diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index e3c89f2cc4..68f11bd4c4 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -184,15 +184,15 @@ class ObjectGroup : public gc::TenuredCell HeapPtrObject proto_; /* Compartment shared by objects in this group. */ - JSCompartment *compartment_; + JSCompartment* compartment_; public: - const Class *clasp() const { + const Class* clasp() const { return clasp_; } - void setClasp(const Class *clasp) { + void setClasp(const Class* clasp) { clasp_ = clasp; } @@ -201,7 +201,7 @@ class ObjectGroup : public gc::TenuredCell } // For use during marking, don't call otherwise. - HeapPtrObject &protoRaw() { return proto_; } + HeapPtrObject& protoRaw() { return proto_; } void setProto(TaggedProto proto); void setProtoUnchecked(TaggedProto proto); @@ -216,7 +216,7 @@ class ObjectGroup : public gc::TenuredCell return res; } - JSCompartment *compartment() const { + JSCompartment* compartment() const { return compartment_; } @@ -255,7 +255,7 @@ class ObjectGroup : public gc::TenuredCell // If non-null, holds additional information about this object, whose // format is indicated by the object's addendum kind. - void *addendum_; + void* addendum_; void setAddendum(AddendumKind kind, void* addendum, bool writeBarrier = true); @@ -264,7 +264,7 @@ class ObjectGroup : public gc::TenuredCell ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT); } - TypeNewScript *newScriptDontCheckGeneration() const { + TypeNewScript* newScriptDontCheckGeneration() const { if (addendumKind() == Addendum_NewScript) return reinterpret_cast(addendum_); return nullptr; @@ -276,8 +276,8 @@ class ObjectGroup : public gc::TenuredCell return nullptr; } - TypeNewScript *anyNewScript(); - void detachNewScript(bool writeBarrier, ObjectGroup *replacement); + TypeNewScript* anyNewScript(); + void detachNewScript(bool writeBarrier, ObjectGroup* replacement); ObjectGroupFlags flagsDontCheckGeneration() const { return flags_; @@ -309,18 +309,18 @@ class ObjectGroup : public gc::TenuredCell setAddendum(Addendum_NewScript, newScript); } - PreliminaryObjectArrayWithTemplate *maybePreliminaryObjects() { + PreliminaryObjectArrayWithTemplate* maybePreliminaryObjects() { maybeSweep(nullptr); return maybePreliminaryObjectsDontCheckGeneration(); } - PreliminaryObjectArrayWithTemplate *maybePreliminaryObjectsDontCheckGeneration() { + PreliminaryObjectArrayWithTemplate* maybePreliminaryObjectsDontCheckGeneration() { if (addendumKind() == Addendum_PreliminaryObjects) - return reinterpret_cast(addendum_); + return reinterpret_cast(addendum_); return nullptr; } - void setPreliminaryObjects(PreliminaryObjectArrayWithTemplate *preliminaryObjects) { + void setPreliminaryObjects(PreliminaryObjectArrayWithTemplate* preliminaryObjects) { setAddendum(Addendum_PreliminaryObjects, preliminaryObjects); } @@ -329,7 +329,11 @@ class ObjectGroup : public gc::TenuredCell setAddendum(Addendum_None, nullptr); } - UnboxedLayout *maybeUnboxedLayout() { + bool hasUnanalyzedPreliminaryObjects() { + return (newScript() && !newScript()->analyzed()) || maybePreliminaryObjects(); + } + + UnboxedLayout* maybeUnboxedLayout() { maybeSweep(nullptr); return maybeUnboxedLayoutDontCheckGeneration(); } @@ -339,12 +343,12 @@ class ObjectGroup : public gc::TenuredCell return *maybeUnboxedLayoutDontCheckGeneration(); } - UnboxedLayout &unboxedLayout() { + UnboxedLayout& unboxedLayout() { maybeSweep(nullptr); return unboxedLayoutDontCheckGeneration(); } - void setUnboxedLayout(UnboxedLayout *layout) { + void setUnboxedLayout(UnboxedLayout* layout) { setAddendum(Addendum_UnboxedLayout, layout); } @@ -452,10 +456,10 @@ class ObjectGroup : public gc::TenuredCell * defineProperty which are on native properties, and on any jitcode which * might update the property with a new type. */ - Property **propertySet; + Property** propertySet; public: - inline ObjectGroup(const Class *clasp, TaggedProto proto, JSCompartment *comp, + inline ObjectGroup(const Class* clasp, TaggedProto proto, JSCompartment* comp, ObjectGroupFlags initialFlags); inline bool hasAnyFlags(ObjectGroupFlags flags) { @@ -508,10 +512,10 @@ class ObjectGroup : public gc::TenuredCell * Get or create a property of this object. Only call this for properties which * a script accesses explicitly. */ - inline HeapTypeSet *getProperty(ExclusiveContext *cx, JSObject *obj, jsid id); + inline HeapTypeSet* getProperty(ExclusiveContext* cx, JSObject* obj, jsid id); /* Get a property only if it already exists. */ - inline HeapTypeSet *maybeGetProperty(jsid id); + inline HeapTypeSet* maybeGetProperty(jsid id); /* * Iterate through the group's properties. getPropertyCount overapproximates @@ -519,20 +523,20 @@ class ObjectGroup : public gc::TenuredCell * getProperty may return nullptr. */ inline unsigned getPropertyCount(); - inline Property *getProperty(unsigned i); + inline Property* getProperty(unsigned i); /* Helpers */ - void updateNewPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id, HeapTypeSet *types); - bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape); + void updateNewPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id, HeapTypeSet* types); + bool addDefiniteProperties(ExclusiveContext* cx, Shape* shape); bool matchDefiniteProperties(HandleObject obj); - void markPropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id); - void markPropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id); - void markStateChange(ExclusiveContext *cx); - void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); - void markUnknown(ExclusiveContext *cx); + void markPropertyNonData(ExclusiveContext* cx, JSObject* obj, jsid id); + void markPropertyNonWritable(ExclusiveContext* cx, JSObject* obj, jsid id); + void markStateChange(ExclusiveContext* cx); + void setFlags(ExclusiveContext* cx, ObjectGroupFlags flags); + void markUnknown(ExclusiveContext* cx); void maybeClearNewScriptOnOOM(); - void clearNewScript(ExclusiveContext *cx, ObjectGroup *replacement = nullptr); + void clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement = nullptr); bool isPropertyNonData(jsid id); bool isPropertyNonWritable(jsid id); @@ -581,7 +585,7 @@ class ObjectGroup : public gc::TenuredCell return offsetof(ObjectGroup, flags_); } - const ObjectGroupFlags *addressOfFlags() const { + const ObjectGroupFlags* addressOfFlags() const { return &flags_; } @@ -631,14 +635,14 @@ class ObjectGroup : public gc::TenuredCell // Update the group of a freshly created array according to // the object's current contents. - static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); + static void fixArrayGroup(ExclusiveContext* cx, ArrayObject* obj); // Update the group of a freshly created 'rest' arguments object. - static void fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj); + static void fixRestArgumentsGroup(ExclusiveContext* cx, ArrayObject* obj); // Create a PlainObject or UnboxedPlainObject with the specified properties. - static JSObject *newPlainObject(ExclusiveContext *cx, - IdValuePair *properties, size_t nproperties, + static JSObject* newPlainObject(ExclusiveContext* cx, + IdValuePair* properties, size_t nproperties, NewObjectKind newKind); // Static accessors for ObjectGroupCompartment AllocationSiteTable. @@ -759,12 +763,12 @@ class ObjectGroupCompartment void sweepNewTable(NewTable* table); void fixupNewTableAfterMovingGC(NewTable* table); - static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table, - const Class *clasp, TaggedProto proto, JSObject *associated); + static void newTablePostBarrier(ExclusiveContext* cx, NewTable* table, + const Class* clasp, TaggedProto proto, JSObject* associated); }; -PlainObject * -NewPlainObjectWithProperties(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties, +PlainObject* +NewPlainObjectWithProperties(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties, NewObjectKind newKind); } // namespace js diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index c307fad860..8fa0ca8b3d 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -63,6 +63,7 @@ * Intrinsics * Block-local Scope * This + * Super * Arguments * [Operators] * Comparison Operators @@ -832,8 +833,8 @@ * This opcode takes the kind of initializer (JSProto_Array or * JSProto_Object). * - * This opcode has an extra byte so it can be exchanged with JSOP_NEWOBJECT - * during emit. + * This opcode has three extra bytes so it can be exchanged with + * JSOP_NEWOBJECT during emit. * Category: Literals * Type: Object * Operands: uint8_t kind (, uint24_t extra) @@ -867,8 +868,12 @@ * * This opcode takes the function and the object to be the home object, does * the set, and leaves both on the stack. + * Category: Literals + * Type: Object + * Operands: uint8_t n + * Stack: homeObject, [...n], fun => homeObject, [...n], fun */\ - macro(JSOP_INITHOMEOBJECT, 92, "inithomeobject", NULL, 1, 2, 2, JOF_BYTE|JOF_SET|JOF_DETECTING) \ + macro(JSOP_INITHOMEOBJECT, 92, "inithomeobject", NULL, 2, 2, 2, JOF_UINT8) \ \ /* * Initialize a named property in an object literal, like '{a: x}'. @@ -988,6 +993,14 @@ */ \ macro(JSOP_NEWARRAY_COPYONWRITE, 102, "newarray_copyonwrite", NULL, 5, 0, 1, JOF_OBJECT) \ \ + /* + * Pushes the prototype of the home object for current callee onto the + * stack. + * Category: Variables and Scopes + * Type: Super + * Operands: + * Stack: => homeObjectProto + */\ macro(JSOP_SUPERBASE, 103, "superbase", NULL, 1, 0, 1, JOF_BYTE) \ /* * Pops the top two values, and pushes the property of one, using the other @@ -1259,7 +1272,18 @@ * Stack: receiver, obj, propval => obj[propval] */ \ macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \ - macro(JSOP_UNUSED126, 126, "unused126", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pushes newly created array for a spread call onto the stack. This has + * the same semantics as JSOP_NEWARRAY, but is distinguished to avoid + * using unboxed arrays in spread calls, which would make compiling spread + * calls in baseline more complex. + * + * Category: Literals + * Type: Array + * Operands: uint24_t length + * Stack: => obj + */ \ + macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 4, 0, 1, JOF_UINT24) \ \ /* * Defines the given function on the current scope. diff --git a/js/src/vm/PIC.cpp b/js/src/vm/PIC.cpp index 1f443797f1..4b8fda59f4 100644 --- a/js/src/vm/PIC.cpp +++ b/js/src/vm/PIC.cpp @@ -296,7 +296,7 @@ ForOfPIC_traceObject(JSTracer* trc, JSObject* obj) const Class ForOfPIC::jsclass = { "ForOfPIC", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, ForOfPIC_finalize, nullptr, /* call */ nullptr, /* hasInstance */ diff --git a/js/src/vm/ReceiverGuard.cpp b/js/src/vm/ReceiverGuard.cpp new file mode 100644 index 0000000000..5b64176dfe --- /dev/null +++ b/js/src/vm/ReceiverGuard.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/ReceiverGuard.h" + +#include "vm/UnboxedObject.h" + +ReceiverGuard::ReceiverGuard(JSObject* obj) + : group(nullptr), shape(nullptr) +{ + if (obj) { + if (obj->is()) { + group = obj->group(); + if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) + shape = expando->lastProperty(); + } else if (obj->is() || obj->is()) { + group = obj->group(); + } else { + shape = obj->maybeShape(); + } + } +} + +ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape) + : group(group), shape(shape) +{ + if (group) { + const Class* clasp = group->clasp(); + if (clasp == &UnboxedPlainObject::class_) { + // Keep both group and shape. + } else if (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) { + this->shape = nullptr; + } else { + this->group = nullptr; + } + } +} + +/* static */ int32_t +HeapReceiverGuard::keyBits(JSObject* obj) +{ + if (obj->is()) { + // Both the group and shape need to be guarded for unboxed plain objects. + return obj->as().maybeExpando() ? 0 : 1; + } + if (obj->is() || obj->is()) { + // Only the group needs to be guarded for unboxed arrays and typed objects. + return 2; + } + // Other objects only need the shape to be guarded. + return 3; +} + +void +HeapReceiverGuard::trace(JSTracer* trc) +{ + if (shape_) + TraceEdge(trc, &shape_, "receiver_guard_shape"); + else + TraceEdge(trc, &group_, "receiver_guard_group"); +} diff --git a/js/src/vm/ReceiverGuard.h b/js/src/vm/ReceiverGuard.h new file mode 100644 index 0000000000..b268774a7d --- /dev/null +++ b/js/src/vm/ReceiverGuard.h @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_ReceiverGuard_h +#define vm_ReceiverGuard_h + +#include "vm/Shape.h" + +namespace js { + +// A ReceiverGuard encapsulates the information about an object that needs to +// be tested to determine if it has the same 'structure' as another object. +// The guard includes the shape and/or group of the object, and which of these +// is tested, as well as the meaning here of 'structure', depends on the kind +// of object being tested: +// +// NativeObject: The structure of a native object is determined by its shape. +// Two objects with the same shape have the same class, prototype, flags, +// and all properties except those stored in dense elements. +// +// ProxyObject: The structure of a proxy object is determined by its shape. +// Proxies with the same shape have the same class and prototype, but no +// other commonality is guaranteed. +// +// TypedObject: The structure of a typed object is determined by its group. +// All typed objects with the same group have the same class, prototype, and +// own properties. +// +// UnboxedPlainObject: The structure of an unboxed plain object is determined +// by its group and its expando object's shape, if there is one. All unboxed +// plain objects with the same group and expando shape have the same +// properties except those stored in the expando's dense elements. + +class HeapReceiverGuard; +class RootedReceiverGuard; + +class ReceiverGuard +{ + public: + ObjectGroup* group; + Shape* shape; + + ReceiverGuard() + : group(nullptr), shape(nullptr) + {} + + inline MOZ_IMPLICIT ReceiverGuard(const HeapReceiverGuard& guard); + inline MOZ_IMPLICIT ReceiverGuard(const RootedReceiverGuard& guard); + + explicit ReceiverGuard(JSObject* obj); + ReceiverGuard(ObjectGroup* group, Shape* shape); + + bool operator ==(const ReceiverGuard& other) const { + return group == other.group && shape == other.shape; + } + + bool operator !=(const ReceiverGuard& other) const { + return !(*this == other); + } + + uintptr_t hash() const { + return (uintptr_t(group) >> 3) ^ (uintptr_t(shape) >> 3); + } +}; + +class HeapReceiverGuard +{ + HeapPtrObjectGroup group_; + HeapPtrShape shape_; + + public: + explicit HeapReceiverGuard(const ReceiverGuard& guard) + : group_(guard.group), shape_(guard.shape) + {} + + bool matches(const ReceiverGuard& guard) { + return group_ == guard.group && shape_ == guard.shape; + } + + void update(const ReceiverGuard& other) { + group_ = other.group; + shape_ = other.shape; + } + + void init(const ReceiverGuard& other) { + group_.init(other.group); + shape_.init(other.shape); + } + + void trace(JSTracer* trc); + + Shape* shape() const { + return shape_; + } + ObjectGroup* group() const { + return group_; + } + + static size_t offsetOfShape() { + return offsetof(HeapReceiverGuard, shape_); + } + static size_t offsetOfGroup() { + return offsetof(HeapReceiverGuard, group_); + } + + // Bits to munge into Baseline IC compiler keys when that IC has a + // HeapReceiverGuard. This uses at most two bits for data. + static int32_t keyBits(JSObject* obj); +}; + +class RootedReceiverGuard +{ + public: + RootedObjectGroup group; + RootedShape shape; + + RootedReceiverGuard(JSContext* cx, const ReceiverGuard& guard) + : group(cx, guard.group), shape(cx, guard.shape) + {} +}; + +inline +ReceiverGuard::ReceiverGuard(const HeapReceiverGuard& guard) + : group(guard.group()), shape(guard.shape()) +{} + +inline +ReceiverGuard::ReceiverGuard(const RootedReceiverGuard& guard) + : group(guard.group), shape(guard.shape) +{} + +} // namespace js + +#endif /* vm_ReceiverGuard_h */ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index b0ff904034..a2c4d4cfe5 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -269,6 +269,7 @@ const Class RegExpObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ diff --git a/js/src/vm/RegExpStatics.cpp b/js/src/vm/RegExpStatics.cpp index d171632b64..304ed31f60 100644 --- a/js/src/vm/RegExpStatics.cpp +++ b/js/src/vm/RegExpStatics.cpp @@ -44,6 +44,7 @@ const Class RegExpStaticsObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ resc_finalize, nullptr, /* call */ diff --git a/js/src/vm/Runtime-inl.h b/js/src/vm/Runtime-inl.h index af11943dba..e9ad4984e8 100644 --- a/js/src/vm/Runtime-inl.h +++ b/js/src/vm/Runtime-inl.h @@ -52,6 +52,8 @@ NewObjectCache::newObjectFromHit(JSContext* cx, EntryIndex entryIndex, gc::Initi // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread. ObjectGroup* group = templateObj->group_; + MOZ_ASSERT(!group->hasUnanalyzedPreliminaryObjects()); + if (group->shouldPreTenure()) heap = gc::TenuredHeap; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index c4ae6df24b..0adc235539 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -276,7 +276,7 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) const char* size = getenv("JSGC_MARK_STACK_LIMIT"); if (size) - SetMarkStackLimit(this, atoi(size)); + gc.setMarkStackLimit(atoi(size)); ScopedJSDeletePtr atomsZone(new_(this)); if (!atomsZone || !atomsZone->init(true)) @@ -509,7 +509,7 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf); rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted(); rtSizes->gc.nurseryDecommitted += gc.nursery.sizeOfHeapDecommitted(); - rtSizes->gc.nurseryHugeSlots += gc.nursery.sizeOfHugeSlots(mallocSizeOf); + rtSizes->gc.nurseryMallocedBuffers += gc.nursery.sizeOfMallocedBuffers(mallocSizeOf); gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); } diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 7f715ffaaf..e048187081 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -183,6 +183,7 @@ SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject nullptr, // setProperty nullptr, // enumerate nullptr, // resolve + nullptr, // mayResolve nullptr, // convert SavedFrame::finalize, // finalize nullptr, // call @@ -1177,8 +1178,10 @@ SavedStacks::chooseSamplingProbability(JSContext* cx) } JSObject* -SavedStacksMetadataCallback(JSContext* cx) +SavedStacksMetadataCallback(JSContext* cx, JSObject* target) { + RootedObject obj(cx, target); + SavedStacks& stacks = cx->compartment()->savedStacks(); if (stacks.allocationSkipCount > 0) { stacks.allocationSkipCount--; @@ -1218,7 +1221,7 @@ SavedStacksMetadataCallback(JSContext* cx) if (!stacks.saveCurrentStack(cx, &frame)) CrashAtUnhandlableOOM("SavedStacksMetadataCallback"); - if (!Debugger::onLogAllocationSite(cx, frame, PRMJ_Now())) + if (!Debugger::onLogAllocationSite(cx, obj, frame, PRMJ_Now())) CrashAtUnhandlableOOM("SavedStacksMetadataCallback"); return frame; diff --git a/js/src/vm/SavedStacks.h b/js/src/vm/SavedStacks.h index fb63d70aeb..9ce80fb274 100644 --- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -260,7 +260,7 @@ struct SavedFrame::HashPolicy inline void AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack); class SavedStacks { - friend JSObject* SavedStacksMetadataCallback(JSContext* cx); + friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target); public: SavedStacks() @@ -400,7 +400,7 @@ class SavedStacks { bool getLocation(JSContext* cx, const FrameIter& iter, MutableHandleLocationValue locationp); }; -JSObject* SavedStacksMetadataCallback(JSContext* cx); +JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target); } /* namespace js */ diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 169abe2b1b..520584ecd8 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -580,6 +580,7 @@ const Class DynamicWithObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -1030,6 +1031,7 @@ const Class UninitializedLexicalObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -2544,6 +2546,7 @@ js::GetDebugScopeForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc) assertSameCompartment(cx, frame); if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx)) return nullptr; + ScopeIter si(cx, frame, pc); return GetDebugScope(cx, si); } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index b9005ff3e9..f5a7a47a3f 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -41,6 +41,7 @@ using namespace js; using namespace js::selfhosted; using JS::AutoCheckCannotGC; +using mozilla::UniquePtr; static void selfHosting_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) @@ -185,16 +186,15 @@ intrinsic_OwnPropertyKeys(JSContext* cx, unsigned argc, Value* vp) return GetOwnPropertyKeys(cx, args, args[1].toInt32()); } -bool -js::intrinsic_ThrowError(JSContext* cx, unsigned argc, Value* vp) +static void +ThrowErrorWithType(JSContext* cx, JSExnType type, const CallArgs& args) { - CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() >= 1); uint32_t errorNumber = args[0].toInt32(); #ifdef DEBUG const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber); MOZ_ASSERT(efs->argCount == args.length() - 1); + MOZ_ASSERT(efs->exnType == type, "error-throwing intrinsic and error number are inconsistent"); #endif JSAutoByteString errorArgs[3]; @@ -203,19 +203,42 @@ js::intrinsic_ThrowError(JSContext* cx, unsigned argc, Value* vp) if (val.isInt32()) { JSString* str = ToString(cx, val); if (!str) - return false; + return; errorArgs[i - 1].encodeLatin1(cx, str); } else if (val.isString()) { errorArgs[i - 1].encodeLatin1(cx, val.toString()); } else { - errorArgs[i - 1].initBytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr()).release()); + UniquePtr bytes = + DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr()); + if (!bytes) + return; + errorArgs[i - 1].initBytes(bytes.release()); } if (!errorArgs[i - 1]) - return false; + return; } JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber, errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr()); +} + +bool +js::intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_RANGEERR, args); + return false; +} + +bool +js::intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 1); + + ThrowErrorWithType(cx, JSEXN_TYPEERR, args); return false; } @@ -915,7 +938,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("IsCallable", intrinsic_IsCallable, 1,0), JS_FN("IsConstructor", intrinsic_IsConstructor, 1,0), JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), - JS_FN("ThrowError", intrinsic_ThrowError, 4,0), + JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4,0), + JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), JS_FN("GetBuiltinConstructorImpl", intrinsic_GetBuiltinConstructor, 1,0), JS_FN("MakeConstructible", intrinsic_MakeConstructible, 2,0), @@ -1084,7 +1108,7 @@ JSRuntime::createSelfHostingGlobal(JSContext* cx) "self-hosting-global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index 8388ebd36b..a00ffaf6d3 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1365,7 +1365,7 @@ class InitialShapeSetRef : public BufferableRef objectFlags(objectFlags) {} - void mark(JSTracer *trc) { + void trace(JSTracer* trc) override { TaggedProto priorProto = proto; if (proto.isObject()) { TraceManuallyBarrieredEdge(trc, reinterpret_cast(&proto), diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 8b8ca5c314..971708c66e 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -110,8 +110,8 @@ namespace js { class Bindings; -class Nursery; class StaticBlockObject; +class TenuringTracer; typedef JSGetterOp GetterOp; typedef JSSetterOp SetterOp; @@ -562,12 +562,12 @@ class Shape : public gc::TenuredCell friend class ::JSObject; friend class ::JSFunction; friend class Bindings; - friend class Nursery; friend class NativeObject; friend class PropertyTree; friend class StaticBlockObject; - friend struct StackShape; + friend class TenuringTracer; friend struct StackBaseShape; + friend struct StackShape; protected: HeapPtrBaseShape base_; @@ -1244,7 +1244,7 @@ class ShapeGetterSetterRef : public gc::BufferableRef public: explicit ShapeGetterSetterRef(AccessorShape* shape) : shape_(shape) {} - void mark(JSTracer* trc) { shape_->fixupGetterSetterForBarrier(trc); } + void trace(JSTracer* trc) override { shape_->fixupGetterSetterForBarrier(trc); } }; static inline void diff --git a/js/src/vm/SharedArrayObject.cpp b/js/src/vm/SharedArrayObject.cpp index 3923443c66..4c99c7329f 100644 --- a/js/src/vm/SharedArrayObject.cpp +++ b/js/src/vm/SharedArrayObject.cpp @@ -313,6 +313,7 @@ const Class SharedArrayBufferObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ SharedArrayBufferObject::Finalize, nullptr, /* call */ diff --git a/js/src/vm/SharedTypedArrayObject.cpp b/js/src/vm/SharedTypedArrayObject.cpp index 068b0b9757..3d7af07a1e 100644 --- a/js/src/vm/SharedTypedArrayObject.cpp +++ b/js/src/vm/SharedTypedArrayObject.cpp @@ -711,6 +711,7 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ nullptr, /* convert */ \ nullptr, /* finalize */ \ nullptr, /* call */ \ @@ -732,6 +733,7 @@ IMPL_SHARED_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ nullptr, /* convert */ \ nullptr, /* finalize */ \ nullptr, /* call */ \ diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 1f35e79904..8a38073e8d 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -26,6 +26,7 @@ using namespace js; +using mozilla::Maybe; using mozilla::PodCopy; /*****************************************************************************/ @@ -425,15 +426,13 @@ js::MarkInterpreterActivations(JSRuntime* rt, JSTracer* trc) /*****************************************************************************/ -// Unlike the other methods of this calss, this method is defined here so that +// Unlike the other methods of this class, this method is defined here so that // we don't have to #include jsautooplen.h in vm/Stack.h. void InterpreterRegs::setToEndOfScript() { - JSScript* script = fp()->script(); sp = fp()->base(); - pc = script->codeEnd() - JSOP_RETRVAL_LENGTH; - MOZ_ASSERT(*pc == JSOP_RETRVAL); + pc = fp()->script()->lastPC(); } /*****************************************************************************/ @@ -1868,75 +1867,86 @@ JS::ProfilingFrameIterator::stackAddress() const return jitIter().stackAddress(); } +Maybe +JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry* entry) const +{ + void* stackAddr = stackAddress(); + + if (isAsmJS()) { + Frame frame; + frame.kind = Frame_AsmJS; + frame.stackAddress = stackAddr; + frame.returnAddress = nullptr; + frame.activation = activation_; + frame.label = nullptr; + return mozilla::Some(frame); + } + + MOZ_ASSERT(isJit()); + + // Look up an entry for the return address. + void* returnAddr = jitIter().returnAddressToFp(); + jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable(); + if (hasSampleBufferGen()) + table->lookupForSampler(returnAddr, entry, rt_, sampleBufferGen_); + else + table->lookupInfallible(returnAddr, entry, rt_); + + MOZ_ASSERT(entry->isIon() || entry->isIonCache() || entry->isBaseline() || entry->isDummy()); + + // Dummy frames produce no stack frames. + if (entry->isDummy()) + return mozilla::Nothing(); + + Frame frame; + frame.kind = entry->isBaseline() ? Frame_Baseline : Frame_Ion; + frame.stackAddress = stackAddr; + frame.returnAddress = returnAddr; + frame.activation = activation_; + frame.label = nullptr; + return mozilla::Some(frame); +} + uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames, uint32_t offset, uint32_t end) const { if (offset >= end) return 0; - void* stackAddr = stackAddress(); + jit::JitcodeGlobalEntry entry; + Maybe physicalFrame = getPhysicalFrameAndEntry(&entry); + + // Dummy frames produce no stack frames. + if (physicalFrame.isNothing()) + return 0; if (isAsmJS()) { - frames[offset].kind = Frame_AsmJS; - frames[offset].stackAddress = stackAddr; - frames[offset].returnAddress = nullptr; - frames[offset].activation = activation_; + frames[offset] = physicalFrame.value(); frames[offset].label = asmJSIter().label(); - frames[offset].hasTrackedOptimizations = false; return 1; } - MOZ_ASSERT(isJit()); - void* returnAddr = jitIter().returnAddressToFp(); - - // Look up an entry for the return address. - jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable(); - jit::JitcodeGlobalEntry entry; - table->lookupInfallible(returnAddr, &entry, rt_); - if (hasSampleBufferGen()) - table->lookupForSampler(returnAddr, &entry, rt_, sampleBufferGen_); - else - table->lookup(returnAddr, &entry, rt_); - - MOZ_ASSERT(entry.isIon() || entry.isIonCache() || entry.isBaseline() || entry.isDummy()); - - // Dummy frames produce no stack frames. - if (entry.isDummy()) - return 0; - - FrameKind kind = entry.isBaseline() ? Frame_Baseline : Frame_Ion; - // Extract the stack for the entry. Assume maximum inlining depth is <64 const char* labels[64]; - uint32_t depth = entry.callStackAtAddr(rt_, returnAddr, labels, 64); + uint32_t depth = entry.callStackAtAddr(rt_, jitIter().returnAddressToFp(), labels, 64); MOZ_ASSERT(depth < 64); for (uint32_t i = 0; i < depth; i++) { if (offset + i >= end) return i; - frames[offset + i].kind = kind; - frames[offset + i].stackAddress = stackAddr; - frames[offset + i].returnAddress = returnAddr; - frames[offset + i].activation = activation_; + frames[offset + i] = physicalFrame.value(); frames[offset + i].label = labels[i]; - frames[offset + i].hasTrackedOptimizations = false; - } - - // Extract the index into the side table of optimization information and - // store it on the youngest frame. All inlined frames will have the same - // optimization information by virtue of sharing the JitcodeGlobalEntry, - // but such information is only interpretable on the youngest frame. - // - // FIXMEshu: disabled until we can ensure the optimization info is live - // when we write out the JSON stream of the profile. - if (false && entry.hasTrackedOptimizations()) { - uint32_t dummy; - mozilla::Maybe index = entry.trackedOptimizationIndexAtAddr(returnAddr, &dummy); - frames[offset].hasTrackedOptimizations = index.isSome(); } return depth; } +Maybe +JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const +{ + jit::JitcodeGlobalEntry unused; + return getPhysicalFrameAndEntry(&unused); +} + bool JS::ProfilingFrameIterator::isAsmJS() const { @@ -1949,3 +1959,22 @@ JS::ProfilingFrameIterator::isJit() const { return activation_->isJit(); } + +JS_PUBLIC_API(void) +JS::ForEachProfiledFrame(JSRuntime* rt, void* addr, ForEachProfiledFrameOp& op) +{ + jit::JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable(); + jit::JitcodeGlobalEntry entry; + table->lookupInfallible(addr, &entry, rt); + + // Extract the stack for the entry. Assume maximum inlining depth is <64 + const char* labels[64]; + uint32_t depth = entry.callStackAtAddr(rt, addr, labels, 64); + MOZ_ASSERT(depth < 64); + for (uint32_t i = depth; i != 0; i--) { + // All inlined frames will have the same optimization information by + // virtue of sharing the JitcodeGlobalEntry, but such information is + // only interpretable on the youngest frame. + op(labels[i - 1], i == 1 && entry.hasTrackedOptimizations()); + } +} diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 75ea750463..0383935487 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -13,6 +13,7 @@ #include "mozilla/TypeTraits.h" #include "gc/Marking.h" +#include "js/UbiNode.h" #include "jscntxtinlines.h" #include "jscompartmentinlines.h" @@ -66,6 +67,23 @@ JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) : mallocSizeOf(flat.rawTwoByteChars()); } +size_t +JS::ubi::Concrete::size(mozilla::MallocSizeOf mallocSizeOf) const +{ + JSString &str = get(); + size_t size = str.isFatInline() ? sizeof(JSFatInlineString) : sizeof(JSString); + + // We can't use mallocSizeof on things in the nursery. At the moment, + // strings are never in the nursery, but that may change. + MOZ_ASSERT(!IsInsideNursery(&str)); + size += str.sizeOfExcludingThis(mallocSizeOf); + + return size; +} + +template<> const char16_t JS::ubi::TracerConcrete::concreteTypeName[] = + MOZ_UTF16("JSString"); + #ifdef DEBUG template diff --git a/js/src/vm/String.h b/js/src/vm/String.h index ffda7b532d..aefd13c6c5 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -1095,13 +1095,13 @@ NameToId(PropertyName* name) return NON_INTEGER_ATOM_TO_JSID(name); } -class AutoNameVector : public AutoVectorRooter +class AutoNameVector : public JS::AutoVectorRooterBase { - typedef AutoVectorRooter BaseType; + typedef AutoVectorRooterBase BaseType; public: explicit AutoNameVector(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoVectorRooter(cx, NAMEVECTOR) + : AutoVectorRooterBase(cx, NAMEVECTOR) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } diff --git a/js/src/vm/Symbol.h b/js/src/vm/Symbol.h index 3209542ef2..9c05cf4fef 100644 --- a/js/src/vm/Symbol.h +++ b/js/src/vm/Symbol.h @@ -62,6 +62,11 @@ class Symbol : public js::gc::TenuredCell } inline void finalize(js::FreeOp*) {} + static MOZ_ALWAYS_INLINE void writeBarrierPre(Symbol* thing) { + if (thing && !thing->isWellKnownSymbol()) + thing->asTenured().writeBarrierPre(thing); + } + #ifdef DEBUG void dump(FILE* fp = stderr); #endif diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index d7a3307766..7653a12843 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -27,6 +27,7 @@ #include "jit/Ion.h" #include "jit/IonAnalysis.h" #include "jit/JitCompartment.h" +#include "jit/OptimizationTracking.h" #include "js/MemoryMetrics.h" #include "vm/HelperThreads.h" #include "vm/Opcodes.h" @@ -419,13 +420,13 @@ TypeSet::isSubset(const TypeSet* other) const } bool -TypeSet::objectsIntersect(const TypeSet *other) const +TypeSet::objectsIntersect(const TypeSet* other) const { if (unknownObject() || other->unknownObject()) return true; for (unsigned i = 0; i < getObjectCount(); i++) { - ObjectKey *key = getObject(i); + ObjectKey* key = getObject(i); if (!key) continue; if (other->hasType(ObjectType(key))) @@ -435,8 +436,9 @@ TypeSet::objectsIntersect(const TypeSet *other) const return false; } +template bool -TypeSet::enumerateTypes(TypeList* list) const +TypeSet::enumerateTypes(TypeListT* list) const { /* If any type is possible, there's no need to worry about specifics. */ if (flags & TYPE_FLAG_UNKNOWN) @@ -468,6 +470,9 @@ TypeSet::enumerateTypes(TypeList* list) const return true; } +template bool TypeSet::enumerateTypes(TypeList* list) const; +template bool TypeSet::enumerateTypes(jit::TempTypeList* list) const; + inline bool TypeSet::addTypesToConstraint(JSContext* cx, TypeConstraint* constraint) { @@ -638,6 +643,9 @@ ConstraintTypeSet::addType(ExclusiveContext* cxArg, Type type) void TypeSet::print(FILE* fp) { + if (!fp) + fp = stderr; + if (flags & TYPE_FLAG_NON_DATA_PROPERTY) fprintf(fp, " [non-data]"); @@ -847,15 +855,15 @@ TypeSet::unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc) return res; } -/* static */ TemporaryTypeSet * -TypeSet::removeSet(TemporaryTypeSet *input, TemporaryTypeSet *removal, LifoAlloc *alloc) +/* static */ TemporaryTypeSet* +TypeSet::removeSet(TemporaryTypeSet* input, TemporaryTypeSet* removal, LifoAlloc* alloc) { // Only allow removal of primitives and the "AnyObject" flag. MOZ_ASSERT(!removal->unknown()); MOZ_ASSERT_IF(!removal->unknownObject(), removal->getObjectCount() == 0); uint32_t flags = input->baseFlags() & ~removal->baseFlags(); - TemporaryTypeSet *res = + TemporaryTypeSet* res = alloc->new_(flags, static_cast(nullptr)); if (!res) return nullptr; @@ -1227,7 +1235,7 @@ TypeSet::ObjectKey::ensureTrackedProperty(JSContext* cx, jsid id) } bool -HeapTypeSetKey::instantiate(JSContext *cx) +HeapTypeSetKey::instantiate(JSContext* cx) { if (maybeTypes()) return true; @@ -1235,13 +1243,13 @@ HeapTypeSetKey::instantiate(JSContext *cx) cx->clearPendingException(); return false; } - JSObject *obj = object()->isSingleton() ? object()->singleton() : nullptr; + JSObject* obj = object()->isSingleton() ? object()->singleton() : nullptr; maybeTypes_ = object()->maybeGroup()->getProperty(cx, obj, id()); return maybeTypes_ != nullptr; } static bool -CheckFrozenTypeSet(JSContext *cx, TemporaryTypeSet *frozen, StackTypeSet *actual) +CheckFrozenTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual) { // Return whether the types frozen for a script during compilation are // still valid. Also check for any new types added to the frozen set during @@ -1758,7 +1766,7 @@ class ConstraintDataFreezeObjectForInlinedCall ConstraintDataFreezeObjectForInlinedCall() {} - const char *kind() { return "freezeObjectForInlinedCall"; } + const char* kind() { return "freezeObjectForInlinedCall"; } bool invalidateOnNewType(TypeSet::Type type) { return false; } bool invalidateOnNewPropertyState(TypeSet* property) { return false; } @@ -1768,8 +1776,8 @@ class ConstraintDataFreezeObjectForInlinedCall return true; } - bool constraintHolds(JSContext *cx, - const HeapTypeSetKey &property, TemporaryTypeSet *expected) + bool constraintHolds(JSContext* cx, + const HeapTypeSetKey& property, TemporaryTypeSet* expected) { return true; } @@ -1781,9 +1789,9 @@ class ConstraintDataFreezeObjectForInlinedCall // invalid. class ConstraintDataFreezeObjectForTypedArrayData { - NativeObject *obj; + NativeObject* obj; - void *viewData; + void* viewData; uint32_t length; public: @@ -1795,18 +1803,18 @@ class ConstraintDataFreezeObjectForTypedArrayData MOZ_ASSERT(tarray.isSingleton()); } - const char *kind() { return "freezeObjectForTypedArrayData"; } + const char* kind() { return "freezeObjectForTypedArrayData"; } bool invalidateOnNewType(TypeSet::Type type) { return false; } - bool invalidateOnNewPropertyState(TypeSet *property) { return false; } - bool invalidateOnNewObjectState(ObjectGroup *group) { + bool invalidateOnNewPropertyState(TypeSet* property) { return false; } + bool invalidateOnNewObjectState(ObjectGroup* group) { MOZ_ASSERT(obj->group() == group); TypedArrayObject &tarr = obj->as(); return tarr.viewData() != viewData || tarr.length() != length; } - bool constraintHolds(JSContext *cx, - const HeapTypeSetKey &property, TemporaryTypeSet *expected) + bool constraintHolds(JSContext* cx, + const HeapTypeSetKey& property, TemporaryTypeSet* expected) { return !invalidateOnNewObjectState(property.object()->maybeGroup()); } @@ -2377,8 +2385,10 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid } bool -js::ClassCanHaveExtraProperties(const Class *clasp) +js::ClassCanHaveExtraProperties(const Class* clasp) { + if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_) + return false; return clasp->resolve || clasp->ops.lookupProperty || clasp->ops.getProperty @@ -2487,13 +2497,13 @@ TypeZone::addPendingRecompile(JSContext* cx, JSScript* script) } void -js::PrintTypes(JSContext *cx, JSCompartment *comp, bool force) +js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force) { #ifdef DEBUG gc::AutoSuppressGC suppressGC(cx); JSAutoRequest request(cx); - Zone *zone = comp->zone(); + Zone* zone = comp->zone(); AutoEnterAnalysis enter(nullptr, zone); if (!force && !InferSpewActive(ISpewResult)) @@ -2506,7 +2516,7 @@ js::PrintTypes(JSContext *cx, JSCompartment *comp, bool force) } for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) { - ObjectGroup *group = i.get(); + ObjectGroup* group = i.get(); group->print(); } #endif @@ -2561,7 +2571,7 @@ UpdatePropertyType(ExclusiveContext* cx, HeapTypeSet* types, NativeObject* obj, } void -ObjectGroup::updateNewPropertyTypes(ExclusiveContext *cx, JSObject *objArg, jsid id, HeapTypeSet *types) +ObjectGroup::updateNewPropertyTypes(ExclusiveContext* cx, JSObject* objArg, jsid id, HeapTypeSet* types) { InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s", InferSpewColor(types), types, InferSpewColorReset(), @@ -2575,7 +2585,7 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext *cx, JSObject *objArg, jsid return; } - NativeObject *obj = &objArg->as(); + NativeObject* obj = &objArg->as(); /* * Fill the property in with any type the object already has in an own @@ -2603,7 +2613,7 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext *cx, JSObject *objArg, jsid } } else if (!JSID_IS_EMPTY(id)) { RootedId rootedId(cx, id); - Shape *shape = obj->lookup(cx, rootedId); + Shape* shape = obj->lookup(cx, rootedId); if (shape) UpdatePropertyType(cx, types, obj, shape, false); } @@ -2618,7 +2628,7 @@ ObjectGroup::updateNewPropertyTypes(ExclusiveContext *cx, JSObject *objArg, jsid } bool -ObjectGroup::addDefiniteProperties(ExclusiveContext *cx, Shape *shape) +ObjectGroup::addDefiniteProperties(ExclusiveContext* cx, Shape* shape) { if (unknownProperties()) return true; @@ -2631,7 +2641,7 @@ ObjectGroup::addDefiniteProperties(ExclusiveContext *cx, Shape *shape) if (!JSID_IS_VOID(id)) { MOZ_ASSERT_IF(shape->slot() >= shape->numFixedSlots(), shape->numFixedSlots() == NativeObject::MAX_FIXED_SLOTS); - TypeSet *types = getProperty(cx, nullptr, id); + TypeSet* types = getProperty(cx, nullptr, id); if (!types) return false; if (types->canSetDefinite(shape->slot())) @@ -2649,14 +2659,14 @@ ObjectGroup::matchDefiniteProperties(HandleObject obj) { unsigned count = getPropertyCount(); for (unsigned i = 0; i < count; i++) { - Property *prop = getProperty(i); + Property* prop = getProperty(i); if (!prop) continue; if (prop->types.definiteProperty()) { unsigned slot = prop->types.definiteSlot(); bool found = false; - Shape *shape = obj->as().lastProperty(); + Shape* shape = obj->as().lastProperty(); while (!shape->isEmptyShape()) { if (shape->slot() == slot && shape->propid() == prop->id) { found = true; @@ -2673,7 +2683,7 @@ ObjectGroup::matchDefiniteProperties(HandleObject obj) } void -js::AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, JSObject *obj, jsid id, TypeSet::Type type) +js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, jsid id, TypeSet::Type type) { MOZ_ASSERT(id == IdToTypeId(id)); @@ -2682,7 +2692,7 @@ js::AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, JSObject *obj, j AutoEnterAnalysis enter(cx); - HeapTypeSet *types = group->getProperty(cx, obj, id); + HeapTypeSet* types = group->getProperty(cx, obj, id); if (!types) return; @@ -2720,36 +2730,36 @@ js::AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, JSObject *obj, j // unboxed group are valid for the native group. if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup()) AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type); - if (ObjectGroup *unboxedGroup = group->maybeOriginalUnboxedGroup()) + if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup()) AddTypePropertyId(cx, unboxedGroup, nullptr, id, type); } void -js::AddTypePropertyId(ExclusiveContext *cx, ObjectGroup *group, JSObject *obj, jsid id, const Value &value) +js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, jsid id, const Value& value) { AddTypePropertyId(cx, group, obj, id, TypeSet::GetValueType(value)); } void -ObjectGroup::markPropertyNonData(ExclusiveContext *cx, JSObject *obj, jsid id) +ObjectGroup::markPropertyNonData(ExclusiveContext* cx, JSObject* obj, jsid id) { AutoEnterAnalysis enter(cx); id = IdToTypeId(id); - HeapTypeSet *types = getProperty(cx, obj, id); + HeapTypeSet* types = getProperty(cx, obj, id); if (types) types->setNonDataProperty(cx); } void -ObjectGroup::markPropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid id) +ObjectGroup::markPropertyNonWritable(ExclusiveContext* cx, JSObject* obj, jsid id) { AutoEnterAnalysis enter(cx); id = IdToTypeId(id); - HeapTypeSet *types = getProperty(cx, obj, id); + HeapTypeSet* types = getProperty(cx, obj, id); if (types) types->setNonWritableProperty(cx); } @@ -2757,7 +2767,7 @@ ObjectGroup::markPropertyNonWritable(ExclusiveContext *cx, JSObject *obj, jsid i bool ObjectGroup::isPropertyNonData(jsid id) { - TypeSet *types = maybeGetProperty(id); + TypeSet* types = maybeGetProperty(id); if (types) return types->nonDataProperty(); return false; @@ -2862,7 +2872,7 @@ ObjectGroup::anyNewScript() } void -ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup *replacement) +ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement) { // Clear the TypeNewScript from this ObjectGroup and, if it has been // analyzed, remove it from the newObjectGroups table so that it will not be @@ -2911,7 +2921,7 @@ ObjectGroup::maybeClearNewScriptOnOOM() } void -ObjectGroup::clearNewScript(ExclusiveContext *cx, ObjectGroup *replacement /* = nullptr*/) +ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/) { TypeNewScript* newScript = anyNewScript(); if (!newScript) @@ -3056,7 +3066,7 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint }; bool -js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, ObjectGroup *group, HandleId id) +js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* group, HandleId id) { /* * Ensure that if the properties named here could have a getter, setter or @@ -3065,10 +3075,10 @@ js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, ObjectGroup *gr */ RootedObject proto(cx, group->proto().toObjectOrNull()); while (proto) { - ObjectGroup *protoGroup = proto->getGroup(cx); + ObjectGroup* protoGroup = proto->getGroup(cx); if (!protoGroup || protoGroup->unknownProperties()) return false; - HeapTypeSet *protoTypes = protoGroup->getProperty(cx, proto, id); + HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id); if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) return false; if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_(group))) @@ -3087,7 +3097,7 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint public: ObjectGroup* group; - explicit TypeConstraintClearDefiniteSingle(ObjectGroup *group) + explicit TypeConstraintClearDefiniteSingle(ObjectGroup* group) : group(group) {} @@ -3178,7 +3188,7 @@ js::TypeMonitorCallSlow(JSContext* cx, JSObject* callee, const CallArgs& args, b } void -js::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap) +js::FillBytecodeTypeMap(JSScript* script, uint32_t* bytecodeMap) { uint32_t added = 0; for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) { @@ -3325,25 +3335,28 @@ PreliminaryObjectArray::sweep() } void -PreliminaryObjectArrayWithTemplate::trace(JSTracer *trc) +PreliminaryObjectArrayWithTemplate::trace(JSTracer* trc) { - TraceEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape"); + if (shape_) + TraceEdge(trc, &shape_, "PreliminaryObjectArrayWithTemplate_shape"); } /* static */ void -PreliminaryObjectArrayWithTemplate::writeBarrierPre(PreliminaryObjectArrayWithTemplate *objects) +PreliminaryObjectArrayWithTemplate::writeBarrierPre(PreliminaryObjectArrayWithTemplate* objects) { - if (!objects->shape()->runtimeFromAnyThread()->needsIncrementalBarrier()) + Shape* shape = objects->shape(); + + if (!shape || !shape->runtimeFromAnyThread()->needsIncrementalBarrier()) return; - JS::Zone *zone = objects->shape()->zoneFromAnyThread(); + JS::Zone* zone = shape->zoneFromAnyThread(); if (zone->needsIncrementalBarrier()) objects->trace(zone->barrierTracer()); } // Return whether shape consists entirely of plain data properties. static bool -OnlyHasDataProperties(Shape *shape) +OnlyHasDataProperties(Shape* shape) { MOZ_ASSERT(!shape->inDictionary()); @@ -3364,8 +3377,8 @@ OnlyHasDataProperties(Shape *shape) // Find the most recent common ancestor of two shapes, or an empty shape if // the two shapes have no common ancestor. -static Shape * -CommonPrefix(Shape *first, Shape *second) +static Shape* +CommonPrefix(Shape* first, Shape* second) { MOZ_ASSERT(OnlyHasDataProperties(first)); MOZ_ASSERT(OnlyHasDataProperties(second)); @@ -3384,7 +3397,7 @@ CommonPrefix(Shape *first, Shape *second) } void -PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext *cx, ObjectGroup *group, bool force) +PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGroup* group, bool force) { // Don't perform the analyses until sufficient preliminary objects have // been allocated. @@ -3396,33 +3409,37 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext *cx, ObjectGro ScopedJSDeletePtr preliminaryObjects(this); group->detachPreliminaryObjects(); - MOZ_ASSERT(shape()->slotSpan() != 0); - MOZ_ASSERT(OnlyHasDataProperties(shape())); + if (shape()) { + MOZ_ASSERT(shape()->slotSpan() != 0); + MOZ_ASSERT(OnlyHasDataProperties(shape())); - // Make sure all the preliminary objects reflect the properties originally - // in the template object. - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject *objBase = preliminaryObjects->get(i); - if (!objBase) - continue; - PlainObject *obj = &objBase->as(); + // Make sure all the preliminary objects reflect the properties originally + // in the template object. + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + JSObject* objBase = preliminaryObjects->get(i); + if (!objBase) + continue; + PlainObject* obj = &objBase->as(); - if (obj->inDictionaryMode() || !OnlyHasDataProperties(obj->lastProperty())) - return; + if (obj->inDictionaryMode() || !OnlyHasDataProperties(obj->lastProperty())) + return; - if (CommonPrefix(obj->lastProperty(), shape()) != shape()) - return; + if (CommonPrefix(obj->lastProperty(), shape()) != shape()) + return; + } } TryConvertToUnboxedLayout(cx, shape(), group, preliminaryObjects); if (group->maybeUnboxedLayout()) return; - // We weren't able to use an unboxed layout, but since the preliminary - // still reflect the template object's properties, and all objects in the - // future will be created with those properties, the properties can be - // marked as definite for objects in the group. - group->addDefiniteProperties(cx, shape()); + if (shape()) { + // We weren't able to use an unboxed layout, but since the preliminary + // still reflect the template object's properties, and all objects in the + // future will be created with those properties, the properties can be + // marked as definite for objects in the group. + group->addDefiniteProperties(cx, shape()); + } } ///////////////////////////////////////////////////////////////////// @@ -3462,9 +3479,9 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun) // Make a TypeNewScript with the same initializer list as |newScript| but with // a new template object. -/* static */ TypeNewScript * -TypeNewScript::makeNativeVersion(JSContext *cx, TypeNewScript *newScript, - PlainObject *templateObject) +/* static */ TypeNewScript* +TypeNewScript::makeNativeVersion(JSContext* cx, TypeNewScript* newScript, + PlainObject* templateObject) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); @@ -3475,7 +3492,7 @@ TypeNewScript::makeNativeVersion(JSContext *cx, TypeNewScript *newScript, nativeNewScript->function_ = newScript->function(); nativeNewScript->templateObject_ = templateObject; - Initializer *cursor = newScript->initializerList; + Initializer* cursor = newScript->initializerList; while (cursor->kind != Initializer::DONE) { cursor++; } size_t initializerLength = cursor - newScript->initializerList + 1; @@ -3514,7 +3531,7 @@ ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocK { MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty())); - Shape *newShape = ReshapeForAllocKind(cx, obj->lastProperty(), + Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), obj->getTaggedProto(), allocKind); if (!newShape) @@ -3895,12 +3912,12 @@ TypeNewScript::trace(JSTracer* trc) } /* static */ void -TypeNewScript::writeBarrierPre(TypeNewScript *newScript) +TypeNewScript::writeBarrierPre(TypeNewScript* newScript) { if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier()) return; - JS::Zone *zone = newScript->function()->zoneFromAnyThread(); + JS::Zone* zone = newScript->function()->zoneFromAnyThread(); if (zone->needsIncrementalBarrier()) newScript->trace(zone->barrierTracer()); } @@ -3939,11 +3956,11 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom) clearObjects(); objectCount = 0; for (unsigned i = 0; i < oldCapacity; i++) { - ObjectKey *key = oldArray[i]; + ObjectKey* key = oldArray[i]; if (!key) continue; if (!IsObjectKeyAboutToBeFinalized(&key)) { - ObjectKey **pentry = + ObjectKey** pentry = TypeHashSet::Insert (zone->types.typeLifoAlloc, objectSet, objectCount, key); if (pentry) { @@ -3973,9 +3990,9 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom) } setBaseObjectCount(objectCount); } else if (objectCount == 1) { - ObjectKey *key = (ObjectKey *) objectSet; + ObjectKey* key = (ObjectKey*) objectSet; if (!IsObjectKeyAboutToBeFinalized(&key)) { - objectSet = reinterpret_cast(key); + objectSet = reinterpret_cast(key); } else { // As above, mark type sets containing objects with unknown // properties as unknown. diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index d8c9775cdd..fa7bca125d 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -438,14 +438,14 @@ class TypeSet * TypeSet b can only contain primitives or be any object. No support for * specific objects. The result should not be modified further. */ - static TemporaryTypeSet *removeSet(TemporaryTypeSet *a, TemporaryTypeSet *b, LifoAlloc *alloc); + static TemporaryTypeSet* removeSet(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc); /* Add a type to this set using the specified allocator. */ void addType(Type type, LifoAlloc* alloc); /* Get a list of all types in this set. */ typedef Vector TypeList; - bool enumerateTypes(TypeList* list) const; + template bool enumerateTypes(TypeListT* list) const; /* * Iterate through the objects in this set. getObjectCount overapproximates @@ -492,7 +492,7 @@ class TypeSet return this->isSubset(other) && other->isSubset(this); } - bool objectsIntersect(const TypeSet *other) const; + bool objectsIntersect(const TypeSet* other) const; /* Forward all types in this set to the specified constraint. */ bool addTypesToConstraint(JSContext* cx, TypeConstraint* constraint); @@ -526,13 +526,13 @@ class TypeSet // Get the type of a possibly optimized out or uninitialized let value. // This generally only happens on unconditional type monitors on bailing // out of Ion, such as for argument and local types. - static inline Type GetMaybeUntrackedValueType(const Value &val); + static inline Type GetMaybeUntrackedValueType(const Value& val); - static void MarkTypeRoot(JSTracer *trc, Type *v, const char *name); - static void MarkTypeUnbarriered(JSTracer *trc, Type *v, const char *name); - static bool IsTypeMarked(Type *v); + static void MarkTypeRoot(JSTracer* trc, Type* v, const char* name); + static void MarkTypeUnbarriered(JSTracer* trc, Type* v, const char* name); + static bool IsTypeMarked(Type* v); static bool IsTypeAllocatedDuringIncremental(Type v); - static bool IsTypeAboutToBeFinalized(Type *v); + static bool IsTypeAboutToBeFinalized(Type* v); }; /* @@ -805,19 +805,19 @@ class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray HeapPtrShape shape_; public: - explicit PreliminaryObjectArrayWithTemplate(Shape *shape) + explicit PreliminaryObjectArrayWithTemplate(Shape* shape) : shape_(shape) {} - Shape *shape() { + Shape* shape() { return shape_; } - void maybeAnalyze(ExclusiveContext *cx, ObjectGroup *group, bool force = false); + void maybeAnalyze(ExclusiveContext* cx, ObjectGroup* group, bool force = false); - void trace(JSTracer *trc); + void trace(JSTracer* trc); - static void writeBarrierPre(PreliminaryObjectArrayWithTemplate *preliminaryObjects); + static void writeBarrierPre(PreliminaryObjectArrayWithTemplate* preliminaryObjects); }; // New script properties analyses overview. @@ -917,7 +917,7 @@ class TypeNewScript js_free(initializerList); } - static void writeBarrierPre(TypeNewScript *newScript); + static void writeBarrierPre(TypeNewScript* newScript); bool analyzed() const { return preliminaryObjects == nullptr; @@ -948,17 +948,17 @@ class TypeNewScript bool rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group); static void make(JSContext* cx, ObjectGroup* group, JSFunction* fun); - static TypeNewScript *makeNativeVersion(JSContext *cx, TypeNewScript *newScript, - PlainObject *templateObject); + static TypeNewScript* makeNativeVersion(JSContext* cx, TypeNewScript* newScript, + PlainObject* templateObject); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; /* Is this a reasonable PC to be doing inlining on? */ -inline bool isInlinableCall(jsbytecode *pc); +inline bool isInlinableCall(jsbytecode* pc); bool -ClassCanHaveExtraProperties(const Class *clasp); +ClassCanHaveExtraProperties(const Class* clasp); /* Whether obj or any of its prototypes have an indexed property. */ bool @@ -1251,9 +1251,9 @@ enum SpewChannel { #ifdef DEBUG -const char * InferSpewColorReset(); -const char * InferSpewColor(TypeConstraint* constraint); -const char * InferSpewColor(TypeSet* types); +const char* InferSpewColorReset(); +const char* InferSpewColor(TypeConstraint* constraint); +const char* InferSpewColor(TypeSet* types); void InferSpew(SpewChannel which, const char* fmt, ...); @@ -1262,9 +1262,9 @@ bool ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Va #else -inline const char * InferSpewColorReset() { return nullptr; } -inline const char * InferSpewColor(TypeConstraint* constraint) { return nullptr; } -inline const char * InferSpewColor(TypeSet* types) { return nullptr; } +inline const char* InferSpewColorReset() { return nullptr; } +inline const char* InferSpewColor(TypeConstraint* constraint) { return nullptr; } +inline const char* InferSpewColor(TypeSet* types) { return nullptr; } inline void InferSpew(SpewChannel which, const char* fmt, ...) {} #endif diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 3ceace7658..307c84fd9d 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -852,6 +852,7 @@ TypedArrayObject::sharedTypedArrayPrototypeClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -1819,6 +1820,7 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ nullptr, /* convert */ \ nullptr, /* finalize */ \ nullptr, /* call */ \ @@ -1864,6 +1866,7 @@ const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = { nullptr, /* setProperty */ \ nullptr, /* enumerate */ \ nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ nullptr, /* convert */ \ nullptr, /* finalize */ \ nullptr, /* call */ \ @@ -1919,6 +1922,7 @@ const Class DataViewObject::class_ = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -2127,6 +2131,51 @@ js::StringIsTypedArrayIndex(const char16_t* s, size_t length, uint64_t* indexp); template bool js::StringIsTypedArrayIndex(const Latin1Char* s, size_t length, uint64_t* indexp); +/* ES6 draft rev 34 (2015 Feb 20) 9.4.5.3 [[DefineOwnProperty]] step 3.c. */ +bool +js::DefineTypedArrayElement(JSContext *cx, HandleObject obj, uint64_t index, + Handle desc, ObjectOpResult &result) +{ + MOZ_ASSERT(IsAnyTypedArray(obj)); + + // These are all substeps of 3.c. + // Steps i-vi. + // We (wrongly) ignore out of range defines with a value. + if (index >= AnyTypedArrayLength(obj)) + return result.succeed(); + + // Step vii. + if (desc.isAccessorDescriptor()) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + + // Step viii. + if (desc.hasConfigurable() && desc.configurable()) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + + // Step ix. + if (desc.hasEnumerable() && !desc.enumerable()) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + + // Step x. + if (desc.hasWritable() && !desc.writable()) + return result.fail(JSMSG_CANT_REDEFINE_PROP); + + // Step xi. + if (desc.hasValue()) { + double d; + if (!ToNumber(cx, desc.value(), &d)) + return false; + + if (obj->is()) + TypedArrayObject::setElement(obj->as(), index, d); + else + SharedTypedArrayObject::setElement(obj->as(), index, d); + } + + // Step xii. + return result.succeed(); +} + /* JS Friend API */ JS_FRIEND_API(bool) diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index d7fe009f7b..b126314cde 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -279,6 +279,14 @@ IsTypedArrayIndex(jsid id, uint64_t* indexp) return StringIsTypedArrayIndex(s, length, indexp); } +/* + * Implements [[DefineOwnProperty]] for TypedArrays and SharedTypedArrays + * when the property key is a TypedArray index. + */ +bool +DefineTypedArrayElement(JSContext *cx, HandleObject arr, uint64_t index, + Handle desc, ObjectOpResult &result); + static inline unsigned TypedArrayShift(Scalar::Type viewType) { diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index 7272b26a7f..e68af4648e 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -8,6 +8,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/Range.h" #include "mozilla/Scoped.h" #include "mozilla/UniquePtr.h" @@ -32,6 +33,7 @@ #include "vm/Debugger-inl.h" using mozilla::Some; +using mozilla::UniquePtr; using JS::HandleValue; using JS::Value; using JS::ZoneSet; @@ -43,7 +45,6 @@ using JS::ubi::SimpleEdge; using JS::ubi::SimpleEdgeVector; using JS::ubi::TracerConcrete; using JS::ubi::TracerConcreteWithCompartment; -using JS::ubi::TracerConcreteWithCompartmentAndClassName; // All operations on null ubi::Nodes crash. const char16_t* Concrete::typeName() const { MOZ_CRASH("null ubi::Node"); } @@ -224,17 +225,37 @@ TracerConcreteWithCompartment::compartment() const return TracerBase::get().compartment(); } -template -const char * -TracerConcreteWithCompartmentAndClassName::jsObjectClassName() const +const char* +Concrete::jsObjectClassName() const { - return TracerBase::get().getClass()->name; + return Concrete::get().getClass()->name; +} + +bool +Concrete::jsObjectConstructorName(JSContext* cx, + UniquePtr& outName) const +{ + JSAtom* name = Concrete::get().maybeConstructorDisplayAtom(); + if (!name) { + outName.reset(nullptr); + return true; + } + + auto len = JS_GetStringLength(name); + auto size = len + 1; + + outName.reset(cx->pod_malloc(size * sizeof(char16_t))); + if (!outName) + return false; + + mozilla::Range chars(outName.get(), size); + if (!JS_CopyStringChars(cx, chars, name)) + return false; + + outName[len] = '\0'; + return true; } -template<> const char16_t TracerConcrete::concreteTypeName[] = - MOZ_UTF16("JSObject"); -template<> const char16_t TracerConcrete::concreteTypeName[] = - MOZ_UTF16("JSString"); template<> const char16_t TracerConcrete::concreteTypeName[] = MOZ_UTF16("JS::Symbol"); template<> const char16_t TracerConcrete::concreteTypeName[] = diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index cf59706648..5c1b704d82 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -11,6 +11,7 @@ #include "jsobjinlines.h" +#include "gc/Nursery-inl.h" #include "vm/Shape-inl.h" using mozilla::ArrayLength; @@ -70,6 +71,8 @@ static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1; /* static */ bool UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) { + gc::AutoSuppressGC suppress(cx); + using namespace jit; if (!cx->compartment()->ensureJitCompartmentExists(cx)) @@ -90,8 +93,8 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) #ifdef JS_CODEGEN_X86 propertiesReg = eax; newKindReg = ecx; - masm.loadPtr(Address(StackPointer, sizeof(void*)), propertiesReg); - masm.loadPtr(Address(StackPointer, 2 * sizeof(void*)), newKindReg); + masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg); + masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg); #else propertiesReg = IntArgReg0; newKindReg = IntArgReg1; @@ -106,8 +109,11 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny(); LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs); - for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter) - masm.Push(*iter); + masm.PushRegsInMask(savedNonVolatileRegisters); + + // The scratch double register might be used by MacroAssembler methods. + if (ScratchDoubleReg.volatile_()) + masm.push(ScratchDoubleReg); Label failure, tenuredObject, allocated; masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject); @@ -129,7 +135,7 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) // buffer entry for the new object. Label postBarrier; for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property &property = layout.properties()[i]; + const UnboxedLayout::Property& property = layout.properties()[i]; if (property.type == JSVAL_TYPE_OBJECT) { Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); Label notObject; @@ -143,12 +149,20 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) masm.jump(&allocated); masm.bind(&postBarrier); + LiveGeneralRegisterSet liveVolatileRegisters; + liveVolatileRegisters.add(propertiesReg); + if (object.volatile_()) + liveVolatileRegisters.add(object); + masm.PushRegsInMask(liveVolatileRegisters); + masm.mov(ImmPtr(cx->runtime()), scratch1); masm.setupUnalignedABICall(2, scratch2); masm.passABIArg(scratch1); masm.passABIArg(object); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); + masm.PopRegsInMask(liveVolatileRegisters); + masm.bind(&allocated); ValueOperand valueOperand; @@ -176,8 +190,10 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) Register payloadReg = masm.extractObject(valueOperand, scratch1); - if (!types->hasType(TypeSet::AnyObjectType())) - masm.guardObjectType(payloadReg, types, scratch2, &failureStoreObject); + if (!types->hasType(TypeSet::AnyObjectType())) { + Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1; + masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject); + } masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, TypedOrValueRegister(MIRType_Object, @@ -204,10 +220,11 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) masm.movePtr(object, ReturnReg); // Restore non-volatile registers which were saved on entry. - for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter) - masm.Pop(*iter); + if (ScratchDoubleReg.volatile_()) + masm.pop(ScratchDoubleReg); + masm.PopRegsInMask(savedNonVolatileRegisters); - masm.ret(); + masm.abiret(); masm.bind(&failureStoreOther); @@ -241,7 +258,7 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) masm.jump(&done); Linker linker(masm); - AutoFlushICache afc("RegExp"); + AutoFlushICache afc("UnboxedObject"); JitCode* code = linker.newCode(cx, OTHER_CODE); if (!code) return false; @@ -253,20 +270,15 @@ UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) void UnboxedLayout::detachFromCompartment() { - remove(); + if (isInList()) + remove(); } -///////////////////////////////////////////////////////////////////// -// UnboxedPlainObject -///////////////////////////////////////////////////////////////////// - -bool -UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, - const Value& v) +static inline bool +SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, + uint8_t* p, JSValueType type, const Value& v) { - uint8_t* p = &data_[property.offset]; - - switch (property.type) { + switch (type) { case JSVAL_TYPE_BOOLEAN: if (v.isBoolean()) { *p = v.toBoolean(); @@ -301,14 +313,16 @@ UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property // Update property types when writing object properties. Types for // other properties were captured when the unboxed layout was // created. - AddTypePropertyId(cx, this, NameToId(property.name), v); + AddTypePropertyId(cx, unboxedObject, id, v); // Manually trigger post barriers on the whole object. If we treat // the pointer as a HeapPtrObject we will get confused later if the // object is converted to its native representation. JSObject* obj = v.toObjectOrNull(); - if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(this)) - cx->asJSContext()->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this); + if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) { + JSRuntime* rt = cx->asJSContext()->runtime(); + rt->gc.storeBuffer.putWholeCellFromMainThread(unboxedObject); + } *reinterpret_cast(p) = obj; return true; @@ -320,20 +334,26 @@ UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property } } -Value -UnboxedPlainObject::getValue(const UnboxedLayout::Property& property) +static inline Value +GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized) { - uint8_t* p = &data_[property.offset]; - - switch (property.type) { + switch (type) { case JSVAL_TYPE_BOOLEAN: return BooleanValue(*p != 0); case JSVAL_TYPE_INT32: return Int32Value(*reinterpret_cast(p)); - case JSVAL_TYPE_DOUBLE: - return DoubleValue(*reinterpret_cast(p)); + case JSVAL_TYPE_DOUBLE: { + // During unboxed plain object creation, non-GC thing properties are + // left uninitialized. This is normally fine, since the properties will + // be filled in shortly, but if they are read before that happens we + // need to make sure that doubles are canonical. + double d = *reinterpret_cast(p); + if (maybeUninitialized) + return DoubleValue(JS::CanonicalizeNaN(d)); + return DoubleValue(d); + } case JSVAL_TYPE_STRING: return StringValue(*reinterpret_cast(p)); @@ -346,6 +366,26 @@ UnboxedPlainObject::getValue(const UnboxedLayout::Property& property) } } +///////////////////////////////////////////////////////////////////// +// UnboxedPlainObject +///////////////////////////////////////////////////////////////////// + +bool +UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, + const Value& v) +{ + uint8_t* p = &data_[property.offset]; + return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v); +} + +Value +UnboxedPlainObject::getValue(const UnboxedLayout::Property& property, + bool maybeUninitialized /* = false */) +{ + uint8_t* p = &data_[property.offset]; + return GetUnboxedValue(p, property.type, maybeUninitialized); +} + void UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj) { @@ -356,7 +396,7 @@ UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj) } const UnboxedLayout& layout = obj->as().layoutDontCheckGeneration(); - const int32_t *list = layout.traceList(); + const int32_t* list = layout.traceList(); if (!list) return; @@ -388,6 +428,12 @@ UnboxedPlainObject::ensureExpando(JSContext* cx, Handle obj if (!expando) return nullptr; + // If the expando is tenured then the original object must also be tenured. + // Otherwise barriers triggered on the original object for writes to the + // expando (as can happen in the JIT) won't see the tenured->nursery edge. + // See WholeCellEdges::mark. + MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj)); + // As with setValue(), we need to manually trigger post barriers on the // whole object. If we treat the field as a HeapPtrObject and later convert // the object to its native representation, we will end up with a corrupted @@ -411,6 +457,21 @@ UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid return false; } +static bool +PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup) +{ + HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id); + TypeSet::TypeList types; + if (!typeProperty->enumerateTypes(&types)) { + ReportOutOfMemory(cx); + return false; + } + MOZ_ASSERT(!types.empty()); + for (size_t j = 0; j < types.length(); j++) + AddTypePropertyId(cx, newGroup, nullptr, id, types[j]); + return true; +} + /* static */ bool UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) { @@ -429,6 +490,8 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) // group and objects that were allocated using the replacement new group. RootedObjectGroup replacementNewGroup(cx); if (layout.newScript()) { + MOZ_ASSERT(!layout.isArray()); + replacementNewGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto); if (!replacementNewGroup) return false; @@ -440,7 +503,7 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) return false; for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property &property = layout.properties()[i]; + const UnboxedLayout::Property& property = layout.properties()[i]; if (!templateObject->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE)) return false; MOZ_ASSERT(templateObject->slotSpan() == i + 1); @@ -458,12 +521,24 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) group->clearNewScript(cx, replacementNewGroup); } + const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_; + size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind()); - size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind()); - RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0)); + if (layout.isArray()) { + // The length shape to use for arrays is cached via a modified initial + // shape for array objects. Create an array now to make sure this entry + // is instantiated. + if (!NewDenseEmptyArray(cx)) + return false; + } + + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0)); if (!shape) return false; + MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0); + + // Add shapes for each property, if this is for a plain object. for (size_t i = 0; i < layout.properties().length(); i++) { const UnboxedLayout::Property& property = layout.properties()[i]; @@ -476,26 +551,26 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) } ObjectGroup* nativeGroup = - ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto, + ObjectGroupCompartment::makeGroup(cx, clasp, proto, group->flags() & OBJECT_FLAG_DYNAMIC_MASK); if (!nativeGroup) return false; // Propagate all property types from the old group to the new group. - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - jsid id = NameToId(property.name); - - HeapTypeSet* typeProperty = group->maybeGetProperty(id); - TypeSet::TypeList types; - if (!typeProperty->enumerateTypes(&types)) + if (layout.isArray()) { + if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup)) return false; - MOZ_ASSERT(!types.empty()); - for (size_t j = 0; j < types.length(); j++) - AddTypePropertyId(cx, nativeGroup, nullptr, id, types[j]); - HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); - if (nativeProperty->canSetDefinite(i)) - nativeProperty->setDefinite(i); + } else { + for (size_t i = 0; i < layout.properties().length(); i++) { + const UnboxedLayout::Property& property = layout.properties()[i]; + jsid id = NameToId(property.name); + if (!PropagatePropertyTypes(cx, id, group, nativeGroup)) + return false; + + HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); + if (nativeProperty->canSetDefinite(i)) + nativeProperty->setDefinite(i); + } } layout.nativeGroup_ = nativeGroup; @@ -526,10 +601,15 @@ UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj) AutoValueVector values(cx); for (size_t i = 0; i < layout.properties().length(); i++) { - if (!values.append(obj->as().getValue(layout.properties()[i]))) + // We might be reading properties off the object which have not been + // initialized yet. Make sure any double values we read here are + // canonicalized. + if (!values.append(obj->as().getValue(layout.properties()[i], true))) return false; } + JSObject::writeBarrierPre(expando); + obj->setGroup(layout.nativeGroup()); obj->as().setLastPropertyMakeNative(cx, layout.nativeShape()); @@ -649,7 +729,10 @@ UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup } #ifndef JS_CODEGEN_NONE - if (cx->isJSContext() && !layout.constructorCode()) { + if (cx->isJSContext() && + !layout.constructorCode() && + cx->asJSContext()->runtime()->jitSupportsFloatingPoint) + { if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group)) return nullptr; } @@ -682,9 +765,9 @@ UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj, /* static */ bool UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc, - ObjectOpResult &result) + ObjectOpResult& result) { - const UnboxedLayout &layout = obj->as().layout(); + const UnboxedLayout& layout = obj->as().layout(); if (const UnboxedLayout::Property* property = layout.lookup(id)) { if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { @@ -699,7 +782,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId return false; return DefineProperty(cx, obj, id, desc, result) && -result.checkStrict(cx, obj, id); + result.checkStrict(cx, obj, id); } // Define the property on the expando object. @@ -762,7 +845,7 @@ UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObjec UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) { - const UnboxedLayout &layout = obj->as().layout(); + const UnboxedLayout& layout = obj->as().layout(); if (const UnboxedLayout::Property* property = layout.lookup(id)) { if (receiver.isObject() && obj == &receiver.toObject()) { @@ -786,7 +869,7 @@ UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id RootedValue nreceiver(cx, (receiver.isObject() && obj == &receiver.toObject()) ? ObjectValue(*expando) : receiver); -return SetProperty(cx, nexpando, id, v, nreceiver, result); + return SetProperty(cx, nexpando, id, v, nreceiver, result); } } @@ -809,7 +892,11 @@ UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { if (expando->containsShapeOrElement(cx, id)) { RootedObject nexpando(cx, expando); - return GetOwnPropertyDescriptor(cx, nexpando, id, desc); + if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc)) + return false; + if (desc.object() == nexpando) + desc.object().set(obj); + return true; } } @@ -819,7 +906,7 @@ UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj /* static */ bool UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult &result) + ObjectOpResult& result) { if (!convertToNative(cx, obj)) return false; @@ -876,13 +963,16 @@ const Class UnboxedExpandoObject::class_ = { const Class UnboxedPlainObject::class_ = { js_Object_str, - Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, + Class::NON_NATIVE | + JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_HAS_CACHED_PROTO(JSProto_Object), nullptr, /* addProperty */ nullptr, /* delProperty */ nullptr, /* getProperty */ nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ nullptr, /* finalize */ nullptr, /* call */ @@ -907,6 +997,619 @@ const Class UnboxedPlainObject::class_ = { } }; +///////////////////////////////////////////////////////////////////// +// UnboxedArrayObject +///////////////////////////////////////////////////////////////////// + +/* static */ bool +UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj) +{ + const UnboxedLayout& layout = obj->as().layout(); + + if (!layout.nativeGroup()) { + if (!UnboxedLayout::makeNativeGroup(cx, obj->group())) + return false; + } + + size_t length = obj->as().length(); + size_t initlen = obj->as().initializedLength(); + + AutoValueVector values(cx); + for (size_t i = 0; i < initlen; i++) { + if (!values.append(obj->as().getElement(i))) + return false; + } + + obj->setGroup(layout.nativeGroup()); + + ArrayObject* aobj = &obj->as(); + aobj->setLastPropertyMakeNative(cx, layout.nativeShape()); + + // Make sure there is at least one element, so that this array does not + // use emptyObjectElements. + if (!aobj->ensureElements(cx, Max(initlen, 1))) + return false; + + MOZ_ASSERT(!aobj->getDenseInitializedLength()); + aobj->setDenseInitializedLength(initlen); + aobj->initDenseElements(0, values.begin(), initlen); + aobj->setLengthInt32(length); + + return true; +} + +/* static */ UnboxedArrayObject* +UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length, + NewObjectKind newKind) +{ + MOZ_ASSERT(length <= MaximumCapacity); + + MOZ_ASSERT(group->clasp() == &class_); + uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType()); + uint32_t nbytes = offsetOfInlineElements() + elementSize * length; + + UnboxedArrayObject* res; + if (nbytes <= JSObject::MAX_BYTE_SIZE) { + gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes); + + // If there was no provided length information, pick an allocation kind + // to accommodate small arrays (as is done for normal native arrays). + if (length == 0) + allocKind = gc::AllocKind::OBJECT8; + + res = NewObjectWithGroup(cx, group, allocKind, newKind); + if (!res) + return nullptr; + res->setInlineElements(); + + size_t capacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize; + res->setCapacityIndex(exactCapacityIndex(capacity)); + } else { + res = NewObjectWithGroup(cx, group, gc::AllocKind::OBJECT0, newKind); + if (!res) + return nullptr; + + res->elements_ = AllocateObjectBuffer(cx, res, length * elementSize); + if (!res->elements_) { + // Make the object safe for GC. + res->setInlineElements(); + res->setInitializedLength(0); + return nullptr; + } + + res->setCapacityIndex(CapacityMatchesLengthIndex); + } + + res->setLength(length); + res->setInitializedLength(0); + return res; +} + +bool +UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v) +{ + MOZ_ASSERT(index < initializedLength()); + uint8_t* p = elements() + index * elementSize(); + return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v); +} + +bool +UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v) +{ + MOZ_ASSERT(index < initializedLength()); + uint8_t* p = elements() + index * elementSize(); + if (UnboxedTypeNeedsPreBarrier(elementType())) + *reinterpret_cast(p) = nullptr; + return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v); +} + +bool +UnboxedArrayObject::appendElementNoTypeChange(ExclusiveContext* cx, size_t index, const Value& v) +{ + // The caller should check these preconditions, and ensure that writing v + // to this object will not change element type information. + MOZ_ASSERT(index == initializedLength()); + MOZ_ASSERT(index < UnboxedArrayObject::MaximumCapacity); + + if (initializedLength() == capacity()) { + if (!growElements(cx, index + 1)) + return false; + } + + setInitializedLength(index + 1); + JS_ALWAYS_TRUE(initElement(cx, index, v)); + if (length() <= index) + setLength(index + 1); + + return true; +} + +Value +UnboxedArrayObject::getElement(size_t index) +{ + MOZ_ASSERT(index < initializedLength()); + uint8_t* p = elements() + index * elementSize(); + return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false); +} + +/* static */ void +UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj) +{ + JSValueType type = obj->as().elementType(); + if (!UnboxedTypeNeedsPreBarrier(type)) + return; + + MOZ_ASSERT(obj->as().elementSize() == sizeof(uintptr_t)); + size_t initlen = obj->as().initializedLength(); + void** elements = reinterpret_cast(obj->as().elements()); + + switch (type) { + case JSVAL_TYPE_OBJECT: + for (size_t i = 0; i < initlen; i++) { + HeapPtrObject* heap = reinterpret_cast(elements + i); + if (*heap) + TraceEdge(trc, heap, "unboxed_object"); + } + break; + + case JSVAL_TYPE_STRING: + for (size_t i = 0; i < initlen; i++) { + HeapPtrString* heap = reinterpret_cast(elements + i); + TraceEdge(trc, heap, "unboxed_string"); + } + break; + + default: + MOZ_CRASH(); + } +} + +/* static */ void +UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old) +{ + UnboxedArrayObject& dst = obj->as(); + const UnboxedArrayObject& src = old->as(); + + // Fix up possible inline data pointer. + if (src.hasInlineElements()) + dst.setInlineElements(); +} + +/* static */ void +UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj) +{ + MOZ_ASSERT(!IsInsideNursery(obj)); + if (!obj->as().hasInlineElements()) + js_free(obj->as().elements()); +} + +/* static */ size_t +UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src, + gc::AllocKind allocKind) +{ + UnboxedArrayObject* ndst = &dst->as(); + UnboxedArrayObject* nsrc = &src->as(); + MOZ_ASSERT(ndst->elements() == nsrc->elements()); + + Nursery& nursery = trc->runtime()->gc.nursery; + + if (!nursery.isInside(nsrc->elements())) { + nursery.removeMallocedBuffer(nsrc->elements()); + return 0; + } + + // Determine if we can use inline data for the target array. If this is + // possible, the nursery will have picked an allocation size that is large + // enough. + size_t nbytes = nsrc->capacity() * nsrc->elementSize(); + if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) { + ndst->setInlineElements(); + } else { + MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0); + + uint8_t* data = nsrc->zone()->pod_malloc(nbytes); + if (!data) + CrashAtUnhandlableOOM("Failed to allocate unboxed array elements while tenuring."); + ndst->elements_ = data; + } + + PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize()); + + // Set a forwarding pointer for the element buffers in case they were + // preserved on the stack by Ion. + bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t); + nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct); + + return ndst->hasInlineElements() ? 0 : nbytes; +} + +// Possible capacities for unboxed arrays. Some of these capacities might seem +// a little weird, but were chosen to allow the inline data of objects of each +// size to be fully utilized for arrays of the various types on both 32 bit and +// 64 bit platforms. +/* static */ const uint32_t +UnboxedArrayObject::CapacityArray[] = { + UINT32_MAX, // For CapacityMatchesLengthIndex. + 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 20, 24, 26, 32, 34, 36, 48, 52, 64, 68, + 72, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, + 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336, + 13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464, + 46137344, 52428800, 59768832, MaximumCapacity +}; + +static const uint32_t +Pow2CapacityIndexes[] = { + 1, // 1 + 2, // 2 + 4, // 4 + 8, // 8 + 13, // 16 + 19, // 32 + 24, // 64 + 27, // 128 + 28, // 256 + 29, // 512 + 30, // 1024 + 31, // 2048 + 32, // 4096 + 33, // 8192 + 34, // 16384 + 35, // 32768 + 36, // 65536 + 37, // 131072 + 38, // 262144 + 39, // 524288 + 40 // 1048576 +}; + +static const uint32_t MebiCapacityIndex = 40; + +/* static */ uint32_t +UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length) +{ + // Note: the structure and behavior of this method follow along with + // NativeObject::goodAllocated. Changes to the allocation strategy in one + // should generally be matched by the other. + + // Make sure we have enough space to store all possible values for the capacity index. + MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= CapacityMask >> CapacityShift); + + // The caller should have ensured the capacity is possible for an unboxed array. + MOZ_ASSERT(capacity <= MaximumCapacity); + + static const uint32_t Mebi = 1024 * 1024; + + if (capacity <= Mebi) { + capacity = mozilla::RoundUpPow2(capacity); + + // When the required capacity is close to the array length, then round + // up to the array length itself, as for NativeObject. + if (length >= capacity && capacity > (length / 3) * 2) + return CapacityMatchesLengthIndex; + + if (capacity < MinimumDynamicCapacity) + capacity = MinimumDynamicCapacity; + + uint32_t bit = mozilla::FloorLog2Size(capacity); + MOZ_ASSERT(capacity == uint32_t(1 << bit)); + MOZ_ASSERT(bit <= 20); + MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21); + + uint32_t index = Pow2CapacityIndexes[bit]; + MOZ_ASSERT(CapacityArray[index] == capacity); + + return index; + } + + MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi); + + for (uint32_t i = MebiCapacityIndex + 1;; i++) { + if (CapacityArray[i] >= capacity) + return i; + } + + MOZ_CRASH("Invalid capacity"); +} + +/* static */ uint32_t +UnboxedArrayObject::exactCapacityIndex(uint32_t capacity) +{ + for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) { + if (CapacityArray[i] == capacity) + return i; + } + MOZ_CRASH(); +} + +bool +UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap) +{ + // The caller should have checked if this capacity is possible for an + // unboxed array, so the only way this call can fail is from OOM. + MOZ_ASSERT(cap <= MaximumCapacity); + + uint32_t oldCapacity = capacity(); + uint32_t newCapacityIndex = chooseCapacityIndex(cap, length()); + uint32_t newCapacity = computeCapacity(newCapacityIndex, length()); + + MOZ_ASSERT(oldCapacity < cap); + MOZ_ASSERT(cap <= newCapacity); + + // The allocation size computation below cannot have integer overflows. + JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double)); + + uint8_t* newElements; + if (hasInlineElements()) { + newElements = AllocateObjectBuffer(cx, this, newCapacity * elementSize()); + if (!newElements) + return false; + js_memcpy(newElements, elements(), initializedLength() * elementSize()); + } else { + newElements = ReallocateObjectBuffer(cx, this, elements(), + oldCapacity * elementSize(), + newCapacity * elementSize()); + if (!newElements) + return false; + } + + elements_ = newElements; + setCapacityIndex(newCapacityIndex); + + return true; +} + +void +UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap) +{ + if (hasInlineElements()) + return; + + uint32_t oldCapacity = capacity(); + uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0); + uint32_t newCapacity = computeCapacity(newCapacityIndex, 0); + + MOZ_ASSERT(cap < oldCapacity); + MOZ_ASSERT(cap <= newCapacity); + + if (newCapacity >= oldCapacity) + return; + + uint8_t* newElements = ReallocateObjectBuffer(cx, this, elements(), + oldCapacity * elementSize(), + newCapacity * elementSize()); + if (!newElements) + return; + + elements_ = newElements; + setCapacityIndex(newCapacityIndex); +} + +bool +UnboxedArrayObject::containsProperty(JSContext* cx, jsid id) +{ + if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength()) + return true; + if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length) + return true; + return false; +} + +/* static */ bool +UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj, + HandleId id, MutableHandleObject objp, + MutableHandleShape propp) +{ + if (obj->as().containsProperty(cx, id)) { + MarkNonNativePropertyFound(propp); + objp.set(obj); + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + objp.set(nullptr); + propp.set(nullptr); + return true; + } + + return LookupProperty(cx, proto, id, objp, propp); +} + +/* static */ bool +UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle desc, + ObjectOpResult& result) +{ + if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { + UnboxedArrayObject* nobj = &obj->as(); + + uint32_t index = JSID_TO_INT(id); + if (index < nobj->initializedLength()) { + if (nobj->setElement(cx, index, desc.value())) + return result.succeed(); + } else if (index == nobj->initializedLength() && index < MaximumCapacity) { + if (nobj->initializedLength() == nobj->capacity()) { + if (!nobj->growElements(cx, index + 1)) + return false; + } + nobj->setInitializedLength(index + 1); + if (nobj->initElement(cx, index, desc.value())) { + if (nobj->length() <= index) + nobj->setLength(index + 1); + return result.succeed(); + } + nobj->setInitializedLength(index); + } + } + + if (!convertToNative(cx, obj)) + return false; + + return DefineProperty(cx, obj, id, desc, result); +} + +/* static */ bool +UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) +{ + if (obj->as().containsProperty(cx, id)) { + *foundp = true; + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + *foundp = false; + return true; + } + + return HasProperty(cx, proto, id, foundp); +} + +/* static */ bool +UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver, + HandleId id, MutableHandleValue vp) +{ + if (obj->as().containsProperty(cx, id)) { + if (JSID_IS_INT(id)) + vp.set(obj->as().getElement(JSID_TO_INT(id))); + else + vp.set(Int32Value(obj->as().length())); + return true; + } + + RootedObject proto(cx, obj->getProto()); + if (!proto) { + vp.setUndefined(); + return true; + } + + return GetProperty(cx, proto, receiver, id, vp); +} + +/* static */ bool +UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result) +{ + if (obj->as().containsProperty(cx, id)) { + if (receiver.isObject() && obj == &receiver.toObject()) { + if (JSID_IS_INT(id)) { + if (obj->as().setElement(cx, JSID_TO_INT(id), v)) + return result.succeed(); + } else { + uint32_t len; + if (!CanonicalizeArrayLengthValue(cx, v, &len)) + return false; + if (len <= INT32_MAX) { + UnboxedArrayObject* nobj = &obj->as(); + nobj->setLength(len); + if (len < nobj->initializedLength()) { + nobj->setInitializedLength(len); + nobj->shrinkElements(cx, len); + } + } + } + + if (!convertToNative(cx, obj)) + return false; + return SetProperty(cx, obj, id, v, receiver, result); + } + + return SetPropertyByDefining(cx, obj, id, v, receiver, false, result); + } + + return SetPropertyOnProto(cx, obj, id, v, receiver, result); +} + +/* static */ bool +UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, + MutableHandle desc) +{ + if (obj->as().containsProperty(cx, id)) { + if (JSID_IS_INT(id)) { + desc.value().set(obj->as().getElement(JSID_TO_INT(id))); + desc.setAttributes(JSPROP_ENUMERATE); + } else { + desc.value().set(Int32Value(obj->as().length())); + desc.setAttributes(JSPROP_PERMANENT); + } + desc.object().set(obj); + return true; + } + + desc.object().set(nullptr); + return true; +} + +/* static */ bool +UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, + ObjectOpResult& result) +{ + if (!convertToNative(cx, obj)) + return false; + return DeleteProperty(cx, obj, id, result); +} + +/* static */ bool +UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) +{ + if (!convertToNative(cx, obj)) + return false; + return WatchProperty(cx, obj, id, callable); +} + +/* static */ bool +UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties) +{ + for (size_t i = 0; i < obj->as().initializedLength(); i++) { + if (!properties.append(INT_TO_JSID(i))) + return false; + } + return properties.append(NameToId(cx->names().length)); +} + +const Class UnboxedArrayObject::class_ = { + "Array", + Class::NON_NATIVE | + JSCLASS_IMPLEMENTS_BARRIERS | + JSCLASS_SKIP_NURSERY_FINALIZE | + JSCLASS_BACKGROUND_FINALIZE, + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + nullptr, /* convert */ + UnboxedArrayObject::finalize, + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + UnboxedArrayObject::trace, + JS_NULL_CLASS_SPEC, + { + nullptr, /* outerObject */ + nullptr, /* innerObject */ + false, /* isWrappedNative */ + nullptr, /* weakmapKeyDelegateOp */ + UnboxedArrayObject::objectMoved + }, + { + UnboxedArrayObject::obj_lookupProperty, + UnboxedArrayObject::obj_defineProperty, + UnboxedArrayObject::obj_hasProperty, + UnboxedArrayObject::obj_getProperty, + UnboxedArrayObject::obj_setProperty, + UnboxedArrayObject::obj_getOwnPropertyDescriptor, + UnboxedArrayObject::obj_deleteProperty, + UnboxedArrayObject::obj_watch, + nullptr, /* No unwatch needed, as watch() converts the object to native */ + nullptr, /* getElements */ + UnboxedArrayObject::obj_enumerate, + nullptr, /* thisObject */ + } +}; + ///////////////////////////////////////////////////////////////////// // API ///////////////////////////////////////////////////////////////////// @@ -921,6 +1624,20 @@ UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) return false; } +static bool +CombineUnboxedTypes(const Value& value, JSValueType* existing) +{ + JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType(); + + if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeIncludes(type, *existing)) { + *existing = type; + return true; + } + if (UnboxedTypeIncludes(*existing, type)) + return true; + return false; +} + // Return whether the property names and types in layout are a subset of the // specified vector. static bool @@ -941,75 +1658,67 @@ PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLa return true; } -bool -js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, - ObjectGroup* group, PreliminaryObjectArray* objects) +static bool +CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape, + UnboxedLayout::PropertyVector& properties) { - if (!templateShape->runtimeFromAnyThread()->options().unboxedObjects()) - return true; + // All preliminary objects must have been created with enough space to + // fill in their unboxed data inline. This is ensured either by using + // the largest allocation kind (which limits the maximum size of an + // unboxed object), or by using an allocation kind that covers all + // properties in the template, as the space used by unboxed properties + // is less than or equal to that used by boxed properties. + MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >= + Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan())); - if (templateShape->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) - return true; - - if (templateShape->slotSpan() == 0) - return true; - - UnboxedLayout::PropertyVector properties; - if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) + if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) { + // Only use an unboxed representation if all created objects match + // the template shape exactly. return false; - - size_t objectCount = 0; - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - objectCount++; - - // All preliminary objects must have been created with enough space to - // fill in their unboxed data inline. This is ensured either by using - // the largest allocation kind (which limits the maximum size of an - // unboxed object), or by using an allocation kind that covers all - // properties in the template, as the space used by unboxed properties - // less than or equal to that used by boxed properties. - MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >= - Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan())); - - if (obj->as().lastProperty() != templateShape || - obj->as().hasDynamicElements()) - { - // Only use an unboxed representation if all created objects match - // the template shape exactly. - return true; - } - - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - Value val = obj->as().getSlot(i); - - JSValueType& existing = properties[i].type; - JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType(); - - if (existing == JSVAL_TYPE_MAGIC || existing == type || UnboxedTypeIncludes(type, existing)) - existing = type; - else if (!UnboxedTypeIncludes(existing, type)) - return true; - } - } - - if (objectCount <= 1) { - // If only one of the objects has been created, it is more likely to - // have new properties added later. - return true; } for (size_t i = 0; i < templateShape->slotSpan(); i++) { - // We can't use an unboxed representation if e.g. all the objects have - // a null value for one of the properties, as we can't decide what type - // it is supposed to have. - if (UnboxedTypeSize(properties[i].type) == 0) - return true; + Value val = obj->getSlot(i); + + JSValueType& existing = properties[i].type; + if (!CombineUnboxedTypes(val, &existing)) + return false; } + return true; +} + +static bool +CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType* elementType) +{ + if (obj->inDictionaryMode() || + obj->lastProperty()->propid() != AtomToId(cx->names().length) || + !obj->lastProperty()->previous()->isEmptyShape() || + !obj->getDenseInitializedLength()) + { + // Only use an unboxed representation if the object has at + // least one element, and no properties. + return false; + } + + for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { + Value val = obj->getDenseElement(i); + + // For now, unboxed arrays cannot have holes. + if (val.isMagic(JS_ELEMENTS_HOLE)) + return false; + + if (!CombineUnboxedTypes(val, elementType)) + return false; + } + + return true; +} + +static size_t +ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape, + UnboxedLayout::PropertyVector& properties) +{ // Fill in the names for all the object's properties. for (Shape::Range r(templateShape); !r.empty(); r.popFront()) { size_t slot = r.front().slot(); @@ -1069,27 +1778,23 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, } } - // The entire object must be allocatable inline. - if (sizeof(JSObject) + offset > JSObject::MAX_BYTE_SIZE) - return true; - - UniquePtr > layout; - layout.reset(group->zone()->new_(properties, offset)); - if (!layout) - return false; - - cx->compartment()->unboxedLayouts.insertFront(layout.get()); + // The final offset is the amount of data needed by the object. + return offset; +} +static bool +SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout) +{ // Figure out the offsets of any objects or string properties. Vector objectOffsets, stringOffsets; - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - MOZ_ASSERT(properties[i].offset != UINT32_MAX); - JSValueType type = properties[i].type; - if (type == JSVAL_TYPE_OBJECT) { - if (!objectOffsets.append(properties[i].offset)) + for (size_t i = 0; i < layout->properties().length(); i++) { + const UnboxedLayout::Property& property = layout->properties()[i]; + MOZ_ASSERT(property.offset != UINT32_MAX); + if (property.type == JSVAL_TYPE_OBJECT) { + if (!objectOffsets.append(property.offset)) return false; - } else if (type == JSVAL_TYPE_STRING) { - if (!stringOffsets.append(properties[i].offset)) + } else if (property.type == JSVAL_TYPE_STRING) { + if (!stringOffsets.append(property.offset)) return false; } } @@ -1105,64 +1810,230 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, { return false; } - int32_t* traceList = group->zone()->pod_malloc(entries.length()); + int32_t* traceList = cx->zone()->pod_malloc(entries.length()); if (!traceList) return false; PodCopy(traceList, entries.begin(), entries.length()); layout->setTraceList(traceList); } + return true; +} + +static inline Value +NextValue(const AutoValueVector& values, size_t* valueCursor) +{ + return values[(*valueCursor)++]; +} + +static bool +GetValuesFromPreliminaryArrayObject(ArrayObject* obj, AutoValueVector& values) +{ + if (!values.append(Int32Value(obj->length()))) + return false; + if (!values.append(Int32Value(obj->getDenseInitializedLength()))) + return false; + for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { + if (!values.append(obj->getDenseElement(i))) + return false; + } + return true; +} + +void +UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx, + const AutoValueVector& values, size_t* valueCursor) +{ + MOZ_ASSERT(CapacityArray[1] == 0); + setCapacityIndex(1); + setInitializedLength(0); + setInlineElements(); + + setLength(NextValue(values, valueCursor).toInt32()); + + int32_t initlen = NextValue(values, valueCursor).toInt32(); + + if (!growElements(cx, initlen)) + CrashAtUnhandlableOOM("UnboxedArrayObject::fillAfterConvert"); + setInitializedLength(initlen); + + for (size_t i = 0; i < size_t(initlen); i++) + JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor))); +} + +static bool +GetValuesFromPreliminaryPlainObject(PlainObject* obj, AutoValueVector& values) +{ + for (size_t i = 0; i < obj->slotSpan(); i++) { + if (!values.append(obj->getSlot(i))) + return false; + } + return true; +} + +void +UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx, + const AutoValueVector& values, size_t* valueCursor) +{ + initExpando(); + memset(data(), 0, layout().size()); + for (size_t i = 0; i < layout().properties().length(); i++) + JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor))); +} + +bool +js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, + ObjectGroup* group, PreliminaryObjectArray* objects) +{ + // Unboxed objects are nightly only for now. The getenv() call will be + // removed when they are on by default. See bug 1153266. +#ifdef NIGHTLY_BUILD + if (templateShape) { + if (!getenv("JS_OPTION_USE_UNBOXED_OBJECTS")) { + if (!group->runtimeFromAnyThread()->options().unboxedObjects()) + return true; + } + } else { + if (!group->runtimeFromAnyThread()->options().unboxedArrays()) + return true; + } +#else + return true; +#endif + + MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags()); + + bool isArray = !templateShape; + + if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) + return true; + + if (!isArray && templateShape->slotSpan() == 0) + return true; + + UnboxedLayout::PropertyVector properties; + if (!isArray) { + if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) + return false; + } + JSValueType elementType = JSVAL_TYPE_MAGIC; + + size_t objectCount = 0; + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + JSObject* obj = objects->get(i); + if (!obj) + continue; + + objectCount++; + + if (isArray) { + if (!CombineArrayObjectElements(cx, &obj->as(), &elementType)) + return true; + } else { + if (!CombinePlainObjectProperties(&obj->as(), templateShape, properties)) + return true; + } + } + + size_t layoutSize = 0; + if (isArray) { + // Don't use an unboxed representation if we couldn't determine an + // element type for the objects. + if (UnboxedTypeSize(elementType) == 0) + return true; + } else { + if (objectCount <= 1) { + // If only one of the objects has been created, it is more likely + // to have new properties added later. This heuristic is not used + // for array objects, where we might want an unboxed representation + // even if there is only one large array. + return true; + } + + for (size_t i = 0; i < templateShape->slotSpan(); i++) { + // We can't use an unboxed representation if e.g. all the objects have + // a null value for one of the properties, as we can't decide what type + // it is supposed to have. + if (UnboxedTypeSize(properties[i].type) == 0) + return true; + } + + layoutSize = ComputePlainObjectLayout(cx, templateShape, properties); + + // The entire object must be allocatable inline. + if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE_SIZE) + return true; + } + + UniquePtr > layout; + layout.reset(group->zone()->new_()); + if (!layout) + return false; + + if (isArray) { + layout->initArray(elementType); + } else { + if (!layout->initProperties(properties, layoutSize)) + return false; + + // The unboxedLayouts list only tracks layouts for plain objects. + cx->compartment()->unboxedLayouts.insertFront(layout.get()); + + if (!SetLayoutTraceList(cx, layout.get())) + return false; + } + // We've determined that all the preliminary objects can use the new layout - // just constructed, so convert the existing group to be an - // UnboxedPlainObject rather than a PlainObject, and update the preliminary - // objects to use the new layout. Do the fallible stuff first before - // modifying any objects. + // just constructed, so convert the existing group to use the unboxed class, + // and update the preliminary objects to use the new layout. Do the + // fallible stuff first before modifying any objects. // Get an empty shape which we can use for the preliminary objects. - Shape* newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_, - group->proto(), - templateShape->getObjectFlags()); + const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_; + Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0); if (!newShape) { cx->recoverFromOutOfMemory(); return false; } - // Accumulate a list of all the properties in each preliminary object, and + // Accumulate a list of all the values in each preliminary object, and // update their shapes. - Vector values; - if (!values.reserve(objectCount * templateShape->slotSpan())) - return false; + AutoValueVector values(cx); for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - if (!objects->get(i)) + JSObject* obj = objects->get(i); + if (!obj) continue; - RootedNativeObject obj(cx, &objects->get(i)->as()); - for (size_t j = 0; j < templateShape->slotSpan(); j++) - values.infallibleAppend(obj->getSlot(j)); - - // Clear the object to remove any dynamically allocated information. - NativeObject::clear(cx, obj); - - obj->setLastPropertyMakeNonNative(newShape); + if (isArray) { + if (!GetValuesFromPreliminaryArrayObject(&obj->as(), values)) + return false; + } else { + if (!GetValuesFromPreliminaryPlainObject(&obj->as(), values)) + return false; + } } if (TypeNewScript* newScript = group->newScript()) layout->setNewScript(newScript); - group->setClasp(&UnboxedPlainObject::class_); + for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { + if (JSObject* obj = objects->get(i)) + obj->as().setLastPropertyMakeNonNative(newShape); + } + + group->setClasp(clasp); group->setUnboxedLayout(layout.get()); size_t valueCursor = 0; for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - if (!objects->get(i)) + JSObject* obj = objects->get(i); + if (!obj) continue; - UnboxedPlainObject *obj = &objects->get(i)->as(); - obj->initExpando(); - memset(obj->data(), 0, layout->size()); - for (size_t j = 0; j < templateShape->slotSpan(); j++) { - Value v = values[valueCursor++]; - JS_ALWAYS_TRUE(obj->setValue(cx, properties[j], v)); - } + + if (isArray) + obj->as().fillAfterConvert(cx, values, &valueCursor); + else + obj->as().fillAfterConvert(cx, values, &valueCursor); } MOZ_ASSERT(valueCursor == values.length()); diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index 6d3507109a..b6e6e0d07c 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -35,7 +35,7 @@ UnboxedTypeNeedsPreBarrier(JSValueType type) return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; } -// Class describing the layout of an UnboxedPlainObject. +// Class tracking information specific to unboxed objects. class UnboxedLayout : public mozilla::LinkedListElement { public: @@ -52,6 +52,14 @@ class UnboxedLayout : public mozilla::LinkedListElement typedef Vector PropertyVector; private: + // If objects in this group have ever been converted to native objects, + // these store the corresponding native group and initial shape for such + // objects. Type information for this object is reflected in nativeGroup. + HeapPtrObjectGroup nativeGroup_; + HeapPtrShape nativeShape_; + + // The following members are only used for unboxed plain objects. + // All properties on objects with this layout, in enumeration order. PropertyVector properties_; @@ -65,12 +73,6 @@ class UnboxedLayout : public mozilla::LinkedListElement // structure as the trace list on a TypeDescr. int32_t* traceList_; - // If objects in this group have ever been converted to native objects, - // these store the corresponding native group and initial shape for such - // objects. Type information for this object is reflected in nativeGroup. - HeapPtrObjectGroup nativeGroup_; - HeapPtrShape nativeShape_; - // If nativeGroup is set and this object originally had a TypeNewScript, // this points to the default 'new' group which replaced this one (and // which might itself have been cleared since). This link is only needed to @@ -84,13 +86,25 @@ class UnboxedLayout : public mozilla::LinkedListElement // from an array of values. HeapPtrJitCode constructorCode_; + // The following members are only used for unboxed arrays. + + // The type of array elements. + JSValueType elementType_; + public: - UnboxedLayout(const PropertyVector &properties, size_t size) - : size_(size), newScript_(nullptr), traceList_(nullptr), - nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr), - constructorCode_(nullptr) - { - properties_.appendAll(properties); + UnboxedLayout() + : nativeGroup_(nullptr), nativeShape_(nullptr), size_(0), newScript_(nullptr), + traceList_(nullptr), replacementNewGroup_(nullptr), constructorCode_(nullptr), + elementType_(JSVAL_TYPE_MAGIC) + {} + + bool initProperties(const PropertyVector& properties, size_t size) { + size_ = size; + return properties_.appendAll(properties); + } + + void initArray(JSValueType elementType) { + elementType_ = elementType; } ~UnboxedLayout() { @@ -98,9 +112,13 @@ class UnboxedLayout : public mozilla::LinkedListElement js_free(traceList_); } + bool isArray() { + return elementType_ != JSVAL_TYPE_MAGIC; + } + void detachFromCompartment(); - const PropertyVector &properties() const { + const PropertyVector& properties() const { return properties_; } @@ -144,22 +162,26 @@ class UnboxedLayout : public mozilla::LinkedListElement return nativeShape_; } - jit::JitCode *constructorCode() const { + jit::JitCode* constructorCode() const { return constructorCode_; } - void setConstructorCode(jit::JitCode *code) { + void setConstructorCode(jit::JitCode* code) { constructorCode_ = code; } + JSValueType elementType() const { + return elementType_; + } + inline gc::AllocKind getAllocKind() const; - void trace(JSTracer *trc); + void trace(JSTracer* trc); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); - static bool makeNativeGroup(JSContext *cx, ObjectGroup *group); - static bool makeConstructorCode(JSContext *cx, HandleObjectGroup group); + static bool makeNativeGroup(JSContext* cx, ObjectGroup* group); + static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group); }; // Class for expando objects holding extra properties given to an unboxed plain @@ -180,7 +202,7 @@ class UnboxedPlainObject : public JSObject // Optional object which stores extra properties on this object. This is // not automatically barriered to avoid problems if the object is converted // to a native. See ensureExpando(). - UnboxedExpandoObject *expando_; + UnboxedExpandoObject* expando_; // Start of the inline data, which immediately follows the group and extra properties. uint8_t data_[1]; @@ -188,44 +210,44 @@ class UnboxedPlainObject : public JSObject public: static const Class class_; - static bool obj_lookupProperty(JSContext *cx, HandleObject obj, + static bool obj_lookupProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject objp, MutableHandleShape propp); - static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, + static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle desc, - ObjectOpResult &result); + ObjectOpResult& result); - static bool obj_hasProperty(JSContext *cx, HandleObject obj, HandleId id, bool* foundp); + static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); - static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, + static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp); - static bool obj_setProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult &result); + static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result); static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc); - static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, - ObjectOpResult &result); + static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, + ObjectOpResult& result); static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties); static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - const UnboxedLayout &layout() const { + const UnboxedLayout& layout() const { return group()->unboxedLayout(); } - const UnboxedLayout &layoutDontCheckGeneration() const { + const UnboxedLayout& layoutDontCheckGeneration() const { return group()->unboxedLayoutDontCheckGeneration(); } - uint8_t *data() { + uint8_t* data() { return &data_[0]; } - UnboxedExpandoObject *maybeExpando() const { + UnboxedExpandoObject* maybeExpando() const { return expando_; } @@ -233,20 +255,28 @@ class UnboxedPlainObject : public JSObject expando_ = nullptr; } - bool containsUnboxedOrExpandoProperty(ExclusiveContext *cx, jsid id) const; + // For use during GC. + JSObject** addressOfExpando() { + return reinterpret_cast(&expando_); + } - static UnboxedExpandoObject *ensureExpando(JSContext *cx, Handle obj); + bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const; - bool setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property, const Value &v); - Value getValue(const UnboxedLayout::Property &property); + static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle obj); - static bool convertToNative(JSContext *cx, JSObject *obj); - static UnboxedPlainObject *create(ExclusiveContext *cx, HandleObjectGroup group, + bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v); + Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false); + + static bool convertToNative(JSContext* cx, JSObject* obj); + static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind); - static JSObject *createWithProperties(ExclusiveContext *cx, HandleObjectGroup group, - NewObjectKind newKind, IdValuePair *properties); + static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group, + NewObjectKind newKind, IdValuePair* properties); - static void trace(JSTracer *trc, JSObject *object); + void fillAfterConvert(ExclusiveContext* cx, + const AutoValueVector& values, size_t* valueCursor); + + static void trace(JSTracer* trc, JSObject* object); static size_t offsetOfExpando() { return offsetof(UnboxedPlainObject, expando_); @@ -261,15 +291,188 @@ class UnboxedPlainObject : public JSObject // provided they all match the template shape. If successful, converts the // preliminary objects and their group to the new unboxed representation. bool -TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape, - ObjectGroup *group, PreliminaryObjectArray *objects); +TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, + ObjectGroup* group, PreliminaryObjectArray* objects); inline gc::AllocKind UnboxedLayout::getAllocKind() const { + MOZ_ASSERT(size()); return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size()); } +// Class for an array object using an unboxed representation. +class UnboxedArrayObject : public JSObject +{ + // Elements pointer for the object. + uint8_t* elements_; + + // The nominal array length. This always fits in an int32_t. + uint32_t length_; + + // Value indicating the allocated capacity and initialized length of the + // array. The top CapacityBits bits are an index into CapacityArray, which + // indicates the elements capacity. The low InitializedLengthBits store the + // initialized length of the array. + uint32_t capacityIndexAndInitializedLength_; + + // If the elements are inline, they will point here. + uint8_t inlineElements_[1]; + + public: + static const uint32_t CapacityBits = 6; + static const uint32_t CapacityShift = 26; + + static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift; + static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1; + + static const uint32_t MaximumCapacity = InitializedLengthMask; + static const uint32_t MinimumDynamicCapacity = 8; + + static const uint32_t CapacityArray[]; + + // Capacity index which indicates the array's length is also its capacity. + static const uint32_t CapacityMatchesLengthIndex = 0; + + private: + static inline uint32_t computeCapacity(uint32_t index, uint32_t length) { + if (index == CapacityMatchesLengthIndex) + return length; + return CapacityArray[index]; + } + + static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length); + static uint32_t exactCapacityIndex(uint32_t capacity); + + public: + static const Class class_; + + static bool obj_lookupProperty(JSContext* cx, HandleObject obj, + HandleId id, MutableHandleObject objp, + MutableHandleShape propp); + + static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle desc, + ObjectOpResult& result); + + static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); + + static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver, + HandleId id, MutableHandleValue vp); + + static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult& result); + + static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, + MutableHandle desc); + + static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, + ObjectOpResult& result); + + static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties); + static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); + + const UnboxedLayout& layout() const { + return group()->unboxedLayout(); + } + + const UnboxedLayout& layoutDontCheckGeneration() const { + return group()->unboxedLayoutDontCheckGeneration(); + } + + JSValueType elementType() const { + return layoutDontCheckGeneration().elementType(); + } + + uint32_t elementSize() const { + return UnboxedTypeSize(elementType()); + } + + static bool convertToNative(JSContext* cx, JSObject* obj); + static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group, + uint32_t length, NewObjectKind newKind); + + void fillAfterConvert(ExclusiveContext* cx, + const AutoValueVector& values, size_t* valueCursor); + + static void trace(JSTracer* trc, JSObject* object); + static void objectMoved(JSObject* obj, const JSObject* old); + static void finalize(FreeOp* fop, JSObject* obj); + + static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src, + gc::AllocKind allocKind); + + uint8_t* elements() { + return elements_; + } + + bool hasInlineElements() const { + return elements_ == &inlineElements_[0]; + } + + uint32_t length() const { + return length_; + } + + uint32_t initializedLength() const { + return capacityIndexAndInitializedLength_ & InitializedLengthMask; + } + + uint32_t capacityIndex() const { + return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift; + } + + uint32_t capacity() const { + return computeCapacity(capacityIndex(), length()); + } + + bool containsProperty(JSContext* cx, jsid id); + + bool setElement(ExclusiveContext* cx, size_t index, const Value& v); + bool initElement(ExclusiveContext* cx, size_t index, const Value& v); + Value getElement(size_t index); + + bool appendElementNoTypeChange(ExclusiveContext* cx, size_t index, const Value& v); + + bool growElements(ExclusiveContext* cx, size_t cap); + void shrinkElements(ExclusiveContext* cx, size_t cap); + + static uint32_t offsetOfElements() { + return offsetof(UnboxedArrayObject, elements_); + } + static uint32_t offsetOfLength() { + return offsetof(UnboxedArrayObject, length_); + } + static uint32_t offsetOfCapacityIndexAndInitializedLength() { + return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_); + } + static uint32_t offsetOfInlineElements() { + return offsetof(UnboxedArrayObject, inlineElements_); + } + + private: + void setInlineElements() { + elements_ = &inlineElements_[0]; + } + + void setLength(uint32_t len) { + MOZ_ASSERT(len <= INT32_MAX); + length_ = len; + } + + void setInitializedLength(uint32_t initlen) { + MOZ_ASSERT(initlen <= InitializedLengthMask); + capacityIndexAndInitializedLength_ = + (capacityIndexAndInitializedLength_ & CapacityMask) | initlen; + } + + void setCapacityIndex(uint32_t index) { + MOZ_ASSERT(index <= (CapacityMask >> CapacityShift)); + capacityIndexAndInitializedLength_ = + (index << CapacityShift) | initializedLength(); + } +}; + } // namespace js #endif /* vm_UnboxedObject_h */ diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index e031b06253..89b42cb598 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 272; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 276; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 390, +static_assert(JSErr_Limit == 388, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/js/xpconnect/idl/nsIXPCScriptable.idl b/js/xpconnect/idl/nsIXPCScriptable.idl index e9c17a7177..9362bcfbe8 100644 --- a/js/xpconnect/idl/nsIXPCScriptable.idl +++ b/js/xpconnect/idl/nsIXPCScriptable.idl @@ -32,7 +32,7 @@ interface nsIXPConnectWrappedNative; * boolean to PR_TRUE before making the call. Implementations may skip writing * to *_retval unless they want to return PR_FALSE. */ -[uuid(402bf112-6d3b-431f-bb0f-d05ec183ee7b)] +[uuid(19b70b26-7c3f-437f-a04a-2a8f9e28b617)] interface nsIXPCScriptable : nsISupports { /* bitflags used for 'flags' (only 32 bits available!) */ @@ -79,7 +79,7 @@ interface nsIXPCScriptable : nsISupports boolean addProperty(in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsid id, - in JSValPtr vp); + in jsval val); // The returnCode should be set to NS_SUCCESS_I_DID_SOMETHING if // this method does something. diff --git a/js/xpconnect/public/xpc_map_end.h b/js/xpconnect/public/xpc_map_end.h index 9824f086eb..8794399d56 100644 --- a/js/xpconnect/public/xpc_map_end.h +++ b/js/xpconnect/public/xpc_map_end.h @@ -79,7 +79,7 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreate(nsISupports* nativeObj, JSContext * c #endif #ifndef XPC_MAP_WANT_ADDPROPERTY -NS_IMETHODIMP XPC_MAP_CLASSNAME::AddProperty(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, jsid id, JS::Value * vp, bool* _retval) +NS_IMETHODIMP XPC_MAP_CLASSNAME::AddProperty(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, jsid id, JS::HandleValue val, bool* _retval) {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;} #endif diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index a37c1f1e29..f35fb42632 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -416,7 +416,7 @@ struct AutoSkipPropertyMirroring // add-ons in a separate compartment while still giving them access to the // chrome window. static bool -sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) +sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) { CompartmentPrivate* priv = CompartmentPrivate::Get(obj); MOZ_ASSERT(priv->writeToGlobalPrototype); @@ -459,7 +459,7 @@ sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleV // readonly attribute (as it calls JSObject::defineProperty). See bug // 1019181. if (pd.object() && !pd.configurable()) { - if (!JS_SetPropertyById(cx, proto, id, vp)) + if (!JS_SetPropertyById(cx, proto, id, v)) return false; } else { if (!JS_CopyPropertyFrom(cx, id, unwrappedProto, obj, @@ -470,7 +470,7 @@ sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleV if (!JS_GetPropertyDescriptorById(cx, obj, id, &pd)) return false; unsigned attrs = pd.attributes() & ~(JSPROP_GETTER | JSPROP_SETTER); - if (!JS_DefinePropertyById(cx, obj, id, vp, + if (!JS_DefinePropertyById(cx, obj, id, v, attrs | JSPROP_PROPOP_ACCESSORS | JSPROP_REDEFINE_NONCONFIGURABLE, JS_PROPERTYOP_GETTER(writeToProto_getProperty), JS_PROPERTYOP_SETTER(writeToProto_setProperty))) @@ -485,7 +485,9 @@ static const js::Class SandboxClass = { "Sandbox", XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1), nullptr, nullptr, nullptr, nullptr, - sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, + sandbox_enumerate, sandbox_resolve, + nullptr, /* mayResolve */ + sandbox_convert, sandbox_finalize, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook, JS_NULL_CLASS_SPEC, { @@ -504,7 +506,9 @@ static const js::Class SandboxWriteToProtoClass = { "Sandbox", XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1), sandbox_addProperty, nullptr, nullptr, nullptr, - sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, + sandbox_enumerate, sandbox_resolve, + nullptr, /* mayResolve */ + sandbox_convert, sandbox_finalize, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook, JS_NULL_CLASS_SPEC, { diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index b1428f309e..1476cd8646 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2528,8 +2528,8 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats, KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted, "Memory being used by the GC's nursery."); - RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-huge-slots"), - KIND_NONHEAP, rtStats.runtime.gc.nurseryHugeSlots, + RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-malloced-buffers"), + KIND_NONHEAP, rtStats.runtime.gc.nurseryMallocedBuffers, "Out-of-line slots and elements belonging to objects in the " "nursery."); diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index ab7f2b90f2..3c67954a10 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -1456,6 +1456,7 @@ static const JSClass XPCOutParamClass = { nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ + nullptr, /* mayResolve */ nullptr, /* convert */ FinalizeStub, nullptr, /* call */ diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 5c2dfb1ee6..0c8726677b 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -413,7 +413,7 @@ DefinePropertyIfFound(XPCCallContext& ccx, /***************************************************************************/ static bool -XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) +XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) { XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id); XPCWrappedNative* wrapper = ccx.GetWrapper(); @@ -428,7 +428,7 @@ XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, static bool XPC_WN_CannotModifyPropertyStub(JSContext* cx, HandleObject obj, HandleId id, - MutableHandleValue vp) + HandleValue v) { return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); } @@ -648,6 +648,7 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = { XPC_WN_Shared_Enumerate, // enumerate XPC_WN_NoHelper_Resolve, // resolve + nullptr, // mayResolve XPC_WN_Shared_Convert, // convert XPC_WN_NoHelper_Finalize, // finalize @@ -688,7 +689,7 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = { /***************************************************************************/ static bool -XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) +XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) { XPCCallContext ccx(JS_CALLER, cx, obj); XPCWrappedNative* wrapper = ccx.GetWrapper(); @@ -748,10 +749,10 @@ XPC_WN_MaybeResolvingDeletePropertyStub(JSContext *cx, HandleObject obj, HandleI static bool XPC_WN_Helper_AddProperty(JSContext* cx, HandleObject obj, HandleId id, - MutableHandleValue vp) + HandleValue v) { PRE_HELPER_STUB - AddProperty(wrapper, cx, obj, id, vp.address(), &retval); + AddProperty(wrapper, cx, obj, id, v, &retval); POST_HELPER_STUB } @@ -1288,6 +1289,7 @@ const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = { nullptr, // setProperty; XPC_WN_Shared_Proto_Enumerate, // enumerate; XPC_WN_ModsAllowed_Proto_Resolve, // resolve; + nullptr, // mayResolve; nullptr, // convert; XPC_WN_Shared_Proto_Finalize, // finalize; @@ -1313,6 +1315,7 @@ const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = { nullptr, // setProperty; XPC_WN_Shared_Proto_Enumerate, // enumerate; XPC_WN_ModsAllowed_Proto_Resolve, // resolve; + nullptr, // mayResolve; nullptr, // convert; XPC_WN_Shared_Proto_Finalize, // finalize; @@ -1331,7 +1334,7 @@ const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = { static bool XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, - MutableHandleValue vp) + HandleValue v) { MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass || js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass, @@ -1391,6 +1394,7 @@ const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = { nullptr, // setProperty; XPC_WN_Shared_Proto_Enumerate, // enumerate; XPC_WN_NoMods_Proto_Resolve, // resolve; + nullptr, // mayResolve; nullptr, // convert; XPC_WN_Shared_Proto_Finalize, // finalize; @@ -1416,6 +1420,7 @@ const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = { nullptr, // setProperty; XPC_WN_Shared_Proto_Enumerate, // enumerate; XPC_WN_NoMods_Proto_Resolve, // resolve; + nullptr, // mayResolve; nullptr, // convert; XPC_WN_Shared_Proto_Finalize, // finalize; @@ -1511,6 +1516,7 @@ const js::Class XPC_WN_Tearoff_JSClass = { nullptr, // setProperty; XPC_WN_TearOff_Enumerate, // enumerate; XPC_WN_TearOff_Resolve, // resolve; + nullptr, // mayResolve; XPC_WN_Shared_Convert, // convert; XPC_WN_TearOff_Finalize, // finalize; diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 6c09a2d563..34fbd8bc78 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -498,13 +498,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "Underlying object has getter"); is(trickyObject.nonConfigurableGetterSetterProp, undefined, "Filtering properly over Xray here too"); + is((trickyObject.nonConfigurableGetterSetterProp = 'redefined'), 'redefined', + "Assigning to non-configurable prop should fail silently in non-strict mode"); checkThrows(function() { - trickyObject.nonConfigurableGetterSetterProp = 'redefined'; - }, /config/, "Should throw when redefining non-configurable prop"); + "use strict"; + trickyObject.nonConfigurableGetterSetterProp = 'redefined'; + }, /config/, "Should throw when redefining non-configurable prop in strict mode"); is(trickyObject.nonConfigurableGetterSetterProp, undefined, - "Redefinition should have thrown"); + "Redefinition should have failed"); is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5, - "Redefinition really should have thrown"); + "Redefinition really should have failed"); checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/, "Should reject shadowing of pre-existing inherited properties over Xrays"); diff --git a/js/xpconnect/wrappers/FilteringWrapper.h b/js/xpconnect/wrappers/FilteringWrapper.h index b70d78213e..55c39c5143 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.h +++ b/js/xpconnect/wrappers/FilteringWrapper.h @@ -15,7 +15,9 @@ struct JSPropertyDescriptor; namespace JS { -class AutoIdVector; +template +class AutoVectorRooter; +typedef AutoVectorRooter AutoIdVector; } namespace xpc { diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 40aa32d197..25c9868fd8 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -869,7 +869,7 @@ ExpandoObjectFinalize(JSFreeOp* fop, JSObject* obj) const JSClass ExpandoObjectClass = { "XrayExpandoObject", JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT), - nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, ExpandoObjectFinalize }; diff --git a/netwerk/base/ProxyAutoConfig.cpp b/netwerk/base/ProxyAutoConfig.cpp index cd383fd0e0..fd3847648b 100644 --- a/netwerk/base/ProxyAutoConfig.cpp +++ b/netwerk/base/ProxyAutoConfig.cpp @@ -669,7 +669,7 @@ const JSClass JSRuntimeWrapper::sGlobalClass = { "PACResolutionThreadGlobal", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; diff --git a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp index e9e63a041f..2851f48200 100644 --- a/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp +++ b/toolkit/components/finalizationwitness/FinalizationWitnessService.cpp @@ -120,6 +120,7 @@ static const JSClass sWitnessClass = { nullptr /* setProperty */, nullptr /* enumerate */, nullptr /* resolve */, + nullptr /* mayResolve */, nullptr /* convert */, Finalize /* finalize */ }; diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 1505622e05..796cd30d52 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -345,7 +345,7 @@ "expires_in_version": "never", "kind": "enumerated", "n_values": 10, - "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1, LegacyGenerator=2, ExpressionClosure=3, LetBlock=4, LetExpression=5, NoSuchMethod=6, FlagsArgument=7" + "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1, LegacyGenerator=2, ExpressionClosure=3, LetBlock=4, LetExpression=5, NoSuchMethod=6, FlagsArgument=7, RegExpSourceProp=8" }, "TELEMETRY_PING": { "expires_in_version": "never", diff --git a/toolkit/devtools/debugger/test/browser_dbg_step-out.js b/toolkit/devtools/debugger/test/browser_dbg_step-out.js index 1d376caf00..9d799d0ee9 100644 --- a/toolkit/devtools/debugger/test/browser_dbg_step-out.js +++ b/toolkit/devtools/debugger/test/browser_dbg_step-out.js @@ -45,7 +45,7 @@ function testNormalReturn() { function testReturnWithException() { waitForCaretAndScopes(gPanel, 24).then(() => { - waitForCaretAndScopes(gPanel, 27).then(() => { + waitForCaretAndScopes(gPanel, 26).then(() => { let innerScope = gVars.getScopeAtIndex(0); let exceptionVar = innerScope.get(""); diff --git a/toolkit/devtools/server/tests/unit/test_stepping-06.js b/toolkit/devtools/server/tests/unit/test_stepping-06.js index 97b733ad26..e2c58bcae8 100644 --- a/toolkit/devtools/server/tests/unit/test_stepping-06.js +++ b/toolkit/devtools/server/tests/unit/test_stepping-06.js @@ -59,7 +59,7 @@ function test_simple_stepping() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check that the exception was thrown. do_check_eq(aPacket.type, "paused"); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 12); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 11); do_check_eq(aPacket.why.type, "resumeLimit"); do_check_eq(aPacket.why.frameFinished.throw, "ah"); diff --git a/tools/profiler/JSStreamWriter.cpp b/tools/profiler/JSStreamWriter.cpp index d735cb3a74..100450cf32 100644 --- a/tools/profiler/JSStreamWriter.cpp +++ b/tools/profiler/JSStreamWriter.cpp @@ -124,6 +124,37 @@ JSStreamWriter::EndArray() } } +void +JSStreamWriter::BeginBareList() +{ + MOZ_ASSERT(!mNeedsName); + MOZ_ASSERT(mStack.GetSize() == 0); + mNeedsComma = false; + mStack.Push(ARRAY); +} + +void +JSStreamWriter::EndBareList() +{ + MOZ_ASSERT(!mNeedsName); + MOZ_ASSERT(mStack.Peek() == ARRAY); + mNeedsComma = true; + mStack.Pop(); + MOZ_ASSERT(mStack.GetSize() == 0); +} + +void +JSStreamWriter::SpliceArrayElements(const char* aElements) +{ + MOZ_ASSERT(!mNeedsName); + MOZ_ASSERT(mStack.Peek() == ARRAY); + if (mNeedsComma) { + mStream << ","; + } + mStream << aElements; + mNeedsComma = true; +} + void JSStreamWriter::Name(const char *aName) { diff --git a/tools/profiler/JSStreamWriter.h b/tools/profiler/JSStreamWriter.h index 7cd1f1574c..2f7ac975ad 100644 --- a/tools/profiler/JSStreamWriter.h +++ b/tools/profiler/JSStreamWriter.h @@ -20,6 +20,17 @@ public: void EndObject(); void BeginArray(); void EndArray(); + + // Begin or end an array without emitting surrounding brackets. This is used + // for saving streamed samples and markers on JS shutdown, as some JS + // samples cannot be symbolicated without a JSRuntime. + void BeginBareList(); + void EndBareList(); + + // Splices aElements into an open array context. Used in conjunction with + // previously saved array elements from {Begin,End}BareList above. + void SpliceArrayElements(const char* aElements); + void Name(const char *name); void Value(int value); void Value(unsigned value); diff --git a/tools/profiler/ProfileEntry.cpp b/tools/profiler/ProfileEntry.cpp index 9f790e5bd6..67cf5b61b6 100644 --- a/tools/profiler/ProfileEntry.cpp +++ b/tools/profiler/ProfileEntry.cpp @@ -140,6 +140,12 @@ void ProfileBuffer::deleteExpiredStoredMarkers() { } } +void ProfileBuffer::reset() { + mGeneration += 2; + mReadPos = mWritePos = 0; + deleteExpiredStoredMarkers(); +} + #define DYNAMIC_MAX_STRING 512 char* ProfileBuffer::processDynamicTag(int readPos, @@ -216,8 +222,8 @@ public: , mStartedTypeList(false) { } - void readType(const char *keyedBy, const char *name, - const char *location, unsigned lineno) override { + void readType(const char* keyedBy, const char* name, + const char* location, unsigned lineno) override { if (!mStartedTypeList) { mStartedTypeList = true; mWriter.BeginObject(); @@ -239,7 +245,7 @@ public: mWriter.EndObject(); } - void operator()(JS::TrackedTypeSite site, const char *mirType) override { + void operator()(JS::TrackedTypeSite site, const char* mirType) override { if (mStartedTypeList) { mWriter.EndArray(); mStartedTypeList = false; @@ -264,12 +270,47 @@ public: void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override { mWriter.BeginObject(); - { // Stringify the reasons for now; could stream enum values in the future // to save space. mWriter.NameValue("strategy", JS::TrackedStrategyString(strategy)); mWriter.NameValue("outcome", JS::TrackedOutcomeString(outcome)); - } + mWriter.EndObject(); + } +}; + +class StreamJSFramesOp : public JS::ForEachProfiledFrameOp +{ + JSRuntime* mRuntime; + void* mReturnAddress; + UniqueJITOptimizations& mUniqueOpts; + JSStreamWriter& mWriter; + +public: + StreamJSFramesOp(JSRuntime* aRuntime, void* aReturnAddr, UniqueJITOptimizations& aUniqueOpts, + JSStreamWriter& aWriter) + : mRuntime(aRuntime) + , mReturnAddress(aReturnAddr) + , mUniqueOpts(aUniqueOpts) + , mWriter(aWriter) + { } + + void operator()(const char* label, bool mightHaveTrackedOptimizations) override { + mWriter.BeginObject(); + mWriter.NameValue("location", label); + JS::ProfilingFrameIterator::FrameKind frameKind = + JS::GetProfilingFrameKindFromNativeAddr(mRuntime, mReturnAddress); + MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion || + frameKind == JS::ProfilingFrameIterator::Frame_Baseline); + const char* jitLevelString = + (frameKind == JS::ProfilingFrameIterator::Frame_Ion) ? "ion" + : "baseline"; + mWriter.NameValue("implementation", jitLevelString); + if (mightHaveTrackedOptimizations) { + Maybe optsIndex = mUniqueOpts.getIndex(mReturnAddress, mRuntime); + if (optsIndex.isSome()) { + mWriter.NameValue("optsIndex", optsIndex.value()); + } + } mWriter.EndObject(); } }; @@ -308,216 +349,187 @@ Maybe UniqueJITOptimizations::getIndex(void* addr, JSRuntime* rt) void UniqueJITOptimizations::stream(JSStreamWriter& b, JSRuntime* rt) { - b.BeginArray(); - for (size_t i = 0; i < mOpts.length(); i++) { - b.BeginObject(); - b.Name("types"); - b.BeginArray(); - StreamOptimizationTypeInfoOp typeInfoOp(b); - JS::ForEachTrackedOptimizationTypeInfo(rt, mOpts[i].mEntryAddr, mOpts[i].mIndex, - typeInfoOp); - b.EndArray(); + for (size_t i = 0; i < mOpts.length(); i++) { + b.BeginObject(); + b.Name("types"); + b.BeginArray(); + StreamOptimizationTypeInfoOp typeInfoOp(b); + JS::ForEachTrackedOptimizationTypeInfo(rt, mOpts[i].mEntryAddr, mOpts[i].mIndex, + typeInfoOp); + b.EndArray(); - b.Name("attempts"); - b.BeginArray(); - JSScript *script; - jsbytecode *pc; - StreamOptimizationAttemptsOp attemptOp(b); - JS::ForEachTrackedOptimizationAttempt(rt, mOpts[i].mEntryAddr, mOpts[i].mIndex, - attemptOp, &script, &pc); - b.EndArray(); + b.Name("attempts"); + b.BeginArray(); + JSScript *script; + jsbytecode *pc; + StreamOptimizationAttemptsOp attemptOp(b); + JS::ForEachTrackedOptimizationAttempt(rt, mOpts[i].mEntryAddr, mOpts[i].mIndex, + attemptOp, &script, &pc); + b.EndArray(); - unsigned line, column; - line = JS_PCToLineNumber(script, pc, &column); - b.NameValue("line", line); - b.NameValue("column", column); - b.EndObject(); - } - b.EndArray(); + unsigned line, column; + line = JS_PCToLineNumber(script, pc, &column); + b.NameValue("line", line); + b.NameValue("column", column); + b.EndObject(); + } } void ProfileBuffer::StreamSamplesToJSObject(JSStreamWriter& b, int aThreadId, JSRuntime* rt, - UniqueJITOptimizations &aUniqueOpts) + UniqueJITOptimizations& aUniqueOpts) { - b.BeginArray(); + bool sample = false; + int readPos = mReadPos; + int currentThreadID = -1; + while (readPos != mWritePos) { + ProfileEntry entry = mEntries[readPos]; + if (entry.mTagName == 'T') { + currentThreadID = entry.mTagInt; + } + if (currentThreadID == aThreadId) { + switch (entry.mTagName) { + case 'r': + { + if (sample) { + b.NameValue("responsiveness", entry.mTagFloat); + } + } + break; + case 'p': + { + if (sample) { + b.NameValue("power", entry.mTagFloat); + } + } + break; + case 'R': + { + if (sample) { + b.NameValue("rss", entry.mTagFloat); + } + } + break; + case 'U': + { + if (sample) { + b.NameValue("uss", entry.mTagFloat); + } + } + break; + case 'f': + { + if (sample) { + b.NameValue("frameNumber", entry.mTagInt); + } + } + break; + case 't': + { + if (sample) { + b.NameValue("time", entry.mTagFloat); + } + } + break; + case 's': + { + // end the previous sample if there was one + if (sample) { + b.EndObject(); + } + // begin the next sample + b.BeginObject(); + + sample = true; + + // Seek forward through the entire sample, looking for frames + // this is an easier approach to reason about than adding more + // control variables and cases to the loop that goes through the buffer once + b.Name("frames"); + b.BeginArray(); - bool sample = false; - int readPos = mReadPos; - int currentThreadID = -1; - while (readPos != mWritePos) { - ProfileEntry entry = mEntries[readPos]; - if (entry.mTagName == 'T') { - currentThreadID = entry.mTagInt; - } - if (currentThreadID == aThreadId) { - switch (entry.mTagName) { - case 'r': - { - if (sample) { - b.NameValue("responsiveness", entry.mTagFloat); - } - } - break; - case 'p': - { - if (sample) { - b.NameValue("power", entry.mTagFloat); - } - } - break; - case 'R': - { - if (sample) { - b.NameValue("rss", entry.mTagFloat); - } - } - break; - case 'U': - { - if (sample) { - b.NameValue("uss", entry.mTagFloat); - } - } - break; - case 'f': - { - if (sample) { - b.NameValue("frameNumber", entry.mTagInt); - } - } - break; - case 't': - { - if (sample) { - b.NameValue("time", entry.mTagFloat); - } - } - break; - case 's': - { - // end the previous sample if there was one - if (sample) { - b.EndObject(); - } - // begin the next sample b.BeginObject(); + b.NameValue("location", "(root)"); + b.EndObject(); - sample = true; + int framePos = (readPos + 1) % mEntrySize; + ProfileEntry frame = mEntries[framePos]; + while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') { + int incBy = 1; + frame = mEntries[framePos]; - // Seek forward through the entire sample, looking for frames - // this is an easier approach to reason about than adding more - // control variables and cases to the loop that goes through the buffer once - b.Name("frames"); - b.BeginArray(); + // Read ahead to the next tag, if it's a 'd' tag process it now + const char* tagStringData = frame.mTagData; + int readAheadPos = (framePos + 1) % mEntrySize; + char tagBuff[DYNAMIC_MAX_STRING]; + // Make sure the string is always null terminated if it fills up + // DYNAMIC_MAX_STRING-2 + tagBuff[DYNAMIC_MAX_STRING-1] = '\0'; - b.BeginObject(); - b.NameValue("location", "(root)"); - b.EndObject(); - - int framePos = (readPos + 1) % mEntrySize; - ProfileEntry frame = mEntries[framePos]; - while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') { - int incBy = 1; - frame = mEntries[framePos]; - - // Read ahead to the next tag, if it's a 'd' tag process it now - const char* tagStringData = frame.mTagData; - int readAheadPos = (framePos + 1) % mEntrySize; - char tagBuff[DYNAMIC_MAX_STRING]; - // Make sure the string is always null terminated if it fills up - // DYNAMIC_MAX_STRING-2 - tagBuff[DYNAMIC_MAX_STRING-1] = '\0'; - - if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') { - tagStringData = processDynamicTag(framePos, &incBy, tagBuff); - } - - // Write one frame. It can have either - // 1. only location - 'l' containing a memory address - // 2. location and line number - 'c' followed by 'd's, - // an optional 'n' and an optional 'y' - if (frame.mTagName == 'l') { - b.BeginObject(); - // Bug 753041 - // We need a double cast here to tell GCC that we don't want to sign - // extend 32-bit addresses starting with 0xFXXXXXX. - unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr; - snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc); - b.NameValue("location", tagBuff); - b.EndObject(); - } else if (frame.mTagName == 'c') { - b.BeginObject(); - b.NameValue("location", tagStringData); - readAheadPos = (framePos + incBy) % mEntrySize; - if (readAheadPos != mWritePos && - mEntries[readAheadPos].mTagName == 'n') { - b.NameValue("line", mEntries[readAheadPos].mTagInt); - incBy++; - } - readAheadPos = (framePos + incBy) % mEntrySize; - if (readAheadPos != mWritePos && - mEntries[readAheadPos].mTagName == 'y') { - b.NameValue("category", mEntries[readAheadPos].mTagInt); - incBy++; - } - readAheadPos = (framePos + incBy) % mEntrySize; - if (readAheadPos != mWritePos && - mEntries[readAheadPos].mTagName == 'J') { - void* pc = mEntries[readAheadPos].mTagPtr; - - // TODOshu: cannot stream tracked optimization info if - // the JS engine has already shut down when streaming. - if (rt) { - JS::ProfilingFrameIterator::FrameKind frameKind = - JS::GetProfilingFrameKindFromNativeAddr(rt, pc); - MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion || - frameKind == JS::ProfilingFrameIterator::Frame_Baseline); - const char* jitLevelString = - (frameKind == JS::ProfilingFrameIterator::Frame_Ion) ? "ion" - : "baseline"; - b.NameValue("implementation", jitLevelString); - - // Sampled JIT optimizations are deduplicated by - // aUniqueOpts to save space. Stream an index that - // references into the optimizations array. - Maybe optsIndex = aUniqueOpts.getIndex(pc, rt); - if (optsIndex.isSome()) { - b.NameValue("optsIndex", optsIndex.value()); - } - } - } - b.EndObject(); - } - framePos = (framePos + incBy) % mEntrySize; + if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') { + tagStringData = processDynamicTag(framePos, &incBy, tagBuff); } - b.EndArray(); - } - break; - } + + // Write one frame. It can have either + // 1. only location - 'l' containing a memory address + // 2. location and line number - 'c' followed by 'd's, + // an optional 'n' and an optional 'y' + if (frame.mTagName == 'l') { + b.BeginObject(); + // Bug 753041 + // We need a double cast here to tell GCC that we don't want to sign + // extend 32-bit addresses starting with 0xFXXXXXX. + unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr; + snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc); + b.NameValue("location", tagBuff); + b.EndObject(); + } else if (frame.mTagName == 'c') { + b.BeginObject(); + b.NameValue("location", tagStringData); + readAheadPos = (framePos + incBy) % mEntrySize; + if (readAheadPos != mWritePos && + mEntries[readAheadPos].mTagName == 'n') { + b.NameValue("line", mEntries[readAheadPos].mTagInt); + incBy++; + } + readAheadPos = (framePos + incBy) % mEntrySize; + if (readAheadPos != mWritePos && + mEntries[readAheadPos].mTagName == 'y') { + b.NameValue("category", mEntries[readAheadPos].mTagInt); + incBy++; + } + b.EndObject(); + } else if (frame.mTagName == 'J') { + void* pc = frame.mTagPtr; + StreamJSFramesOp framesOp(rt, pc, aUniqueOpts, b); + JS::ForEachProfiledFrame(rt, pc, framesOp); + } + framePos = (framePos + incBy) % mEntrySize; + } + b.EndArray(); + } + break; } - readPos = (readPos + 1) % mEntrySize; } - if (sample) { - b.EndObject(); - } - b.EndArray(); + readPos = (readPos + 1) % mEntrySize; + } + if (sample) { + b.EndObject(); + } } void ProfileBuffer::StreamMarkersToJSObject(JSStreamWriter& b, int aThreadId) { - b.BeginArray(); - int readPos = mReadPos; - int currentThreadID = -1; - while (readPos != mWritePos) { - ProfileEntry entry = mEntries[readPos]; - if (entry.mTagName == 'T') { - currentThreadID = entry.mTagInt; - } else if (currentThreadID == aThreadId && entry.mTagName == 'm') { - entry.getMarker()->StreamJSObject(b); - } - readPos = (readPos + 1) % mEntrySize; + int readPos = mReadPos; + int currentThreadID = -1; + while (readPos != mWritePos) { + ProfileEntry entry = mEntries[readPos]; + if (entry.mTagName == 'T') { + currentThreadID = entry.mTagInt; + } else if (currentThreadID == aThreadId && entry.mTagName == 'm') { + entry.getMarker()->StreamJSObject(b); } - b.EndArray(); + readPos = (readPos + 1) % mEntrySize; + } } int ProfileBuffer::FindLastSampleOfThread(int aThreadId) @@ -642,20 +654,88 @@ void ThreadProfile::StreamJSObject(JSStreamWriter& b) } b.NameValue("tid", static_cast(mThreadId)); - b.Name("samples"); UniqueJITOptimizations uniqueOpts; - mBuffer->StreamSamplesToJSObject(b, mThreadId, mPseudoStack->mRuntime, uniqueOpts); - if (!uniqueOpts.empty()) { + b.Name("samples"); + b.BeginArray(); + if (!mSavedStreamedSamples.empty()) { + b.SpliceArrayElements(mSavedStreamedSamples.c_str()); + mSavedStreamedSamples.clear(); + } + mBuffer->StreamSamplesToJSObject(b, mThreadId, mPseudoStack->mRuntime, uniqueOpts); + b.EndArray(); + + // Having saved streamed optimizations implies the JS engine has + // shutdown. If the JS engine is gone, we shouldn't have any new JS + // samples, and thus no optimizations. + if (!mSavedStreamedOptimizations.empty()) { + MOZ_ASSERT(uniqueOpts.empty()); b.Name("optimizations"); - uniqueOpts.stream(b, mPseudoStack->mRuntime); + b.BeginArray(); + b.SpliceArrayElements(mSavedStreamedOptimizations.c_str()); + mSavedStreamedOptimizations.clear(); + b.EndArray(); + } else if (!uniqueOpts.empty()) { + b.Name("optimizations"); + b.BeginArray(); + uniqueOpts.stream(b, mPseudoStack->mRuntime); + b.EndArray(); } b.Name("markers"); - mBuffer->StreamMarkersToJSObject(b, mThreadId); + b.BeginArray(); + if (!mSavedStreamedMarkers.empty()) { + b.SpliceArrayElements(mSavedStreamedMarkers.c_str()); + mSavedStreamedMarkers.clear(); + } + mBuffer->StreamMarkersToJSObject(b, mThreadId); + b.EndArray(); b.EndObject(); } +void ThreadProfile::FlushSamplesAndMarkers() +{ + // This function is used to serialize the current buffer just before + // JSRuntime destruction. + MOZ_ASSERT(mPseudoStack->mRuntime); + + // Unlike StreamJSObject, do not surround the samples in brackets by calling + // b.{Begin,End}Array. The result string will be a comma-separated list of + // JSON object literals that will prepended by StreamJSObject into an + // existing array. + std::stringstream ss; + JSStreamWriter b(ss); + UniqueJITOptimizations uniqueOpts; + b.BeginBareList(); + mBuffer->StreamSamplesToJSObject(b, mThreadId, mPseudoStack->mRuntime, uniqueOpts); + b.EndBareList(); + mSavedStreamedSamples = ss.str(); + + // Reuse the stringstream. + ss.str(""); + ss.clear(); + + if (!uniqueOpts.empty()) { + b.BeginBareList(); + uniqueOpts.stream(b, mPseudoStack->mRuntime); + b.EndBareList(); + mSavedStreamedOptimizations = ss.str(); + } + + // Reuse the stringstream. + ss.str(""); + ss.clear(); + + b.BeginBareList(); + mBuffer->StreamMarkersToJSObject(b, mThreadId); + b.EndBareList(); + mSavedStreamedMarkers = ss.str(); + + // Reset the buffer. Attempting to symbolicate JS samples after mRuntime has + // gone away will crash. + mBuffer->reset(); +} + JSObject* ThreadProfile::ToJSObject(JSContext *aCx) { JS::RootedValue val(aCx); diff --git a/tools/profiler/ProfileEntry.h b/tools/profiler/ProfileEntry.h index ea803c3cc9..cb6c3e59eb 100644 --- a/tools/profiler/ProfileEntry.h +++ b/tools/profiler/ProfileEntry.h @@ -108,7 +108,10 @@ public: void DuplicateLastSample(int aThreadId); void addStoredMarker(ProfilerMarker* aStoredMarker); + + // The following two methods are not signal safe! They delete markers. void deleteExpiredStoredMarkers(); + void reset(); protected: char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff); @@ -157,6 +160,13 @@ public: PseudoStack* GetPseudoStack(); mozilla::Mutex* GetMutex(); void StreamJSObject(JSStreamWriter& b); + + /** + * Call this method when the JS entries inside the buffer are about to + * become invalid, i.e., just before JS shutdown. + */ + void FlushSamplesAndMarkers(); + void BeginUnwind(); virtual void EndUnwind(); virtual SyncProfile* AsSyncProfile() { return nullptr; } @@ -192,6 +202,14 @@ private: const nsRefPtr mBuffer; + // JS frames in the buffer may require a live JSRuntime to stream (e.g., + // stringifying JIT frames). In the case of JSRuntime destruction, + // FlushSamplesAndMarkers should be called to save them. These are spliced + // into the final stream. + std::string mSavedStreamedSamples; + std::string mSavedStreamedMarkers; + std::string mSavedStreamedOptimizations; + PseudoStack* mPseudoStack; mozilla::Mutex mMutex; int mThreadId; diff --git a/tools/profiler/PseudoStack.h b/tools/profiler/PseudoStack.h index 011467f2f0..12642be35b 100644 --- a/tools/profiler/PseudoStack.h +++ b/tools/profiler/PseudoStack.h @@ -347,10 +347,16 @@ public: return sMin(mStackPointer, mozilla::sig_safe_t(mozilla::ArrayLength(mStack))); } - void sampleRuntime(JSRuntime *runtime) { + void sampleRuntime(JSRuntime* runtime) { + if (mRuntime && !runtime) { + // On JS shut down, flush the current buffer as stringifying JIT samples + // requires a live JSRuntime. + flushSamplerOnJSShutdown(); + } + mRuntime = runtime; + if (!runtime) { - // JS shut down return; } @@ -359,7 +365,7 @@ public: js::SetRuntimeProfilingStack(runtime, (js::ProfileEntry*) mStack, (uint32_t*) &mStackPointer, - uint32_t(mozilla::ArrayLength(mStack))); + (uint32_t) mozilla::ArrayLength(mStack)); if (mStartJSSampling) enableJSSampling(); } @@ -413,6 +419,8 @@ public: PseudoStack(const PseudoStack&) = delete; void operator=(const PseudoStack&) = delete; + void flushSamplerOnJSShutdown(); + // Keep a list of pending markers that must be moved // to the circular buffer ProfilerSignalSafeLinkedList mPendingMarkers; diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index 4a2ba78115..e08eabc3d1 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -359,6 +359,41 @@ void TableTicker::StreamJSObject(JSStreamWriter& b) b.EndObject(); } +void TableTicker::FlushOnJSShutdown(JSRuntime* aRuntime) +{ + SetPaused(true); + + { + mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); + + for (size_t i = 0; i < sRegisteredThreads->size(); i++) { + // Thread not being profiled, skip it. + if (!sRegisteredThreads->at(i)->Profile()) { + continue; + } + + // Thread not profiling the runtime that's going away, skip it. + if (sRegisteredThreads->at(i)->Profile()->GetPseudoStack()->mRuntime != aRuntime) { + continue; + } + + MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); + sRegisteredThreads->at(i)->Profile()->FlushSamplesAndMarkers(); + } + } + + SetPaused(false); +} + +void PseudoStack::flushSamplerOnJSShutdown() +{ + MOZ_ASSERT(mRuntime); + TableTicker* t = tlsTicker.get(); + if (t) { + t->FlushOnJSShutdown(mRuntime); + } +} + // END SaveProfileTask et al //////////////////////////////////////////////////////////////////////// @@ -496,11 +531,18 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native registerState, startBufferGen); for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) { - uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames); - MOZ_ASSERT(extracted <= (maxFrames - jsCount)); - jsCount += extracted; - if (jsCount == maxFrames) - break; + // See note below regarding 'J' entries. + if (aSample->isSamplingCurrentThread || jsIter.isAsmJS()) { + uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames); + jsCount += extracted; + if (jsCount == maxFrames) + break; + } else { + mozilla::Maybe frame = + jsIter.getPhysicalFrameWithoutLabel(); + if (frame.isSome()) + jsFrames[jsCount++] = frame.value(); + } } } } @@ -575,9 +617,9 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native // Check to see if JS jit stack frame is top-most if (jsStackAddr > nativeStackAddr) { MOZ_ASSERT(jsIndex >= 0); - addDynamicTag(aProfile, 'c', jsFrames[jsIndex].label); + const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex]; - // Stringifying optimization information is delayed until streaming + // Stringifying non-asm.js JIT frames is delayed until streaming // time. To re-lookup the entry in the JitcodeGlobalTable, we need to // store the JIT code address ('J') in the circular buffer. // @@ -590,11 +632,12 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native // its JIT code. This means that if we inserted such 'J' entries into // the buffer, nsRefreshDriver would now be holding on to a backtrace // with stale JIT code return addresses. - MOZ_ASSERT_IF(jsFrames[jsIndex].hasTrackedOptimizations, - jsFrames[jsIndex].kind == JS::ProfilingFrameIterator::Frame_Ion); - if (!aSample->isSamplingCurrentThread && - (jsFrames[jsIndex].kind == JS::ProfilingFrameIterator::Frame_Ion || - jsFrames[jsIndex].kind == JS::ProfilingFrameIterator::Frame_Baseline)) { + if (aSample->isSamplingCurrentThread || + jsFrame.kind == JS::ProfilingFrameIterator::Frame_AsmJS) { + addDynamicTag(aProfile, 'c', jsFrame.label); + } else { + MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion || + jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline); aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress)); } diff --git a/tools/profiler/TableTicker.h b/tools/profiler/TableTicker.h index d8771020a8..45c486cf37 100644 --- a/tools/profiler/TableTicker.h +++ b/tools/profiler/TableTicker.h @@ -196,6 +196,7 @@ class TableTicker: public Sampler { virtual JSObject *ToJSObject(JSContext *aCx); void StreamMetaJSCustomObject(JSStreamWriter& b); void StreamTaskTracer(JSStreamWriter& b); + void FlushOnJSShutdown(JSRuntime* aRuntime); bool HasUnwinderThread() const { return mUnwinderThread; } bool ProfileJS() const { return mProfileJS; } bool ProfileJava() const { return mProfileJava; } diff --git a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp index 569b7a6af5..c44b8c2e27 100644 --- a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp +++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp @@ -88,7 +88,7 @@ CreateGlobalAndRunTest(JSRuntime* rt, JSContext* cx) "global", JSCLASS_GLOBAL_FLAGS, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook };