From 6fb428936334e36ea25d062621c5a173e89499c8 Mon Sep 17 00:00:00 2001 From: Kevin Ness Date: Sun, 14 Apr 2024 19:22:25 -0400 Subject: [PATCH 001/212] Add vhs-action for boa_cli --- .github/tapes/boa_cli.tape | 37 ++++++++++++++++++++++++++++++++ .github/workflows/artifacts.yml | 31 ++++++++++++++++++++++++++ cli/README.md | 2 ++ cli/assets/boa_cli.gif | Bin 0 -> 44429 bytes 4 files changed, 70 insertions(+) create mode 100644 .github/tapes/boa_cli.tape create mode 100644 .github/workflows/artifacts.yml create mode 100644 cli/assets/boa_cli.gif diff --git a/.github/tapes/boa_cli.tape b/.github/tapes/boa_cli.tape new file mode 100644 index 00000000000..77b389257b2 --- /dev/null +++ b/.github/tapes/boa_cli.tape @@ -0,0 +1,37 @@ +Output cli/assets/boa_cli.gif + +# Setup env +Hide + +Set TypingSpeed 50ms +Set Theme "GruvboxDark" +Set Shell fish + +# boa_cli needs to be installed for the tape to run. +Require boa + +Show + +Type "boa" + +Sleep 100ms + +Enter + +Type "2 + '2'" + +Enter + +Sleep 100ms + +Type "const sayHello = () => { return 'Hello World!' };" + +Enter + +Sleep 100ms + +Type "sayHello();" + +Enter + +Sleep 2s diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml new file mode 100644 index 00000000000..57d25e2cd2a --- /dev/null +++ b/.github/workflows/artifacts.yml @@ -0,0 +1,31 @@ +name: Build Artifacts + +on: + push: + branches: + - main + +jobs: + build_cli_tape: + name: Generate boa_cli GIF + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo install --path ./cli + # Below is from vhs-actions examples + # https://github.com/charmbracelet/vhs-action/blob/main/examples/auto-commit.yml + - uses: charmbracelet/vhs-action@v1 + with: + path: './.github/tapes/boa_cli.tape' + - uses: stefanzweifel/git-auto-commit-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit_message: Update generated VHS GIF + branch: main + commit_user_name: vhs-action 📼 + commit_user_email: actions@github.com + commit_author: vhs-action 📼 + file_pattern: '*.gif' diff --git a/cli/README.md b/cli/README.md index ac8ea12996c..fd9b9ecb5e0 100644 --- a/cli/README.md +++ b/cli/README.md @@ -18,6 +18,8 @@ your CLI. +![](./assets/boa_cli.gif) + Once installed, your good to go! To execute some JavaScript source code, navigate to the directy of your choosing and type: diff --git a/cli/assets/boa_cli.gif b/cli/assets/boa_cli.gif new file mode 100644 index 0000000000000000000000000000000000000000..7cf28716b68738f3254f514a6607b5bbb1ad4ea6 GIT binary patch literal 44429 zcmeFZXHXOT+BcX40wJM<-XZkR5kfD9-XZi*4OPHK6Hrh?=)G4FLlqE^UIppWq@w{9 z>0+m-fLJ!(_c_n;Jnx*DXJ>cc54+0@!-ss~->&P|t{55{sHnO(Qer_zfHQz!0HC}f z=&U0(<_bhf8YZg+Gu5J1GNO~$W^fE=lvZU_H)hmvV7lbYjJ9SrbY+o7v0g}LQ?y~j z-DH-aEUK5gkO@IUxt(43y;t?LR?A_5SJ2A;t+5R z6%^7DL<$HhqXiS1g^V4AMTLY##l=L0#K=G5d`J-iq!>RE$to_+ATA{;A;K#m0hScu zmXe^5mPJV`8%pb2N}D^$h%n1YipYfL$x6UwCAekv1Y~_~$O&=D$-(5juFFMSJcE`v zb1D8zMyI?iue_YNf|iql!vzJGODGX46p9jsl0*^86jA(&s!EFXIAtYf<@gHalv3rC z2P*DCDiQgrb-8N%jA~*)wad}!Dwj1BL^aVjwA2*N+9{*?A!r#+w1Kj=y0~^wy!MSe z9eqQclzTc2g}UlcT}>Wcw32Rog>Fu;Z)vJ$nQ+&#C>e7_-^x_k%0|^HBH6~?#HRkXEvD4L-V7@P!KwkAP<&3h!cI0^ z&KOnaFe^7l({paBI2|y~28j!FbU)|l;mPP}1@-jc_3}Xac{%!BbM_DMxS+~=!QJ$N zzs99=s(}~XgDzVJ1*!yPoDVir4h}niHSt=gDb@8*wFp(dv^pU~lkqMV> zTteb6vPBsoqptf!;YFhG>VzmGLUDKuJ~Tf5Y*MIw$}P8)XycUF(3Iwzsj=2+Sk<%` zpETl)^fc9sG@FcjvDtoSvoDxr$Eatw#^+w7&$|rIH{#5%PcO*4QRt2?%89<65nLK6 zR-T9~zmriJ4zDCASKTI5Rc2NDplgT;_Y>9bXV^E`^0(Axx8j6bGqqZ)qFd|lv=uqG zC-C&NmJVg{4|Nm_-!&ZWCXQ6PjP_PN?ys9DQJWZUnXkt#O|?FotS8l;C3OZopX+|v z=D6DBx%zZq^J(i&J@R#r-5XNVo8<>@yG`G9Y9IEf2>gOs=-b-pYGd@}W#lM;002PX zgc3|i{wqcP1^j*{@&ErL`Trlu|Cb*Ly}%57iwK6+v}fq1Z+;+ zG3{u0*%N;F_T^;9gIAC66fi!s&c^kj1ZGLMsm`X&#~DJVC1zdC+mi(f7gndbT3*i( zb(8tbyIbEZRNLHld(z$Z?pdSfWQloC`-kV9!S7a|^mKfB)gKMxw|LmOzd4pId2ae) z*Wv5ga?`sOz1?5lkyZ$K$s>)9FW}3#=-OY3~mH1YM9-H4*rjcmPR+gF4 zo2_h&f%tZgt-arNF4m`JJI^Kb&2~O6PJE}pE6;DI(6_2)r|4q)o1NPMqvEfNL!SD* zz7xJv^SUJR>zmhi@l+DK#27aJ-O>cn+TF4grMJ7~83qz>Dv$@5X{`dETJfr5J>56= z!koI_RG)@G-`1ROCQ85{%K_pw1qZ)|(rc=pO6=6u@d^7^9p@C))U~ciT+H{aut}qX zI9{JoZFvvWqcuU7(P1K3 z{)V%K#_PHTNv)%B1p~UYeowg}h$K(b5bqFe;1u(-NE{6gFQ82$VoNKl&pC8BD)G~; zoCYr2D5s~|d^w3hSh0U#y1N$C^jPXycsU)atbae~sppo7E|Qd>GXhicEi;#E31?0f z%0QPtk@7@bID84p>whc|iT{zX=zbR(ofA5Tth8A9#kYvwvZd!*`~vN%>8`O`6i{#+EekmmBpxw?yg&9|f-E#e-6~nJ(K#H^d7R(yNC`dj$_F$jlhBSs>gY+15RF9#F=p0q^8K5vM*5xr> zyGwsa@qlRyaXuchA@J)bn@T+86Gpj+D6w5DdXyO^XkZP;6g=w zIu7ZM+h4OqxiwN+HAeHU1dK{aoQabyu}pbat^+&2p5{mbq>i^}b58=}E5`S8KGt~h znj+!=&@aq4!oO3w{duJ5y@wYg7isMD1084@FFeT9UMy05qaxo zOk>#pYvrk$#^fd7Eh<#)M^NCKfOuup90gaW3Af@dI%s6%g{E&n<^{ZgTFRHMAieS& zN^J@hi81|ZLOHvm*SU9U*G3->qTwW>w9=JHw>N76r(cFZLNGT=5OrCxP2Vi4;A7c`!?&g9McEKlQ827V{INGWLFzbrx z#+zBIT9#{3hMdP%tSuAPy%Ho%ugG+XP!l`Uo^KkbZ!~oaIz~vqAC-r@TL_TtoCE1=Uo&kc&ZGoOGIIE$W%_`8tdL z)u52pQoKoQlZ)7t9Jo4<2<{_Sb! zbX!B%lXq^X-=6i;w>QR{zxUuiC5>6MH|IZj@1uUYJe$_ude8iWzw_w|X}Z0=FV)xN8iztPd86bUjZ08 z`>8GV2;AS-AeNnj?9+R3>fhJl>7B!37W+xg-#0jBI!BeK_tPT2Zz34F#tbbEvTlFh zLjJPsnsAst$m{vOEtlRkb>8Bz==t{@m6@*Tu<65+)9!ORe*dx7eBPrybK@8X|tY!!h)b9$S2EzWw99uVv55v+1wxJwHAK zr1!jdZE@87{Kv=enV!|7>7%~WAD{3H57($Ij|aJb?j=}0++d$M9##LjpOOA>OU&|Q z!ujVx!OX)Q<(ZS|h@XcFVjvqke|Iy?D#-8{EH+$1M8}@@Ky9sQ)^dP4D}B&+^Ax=U?AQGksq>W`2B( z_;tF*@aWg^W6Pfhw|{-#wS4sL+04(cJ->eJr9b-q+Va=8=f8d)&pi5hH1q4{DH#WF z;6W;QN-Ul#91khLLp$-bBs~2w9?lWPq!Pt~jbaOr;wXsX>WtzcMe!X+Avg$vDgmH(5L6OkunBSD2?+%WNu3EPq=d9z z#|ar6iCHR%IoQO!@Wg_G#G=l`Vp3wsaUzi;sZ1rQ0-ID7o>X0sRNI+UM@niqPHN;x zZdOTd#U{6hCwCSkcXuW~BqjG9C--xt4639IV^c=MQ^pEXCOT85NGa3DDYG1@^D3!} z*wm-tsicC`mCn=`q}0{p)HRN@4VAPlY}!tE+HOJG+s?H2q_mI6X?q;$2P)~GvFTsK z(~k?%zjda6C#C;9P6u#ifK)Rm9W$t|Wk3oupj{cX%Ng`18F0=_Ce=(9$4s_snH+_g zTwR$w%b9#9nF!7-LDei_$1KrnS;)dHiLNZEdDU!`W46+@Y!$1*Y_+az zjpb~ulWa6+j*e=Mo@0)|wH%|u9Fwjbv*jF%lN=0ZuC;2etz)kJwOnjru2Wa8%W|&U zNiL2v&qFoO%Q4UATApuVo_|-~Eq3@HEHe~fLZ+Hb0086$00^O+Eh7#@QNRRIBpt+1 zJT;?eBr+2>ln9m9@#+i^GE8CDOiEFaFW1cy(JWyR-}f}h<#H#uoXm?%=80Z-n?l<6 zn7Tu4_?438v-eDf{#8D6Xo&bioI#>i{hJW~iCU>EZn@q2=N*bgB}X;Hp9(rQ@lUSU zCbPJs|HXT~0eS!gfQS4ak;S1_4x>h#HdpCaahI_S0YDU$5WOC$abqx^NyK{VF&X!# zaVT)TIBpVtlp#oyg?hv2J;5gF7!)#IR`sNanfbsoAzi>If#M1olV7~RYw7o6$F|ViEOwfiE-2gd?T!m2d-L4t@KlxX`-7xLM$rOw|A@xXy!>N=$Q!0$YPzB%_#jA)= zdLiBz>$TwAaFugLkb$SkVkLk^3$@bEc&tXl;@+Utv-jb=36bV3BND5Phns=-Q?w~` zj>9(}!xd@W4LCAl1cp+*n&4o;me2DrnFPH+JDt-C<`g8cGw6w04}Rl`*?Qt*of}Ge z97sAv)v!g)Q-o8I--;gzxtqF-&Jy@RvGn@*=Y|d6Ds4erI|w?H17|`Er&XQoakK)5YJNnz0+c zY^Y1C4Nqzh2_tBtnn3fOZt6?5z}2T+hjC+muxg&zhQdmkOn&W*>vKb+o8s6~)6vh3 z?$K0q+HYQb2!tvj4OpeI)d{6u48oQrdUv9!{YRd4s8bk5Q9g0*3VJg?Q2u&c+BQe( zI{IKVZKV!V<&(Sl!o%R)tmpNC5lldfJ>N%_o)t%WH_?`+EDaDWZFP z{_jEt3iw1wz2SFUb>ld|4jNZVHT;60_Gx!6ucDp%NX*i_Jwe!rSkL<2jzu<4o%Oe| zFzcUgH^cMXyiiN}`L3vkxO}|oxL$^3)U_{8UK&fN?3m|WwzHaKZde*$)riP4UCkkL zLs1VK8UsK|H6u)}8orq#KXD97mhMJcS!z68^g#bnJRVRwfPet9Y6YPL04XW|bw2;+ zKhPkbC>Egj_pASd?fW*y{$~5_xWCvwNW?!f;rFZOR=&w&EbnfVDq>TsA#PPUnJFdI zSDbi8RIf-yd}XVkMQrd6{SBxzBxGZ@Tt8h9TpQv$U!`?dXT))F*S6N|fyc|5T%V-| zw-$n{_|=OO%|6Cu=BI(u&KVx1%{X8=neAVFZ6BhZqc5PB3xLs^am*F<2HsvlhB2Jm ze4HlqDmqR@lr)ll^m1Y9+TqrO)W89<;{loNe>X9Va1KH}BD4K_qwJTc-er~_jj#4z z`SKyXD{QjrQbZLEGp|`SCJJ-1zb(uT%YVszk48el1%1|6&?JX*!kFsU-JeBD77R~} zz+CGCxu|&cXNziKXo{SnD;&WRJf}sgb?%U+;fRMq#;d7$MOWO>3{!np3htq z6J)9|j>Gat@bR1{@IJsLxSzS5G3OR^n!9lKmde$&L5P=zpjjNK&%=;~FAc7Ts1VL} zm+!#n#L|d2jLpO-Jh}P;qA(g1uhEGR z>K~iPE9TLz4Dx+@JeKE1sL3c;$b6HP`(dPWC$G;IR08DfA*T-02Mj^qMJ}o zvEM!3Xo{!i@q6ALKHRd*wg_zcSYsj+^WJ3agQ-Ma<808+Dq+_i6lX-nz`M6qpRUl{ z-3jm;mw!0x!ElzRX_&@cw9uzSH8fp;!_zBTw7afsQbshtE@(V6#>KkdkgU%BmqzOY zZ~^cGc>lV!zcs211OOoSyAHw81Mw7&DK#+(Wg|%(;%YI+d-~N8$LJ(lW2PZ z2i&Lw2J-R%IJsceQ9uYU)@5lTkN+7P!XgtqtY4SO%;r*espo1e4g5yRdtqFRDfhtj zqy43cB}yPHnucR7fP_!&O@2v_8n_nc0OFwq%EKuRH&Sn2f({!mL;MyZm6=$Y2>oCg zms)sd@wIolx2e9huH*0kIO&pi$KHK~&%2xoIK=t;kt0vELS*+)0U)729i-}ZJ4t-!g~E;lX^z>JAC9gM)xMejU|j6$3?=8N}eE|dx| zPsPp?fE$iAuyyZ;FP!mt31(`a8^sEjfOPP$G^?ZN=}-X!$?Ud)QS_r%N%`UGtn8fe zbS0v2^VzG4L)T2s_%3E9iN`bDFyaEQW`TMet&OEeY?;h425;hJ?(Ys(=DUV=ndVdH zI{LtT^15`2E)>=16#m&ug2-nm_}3Y}A(t{=?cX}1G!FS2Z@}0MmDm7H#Jl|xB4PvJqKg_pkNykH|Ws z;7c)EVnP`>*F=AXrJuIi;GRjnKj;Fi-Z{Q`4{9&&(Cl49{~<}S-YH6Ew!8kD&gGR_ zw@2OE@y{+V_gy*y#dYU@!m!>PpqFv#^iLmlx}Qd;5w`0&5oY6rGsW(~SRHynydQ$k zSj<5ngml$(Xev$x$MDIJ4eY39qFTa`5Q ztQ__|{kp#}9(;cK#&)<~t14XV_v-oR)N+s(cT95SMNIbmY-^N?wBE~axoh8d$jqB` z%Ft&}&~ft&HdDr@ToaW{nW(+=^58aCm|1&wSn|%*$q)!#gE!k%PoFBYd>L6^ zyoglJXvX8jM`uhwcfw%f(L?edHfRd`aId=WP_>^Q^P1D?*}Wbr<=lSd)x~E9UOQiw z2Hn{TjTgnQ-qZ78qYLEy)-gNjJ+WprMVtObUe;wZ+Qe}!iB-Cr80N_1136}sj5OR% zc5B!qR=@P3DXUX}QTpcco&0mb?*V#gZSb=ah+pn67gxEzxNIsd6KM$a5pCP{mikWYMC z*+|RUhp!CSZoAAH60-5^zT=2au1T9ew$1z#P0^gv8|%(Mv8c~)*(BY{E}n?q zNOriqoPT3su=k#W2Ls#XPd`M|95#~J?LPhVe#j2kX}Jlb<^$nI9LH~uLs36iFYb<9 zSMLyg*x8ILDS|2Y(J_D6>Gu(`RC39`cje_BMuxoNHr2DQbHZ+39oDG6T4a!XB$JrE zV^W-!)xkxtVERnLb?52_wOcoDaO5*@#>bq6do54@`J#z`-0457azO?d1^|=Mp%uhR zB$-3jU$2#_Y&bz_;vaYVh;uazCdT6^4xrQ?_g_6 zq+5?BTXekc(b<*RiVL|7uB$!CjGy@45U(9dBg)QRxd}c!{BxHY{=+W)zoK^`>s1u2 zmnklB?JmSc13|hvC!^3852k0!&rjijJ(mt|-`I6q^CMip@~WDbxycuCo!q-t^DkU( z+VEjTd-I3E-M>Wm#J35c`d{MifnkEeRjUS+B&&Q!HEkaPSbI6l$>HC67dnW3^y%t< z^)3Q_GO&_BrG}dA2g|u(M!;%tfZo-Mifqe)nSm-qG|Kqd67B240PAQ2ks6Kw+=_aF|vh-3K!6lI^okAK$$l)Gxth`ok-Z%Pp#GAKhH$ zT7p35Vp(!&CzgB-UOp`NaPY-ciYaqIs;X|oTXG5M+{Ri!;PPo@ zwBbgY$b;Wv3NEF(J88Jn@8258ZI0yyf-7P{QD}NuW$Axcqo-LUHFv1j! zg;HPRU?EGkp25n~6`Nk=TY(g`+E7k`ME$Iaa@y3KJZktQRlkxLE4Pq}Tns6rGLI&6 z?3|ffGu~FHts;n;Q4#(HUg)ipSf#H99bD(d^sDB;lzLeoC$jo{jY=GXs`p=bD_ba$Vc9GOWG-G%$u0nq}wcUnD;qFjT+n z9TrWif$_g*W@%NK;p}wT`=Wj91~!BEnD2I7Gh}Yu+ETNNlDUVOMe^5XAfk03A`7qrIuTGpOm?BTS-6fFfJXu^L$qNWe>%Njo$lVpI-Fc zd+b40Y+4jDr0!4y)rwmFv}1 z&`c*i7xwf0mBfB2Q_pM`#QlyZEJc( zLcg}TY6jkL9#+)POc(ejyoHJm)C7oEc;~xdDmNES!4Q2UZ8d&tj*@vwaer1}l8Lez zhaf=nHWQ)r*M<46?c8BbU3t zQTewRrenFsR|#m0bMlkg!&7M;y2m=ZfblaVwWOgV`NYgfg%a`Pn%;f7k1xi=CIa^dk7u!gYE*jCD%mTBj zJKz^qB2DrD0_rGcBuqHoKd_i-7)(VXldz2~kbGTB zSu|-@W=s*tXrlJoRzswNQ}PWhLtB%i%#=dn;Ivv9a&Cyp@JJHQW#UE=GiM}wW@rZ8 zGECrKdBHrR>XIL9p*o9@$Mqkr4SiAFh;&U8m9y7eSaSSi^HnqWfsj&v<3{w%i;!2p z4$Gb^>fiayWc10HxMms?h)Q_}{5kJ0I}1O7ET)04jQnH8{9;_NVGJ#gk63IG=W$PzsSk6n_`3(Vyti z=)oL?h(1;82La_{qIQE3YZ?L2bHYNLOcleNIj@lnhz&MbmQis&D~LpAe+*2!^!S(U zt`d83Df~80|4h;PfFb1uL_sc!CJjezk^zcmq;v-xkEs$Mff0IjMJ$?fAbCo`#1W%7 zIV1OYWcw9LL&&g@(Yg+U{RTv0cLUv5-7D8^p2WM0=Hy?8u?Kl_gK&ONsC zMr$;a0-8VehtlnAqG;3tC>5BRGja+B7S|qY$rsFJ_H~Xh0swl^s0UeettFck95D(R z0*UEfV3DVAedhj5+Taj(31zDUp~!fFj+U*TUNkDqECePKvp8zw{b5FZrl+tt2BqLzzr!{B>O{&fYC8UEe>8&j z-f4q)20ccH5T!B%F{mDow6HGB_uApl-2{rOg&;(+y4=dwtP=Ll++bq$MC{WHn^Vn$ zd2!nYS`)n)c{9RNt~;i#ObZFqts)Oz#9M?-EUb5ZuHqZ_vy51=B>HcGNdfWi@+Ro3 zB-+>wG-XOSL61$o~Ks{71y_A3yK`WRO*kzZLnv@idGL^)Hpf*26HR)OSz?JazmjeBS_Mr)<%MOaEnv`)O^h08^fW*xnl3+)wF8OSPD-Z+3fqwU zfmA!g$`roiOxhG+f~5U!YSA~?1=sD1TT^mI`?`jD-uM2Vu%1cif>yi7HT6E>v7y}oz`(YalSUzpeACXI!EPL zS`tT-0!5-U&+rgAiGvmTxAq!L-u?fsb^o>dWH$4c)@|5StRDJ%_pyFpy+rE$xt{p+ zit#L@dG9f#8PRuxy!$D}%;&0erRk>E$h$vNa%wh*D{L8=Dc8Kh^dym`X1)x3o9~8N zKr9#rh9V$D^gt3TMdiezPzMP z!bFGt%7@kWe#_k;Z)@{XiG-acqUw3X z-xbc65kqV&tDSj(PBzIkV$)+4b81D+MMV*X9t4zHI04a=0~F$amAWUzS_r+mMlOz8 zL`|Ht+%c1(QS1zNkzPs419@A+U>bYUh9|(OMFmxdUEjU577?9EPiht!Ab@~)e zLToGOTZfkgy>w%#eG)0+B-zD5)chGz_R$qd@5kF~UU%mCYEy#X>cue$3g@Hu8q90X z892EC2_Oh%%yIPAjpGMP#xeagN2MbDl!1t-D6r`W^%{=Ls6v-jy#k;Er8tH^re?!n zZU-J3=jd=<-`&^u^!Gi(6`4C88x5wE&d_1hsfdb!_pvq-AUQ9U__PPLae1H%AB^D1 z9kGxKFce}e4EEwZd1VK*-p;o)^RB7H*g5$v<>5k&m-s$j^b%D+pZ8{(PsYkSnoeVw zoGc9Nqao{;|LSwUo0H=|t-ya7M?9o5a!i!Lv5~0VRHmOKhm#`5L_Kv95WLOA(FQNo zu=f^KdXDt@kaRgu30ls1gRY3gYSP-*S)R=1P@**18Ypkx7GqLuA%6D?@~$fd;@aL99>Un} z-xUTuo4G5mk#p_)+6pPq5s*1#7Bpg-FNyJ$r#KI!q^~~rFfHo(0wHz>`S9ikc?Dzk z2etwDieK#l#m8sd$`P@pz=)*#t_yEZ4&yCW9-J!)?fLNJQxkeRi(?^8Yb(%g4~2C6-QW zeEWPjbN1VAG)d<2-srhokM`kI@`o%57V5i=V+pV+RRVqLL1cj^DyfcG6vxvS?RECY z6h#sf3R01z_H~RR4fPbyOfTg?lSr$)bsX%ezICyM4(SGHI(OwKa1D$+_-vDosx_16 zL0)@^juz~mQnrxH?w_a6pVU(_(E9b+lOCB}FyyW{-ZUGHQp^%c6wE?_On8&#XNS12 z5`Ph^@lSAy{h4rlnTntQ8gS(s8+^dY(rugG5NU z)swSd%vz`!%IrPU&+fFA8MRkB>d!H|&H|?+o7Swj&i5S9M0h+_jM{XlLmI#Unb;i#f$s!k~+C|UKO+IL31$IL@_ zY^f>Z!8o#|@}NZO)&0SzXBGmjW|UgX=m*sg0hAJ7UUbIz)Bf^gNJJT#HI0BYe+}?T zJq=K3(tKX9p0DeC6f956)YNZ>eYmR5rF?iv$XH+VGjVt+zq&;VX^E<)ot1Ej9Ze0B z?4BBCJw(hhSmvyQd6SGTGN`5TtYZ4OWmb(uTp`jX`NpF9f%F&p35@L4bPT(QMDKt=?#T>2zdF5W zWCjJ#CUFRcpG~@ir(pBt5CpK4Lb0Sk*?GVw+=JY=jC8m{d8pMiw5e}Dd|(Oca@sYq z;i^k8$jaF=^RM&dOz2Icb`95Is1q6$>zB#CPy?k^Q7AMCic7i@B@93gQkV>;3Ffx~ zMRXsd=BASU8lzqP!-+%j zlYb;=ysQ%M=!b5W!y7=GRf~GnF>aG;UzB)l(anN1l#dHh3K^6~4RrQL&hGM$q!P>3I1-!&Slsr-{O{_jD@Mo~IZ z4}dI4xPS-ZX`M55$?V*4BAc`wdJXw}qi(TPTB%}A;obulesQ)stb3Tao0vYCJZ1>S zu;?K4;9m>vE-XbMP&`OD0*{9WGU4D>SaCNnm`%lKABTo0@mpk6xmS$DbCZ5Z0DvFXR z5Fy|k?W>8@0nk7)4T3ub&H(8FC1H**4lII=5{jK=1xE+^y6CZW9t(oy=T#i>RrQ_O zj%38Vm8r0<=;f*}m?(VRl>Rqjwi{2&{f(IInD^Jn(;I~UhM1TCcuN26&~yO5&EVa5 za>VGPjL&d&=1Z;wW-(|tIS(_QEo!sMOetS6ka@Z{QY_xGGm$4lleYEv3P0I6`=si@ zc;+=)TRuu+``VTHq%wodZ!TQ6aI12)PgZskIn@vtlm!dd4$#L!Swb)WSpHm-z++dt z)`($?Ve9gLcHlg|@s5k%Iuwa*8U#@>1%Bfh>uHw+3<)%#=Fp@;8sWfD1nyy*G=Nc( z>7wyrKOvSvyx~}-6t7^{2Y|pJXU-2j1M6=@VlwRSulDK+3HkeIJ9YIlu1~~MXDaf?uBvo~!ze0_5dyb>gz~;}^;iis z1j^D~*uH9;>Ik+2a0_P+vr#}ugJCXRhVZML!uunjCv=~AqV9ZfzyJ|mcpYXSTxspf zdK;dNaL{BAkI^=+9-v{xDMf+c569f%E$LcyxtuRj4`OBg+h)ikH(l(ep!!)#0q4jJ z9iSf5Rzjw+PglAp#X5(5F8IT`g@ISI%}&9`_j6hK&`6O?I;A}XdE{n-M(p2&ga7z8 zUz3kn=AV%HcfyjKo&%8eoZtQ^7kRQOr(ln$PoPx*;mD1`klZMuSjeIuB#K?y-^)m7 zQ8V(h+r+T)p&LVvI9-Fw-wMx7DA*+AoDQQP3zCR-UrVYmVa2_mA$ntO{|y&{`$j#( z-|lWy2mMocR4TkjS_*3fKM(T+NJ?Rl=x)6Ati8|$^J?_vJi!9R;IHpdA!!m8ttxYY z9wSMMwXa6$GPj$3s%S{Fml-=iS;iq$v8Z2 zk>a}18MGf5f*)q3ak;nRHEf)TW>vq~s7>VJo)X>I0cRq7s@S$ZXQmU`tsdjLU6V zMuY%vHHovaxLNOIhxz=}`HKGDTfL=+U%y^h>^V_u&lLTb;3cO{ojg2E3mB4Y)YZ_F z+8<`7fd5Z?OiJKC!OZ`K_TDu}hy|!nV2GFis^2mT=;iq&gJf2&rxlLhDwDv`I_01{ z;&S3yvRb4H8i1X@ZLOlBT)3QoX%UEr=K7DMgIy>D&eV7^*tX_WYWq?LgUG-9tmo_@ zl*PUDifbF5qJvjzaPM3{!5QnTakSSzoJ3<0dXYVN>q##Et2>4E^4^c`FyFe&L#^ns zlx)s0>+gv0xtXb-5We>~9sq`3T=~l5d=!mx6vYEFH~o^IS5P;goxDwpD{nw)N2`D^>Jg+7F@ z8bK%m01(<5dgL)cW+j$C_L`ZzrT=&J=61M%AUX;FWI0w}>!7;Js9 zcrXE&)aXx>3DTpddl6wOW+V{LFk^Q$0UOvFu|7{JjpNvh^5;4cTztlT6KsXn210!u za`0`8V|-vJ61PTBpgU$V%U?n*#PLjfamUU1lHB88xvOAgKYCRNs!RIBJb|$f{;lV3 z!chOCaIC-|SN(re`~B<>`(3gjcp@6e$VR1R(w$qgkbt}Tm-0@}zf)pOsqT zm?UrbgiYLnlK65w|J<`jtn{MVC+uIg=<7VhDMyiPXopjZm-hGRe}{>-af zJ^ih*0B>Eu0+8>xlWfX6&*%a%vzgix^k52sVn}ACG4NT*EBYC{4)p=sg#LwVKO-bB zt`ApMv~@PD#rTRrh!G4H?n+4#U@8ef)AN(nXBG|#{fy*p0Q8~346r&kR`J<2z7f*_ zSyyZ8KoJ-5l-vRdBaWV~iE^_*b9mKn3Ai+t635%Qc%G3Jj|S@?)&*%`2#lass;bWt zl%l?o;OAil0AdZV#;;*i+KK$>Q1o7T+;zX$b!Ti%<&qY8;SmY^`TW`zf1DsOm-zDQ=a+&`S^kF+zo@&kK{BWnVcAx zbB(-a_Qh0Nm&;sw+X{YKXH)2ud%`Mpw$|lN9ymEvYP`+yc_^#N;zg@g$jYcRIqf%begf2w^B7u?aLtp4g@L~P#41~{EOyDUciXC>GAqq_V*=@yO9{5FXs z?>lW%Bi!?4ya$t5pL-is*E{%q#%)#SkhvY_7nS6^cNy&cA)3!8!#*`xyYH5P1Hu@~ zEy&Q-4drF$+9Nx-QD{~e#MzQg!*He!yDvDJ_AZpy`YrZV-z|#seDg!~b z`JVmU>p4DDs&ARyqA!mK{dA`Jc4-MXojkYw`ro$fKO!{~@?OgQ6RG_(n@ULz@xZBQ zePX_I(#!!TRId@Ms>#5Xq&nd09j^(aWARpO;e ztdpvq!l;R&Gi&|I9d7-ucZvohHq9Y5E4-aoYjoI52fK6^Jq6bK&o3~^OxY2iU0q+( zU_DmZTVt0+%C^oWuht=Dd1hiY!i{$a%ST=&FM290E|rM%u+_uq)WahrYU4Dn2Bbf8 zW>*X9uxR-(9Ws|UumC!7iY?hlyic*F`f)ca{jiIz;6}vJ`fv-hHO-+^D14g`A;#y3 zNE496N!ATb-$ASg-Ehdep2OG*XV`wRX>_TU#&-!P-6+Qg&`{Bxk+2GXjdL|JuX=So zcQ}}iM-Ej`;sv&+>W7(zGg}JOfWtRjCAYWfW!#Iyp2rbNv1eP*T`z&~&k*hr(<)))mn_#y ziXBvw+j?51+dE&K^O1{(gOc}*<|#@-n=67?zd+gX-lbc-w;3xBbCX3#Clytm<{$@( z%QmdIb?s+gwO}gQQfwP|D%g{*$?vdplnGtgMrG#SXKmah5L&xn`nvnu^@ZC0%&^Va z{*e?@_K=uMS3bS!xKkB7wtTqH)(?EnTd-iGanY9v=B4S>**|c9m)F0cnCHWVDAbyZ zvtdu=?%Kj!%T-gM3r7uKMmccVLb;c#g(sEXnhbOW1Ori zCT%6s-r%i|`bGk=6?gIr94J4mPwOL_vo{hyzIR`U0(+1BHds|WoGe-qH~wWhZ;qx#U|Ow%bM@n zVpnP}_V!)na(Mfq>S_ytZc0{qav(RDC@?%g)`;uWXi5Y}JbFB}d$C(?9R zrHdRle3~xZ+92y?A+Hn4Oz&&C?tzuUI6Ygk>3Y~#GGyKMNKbmguKXJ>j3Y1ip96OP zUZ4TLZ(DQqaD(9AEI-_8cqjqx7tbB(BZXRTo z=x;>*Go&*cF?B;Hn|{ghbM<_MUNZFkiLI|~jqQC57pV7V=@oa+n-Mju&cTk!BA?^oC1>g>(O>Fjilm0?$2HHXVz zc2 zS+SyFoQEw7cQakTev{fmkiS@iY4?97dS#J!R`9=<>%WE9zefqi8a&9(6InkAZ<4aZ|A&53bR8(ar5$~Q9mv37Tw6lK>`+csqt~9&K zr~eJ*yT~pZ=gWIQ7#Xg$5B8vI5u3{3lkFq87BTl{=s)%vH_53sWsjA9*p8jFN8tIA zO@I+pbu8ujSbpnm?yHGU<)WhKsGio?l*!9p-}}PJTK`m@oU_kT?6SQl5}~{)6@706 zPSA`ajbtY{c3fO0);`72L@?;u{JoRfRw>1$DK_W(BH-;~10=Cl!@ zb(e+fg-YRy`=hGyt&bnddP7EL9w&tce;K8_gEZn9JMowt1^rOL55fYBbGO_6J83-F-SyGK{suzi?fECQpB`2oDs=WS~gynP zrHAl!k4eBR`(@V^g^R5q*A_{jBxZi`S<9Ais8y_W;)=Lg{jd_(b~bg*v3C0U(YqYl zZh(@b)#^r<4Z>*y50Y8!J+=06{YBZ?5Aj^dEgMrkgm42?in^S2pNrqK%>XAQ&*G3C zwH5Q+ou%f?*oo51Y@*1b;9g}9{rIWMvuBWIWS_CP_vx*Qvdx!~pFIxm-*netOdCHQ2A za&<4~4P@H985bh!6mG4QLl$D;E3$KSit=80YNy(vqW&B6Iq&I3K6j5cA42fQNMx&> zpS(AbM|g8f?qrQhSdgW&-#5Zjsx@Z_n6X9j1!*L&QU}Sf81_l>Fkg_LqgWw!X5fkY zv7Z8~`O&_9&Y0uPGuwLB-m({t9SfMGvlQ?)5JCf@9wVwaI+4y&LdvK80pOHFC^SP# zE-hu`X8^vC6~a9!IQR}0@3!DZXZol?7*ICiJ*AGz~PFP1VR zv@BhDriZ-o?K$rLl$Fk;&pe4?Mf9b8`nZ*F?O<4vm^n?TLGN%T z94HyJ*}1u|duScMslcc2zBX*-2#0-5+s4FwdMoKPm9?C5^bbZFJRdi{jW zA@g|Xx}QA0d+~cH$V(EW$KVWG5P-eVXrw#3ZTML4JX@3^qlaIGBBPKNC1K{Q`aVd5 z2*mVr`(c1}1PpI{(EO<_$jJ}2Q7N1tlY|9BD@ljSFG-s@A}s)mt%?|3-a|_n3hZxQ z8Co*!tbbvOX5K%g9=OhUjG8x9{gdVlr2 z|6??Injo1eWLkuD?IH{bg`9a==D_GWlOe{EBP z;K)0SXlq6xS{!e11<62AGOUVbJIv((f{0>B1}ClvToCeMKGsHtNGuwI$tpefrbC6X zRADF@zbb5@$c{?2RQa$fVtHVtMkM{n51tY#{fEUgBjB7WjAm{Vz0-c3Qx{hMcRcj- z?zONm>p=|$-GGT#MaT_P!IZU z-e*#v(v=2sP4YFk54%tzXOAqZVx|e&xOfM5y?*suxB^Vl&CWgdYHsZ#^flX-)3zQh zs7HPQ2)i-f5Ym%M#H!&O_XT>CE3ytx!o+32OpFP*DXY+y3w;!MJnQpOkn-BX9H$RX zsrwp;!=>S^&*_^3Ai-eW3Xr0wc@z>>B6$0$* z4sM|UQlN?N()dAju6@dw!B8KYt)0dpHfzZ5ODaYHh5d5|`p2sE7e(T~x1hU2_}hZ+ zlTy_vS$lYgPcuZ&w(P4%+P9~|tCQ6RJCY~NCM2h-?JnBgj8I*hB1vN;c(l+s;Qm z^Lipryd7b=`D&IMJ0Na-JB%a~u9V8n8NOYk{h7&^q#bP{_UXoPRm~hv_h_L5?~=K* zOZppxg4UMCGqmoq&S@TA8d1|zJnhatRezkeMg%x~Pbw7iT|gwdB<>sDWjq%dy7i}S zcz^!(;b%Zw>JvE|Z#iS|hk(5Dkx=)|vyKUBAU^w$&4)QBGip{kr)={Ml=0t(iHvsi z4syuTAAask+wmCF{!SHP(flyw{Xi{f>&?1?A(aa=Mh+_WV8d@4L#W8gG3}SgEYKtGT|M zA4L|mRtL*j)g!k-u_CK8_SRbS9y8YJc`YC!cYR>9rLOFX_iMEVnj#nPtVFE{Ti&ij zx(E)k-A^@b^?snWNz3x)(Uv%y8s^bzpRNnMWJW>Bev%Vx+7|ZdM}7v;B}-)yA#mQD zcRv@?kId49nWP@?zDn1S=~kfUO}Xu9<@eY^*i^t*VUUh@Y1jUNlKwUkuKA@!c_=Zd zQ^-2|JErfdRGG!Y7?z}zN5YzowBE70S(BzGG3C0RSC4#dA-f(pHZkcVvNmxmML5ai zVN%$;lm%(FjEt;wzZzVwi@;b{a|+ADF69?DCH${hA6b#Gd&=nx!fmP7$YoKuugYkT zAENmWu@R1#lmisM+Y3((WVRIyq~CwbVpB(3Jqk=F&k)}umHUsI&z{IYVtQ%!C$z}$qC#i=FN?T?OCQEo7vCC>!wCxr{T{N5&iAjX7Dp5*a5-g;Xe7F znmZ5^$?z|;#LI%9q`ZXCp&0(w%vgw9BQuRT7W!B{M?aVph;Dk# zKwfCuuy>KB*vx*kPcJ{4OmEqFaIP;g4>}(`F12Tf=rKMZ{v!?`0A1eI;=AxXZF@I{J$tpIxD0-VAl)j2xKMaf$dsm@m1bm=C zaTQKZobpC6^mA;2RIkmw#2F7*2(@3S4$K^TFG3y|D^WWRf`YI~KV&4kk zdW20-<=LT|$oJ%1osSolXd2{h$8oQM7)6S=>5Qoy2QtG*L1m0F+|*%DYd29t0#b7j z>l+3w1tU7&SoY||7t9&t`}^zD#TuFiL57%ni?L{4teETDqy>;?kl&bKpz&pU&#J>E zv;#2{1lmkpDhq~_cQCeI7@|?*`z6;>*zD1;81os`n;zm4VGGD)<0+W08DA*M=&t5#dwY^n?{iUIwTI4$~D2^{(5 zulp~&IUncofAZ$KA_euy{GTULsc8%+=VuenRGRPe<|0T<^C=gn=^60)25sPD=9^0e{`;Z*8x} z#%PKcwg`us+sKlzIlYfR((&nnL^D}ttO(Yrhn@hPd(k8Xj!wz z7#5QgZF+!_@Kt=f>tw5qdEW6^vJ5h3UDelTs_PO{7tgC9cGf#lZ^Rht4sUu6oxkAp zA~Jk)DKGac>vv|&rq>O$Tjzh6eK|4l*eI*YiRIQ-;Ipz5FQcz$y$^grvHh9*^lR(# zGXuBe6eIhp_pg<5`T|)6BF3q;ksTw0SB`#4nK|ZxU>qw4{DWT~pMd@LeEw}&*8ArH1CL?ApN&lXiv%gXQ?obLaXY zWL8PL5G<0gU;tBdMZ(4+c3)I*!MoxevC}foAZgo;s)Z`28Bm(5t+XIvJ+v%%ddpCX z5=Tbafe>Q+LQIRM`rH8*`M3?^1z<9G8&H{&*Ff^uHLTDf!_~<+S@G#>E~@Ycf@d$m zNW!KW5a)n%$F7KuUg%|r*W1VrXddf<7Bv^o1Sqk<*R|H*s0YMWjL#q3 zWk_Om3oNB$2o+*aK*N^?JnT?QNC=0f4U&t=#G$l10<1R82}SOTBBM~CLy)*YrWsCp zRlUc(aOtJ6EXu>iZabW|Z_ZSvEO?B=liwf+WFh&mwPOJs$6JUJh#)zA&$ z@P{zIumco$cbq@BVreJRcsaPFqJoO_!Co~k?0BRib}R>c0>KaKF`^Jh@SY4vp+!1A zQOGa@R$<<(@R8nA)=={W1B#5D7?sD$OE32bvyEUVV!EfCD@h|nQDTI zNtY2~s3_#IK9;cjyfZu?zgx^;V*&=TQNIz{_K-_h3h2-=Si;?Szmvxe_LwjR{JJTO zyBiSJ zu4W}G44ah0)a3S?W2mGLHR+V&IMw9EE|;Uc$E$H8nz@rWA8_zxWN=Q z-7=>h;+62ZgbTQR1}*+V&L!6q@x>52DGzY?Q^l%zGsZ1%z~|hR@W>20kh_qeXt|eR zu;uO}tR;S|KaGME=*CrRtEldPxila4gX02ob&=jli;%qG;Ha9B_zY4QUF{4 zR6>7zR@Iw4DRdNvteL8W9f>z2>hofZWg&=_@kgE6d0$I}I*)cfbr}Jv^|@AfN=T2M z9?CQm#RNKhda^^>X?yfm+l}MbR2W^6xP1AZ#sqdw_vbU)3muR%PSM&Q&MuCk)siwG#J7>;@#$o^azx9Hbo z0RjsfPpvdDF-+S46tk*}a0P2|fhQpzP+pE$&`)&a=cf>73XOUj#DI`~o)>rgTpalp z+$KmM{A;P`V_m>t-h*6qVDAwbDLg|Xp&bX);?VvOA_=-P4;ajo|C{{qh~FOmf3VU@ zC5Zr*Mt@;7VCAacuo|6<9Q6OdYNSp8SdCrfkr*cd_|0nI)&3=KeNyS#rkm<&L!XAS zY9Z7@APKM*|G4v~8olC(Y;IFE1?L8gljww7UR@PzXLT96AI69sw-5?)1n*gEeFbaRs{g=Wx>GTq$a;i^@7AjbW@$?;el zoXf@F!F(2miP&1We&K6F?}{-QAd}qNZEX9B*K?h*lANXGk-&u!;GH^z$k1Vf_ZkEE z8bDLPSh{Xk+m{(t+lBV;UY)4psC9y_-6S|%H_gkHm0DfJvU|DVk0Q>O;p+oRidiD_ z0-tt@)LyNeMW1@map}{uxo02Fcy_sckAPYTZO!-FK^Gg4+&fG6K??o1XZM=jScHv? z#}STFFnTje*r#l_R=U&xoXuX~|+&SB4U&q3y z-SmDh^Z*>vNIWc}a1nOo24FJVKXHYV(#bYxU86&YlFCfKfvQSBR!W1h&Sc8Q*T1}P2@ zvWq|Oux!t#r$2PDxhBVPvX^$Y_zO$p;L-zf zaQASIMUzB%i-o>F#troyT!qKNiGA1OomYUCc(NRM&STq@8edyxT+SMouLpjF#+I5H z!aZ0i5a%oYbDCUbz&j&hBhif}J>$a1WTwK2EJWx@XOD?VgXCN>O_dgWMyT8qHeLyd z2?fvO!u$SF)Cn4`VWIZCqQr|*-;o43mu^i)6CE9MN8V=~%5wo*D8nwnt7~=LV~`*M0&U_r*V$?(*@g>ogLDpcK#SJHEy?7deRO=bH+~Re&~npbY~DO~ zqelKltBq$`I+)<%cI1ZeorTAbjr&T*buAfoJ5NC;LlC#sdcaWnpl-%MOb_vFEeimi zSh;85I3?95`q9pT7$VEZv1L01ieM9aFuk($9>1X|gBQ^*wI&zD1p|aJdJY&O6os!U zHi=0UCqoRRCfw|24Pm0Lcet3q)|PSYd{j@Jn-6}0untN&Ik_IwDvOR>9p#e+8?;(> zEpMe9BpVP{QeE`7{<-D1()1tq1^=DFSi>e3N)MK?>2{?7`&zrpc3cv|3AtiCAu5w+ z(Tp<<_Kfut4I-xTaKEY1#Bw7>)^>Z1Ry?j$E=g9;BoHHHz{t?8c?Iqc?%C#ML;+_z zk3KtIK$dg{MjAchXrc4B58C$qKAja@?FZKij;{~FuL9j+581Mqc!q8K&MZ0UW!>@E z@l#I@pBhA-BXYxJue`hN6{`P49 z=T%?PCYAz%Lg;Nko&)e0U^sUh1}ex!tw_6H%yXj*snFq@{A{(yTL9*H0_hLtdEmmM zedhUC683Uytux8k5RVL6PA4`)v!-u;+YSLEKn^m`mPb;Jri)~ zcxhm1XmZ7kM=$1%&hD+Q`X{u(eo@eoe4%m0;hjAo!x;k;uy8^=T6!?g>KIjzRj}mR z+P0)%>**L2UZI;201^kIg?F8u9Z!c=lTy~J_oNeIQwXq-4^Ve-`A5k>Uj zt`Ma@r$*R8+mG8`ev5z-(NcP7BBCZwiSi{ zD2eVjtNR`ZF+VJZ7R%%KT>xoNXljMTfQ11vO zr;jA4N;@^^n=uH$=^0FVd{57fp0roKVC9SWm+lDOSN*hqci=!#G;y%*=lEH=%ADynih z_C~e=%yArJ+XFIzmEsa+ENk+Q3ER-JAbl+2z3_vX&e^Vt!zSLT0XGwPDKaeC<;iMX z;i3+HT#^$9w+x2TvK@{AxRq&>4}e>R0Jzmr^5;WrZvVaV^YZtb^$U3X9}WFtJ=`jt zFabNNamPP29fG47vc~{w^^k2I7k#SI+%VhAM`Zo@!C?kw>&bHWyoe7P3LAKj6XU{` zmqTP7+I+@*4b9uuoylP%!<*}Qr;;0L-mz{4aM$4nB40l|#k|)zKNu_7Ch$=5vW&K} zg7Ob7D#L5<%3W3!DUItZ=qfE8?}|?7`e;RYmjb|UbDYdNcQ;I^1z|g1e@jxIyiTQGd>>xH&@uHXjanTDAG#HF1dd;VRMrNb>3 zL#luN)VkAJVhTcs*`NM8Nlew^Qyol;VgBef6WzDt0Zfrzu)nFP)qyAb_^+-P7!!f6 z_xBt!_nESNlnQW~=-Kz0=lnB=j85@t%YMzz0|;ly8uq>#=Y?&4<&cr3ox1?V_3_Da zqb&6Iq5T{(Sj^QZPq<*$MdxO(xg<6aGmol2&o^FlJc8tNqMx#0^XeI2P(U4Yjm+TQ ze(uun44=M8PiNHAvZ=67@ve6fNBd_GF$_I7A|G>%4tj0ANCs@AU)?{W4CIjAN!w8& znBsW6g~NHzO|NI-%D%yTYZRG9ZOaWVl9k;&k`G1)ts6~UevPs%uEL=4PqHMYHC<9Z zi)w#<7prKbZ}*T_mKj%u8Tv`c$UKCY&8YHFTRzYdIS%qFIuI{u@o6vAZjwV(-y=mb zf9YK!p2bfEDq5bBX)YIvl%N{vZ0rn4)H1&xVp;mU6ffJ_z5rpS-#md(JhOY42UHRT z8Bq|p8YHNvH05b7WuHIDk4!}>53^*s_)PQFr<`T zxYR+Dx>_53$d}n>>0v2TbSLIIv*%pHNhzlp9>FwCy;5?SZ2qzl^+FBEFC!~nOi(tT z-&3Dr^^l|TlCg)9f7$6x_Mx5(K$O|i(zSLd9_l9=!*O91zlPVDA~lE*MrpPBHXiTH z#r&DxF$vF4_l!s^`vabASs3O74k?Av=OJW}?R^ZRHAkEgUkCM6!VBIdy?e0N8OE|Q zm-*yODoNuD{(@U#wrHtlx`VV>m<2V*e< z8(uO0M(iQ=&GYDWTSGjJEI75mVuRt>5i`8T(=zP)Vx3z?hLmW57v{VI#0qV7mueB$ z9l^r}@Slf2#3Tsy%W!GE+{?x$dq(-mp0=SLim)k)o$7|)tr=i>jxG5yN4#`ldwTJ# zClgzfEN5X1xlmM%;y1C#>4Z0D&PK+4twFFlUNZ7b)}ka?)|woZqs`w&r1k3GK8HgE1T%3?NEMvs zCwX(6z3+E0oAeVxTo`>~*}Oz?h{+y~a0;sk`MAy+Tnk23T!{HOLFR5g7q{Zuk5xTA z3@;O1#uBiNNVEY%29FWWt$bYBf!)Y#kY0WZn@sWT4eq1VuyoaGq(l#}lnfdgAJL?k zr_P7K3Opv&4#8Zzg<(gKlm&mB+&Q61uB`SixSIFo0m;R2iv@|EK8viOlt6)Z zNEb}VTR*n-V=WebR34-XOvLe|9^WElWT%&-Wzc|y^#YT-?G%(d7HV{)Wpe3fV<>Mf z56KPzfwC4%MTS;Fc`aI=bj8;~%`h>&{TlcM)3u`bO`yy}b&7wyw#X;yU5#$fLvm$s zH$9Y*^6P#|R!O|3C=(7jrlWnx)*4i{8yMYxJqK;xpH}+l#TD3#{K>b;MuJwft!oA?kRIMrC@x zyucDRjUWHP5o1KyVkKbd(go@04sSQHpTX)2OrF(ppF6<9S7*p$Y{tjq6F^$b}b1fA>?|7a$Yt_new7N5ZT6>DPd8;-qJPB z1>pyn;|v)rZwRugJoNGu+j^GrV58F`{T#lJAQ&o1cX3mO7bUvoqBIJhzMLU#Hz~&Z zfx#dP7B4}6&DXQ-fEHmeRwhd)&M3tTN#7fMP)V`W+CZ2s`)6bd!e^VoLevBm1JZ+v zf9!Uk=%K=oRxI)=mfC86WR!^)Y7kaT(ypmwaOQ(fe2=-FAuhqWJB9##IVEZSt#C%M zU0+N>G)Z%m=y_vUf9P#cg0)X?Ki$p^={l7KkLH2^#OGmGR|~zH>Thu}Jl9+yj~4Id z6Xlh#3bD?|WNPWKP$`Oq^rgp0jn0O0j{H{E^?83jm>_&sv!+h48f@c%!+ z^Vqb(h=Z(ItL2^ILwrtKuAP3HT?4aDXN=no=YA~neey$e+p~yFOY$r()+aGBeq0YL z0fJo>4@ZY7H=i82EZEs>Qh3zwVIhd;bWb@D?0SQb;kSJtg6^M_)xW5C0TBpbqVZq) z0{$I$Mz-`kx~HG`Pu!WQzRc)4ciACnBe<#=Fxrw!Y2oLbt!&r;oy{7*x^Ft>)UK0l zxUB*KU+%=zMDX->x=rw2;RyMfOL$IVI~Ch`xovZ*>)Q{G3Pp8Z{yTJ^g`KpiJ9GEr z+1Vj1D)&l&5KWz1K5f&TvAq0lBpm z8SvkQA^#TY=Kbxg{s&X=A#+mN*3Rru^Xu_!nbWQPubUhGeEYi94@BTocLOi%tMtv05%23r-H=Us8pT3uIs}(w09c}39 zEdMf)+8J_3D_^!p&R28D)!+gR)8p{Im1ova(X}c?IYGcgBp{Dl1#(SiEew#Yxjm3O zc3p#zE@ZBYxNQ;H(k+xBPRC$*>%$rK?xt|NQB<@27$Ea`AKock)W`2^YeHjFT9H z$o5yjs>diSLo0G7_Hh96DF8_G1AugbV8md|-}QCcfZxb>$iFiP|F=obF|v01NzMa9 z|C;1{vL_eo_=Vr>pGnSd257h||4ee`V&Ynz{xiwhCF$s$;y;s|qb+9b{*mOYl_O`~ z^()C)!S${l%daHoCJN_~UrEktBGvuBlAQVc&mZ_B$r+Ju^+%Gkk8|IClJlvurZf9V z&Y#@Jy7rTt`8~H5fh6Zk7o+x*oMjKE14+)IU5{=6NzMXF+gtld&UXth0!)n6&|GUE z$vLwAeLd*vv7$fxrNn-rsI8GCMkl)Bue!pdWmB?IUvu8JX90loW0ww z5B$45&j~~jkQ*#0Riq(Q2-_(TW0tS8JH+dX_LtCiH)0~@v6I6^SdQpDj44)?8;0;t z^@}8Nn1MXSC?E>QFpvDIB()@4>Z&-6tf#+P+Hmiu6f!kZH^MWe{o(v#5?yJ{H5Tpd z`7s@8)Y?2k{x(d6C8e`H;qV*v-o7S!p>^SE`5HL3UU3UfhwG9`mMNd>lBiL>F?_W? zcd_Bza&1+141$ZZus2OIdftvzYs#Ta0-=M^W6@g1(%o+uC?4)bP_fN7)Z4ooS8FK^ zuhO)3=Q$q?EEAe4u+iD)6t!Mp{it{L)<$mJoreU%)vWBwhd69CVF|-ZZx>lR*FUD5 z(A+c8mX_O|SGpH_YeFPVZN0_ZTBF##CRS`idy}Q-4f0wnR>fd)L~|?qQN7j-#;Xq- zu)7F2ws)UPRUY3Z!*D^nkdxXMa$Rj6njijoRCS6Q60D%TMG;|LxFaL;aGz8 zqvPI1od!If26p3`v{;f6%8hMGA4O1avUAPc)+jq!}SlEt#o3;{UuLD%M|3u>yd zJQ~7hZP&RFA2Dg6>cVbUHPQUj!K_DCQ!#sw1b=>G#1g|H2iLKE$r1BOQ*k{wGO|EC z0z}8Vf#dr`c=`1_p0+o34IFHlZQVwfk1_oe(lLg22*n4hjQ3|f2F@32o&OdNzckI$<{sOvK*oV67=n83go z*DLT0NuL&hWY(%mPM|zbXA`G45Q=9_^2~P>8n$IUG6Q1p4sw?mZF2`q`CphEx<{T=Qr3i61W8mfmNS}~+C9w7zkN?XGN>fOO@B3+X&n2j)n!$`Rf ztg;pi`aXQk|BXl|vVOS#SWqTIR-I_dJt9k3tT2OnIk*?_pY^KO=bNJ4Q=JNDA%vCX za7CTalT+<*>6|QdAE`RzNBwCURpfo;jgPnrd=TR0DpwG5wo0$0gYAd`g>f9VIH|{- zeynAZA!}p)(ET75#djXIio>pr{4MbFu_7FiM13=7UTByrGI!nNK1NrademXJ?7+Yw zI2p7$z+9o7>5hup24*wgw-<;`|^YY!gn4P(csOdxY#4(n=rr_CuD!eMyUSY ze8Q0H@O4HUnq_(wcJrs#c1F$>E??DIIvjqK^ZBzFLEm|d3Pt4LwV*l{OfYMol5*q3 zT$;Gr3bXw@*>9IB^YFC4`KdxQ@2Bp2QhGb8VWL#%$Q@o)U(Y0)c{1<#cHjMDeAcnp zGL9S=RMI*4xIs&H8G5t+kbnvlcfFi@Q`Jzu-xJQ?B7sueG?efKD@XCGb@Fss@kI2n z-cIyJFRu^yWF;Z)c)rg~UMC3_EmVYLd!tLTr)ncWOeZ&_tGq4`d|Mr1H6FcyQQu}b zq?*9S%{^KQ5o%2Q(4k%*58+#H$VzTx*Y!#B-ij!Twu5hrxs@e+v2*5$ofoc{p0#99l2xD8#9C{r@CrOem0F+ySA>@ zo3?SM*eb7D&yI(-4&2%Fal8ENuOw%;QklUs5-UKG^MhN+htX|as(=U8`;TvI3S1D~L#khPCb5e?JxG^|7{~M6x z{PO;N{qc9cY5dCyM>KRrU%V@PyS=QK&@p^!{M_h*_G_i%;SUl{A7m?9fGG#zidi3m zrT7<(5j zGlTg!mCTIG$1&m?LYf}w9H@gCAk!wmT=^j3zy}?(3*%vHVIcOp?Kt_&$xtgBPbSV! zpaW{b$wP4 z13=s1L)W|ZrugovOWEKUHGx=q`UUp7JuUGv>bK>?m*`2Dfu$^=6lIx!@5LuSng zQ#?uH*&|=~g!I}lk6|y=ZNqR1kg=1Hcw;6NlMB5KOg^+|5oD+|K4S4TS)o0)Y7#uQ z9j?c7-Zn1^f;(Sw0WM?`CZ<5gVG?SBCoSEN_2S_bKt|w@^kYxJ_U$3*Pa@aaq5@1J zzE{V3;}h~cA)dyOFi%MSVLH3KC@=8@Q=a(qcuI~US$igqjzk$Uj^niLAuAXs#H%L` zf)gdg6SMOZdpr}Xs}qay{Jqt|WfDoBu#}Rpq)x@N@WV;v_@tV>q}x3Z3XN$$$r+z~ z7LweF=acPq?phVT#glTD{r9o*-!}RG@BiQn>ineN zj>3~+RC96f$Voea?hU;S3>^cW8-o<;m7JX)5$tIKdJz*nCk zkt!e2wbU^G__{R-e$`}05kp)6a(=f@tv~T4Nx2BC!>}YW6k-3hGqgz03@y$qb$om@ z?EQlgQ6QWaU@}3$?w;8{{NJAiF>vacc)ov)$QiJ54W(ySs$D>y&Hl_$-p z4jAomt~8fEtDwd7kU%hqcEljH5>8%ucFC?rPhyN)R&UB@2Mmb}dJU0Xn#lAWFVXN* zUjR9qGCd(fd1ZpQ=pbPUL}=h#-ix3K7mtDy?o6@8c6N?oPfr|ZZ4k1xm|H?wNL003 zCrX8*GLs}1mHVuXZ#Kk>sjt+Or^%e{halAGC6}oBf0I9k{L67PcqH5GJG{29>^r=^ zH7zp9D68c{3T*+r^p|&158|R#mKDlz)USIahYs5lHJM^e?=G7*`;O%)BAz`rne10`a{P)bq|K*hXHu)WiZeBGg*xsay-hiNIem=Y(^f; zJnq$^Ei@vicIij=SSHrsaStmXJMLY9Gl3)c#QnZ^Zj1mn?LLVh;1J=E){XPB1ux%n zNp_h488Yp0;9$Hm+jlQ1n5?PrMi@&2Mn7};&h?~YBoD?UQNI?HP^=#O%Zje7N6q%Y zwPNAPFXoPbNC`0UO_%#nC{AOcl`k4*!eVsuvUNTxjQomPR}GBy{`6Fe_y z=1(tFbDLi>of5aXte?20!n<2#UTJ>GPJLmXNJ*7dk@Fzp3{;|Q${`IC#_74&Aga-y zy{qX*sy$O(2Q%mgm=F-IRVrDSG46&ZKm#BOC$ch!U-L-}TD&pZZQ(3B_-m%FkX;)?brQiV*p=y_fAD?dNO0OIU05jA<_ekpZV0X^2_gfEph7cIY*i_TqGGGPanS^?r3{mUi4=VZ zl*uO<`VSi{tSyqP_y!x00sVu_ zKD(8?bJZ>bmaK$snvh|*hB9lOH6besMf44oEccy#>2_k=i-bfzI(Q&12KC-Kzb;G4wK3tAmV6W!% z)g-8SkfOkh$3j{LE;zZS>M*zw7C+y|oWHEIk|)BH){22@u9n8LqL_WaTzcP1p-_H~ z3k6UfST~VDwv||(CN5#)eMyVGotH!x-cksYzAZizX5>orF_xBjDtgKKm{^8^;^U>t zyx7K;O5D-t&nNQvQ#EW*g)MJZE}zlNt||l~y!)<%t%QrSsO&FQ{|X{51`hRqW1mUI zxehi6_38a#eL^+th~!cBAB9=Q8YFR^%I<4>`=TpL%r-Yg?Ujg0jIi}Pt&THQqXm=n ztWkad>R-4|&Fqw~HZPWqVm&H_5;P}CQ#l-lL;P&7*j49V>ztCDMoSw=Omx%g=vV+r zKW$jG#u)^L#Y0OC9~2$&yIN#3(z-cIl4jMm=`&*C5Otf`o3NqDKWPhgRyXMrGav$1 z45BYc-NBFV3M+_u0F*)UBPc_^h0@QT#x?4F2Q#43DWQtb1 zG}EB*BLSE4qU+^BB29?yGBdj_!I(n`l1L|f@n^%dA5K4zqean!hy}+$;n#0L+Ayxc zASmkHt7;FS9}{Im_VqRS|D4}y5 z4SvG!N64Uyn)sZP#h~Tl0fqn{)owaw^&o6C|GW3OFlgMN-bic`rq@+c*B6z3mT9OY zS?-n&-OfZ#M101Fwt2~<9hJzLOoVBTLA37#U=ku5#zsCfp3Jqr)8WKxPmyv`ZRU@6= zbaTN0gWhKSEo^@1q%Ml$9FwjtdGE&^-kGiL+mW2N=%44m90oSW(rA;8MtyXC2Ik{P zLF4Nl$ons_TOEe4yYuX&k+R{6bDbKDdV}t_b~l?F-Ife{#kQUg=h`Al#(ecjuX1AC zXqcr3j9?fXxXyZ271;RrAMMzHjgS`Xtw^@tx1lhlKIUwR?*+8&se$dF}7 zB5nFSnu5{0=mIsKfzzYIXnqY~o{Te!j)VnWxYeqwgve2Xkgl>x%>r-XAEIPmbs>bz z%=}`qLhs-GoNoWibN_$tFQAHEQSD~{ipD2RGU~v?Y<)7XP}1}J);=R?vif)MjgY=X zIM+c35V!}k&xK;hF}2s{(((WC?M-=i^+ffN6L3_9cV9fxw2M9n?pQO$C2T+sqhi*l z$wy1f1PCNOh?;9hRceGC4T?DPs_Y6P3PFiO@_KM*ZStZ z@=3Pf`QfjhB%idU3m(z6R{=E~#}%Ro^CZSzAz)Ma)I*<{Rd0_LgKFy5!~P~O{I`9= zDDbrZFZ2oU7R(}ujsb$^M+)hA*wrXkwl+{-Axbqa!Bl%D9arreJi!G1pt#g@d6NoJ z$zI~RbB&Semq89aED=Ok@$KpSCs};CPZl6kAq9ZMO_hooww)!Hq%zqZ(Cio}Tu@V8 ziy_4hyvocY19PC!LE!=@P3}|QJDaeuo3=|t+GTM|xMN@db*_x~hPl?IUhYel-u4&- zp)wRD+-A{kWatr(&7e;tLMWB}Pl>qLkoI6_iV1P?0b16z-be9eSZ8ppG>R63B#f0Z zFo}CKOh(QpI)We&kGRKR9-D^w3+aiK8Bl(^?oy*;p0NCQ=^z3r0Zm-Zy<{s;X{WN+ zA}V64qPm=8qTK3Go*GS9e3g#$j4q%PNvYJ0M3Q}E>_(U0pjN3I_0GCA`Mwrn-a!y8_EFg~D}mg@2;0vw+4>BT}@G@^{ea0boHlQG*F zC*3xG@ZmM93qMA0$h!cBgu#`hP6gd(&S$1OR!(!hohp%h$$!*U?ev2oeav^^RbY&Gvh=ng(R@zD1wJh5a54U7ASY znKaUf`nt8rCz9c zMoH*q)91u?wyi;#$=a9I1YFP2QV(rBHw-6*)=tx`Z^2_H%b(n_!44N2nulQAj6V$z z2;_%uJ!nDZbl>8RALv70{0@<;>7UiH)L|7bzLk$bp8P~ML=Qd^U+&Beo;%mNB(+!^ z{n_y#%;&MrDf7YB?R-~N!t`m=b?wd18~&ucw;!HGb$)z{#_~rB>puN-s*lXXw-V^I z^Zr2C2P^sF2-VcqGoS2i4@X}%LS5MEouSTtZ{>J$4L|t5+B?svrqXr|r;&sd657x) z5X3mN(7O{zkbsnch~PLF1QA5WP-JL>q(K4$0R=@wOaPIl6Hp=c(2IgWoiPY1MT(*h zC<^E(C!V#=`o3ASJnQ>${+zYu_xoqB=icw$``P#N-k0Iih=c;d!l33Bzo%C}oyuP9 z+Zkx388+eG9^S)>+sBL+GU1QT1tZOI)dPwVWS(e)T4QuM`oD9L{!-&GQ#1PVduma zuU&?=)%fYSrCpfKLSE*6i2=M$^J{C4`lfh~Ot@R;!h^=pvAsQGVFv`nNO^vQU&RDg z_jHc?v3D%4e1xsH{Z)#&UcwkNaTC{UXTtnMbMm#<#%6jKDUBsNXJPHzv`0@5GGhG1 z$ePiDOTRi+sjPSjydq7^ejK>@Y0DJ(Y6>4EkD{);S{jNS2NApNE51yCgF7`f8PFDb z7YpSe7yf1B+f7l+M_;7_E6l%oF}J5c+qO;E-%`^jz>)J)>RY0FO9{_Qha4fU9zd4A9r82iZxj_>6cehuH8R~&~>TiAMm z-sQXeZCmqbe%Rf{ACNibW9^ZTJ5R^&uzY+i;MLS)*AT0xw_HDg472PlCcEsE@Dby` z5vThIn&c|CAe))nCI(SYP$M>Tca?5rEBU*KX7AO*KJ4r8#Kp6k_h^+`Sf?&<+Txln z71CDv$J-4)820O}yqdf$fWc_`?~`~#7|{FhHI;c?yDM)+f>w==gwsOm*%l8&e%%cRpN&Fp6OkggS6f$yDZ-ae4X zASsx@&JNwXil=yBj9Ud=viIo1IN6_X_G+@oHG95S=(>Wt*L5hHjt_8NkzCAUp>E{x zT&E`B-(w`>_~8?##tKq#wv?$L6{kShri&DH?uO@_X;Xk*NK$}HMd>O?f4EEX59>>#xnpxY(D#xAZ+z#Gv3ClG|Iin*7W+v zd~9nKHT;E#o%tknNR$Th#)LR%Kowr#UNE+}Sq*`9NCROUqGZwvHM@)T1BTn?dpmWT zNFaxMRkR?J_(<_vv4<6E*G1DJQfrYTL%NQX_6*uAm?LGuctyxZ(xqva4q8i|@H`L&6o=UMK+KI@u6ax8?7~-(cG7|i1 zm@;`-qk?uneeOZ5XHSy5tO#w=c?}%sTqYcNq?dGLq1IF`=^A!<;M6tj9JPv0>t*AO zi)=F?&kONZ7p##c+i0r!OB0?+0W)s$oWoa^q*)@hCTy6M9E?um;rG`Rc;5ZZD#6nH{+rNZ8%2`ny~TwMy&2 z?#1b85e^s(FLF$KC5S`IccBU{*Nx?M7~_=+t+BPR3aPi73lyTa2tbGE zQ^#kwRuU+KIa2&HV^6*i-GZJ0Od5ol7F=X zW=A~Gc)b2)cEzfP6;t|Mc?TUeDe-oDmb&p(!rfzFk%#t+e$v#0_2HT>r7aM9tTt|M z5HGgEGlG~4$}rZ?Psw$Cf)ykQjS58cJjdNvPrEaQHG!INLVV*0C)Hjo5{otjJGAiE zJ7b{5449={S7R{U^N1bKKcHpQW@P>_1+!^U>-{B*kc#2hA5-sqVF7DG@!O%V~1G&l&hrXkB(?y15%D z)ef~DtG!;f??k3fVhyd6{w(kaY5wfhswRGLB%I>0fJ-jY%ulPFt_wx=Ji$FE)-sPg z`%vo5)j2s8!I=zSAmri0z-%p&r~dQB?yO&fsLbYIYZ%(26@+*;R%rPV zv~f55^Y>S5VC;>t{`!aJHy%r9=hVFmri#+$x#0>!lt)&+njYddH3|EhF%TtCE zH3UF45y~*-me257b0d7%ND)wv%mVB7<_xY4l{i@ujO_HSbBQTB4uD1SoLkEQjo=r$ z`Wld?Q6whtj<65fK!%U1MjFz=8&+tiNC#=2;%XqeiZ^FoX(pq|v_>7o(ZmpUsH5$E z%@}TXqgP4Mz8$|f3%j9~^C=pF(BYu%saqSu0US~<&99}?OC$JpWk$7&fn2ziaN+W4 zBFE8C`CRW<7%eYZ+GVhJmm16N0M?GOS)V8bVl)Z_i~gCYkL!DnHAY{Wqul=qiVQj# ze7j`PQ@eDkBzVoGbkK1ZlMs;Xn;+E^!dH8IPV1%AtE3f98t$P@h6qD{oNmF7!VcRfaH24L}SmsdAQB@hn7N&>4l#&M|+I`M%-VR zO@7fK;y6-0Y_Pqkqf*-)_tH04ut zm^XE4o(?za|QLe8%FB`65$bdv+;k6UU0UD~4 zjwlsFi4XZ_R^iRXsJJvlB@NC=;b)fftt5O;Yw+nbNa`Y7G>^PVLU3uEZ)HH+3BIr& zA?-(0G9VLqeAjxur2J<9jgOxI;<1osC*DxM?v8n&L2;pwkl)FGYE|b&#j@I+3Q0kH z18Koc_%Am}%7>iTrea7_{Vz8e2=B$*3<>5rq3~t7dI%j>wumT#^J|HGJOJ=5Rw+IQ zUdcf86r(y;;nBssbSf4HFV2z_fy33q`}5yW5!&exepumttl&0*uj34cmt$pe7D(r) zwZdGL5P7-UUrgACt6Wp5@`Zi`(g*7P9C%!gwH;vD#~m}D1hy|C^0Xmdu?oZhuoTN@ zGNAm}GPRKspz_fG9d$#6AkTpe9(R9niD6D@#9G|ar+Jsp&aY5s=6(lEF#w zpsz7bF7!oj4vDg(MH%g{s7=>e>dQXgp!Oe`KVNO7{#j7`Wd;EM9S-x=lEeVs15^Qq z005lwKZDT<$5w?>FIJ$8v3Sc;Y6*_a*TTneBukOrc^mX{L!4|Peb}3jb(g{tXDIoW zO1*CKR?|Djt@nfeqo}8^UdlfUf0ONB>NEqwzr@AhkYCh+kjVP(X&Ag`CH2>b2<@5I zapV1o_)D4&gOg+4t+hT`E7e2rxYmt6*&nNc6AE_p&?%>csqH?t=K{-ZGCy};Nsbv3 zITj$i1_Pf5)die2k7t*j(6#O{vaQx01#j$YoLTOWIP0abCPgg@McD2piQ?bxZ zs@-;MGf Date: Fri, 19 Apr 2024 20:01:11 -0400 Subject: [PATCH 002/212] Compare GIFs in CI with ImageMagick --- .github/tapes/boa_cli.tape | 37 ++------------------------------- .github/tapes/boa_cli_src.tape | 37 +++++++++++++++++++++++++++++++++ .github/tapes/ci_cli_intro.tape | 4 ++++ .github/workflows/artifacts.yml | 19 +++++++---------- 4 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 .github/tapes/boa_cli_src.tape create mode 100644 .github/tapes/ci_cli_intro.tape diff --git a/.github/tapes/boa_cli.tape b/.github/tapes/boa_cli.tape index 77b389257b2..1b8bd959ca9 100644 --- a/.github/tapes/boa_cli.tape +++ b/.github/tapes/boa_cli.tape @@ -1,37 +1,4 @@ Output cli/assets/boa_cli.gif -# Setup env -Hide - -Set TypingSpeed 50ms -Set Theme "GruvboxDark" -Set Shell fish - -# boa_cli needs to be installed for the tape to run. -Require boa - -Show - -Type "boa" - -Sleep 100ms - -Enter - -Type "2 + '2'" - -Enter - -Sleep 100ms - -Type "const sayHello = () => { return 'Hello World!' };" - -Enter - -Sleep 100ms - -Type "sayHello();" - -Enter - -Sleep 2s +# The below path assumes that we are calling from Boa's root. +Source ./.github/tapes/boa_cli_src.tape diff --git a/.github/tapes/boa_cli_src.tape b/.github/tapes/boa_cli_src.tape new file mode 100644 index 00000000000..c2c13be4687 --- /dev/null +++ b/.github/tapes/boa_cli_src.tape @@ -0,0 +1,37 @@ +# The source tape file for creating the CLI intro GIF + +# Setup env +Hide + +Set TypingSpeed 50ms +Set Theme "GruvboxDark" +Set Shell fish + +# boa_cli needs to be installed for the tape to run. +Require boa + +Show + +Type "boa" + +Sleep 100ms + +Enter + +Type "2 + '2'" + +Enter + +Sleep 100ms + +Type "const sayHello = () => { return 'Hello World!' };" + +Enter + +Sleep 100ms + +Type "sayHello();" + +Enter + +Sleep 2s \ No newline at end of file diff --git a/.github/tapes/ci_cli_intro.tape b/.github/tapes/ci_cli_intro.tape new file mode 100644 index 00000000000..c0a063b268b --- /dev/null +++ b/.github/tapes/ci_cli_intro.tape @@ -0,0 +1,4 @@ +Output tmp/boa_cli.gif + +# The below path assumes that we are calling from Boa's root. +Source ./.github/tapes/boa_cli_src.tape diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index 57d25e2cd2a..462551a88a1 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -1,7 +1,7 @@ name: Build Artifacts on: - push: + pull_request: branches: - main @@ -18,14 +18,11 @@ jobs: # https://github.com/charmbracelet/vhs-action/blob/main/examples/auto-commit.yml - uses: charmbracelet/vhs-action@v1 with: - path: './.github/tapes/boa_cli.tape' - - uses: stefanzweifel/git-auto-commit-action@v4 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + version: v0.7.1 + path: './.github/tapes/ci_cli_intro.tape' + - uses: mfinelli/setup-imagemagick@v5 with: - commit_message: Update generated VHS GIF - branch: main - commit_user_name: vhs-action 📼 - commit_user_email: actions@github.com - commit_author: vhs-action 📼 - file_pattern: '*.gif' + cache: true + - run: magick compare -verbose ./tmp/boa_cli.gif ./cli/assets/boa_cli.gif + # Clean up tmp directory created by ci_cli_intro.tape + - run: rm -rf ./tmp From f5d050ef39394c1bcaa5daa6e7ac5f9f64d092c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 07:32:24 +0200 Subject: [PATCH 003/212] Bump rustls from 0.22.3 to 0.22.4 (#3821) Bumps [rustls](https://github.com/rustls/rustls) from 0.22.3 to 0.22.4. - [Release notes](https://github.com/rustls/rustls/releases) - [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md) - [Commits](https://github.com/rustls/rustls/compare/v/0.22.3...v/0.22.4) --- updated-dependencies: - dependency-name: rustls dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29d0f173718..d3177e1f06f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3193,9 +3193,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", From 0f38f214ca1f16f67427107d1b15d6e90f5cb907 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:52:09 +0200 Subject: [PATCH 004/212] Bump serde from 1.0.197 to 1.0.198 (#3823) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.197 to 1.0.198. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.197...v1.0.198) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3177e1f06f..b1c5fbab9ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3345,9 +3345,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] @@ -3373,9 +3373,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index a5c00b2bcf6..9276a6f2683 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ regex = "1.10.4" regress = { version="0.9.1", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } serde_json = "1.0.115" -serde = "1.0.197" +serde = "1.0.198" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" From 7f079ae689515e14c73878f7a299071f5277923a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:52:27 +0200 Subject: [PATCH 005/212] Bump thiserror from 1.0.58 to 1.0.59 (#3824) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.59. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.59) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- core/engine/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1c5fbab9ff..9f502818d66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3799,18 +3799,18 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 79ffc64c0f5..385562358f3 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -89,7 +89,7 @@ once_cell = { workspace = true, features = ["std"] } tap = "1.0.1" sptr = "0.3.2" static_assertions.workspace = true -thiserror = "1.0.58" +thiserror = "1.0.59" dashmap = "5.5.3" num_enum = "0.7.2" pollster.workspace = true From 0cc98fb1a15e4a84a037d9a31997e8c34f9b470b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:53:01 +0000 Subject: [PATCH 006/212] Bump proc-macro2 from 1.0.80 to 1.0.81 (#3826) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.80 to 1.0.81. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.80...1.0.81) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f502818d66..fbb1bb43269 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2920,9 +2920,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] From c9d914ca55f036f8a5d4e937728c0f61a17aaf68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:53:32 +0000 Subject: [PATCH 007/212] Bump syn from 2.0.59 to 2.0.60 (#3827) Bumps [syn](https://github.com/dtolnay/syn) from 2.0.59 to 2.0.60. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.59...2.0.60) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 64 +++++++++++++++++++++--------------------- core/macros/Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fbb1bb43269..7b0d05daa2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,7 +521,7 @@ version = "0.18.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "synstructure", ] @@ -661,7 +661,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -806,7 +806,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1096,7 +1096,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1107,7 +1107,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1142,7 +1142,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "synstructure", ] @@ -1172,7 +1172,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1205,7 +1205,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1308,7 +1308,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1538,7 +1538,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2049,7 +2049,7 @@ checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2571,7 +2571,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2724,7 +2724,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2753,7 +2753,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3259,7 +3259,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3379,7 +3379,7 @@ checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3401,7 +3401,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3649,7 +3649,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3671,9 +3671,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -3688,7 +3688,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3765,7 +3765,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3776,7 +3776,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "test-case-core", ] @@ -3814,7 +3814,7 @@ checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4016,7 +4016,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4249,7 +4249,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -4283,7 +4283,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4316,7 +4316,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4901,7 +4901,7 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "synstructure", ] @@ -4922,7 +4922,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -4942,7 +4942,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", "synstructure", ] @@ -4988,7 +4988,7 @@ checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] diff --git a/core/macros/Cargo.toml b/core/macros/Cargo.toml index 62399365164..f877acd4723 100644 --- a/core/macros/Cargo.toml +++ b/core/macros/Cargo.toml @@ -13,7 +13,7 @@ proc-macro = true [dependencies] quote = "1.0.36" -syn = { version = "2.0.59", features = ["full"] } +syn = { version = "2.0.60", features = ["full"] } proc-macro2 = "1.0" synstructure = "0.13" From 9ec0a13b4de006657d4de71a615c2d5995cdd59d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:35:30 +0200 Subject: [PATCH 008/212] Bump serde_json from 1.0.115 to 1.0.116 (#3825) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.115 to 1.0.116. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.115...v1.0.116) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b0d05daa2c..1084a7925b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3384,9 +3384,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index 9276a6f2683..4890c3f72b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ pollster = "0.3.0" regex = "1.10.4" regress = { version="0.9.1", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } -serde_json = "1.0.115" +serde_json = "1.0.116" serde = "1.0.198" static_assertions = "1.1.0" textwrap = "0.16.0" From 61567687cf4bfeca6bd548c3e72b6965e74b2461 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Fri, 26 Apr 2024 11:26:11 -0700 Subject: [PATCH 009/212] Implement TryFromJs for Either (#3822) * Implement TryFromJs for Either * Add explicit feature in Cargo.toml --- Cargo.lock | 1 + core/engine/Cargo.toml | 2 + core/engine/src/value/conversions/either.rs | 44 +++++++++++++++++++++ core/engine/src/value/conversions/mod.rs | 1 + 4 files changed, 48 insertions(+) create mode 100644 core/engine/src/value/conversions/either.rs diff --git a/Cargo.lock b/Cargo.lock index 1084a7925b2..562face6db4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,6 +399,7 @@ dependencies = [ "cfg-if", "criterion", "dashmap", + "either", "fast-float", "fixed_decimal", "float-cmp", diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 385562358f3..40883df1bd9 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [features] profiler = ["boa_profiler/profiler"] deser = ["boa_interner/serde", "boa_ast/serde"] +either = ["dep:either"] # Enables the `Intl` builtin object and bundles a default ICU4X data provider. # Prefer this over `intl` if you just want to enable `Intl` without dealing with the @@ -104,6 +105,7 @@ intrusive-collections = "0.9.6" cfg-if = "1.0.0" time.workspace = true hashbrown.workspace = true +either = { version = "1", optional = true } # intl deps boa_icu_provider = { workspace = true, features = ["std"], optional = true } diff --git a/core/engine/src/value/conversions/either.rs b/core/engine/src/value/conversions/either.rs new file mode 100644 index 00000000000..fa8eebe99fb --- /dev/null +++ b/core/engine/src/value/conversions/either.rs @@ -0,0 +1,44 @@ +//! Implementation of [`TryFromJs`] for [`Either`]. +//! +//! This will try to deserialize for the [`Either::Left`] type +//! first, and if it fails will try the [`Either::Right`] type. +//! +//! Upon failure of both, the second failure will be returned. +#![cfg(feature = "either")] + +use crate::value::TryFromJs; +use boa_engine::{Context, JsResult, JsValue}; +use either::Either; + +impl TryFromJs for Either +where + L: TryFromJs, + R: TryFromJs, +{ + #[inline] + fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { + L::try_from_js(value, context) + .map(Self::Left) + .or_else(|_| R::try_from_js(value, context).map(Self::Right)) + } +} + +#[test] +fn either() { + let v = JsValue::Integer(123); + let mut context = Context::default(); + + assert_eq!( + Either::::try_from_js(&v, &mut context), + Ok(Either::Left(123)) + ); + assert_eq!( + Either::::try_from_js(&v, &mut context), + Ok(Either::Left(123)) + ); + assert_eq!( + Either::::try_from_js(&v, &mut context), + Ok(Either::Right(123)) + ); + assert!(Either::::try_from_js(&v, &mut context).is_err()); +} diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index 6d68ea075e6..a23f048d080 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -4,6 +4,7 @@ use crate::js_string; use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; +mod either; mod serde_json; pub(super) mod try_from_js; From 6ee208fc5db104ae07bc3b013f5d04bb3e19ceb2 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Sun, 28 Apr 2024 13:37:15 +0200 Subject: [PATCH 010/212] Decouple `Context` from `ByteCompiler` (#3829) * Decouple GlobalDeclarationInstantiation from Context * Decouple EvalDeclarationInstantiation from Context * Remove `Context` from `ByteCompiler` * Fix typo * Apply review --- core/engine/src/builtins/eval/mod.rs | 40 +- core/engine/src/builtins/function/mod.rs | 2 +- core/engine/src/builtins/json/mod.rs | 2 +- core/engine/src/bytecompiler/class.rs | 10 +- core/engine/src/bytecompiler/declarations.rs | 707 +++++++++++------- core/engine/src/bytecompiler/function.rs | 7 +- core/engine/src/bytecompiler/mod.rs | 36 +- core/engine/src/module/source.rs | 2 +- core/engine/src/module/synthetic.rs | 2 +- core/engine/src/script.rs | 21 +- core/engine/src/vm/code_block.rs | 21 +- core/engine/src/vm/flowgraph/mod.rs | 16 +- .../src/vm/opcode/control_flow/throw.rs | 38 + core/engine/src/vm/opcode/define/mod.rs | 53 -- core/engine/src/vm/opcode/global.rs | 220 ++++++ core/engine/src/vm/opcode/mod.rs | 62 +- 16 files changed, 864 insertions(+), 375 deletions(-) create mode 100644 core/engine/src/vm/opcode/global.rs diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 3007340c234..d76435e5a57 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -9,11 +9,13 @@ //! [spec]: https://tc39.es/ecma262/#sec-eval-x //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval +use std::rc::Rc; + use crate::{ builtins::{function::OrdinaryFunction, BuiltInObject}, - bytecompiler::ByteCompiler, + bytecompiler::{eval_declaration_instantiation_context, ByteCompiler}, context::intrinsics::Intrinsics, - environments::Environment, + environments::{CompileTimeEnvironment, Environment}, error::JsNativeError, js_string, object::JsObject, @@ -229,24 +231,48 @@ impl Eval { let var_environment = context.vm.environments.outer_function_environment(); let mut var_env = var_environment.compile_env(); + let lex_env = context.vm.environments.current_compile_environment(); + let lex_env = Rc::new(CompileTimeEnvironment::new(lex_env, strict)); + + let mut annex_b_function_names = Vec::new(); + + eval_declaration_instantiation_context( + &mut annex_b_function_names, + &body, + strict, + if strict { &lex_env } else { &var_env }, + &lex_env, + context, + )?; + let mut compiler = ByteCompiler::new( js_string!("
"), body.strict(), false, var_env.clone(), - context.vm.environments.current_compile_environment(), - context, + lex_env.clone(), + context.interner_mut(), ); - let env_index = compiler.push_compile_environment(strict); + compiler.current_open_environments_count += 1; + + let env_index = compiler.constants.len() as u32; + compiler + .constants + .push(crate::vm::Constant::CompileTimeEnvironment(lex_env.clone())); + compiler.emit_with_varying_operand(Opcode::PushDeclarativeEnvironment, env_index); - let lex_env = compiler.lexical_environment.clone(); if strict { var_env = lex_env.clone(); compiler.variable_environment = lex_env.clone(); } - compiler.eval_declaration_instantiation(&body, strict, &var_env, &lex_env)?; + #[cfg(feature = "annex-b")] + { + compiler.annex_b_function_names = annex_b_function_names; + } + + compiler.eval_declaration_instantiation(&body, strict, &var_env, &lex_env); compiler.compile_statement_list(body.statements(), true, false); let code_block = Gc::new(compiler.finish()); diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 0d1604fcba4..cdb7143c656 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -624,7 +624,7 @@ impl BuiltInFunctionObject { &body, context.realm().environment().compile_env(), context.realm().environment().compile_env(), - context, + context.interner_mut(), ); let environments = context.vm.environments.pop_to_global(); diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index 817fecf40d1..3ba82ce549c 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -118,7 +118,7 @@ impl Json { true, context.realm().environment().compile_env(), context.realm().environment().compile_env(), - context, + context.interner_mut(), ); compiler.compile_statement_list(script.statements(), true, false); Gc::new(compiler.finish()) diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index 27e134595b2..ca43318bd6f 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -54,7 +54,7 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; @@ -287,7 +287,7 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); // Function environment @@ -315,7 +315,7 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); let _ = field_compiler.push_compile_environment(true); if let Some(node) = field { @@ -353,7 +353,7 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); let _ = field_compiler.push_compile_environment(true); if let Some(node) = field { @@ -387,7 +387,7 @@ impl ByteCompiler<'_> { false, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); let _ = compiler.push_compile_environment(true); diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index f60eb2d6673..16de09bcffd 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -3,11 +3,12 @@ use std::rc::Rc; use crate::{ bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind}, environments::CompileTimeEnvironment, - vm::{create_function_object_fast, BindingOpcode, Opcode}, - JsNativeError, JsResult, + vm::{BindingOpcode, Opcode}, + Context, JsNativeError, JsResult, }; use boa_ast::{ declaration::{Binding, LexicalDeclaration, VariableList}, + expression::Identifier, function::{FormalParameterList, FunctionBody}, operations::{ all_private_identifiers_valid, bound_names, lexically_declared_names, @@ -24,6 +25,361 @@ use boa_ast::operations::annex_b_function_declarations_names; use super::{Operand, ToJsString}; +/// `GlobalDeclarationInstantiation ( script, env )` +/// +/// This diverges from the specification by separating the context from the compilation process. +/// Many steps are skipped that are done during bytecode compilation. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation +#[cfg(not(feature = "annex-b"))] +#[allow(clippy::unnecessary_wraps)] +#[allow(clippy::ptr_arg)] +pub(crate) fn global_declaration_instantiation_context( + _annex_b_function_names: &mut Vec, + _script: &Script, + _env: &Rc, + _context: &mut Context, +) -> JsResult<()> { + Ok(()) +} + +/// `GlobalDeclarationInstantiation ( script, env )` +/// +/// This diverges from the specification by separating the context from the compilation process. +/// Many steps are skipped that are done during bytecode compilation. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation +#[cfg(feature = "annex-b")] +pub(crate) fn global_declaration_instantiation_context( + annex_b_function_names: &mut Vec, + script: &Script, + env: &Rc, + context: &mut Context, +) -> JsResult<()> { + // SKIP: 1. Let lexNames be the LexicallyDeclaredNames of script. + // SKIP: 2. Let varNames be the VarDeclaredNames of script. + // SKIP: 3. For each element name of lexNames, do + // SKIP: 4. For each element name of varNames, do + + // 5. Let varDeclarations be the VarScopedDeclarations of script. + // Note: VarScopedDeclarations for a Script node is TopLevelVarScopedDeclarations. + let var_declarations = var_scoped_declarations(script); + + // SKIP: 6. Let functionsToInitialize be a new empty List. + + // 7. Let declaredFunctionNames be a new empty List. + let mut declared_function_names = Vec::new(); + + // 8. For each element d of varDeclarations, in reverse List order, do + for declaration in var_declarations.iter().rev() { + // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then + // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration. + // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used. + let name = match declaration { + VarScopedDeclaration::Function(f) => f.name(), + VarScopedDeclaration::Generator(f) => f.name(), + VarScopedDeclaration::AsyncFunction(f) => f.name(), + VarScopedDeclaration::AsyncGenerator(f) => f.name(), + VarScopedDeclaration::VariableDeclaration(_) => { + continue; + } + }; + + // a.iii. Let fn be the sole element of the BoundNames of d. + let name = name.expect("function declaration must have a name"); + + // a.iv. If declaredFunctionNames does not contain fn, then + if !declared_function_names.contains(&name) { + // SKIP: 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn). + // SKIP: 2. If fnDefinable is false, throw a TypeError exception. + // 3. Append fn to declaredFunctionNames. + declared_function_names.push(name); + + // SKIP: 4. Insert d as the first element of functionsToInitialize. + } + } + + // // 9. Let declaredVarNames be a new empty List. + let mut declared_var_names = Vec::new(); + + // 10. For each element d of varDeclarations, do + // a. If d is either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then + for declaration in var_declarations { + let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else { + continue; + }; + + // i. For each String vn of the BoundNames of d, do + for name in bound_names(&declaration) { + // 1. If declaredFunctionNames does not contain vn, then + if !declared_function_names.contains(&name) { + // SKIP: a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn). + // SKIP: b. If vnDefinable is false, throw a TypeError exception. + // c. If declaredVarNames does not contain vn, then + if !declared_var_names.contains(&name) { + // i. Append vn to declaredVarNames. + declared_var_names.push(name); + } + } + } + } + + // 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object. + // However, if the global object is a Proxy exotic object it may exhibit behaviours + // that cause abnormal terminations in some of the following steps. + + // 12. NOTE: Annex B.3.2.2 adds additional steps at this point. + // 12. Perform the following steps: + // a. Let strict be IsStrict of script. + // b. If strict is false, then + if !script.strict() { + let lex_names = lexically_declared_names(script); + + // i. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames. + // ii. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause, + // or DefaultClause Contained within script, do + for f in annex_b_function_declarations_names(script) { + // 1. Let F be StringValue of the BindingIdentifier of f. + // 2. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier + // would not produce any Early Errors for script, then + if !lex_names.contains(&f) { + let f_string = f.to_js_string(context.interner()); + + // a. If env.HasLexicalDeclaration(F) is false, then + if !env.has_lex_binding(&f_string) { + // i. Let fnDefinable be ? env.CanDeclareGlobalVar(F). + let fn_definable = context.can_declare_global_function(&f_string)?; + + // ii. If fnDefinable is true, then + if fn_definable { + // i. NOTE: A var binding for F is only instantiated here if it is neither + // a VarDeclaredName nor the name of another FunctionDeclaration. + // ii. If declaredFunctionOrVarNames does not contain F, then + if !declared_function_names.contains(&f) && !declared_var_names.contains(&f) + { + // i. Perform ? env.CreateGlobalVarBinding(F, false). + context.create_global_var_binding(f_string, false)?; + + // ii. Append F to declaredFunctionOrVarNames. + declared_function_names.push(f); + } + // iii. When the FunctionDeclaration f is evaluated, perform the following + // steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6: + // i. Let genv be the running execution context's VariableEnvironment. + // ii. Let benv be the running execution context's LexicalEnvironment. + // iii. Let fobj be ! benv.GetBindingValue(F, false). + // iv. Perform ? genv.SetMutableBinding(F, fobj, false). + // v. Return unused. + annex_b_function_names.push(f); + } + } + } + } + } + + // SKIP: 13. Let lexDeclarations be the LexicallyScopedDeclarations of script. + // SKIP: 14. Let privateEnv be null. + // SKIP: 15. For each element d of lexDeclarations, do + // SKIP: 16. For each Parse Node f of functionsToInitialize, do + // SKIP: 17. For each String vn of declaredVarNames, do + + // 18. Return unused. + Ok(()) +} + +/// `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )` +/// +/// This diverges from the specification by separating the context from the compilation process. +/// Many steps are skipped that are done during bytecode compilation. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation +pub(crate) fn eval_declaration_instantiation_context( + #[allow(unused, clippy::ptr_arg)] annex_b_function_names: &mut Vec, + body: &Script, + #[allow(unused)] strict: bool, + #[allow(unused)] var_env: &Rc, + #[allow(unused)] lex_env: &Rc, + context: &mut Context, +) -> JsResult<()> { + // SKIP: 3. If strict is false, then + + // 4. Let privateIdentifiers be a new empty List. + // 5. Let pointer be privateEnv. + // 6. Repeat, while pointer is not null, + // a. For each Private Name binding of pointer.[[Names]], do + // i. If privateIdentifiers does not contain binding.[[Description]], + // append binding.[[Description]] to privateIdentifiers. + // b. Set pointer to pointer.[[OuterPrivateEnvironment]]. + let private_identifiers = context.vm.environments.private_name_descriptions(); + let private_identifiers = private_identifiers + .into_iter() + .map(|ident| { + context + .interner() + .get(ident.as_slice()) + .expect("string should be in interner") + }) + .collect(); + + // 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception. + if !all_private_identifiers_valid(body, private_identifiers) { + return Err(JsNativeError::syntax() + .with_message("invalid private identifier") + .into()); + } + + // 2. Let varDeclarations be the VarScopedDeclarations of body. + #[cfg(feature = "annex-b")] + let var_declarations = var_scoped_declarations(body); + + // SKIP: 8. Let functionsToInitialize be a new empty List. + + // 9. Let declaredFunctionNames be a new empty List. + #[cfg(feature = "annex-b")] + let mut declared_function_names = Vec::new(); + + // 10. For each element d of varDeclarations, in reverse List order, do + #[cfg(feature = "annex-b")] + for declaration in var_declarations.iter().rev() { + // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then + // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration. + // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used. + let name = match &declaration { + VarScopedDeclaration::Function(f) => f.name(), + VarScopedDeclaration::Generator(f) => f.name(), + VarScopedDeclaration::AsyncFunction(f) => f.name(), + VarScopedDeclaration::AsyncGenerator(f) => f.name(), + VarScopedDeclaration::VariableDeclaration(_) => { + continue; + } + }; + + // a.iii. Let fn be the sole element of the BoundNames of d. + let name = name.expect("function declaration must have a name"); + + // a.iv. If declaredFunctionNames does not contain fn, then + if !declared_function_names.contains(&name) { + // SKIP: 1. If varEnv is a Global Environment Record, then + + // 2. Append fn to declaredFunctionNames. + declared_function_names.push(name); + + // SKIP: 3. Insert d as the first element of functionsToInitialize. + } + } + + // 11. NOTE: Annex B.3.2.3 adds additional steps at this point. + // 11. If strict is false, then + #[cfg(feature = "annex-b")] + if !strict { + let lexically_declared_names = lexically_declared_names(body); + + // a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames. + // b. For each FunctionDeclaration f that is directly contained in the StatementList + // of a Block, CaseClause, or DefaultClause Contained within body, do + for f in annex_b_function_declarations_names(body) { + // i. Let F be StringValue of the BindingIdentifier of f. + // ii. If replacing the FunctionDeclaration f with a VariableStatement that has F + // as a BindingIdentifier would not produce any Early Errors for body, then + if !lexically_declared_names.contains(&f) { + // 1. Let bindingExists be false. + let mut binding_exists = false; + + // 2. Let thisEnv be lexEnv. + let mut this_env = lex_env.clone(); + + // 3. Assert: The following loop will terminate. + // 4. Repeat, while thisEnv is not varEnv, + while this_env.environment_index() != lex_env.environment_index() { + let f = f.to_js_string(context.interner()); + + // a. If thisEnv is not an Object Environment Record, then + // i. If ! thisEnv.HasBinding(F) is true, then + if this_env.has_binding(&f) { + // i. Let bindingExists be true. + binding_exists = true; + break; + } + + // b. Set thisEnv to thisEnv.[[OuterEnv]]. + if let Some(outer) = this_env.outer() { + this_env = outer; + } else { + break; + } + } + + // 5. If bindingExists is false and varEnv is a Global Environment Record, then + let fn_definable = if !binding_exists && var_env.is_global() { + let f = f.to_js_string(context.interner()); + + // a. If varEnv.HasLexicalDeclaration(F) is false, then + // b. Else, + if var_env.has_lex_binding(&f) { + // i. Let fnDefinable be false. + false + } else { + // i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F). + context.can_declare_global_var(&f)? + } + } + // 6. Else, + else { + // a. Let fnDefinable be true. + true + }; + + // 7. If bindingExists is false and fnDefinable is true, then + if !binding_exists && fn_definable { + // a. If declaredFunctionOrVarNames does not contain F, then + if !declared_function_names.contains(&f) { + // i. If varEnv is a Global Environment Record, then + if var_env.is_global() { + let f = f.to_js_string(context.interner()); + + // i. Perform ? varEnv.CreateGlobalVarBinding(F, true). + context.create_global_var_binding(f, true)?; + } + + // SKIP: ii. Else, + // SKIP: iii. Append F to declaredFunctionOrVarNames. + } + + // b. When the FunctionDeclaration f is evaluated, perform the following steps + // in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6: + // i. Let genv be the running execution context's VariableEnvironment. + // ii. Let benv be the running execution context's LexicalEnvironment. + // iii. Let fobj be ! benv.GetBindingValue(F, false). + // iv. Perform ? genv.SetMutableBinding(F, fobj, false). + // v. Return unused. + annex_b_function_names.push(f); + } + } + } + } + + // SKIP: 12. Let declaredVarNames be a new empty List. + // SKIP: 13. For each element d of varDeclarations, do + // SKIP: 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a + // Global Environment Record and the global object is a Proxy exotic object. + // SKIP: 15. Let lexDeclarations be the LexicallyScopedDeclarations of body. + // SKIP: 16. For each element d of lexDeclarations, do + // SKIP: 17. For each Parse Node f of functionsToInitialize, do + // SKIP: 18. For each String vn of declaredVarNames, do + + // 19. Return unused. + Ok(()) +} + impl ByteCompiler<'_> { /// `GlobalDeclarationInstantiation ( script, env )` /// @@ -35,7 +391,7 @@ impl ByteCompiler<'_> { &mut self, script: &Script, env: &Rc, - ) -> JsResult<()> { + ) { // 1. Let lexNames be the LexicallyDeclaredNames of script. let lex_names = lexically_declared_names(script); @@ -44,47 +400,34 @@ impl ByteCompiler<'_> { // 3. For each element name of lexNames, do for name in lex_names { - let name = self - .context - .interner() - .resolve_expect(name.sym()) - .utf16() - .into(); + let name = name.to_js_string(self.interner()); // Note: Our implementation differs from the spec here. // a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception. // b. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. if env.has_binding(&name) { - return Err(JsNativeError::syntax() - .with_message("duplicate lexical declaration") - .into()); + self.emit_syntax_error("duplicate lexical declaration"); + return; } // c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name). - let has_restricted_global = self.context.has_restricted_global_property(&name)?; + let index = self.get_or_insert_string(name); + self.emit_with_varying_operand(Opcode::HasRestrictedGlobalProperty, index); // d. If hasRestrictedGlobal is true, throw a SyntaxError exception. - if has_restricted_global { - return Err(JsNativeError::syntax() - .with_message("cannot redefine non-configurable global property") - .into()); - } + let exit = self.jump_if_false(); + self.emit_syntax_error("cannot redefine non-configurable global property"); + self.patch_jump(exit); } // 4. For each element name of varNames, do for name in var_names { - let name = self - .context - .interner() - .resolve_expect(name.sym()) - .utf16() - .into(); + let name = name.to_js_string(self.interner()); // a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. if env.has_lex_binding(&name) { - return Err(JsNativeError::syntax() - .with_message("duplicate lexical declaration") - .into()); + self.emit_syntax_error("duplicate lexical declaration"); + return; } } @@ -119,16 +462,13 @@ impl ByteCompiler<'_> { // a.iv. If declaredFunctionNames does not contain fn, then if !declared_function_names.contains(&name) { // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn). - let fn_definable = self - .context - .can_declare_global_function(&name.to_js_string(self.interner()))?; + let index = self.get_or_insert_name(name); + self.emit_with_varying_operand(Opcode::CanDeclareGlobalFunction, index); // 2. If fnDefinable is false, throw a TypeError exception. - if !fn_definable { - return Err(JsNativeError::typ() - .with_message("cannot declare global function") - .into()); - } + let exit = self.jump_if_true(); + self.emit_type_error("cannot declare global function"); + self.patch_jump(exit); // 3. Append fn to declaredFunctionNames. declared_function_names.push(name); @@ -155,16 +495,13 @@ impl ByteCompiler<'_> { // 1. If declaredFunctionNames does not contain vn, then if !declared_function_names.contains(&name) { // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn). - let definable = self - .context - .can_declare_global_var(&name.to_js_string(self.interner()))?; + let index = self.get_or_insert_name(name); + self.emit_with_varying_operand(Opcode::CanDeclareGlobalVar, index); // b. If vnDefinable is false, throw a TypeError exception. - if !definable { - return Err(JsNativeError::typ() - .with_message("cannot declare global variable") - .into()); - } + let exit = self.jump_if_true(); + self.emit_type_error("cannot declare global variable"); + self.patch_jump(exit); // c. If declaredVarNames does not contain vn, then if !declared_var_names.contains(&name) { @@ -175,60 +512,15 @@ impl ByteCompiler<'_> { } } - // 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object. + // NOTE: These steps depend on the global object are done before bytecode compilation. + // + // SKIP: 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object. // However, if the global object is a Proxy exotic object it may exhibit behaviours // that cause abnormal terminations in some of the following steps. - - // 12. NOTE: Annex B.3.2.2 adds additional steps at this point. - // 12. Perform the following steps: - // a. Let strict be IsStrict of script. - // b. If strict is false, then - #[cfg(feature = "annex-b")] - if !script.strict() { - let lex_names = lexically_declared_names(script); - - // i. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames. - // ii. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause, - // or DefaultClause Contained within script, do - for f in annex_b_function_declarations_names(script) { - // 1. Let F be StringValue of the BindingIdentifier of f. - // 2. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier - // would not produce any Early Errors for script, then - if !lex_names.contains(&f) { - let f_string = self.resolve_identifier_expect(f); - - // a. If env.HasLexicalDeclaration(F) is false, then - if !env.has_lex_binding(&f_string) { - // i. Let fnDefinable be ? env.CanDeclareGlobalVar(F). - let fn_definable = self.context.can_declare_global_function(&f_string)?; - - // ii. If fnDefinable is true, then - if fn_definable { - // i. NOTE: A var binding for F is only instantiated here if it is neither - // a VarDeclaredName nor the name of another FunctionDeclaration. - // ii. If declaredFunctionOrVarNames does not contain F, then - if !declared_function_names.contains(&f) - && !declared_var_names.contains(&f) - { - // i. Perform ? env.CreateGlobalVarBinding(F, false). - self.context.create_global_var_binding(f_string, false)?; - - // ii. Append F to declaredFunctionOrVarNames. - declared_function_names.push(f); - } - // iii. When the FunctionDeclaration f is evaluated, perform the following - // steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6: - // i. Let genv be the running execution context's VariableEnvironment. - // ii. Let benv be the running execution context's LexicalEnvironment. - // iii. Let fobj be ! benv.GetBindingValue(F, false). - // iv. Perform ? genv.SetMutableBinding(F, fobj, false). - // v. Return unused. - self.annex_b_function_names.push(f); - } - } - } - } - } + // SKIP: 12. NOTE: Annex B.3.2.2 adds additional steps at this point. + // SKIP: 12. Perform the following steps: + // SKIP: a. Let strict be IsStrict of script. + // SKIP: b. If strict is false, then // 13. Let lexDeclarations be the LexicallyScopedDeclarations of script. // 14. Let privateEnv be null. @@ -298,30 +590,34 @@ impl ByteCompiler<'_> { body, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); // Ensures global functions are printed when generating the global flowgraph. - let _ = self.push_function_to_constants(code.clone()); + let function_index = self.push_function_to_constants(code.clone()); // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. - let function = create_function_object_fast(code, self.context); + self.emit_with_varying_operand(Opcode::GetFunction, function_index); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). - let name = name.to_js_string(self.interner()); - self.context - .create_global_function_binding(name, function, false)?; + let name_index = self.get_or_insert_name(name); + self.emit( + Opcode::CreateGlobalFunctionBinding, + &[Operand::Bool(false), Operand::Varying(name_index)], + ); } // 17. For each String vn of declaredVarNames, do for var in declared_var_names { // a. Perform ? env.CreateGlobalVarBinding(vn, false). - let var = var.to_js_string(self.interner()); - self.context.create_global_var_binding(var, false)?; + let index = self.get_or_insert_name(var); + self.emit( + Opcode::CreateGlobalVarBinding, + &[Operand::Bool(false), Operand::Varying(index)], + ); } // 18. Return unused. - Ok(()) } /// `BlockDeclarationInstantiation ( code, env )` @@ -416,7 +712,7 @@ impl ByteCompiler<'_> { strict: bool, var_env: &Rc, lex_env: &Rc, - ) -> JsResult<()> { + ) { // 2. Let varDeclarations be the VarScopedDeclarations of body. let var_declarations = var_scoped_declarations(body); @@ -434,9 +730,8 @@ impl ByteCompiler<'_> { // 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception. // 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration. if var_env.has_lex_binding(&name) { - return Err(JsNativeError::syntax() - .with_message("duplicate lexical declaration") - .into()); + self.emit_syntax_error("duplicate lexical declaration"); + return; } } } @@ -452,19 +747,15 @@ impl ByteCompiler<'_> { // declaration so it doesn't need to be checked for var/let hoisting conflicts. // 2. For each element name of varNames, do for name in &var_names { - let name = self - .context - .interner() - .resolve_expect(name.sym()) - .utf16() - .into(); + let name = self.interner().resolve_expect(name.sym()).utf16().into(); // a. If ! thisEnv.HasBinding(name) is true, then if this_env.has_binding(&name) { // i. Throw a SyntaxError exception. // ii. NOTE: Annex B.3.4 defines alternate semantics for the above step. let msg = format!("variable declaration {} in eval function already exists as a lexical variable", name.to_std_string_escaped()); - return Err(JsNativeError::syntax().with_message(msg).into()); + self.emit_syntax_error(&msg); + return; } // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration. } @@ -478,30 +769,17 @@ impl ByteCompiler<'_> { } } - // 4. Let privateIdentifiers be a new empty List. - // 5. Let pointer be privateEnv. - // 6. Repeat, while pointer is not null, - // a. For each Private Name binding of pointer.[[Names]], do - // i. If privateIdentifiers does not contain binding.[[Description]], - // append binding.[[Description]] to privateIdentifiers. - // b. Set pointer to pointer.[[OuterPrivateEnvironment]]. - let private_identifiers = self.context.vm.environments.private_name_descriptions(); - let private_identifiers = private_identifiers - .into_iter() - .map(|ident| { - self.context - .interner() - .get(ident.as_slice()) - .expect("string should be in interner") - }) - .collect(); - - // 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception. - if !all_private_identifiers_valid(body, private_identifiers) { - return Err(JsNativeError::syntax() - .with_message("invalid private identifier") - .into()); - } + // NOTE: These steps depend on the current environment state are done before bytecode compilation, + // in `eval_declaration_instantiation_context`. + // + // SKIP: 4. Let privateIdentifiers be a new empty List. + // SKIP: 5. Let pointer be privateEnv. + // SKIP: 6. Repeat, while pointer is not null, + // a. For each Private Name binding of pointer.[[Names]], do + // i. If privateIdentifiers does not contain binding.[[Description]], + // append binding.[[Description]] to privateIdentifiers. + // b. Set pointer to pointer.[[OuterPrivateEnvironment]]. + // SKIP: 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception. // 8. Let functionsToInitialize be a new empty List. let mut functions_to_initialize = Vec::new(); @@ -531,17 +809,15 @@ impl ByteCompiler<'_> { if !declared_function_names.contains(&name) { // 1. If varEnv is a Global Environment Record, then if var_env.is_global() { - let name = name.to_js_string(self.interner()); + let index = self.get_or_insert_name(name); // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn). - let fn_definable = self.context.can_declare_global_function(&name)?; + self.emit_with_varying_operand(Opcode::CanDeclareGlobalFunction, index); // b. If fnDefinable is false, throw a TypeError exception. - if !fn_definable { - return Err(JsNativeError::typ() - .with_message("cannot declare global function") - .into()); - } + let exit = self.jump_if_true(); + self.emit_type_error("cannot declare global function"); + self.patch_jump(exit); } // 2. Append fn to declaredFunctionNames. @@ -558,105 +834,20 @@ impl ByteCompiler<'_> { // 11. If strict is false, then #[cfg(feature = "annex-b")] if !strict { - let lexically_declared_names = lexically_declared_names(body); - - // a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames. - // b. For each FunctionDeclaration f that is directly contained in the StatementList - // of a Block, CaseClause, or DefaultClause Contained within body, do - for f in annex_b_function_declarations_names(body) { - // i. Let F be StringValue of the BindingIdentifier of f. - // ii. If replacing the FunctionDeclaration f with a VariableStatement that has F - // as a BindingIdentifier would not produce any Early Errors for body, then - if !lexically_declared_names.contains(&f) { - // 1. Let bindingExists be false. - let mut binding_exists = false; - - // 2. Let thisEnv be lexEnv. - let mut this_env = lex_env.clone(); - - // 3. Assert: The following loop will terminate. - // 4. Repeat, while thisEnv is not varEnv, - while this_env.environment_index() != lex_env.environment_index() { - let f = f.to_js_string(self.interner()); - - // a. If thisEnv is not an Object Environment Record, then - // i. If ! thisEnv.HasBinding(F) is true, then - if this_env.has_binding(&f) { - // i. Let bindingExists be true. - binding_exists = true; - break; - } - - // b. Set thisEnv to thisEnv.[[OuterEnv]]. - if let Some(outer) = this_env.outer() { - this_env = outer; - } else { - break; - } - } - - // 5. If bindingExists is false and varEnv is a Global Environment Record, then - let fn_definable = if !binding_exists && var_env.is_global() { - let f = f.to_js_string(self.interner()); - - // a. If varEnv.HasLexicalDeclaration(F) is false, then - // b. Else, - if self.variable_environment.has_lex_binding(&f) { - // i. Let fnDefinable be false. - false - } else { - // i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F). - self.context.can_declare_global_var(&f)? - } - } - // 6. Else, - else { - // a. Let fnDefinable be true. - true - }; - - // 7. If bindingExists is false and fnDefinable is true, then - if !binding_exists && fn_definable { - let mut function_names = Vec::new(); - - // a. If declaredFunctionOrVarNames does not contain F, then - if !declared_function_names.contains(&f) - //&& !var_names.contains(&f) - && !function_names.contains(&f) - { - // i. If varEnv is a Global Environment Record, then - if var_env.is_global() { - let f = f.to_js_string(self.interner()); - // i. Perform ? varEnv.CreateGlobalVarBinding(F, true). - self.context.create_global_var_binding(f, true)?; - } - // ii. Else, - else { - let f = f.to_js_string(self.interner()); - // i. Let bindingExists be ! varEnv.HasBinding(F). - // ii. If bindingExists is false, then - if !var_env.has_binding(&f) { - // i. Perform ! varEnv.CreateMutableBinding(F, true). - // ii. Perform ! varEnv.InitializeBinding(F, undefined). - let binding = var_env.create_mutable_binding(f, true); - let index = self.get_or_insert_binding(binding); - self.emit_opcode(Opcode::PushUndefined); - self.emit_with_varying_operand(Opcode::DefInitVar, index); - } - } - - // iii. Append F to declaredFunctionOrVarNames. - function_names.push(f); - } - - // b. When the FunctionDeclaration f is evaluated, perform the following steps - // in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6: - // i. Let genv be the running execution context's VariableEnvironment. - // ii. Let benv be the running execution context's LexicalEnvironment. - // iii. Let fobj be ! benv.GetBindingValue(F, false). - // iv. Perform ? genv.SetMutableBinding(F, fobj, false). - // v. Return unused. - self.annex_b_function_names.push(f); + // NOTE: This diviates from the specification, we split the first part of defining the annex-b names + // in `eval_declaration_instantiation_context`, because it depends on the context. + if !var_env.is_global() { + for name in self.annex_b_function_names.clone() { + let f = name.to_js_string(self.interner()); + // i. Let bindingExists be ! varEnv.HasBinding(F). + // ii. If bindingExists is false, then + if !var_env.has_binding(&f) { + // i. Perform ! varEnv.CreateMutableBinding(F, true). + // ii. Perform ! varEnv.InitializeBinding(F, undefined). + let binding = var_env.create_mutable_binding(f, true); + let index = self.get_or_insert_binding(binding); + self.emit_opcode(Opcode::PushUndefined); + self.emit_with_varying_operand(Opcode::DefInitVar, index); } } } @@ -678,17 +869,15 @@ impl ByteCompiler<'_> { if !declared_function_names.contains(&name) { // a. If varEnv is a Global Environment Record, then if var_env.is_global() { - let name = name.to_js_string(self.interner()); + let index = self.get_or_insert_name(name); // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn). - let vn_definable = self.context.can_declare_global_var(&name)?; + self.emit_with_varying_operand(Opcode::CanDeclareGlobalVar, index); // ii. If vnDefinable is false, throw a TypeError exception. - if !vn_definable { - return Err(JsNativeError::typ() - .with_message("cannot declare global variable") - .into()); - } + let exit = self.jump_if_true(); + self.emit_type_error("cannot declare global function"); + self.patch_jump(exit); } // b. If declaredVarNames does not contain vn, then @@ -769,7 +958,7 @@ impl ByteCompiler<'_> { body, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); // c. If varEnv is a Global Environment Record, then @@ -818,15 +1007,20 @@ impl ByteCompiler<'_> { // 18. For each String vn of declaredVarNames, do for name in declared_var_names { - let name = name.to_js_string(self.interner()); - // a. If varEnv is a Global Environment Record, then if var_env.is_global() { + let index = self.get_or_insert_name(name); + // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true). - self.context.create_global_var_binding(name, true)?; + self.emit( + Opcode::CreateGlobalVarBinding, + &[Operand::Bool(true), Operand::Varying(index)], + ); } // b. Else, else { + let name = name.to_js_string(self.interner()); + // i. Let bindingExists be ! varEnv.HasBinding(vn). let binding_exists = var_env.has_binding(&name); @@ -844,7 +1038,6 @@ impl ByteCompiler<'_> { } // 19. Return unused. - Ok(()) } /// `FunctionDeclarationInstantiation ( func, argumentsList )` diff --git a/core/engine/src/bytecompiler/function.rs b/core/engine/src/bytecompiler/function.rs index 1d570836242..2a09a497b96 100644 --- a/core/engine/src/bytecompiler/function.rs +++ b/core/engine/src/bytecompiler/function.rs @@ -6,10 +6,11 @@ use crate::{ environments::CompileTimeEnvironment, js_string, vm::{CodeBlock, CodeBlockFlags, Opcode}, - Context, JsString, + JsString, }; use boa_ast::function::{FormalParameterList, FunctionBody}; use boa_gc::Gc; +use boa_interner::Interner; /// `FunctionCompiler` is used to compile AST functions to bytecode. #[derive(Debug, Clone)] @@ -91,7 +92,7 @@ impl FunctionCompiler { body: &FunctionBody, variable_environment: Rc, lexical_environment: Rc, - context: &mut Context, + interner: &mut Interner, ) -> Gc { self.strict = self.strict || body.strict(); @@ -103,7 +104,7 @@ impl FunctionCompiler { false, variable_environment, lexical_environment, - context, + interner, ); compiler.length = length; compiler diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index 13b8867882f..4b64f6774e6 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -21,7 +21,7 @@ use crate::{ BindingOpcode, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, Handler, InlineCache, Opcode, VaryingOperandKind, }, - Context, JsBigInt, JsString, + JsBigInt, JsString, }; use boa_ast::{ declaration::{Binding, LexicalDeclaration, VarDeclaration}, @@ -41,10 +41,13 @@ use boa_ast::{ use boa_gc::Gc; use boa_interner::{Interner, Sym}; use rustc_hash::FxHashMap; +use thin_vec::ThinVec; +pub(crate) use declarations::{ + eval_declaration_instantiation_context, global_declaration_instantiation_context, +}; pub(crate) use function::FunctionCompiler; pub(crate) use jump_control::JumpControlInfo; -use thin_vec::ThinVec; pub(crate) trait ToJsString { fn to_js_string(&self, interner: &Interner) -> JsString; @@ -277,7 +280,7 @@ pub struct ByteCompiler<'ctx> { /// The current lexical environment. pub(crate) lexical_environment: Rc, - current_open_environments_count: u32, + pub(crate) current_open_environments_count: u32, current_stack_value_count: u32, pub(crate) code_block_flags: CodeBlockFlags, handlers: ThinVec, @@ -293,11 +296,10 @@ pub struct ByteCompiler<'ctx> { pub(crate) async_handler: Option, json_parse: bool, - // TODO: remove when we separate scripts from the context - pub(crate) context: &'ctx mut Context, + pub(crate) interner: &'ctx mut Interner, #[cfg(feature = "annex-b")] - annex_b_function_names: Vec, + pub(crate) annex_b_function_names: Vec, } impl<'ctx> ByteCompiler<'ctx> { @@ -313,8 +315,7 @@ impl<'ctx> ByteCompiler<'ctx> { json_parse: bool, variable_environment: Rc, lexical_environment: Rc, - // TODO: remove when we separate scripts from the context - context: &'ctx mut Context, + interner: &'ctx mut Interner, ) -> ByteCompiler<'ctx> { let mut code_block_flags = CodeBlockFlags::empty(); code_block_flags.set(CodeBlockFlags::STRICT, strict); @@ -343,7 +344,7 @@ impl<'ctx> ByteCompiler<'ctx> { json_parse, variable_environment, lexical_environment, - context, + interner, #[cfg(feature = "annex-b")] annex_b_function_names: Vec::new(), @@ -367,7 +368,7 @@ impl<'ctx> ByteCompiler<'ctx> { } pub(crate) fn interner(&self) -> &Interner { - self.context.interner() + self.interner } fn get_or_insert_literal(&mut self, literal: Literal) -> u32 { @@ -568,6 +569,15 @@ impl<'ctx> ByteCompiler<'ctx> { self.emit_with_varying_operand(Opcode::SetPropertyByName, ic_index); } + fn emit_type_error(&mut self, message: &str) { + let error_msg = self.get_or_insert_literal(Literal::String(js_string!(message))); + self.emit_with_varying_operand(Opcode::ThrowNewTypeError, error_msg); + } + fn emit_syntax_error(&mut self, message: &str) { + let error_msg = self.get_or_insert_literal(Literal::String(js_string!(message))); + self.emit_with_varying_operand(Opcode::ThrowNewSyntaxError, error_msg); + } + fn emit_u64(&mut self, value: u64) { self.bytecode.extend(value.to_ne_bytes()); } @@ -1308,7 +1318,7 @@ impl<'ctx> ByteCompiler<'ctx> { body, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); self.push_function_to_constants(code) @@ -1383,7 +1393,7 @@ impl<'ctx> ByteCompiler<'ctx> { body, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); let index = self.push_function_to_constants(code); @@ -1430,7 +1440,7 @@ impl<'ctx> ByteCompiler<'ctx> { body, self.variable_environment.clone(), self.lexical_environment.clone(), - self.context, + self.interner, ); let index = self.push_function_to_constants(code); diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index c36b11966a4..edc1757db87 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1432,7 +1432,7 @@ impl SourceTextModule { false, env.clone(), env.clone(), - context, + context.interner_mut(), ); compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index fa151d468f4..734b1e9dd9e 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -286,7 +286,7 @@ impl SyntheticModule { false, module_compile_env.clone(), module_compile_env.clone(), - context, + context.interner_mut(), ); // 4. For each String exportName in module.[[ExportNames]], do diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index bb34e981884..3351ae4492d 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -17,7 +17,7 @@ use boa_parser::{source::ReadChar, Parser, Source}; use boa_profiler::Profiler; use crate::{ - bytecompiler::ByteCompiler, + bytecompiler::{global_declaration_instantiation_context, ByteCompiler}, js_string, realm::Realm, vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock}, @@ -119,19 +119,34 @@ impl Script { let _timer = Profiler::global().start_event("Script compilation", "Main"); + let mut annex_b_function_names = Vec::new(); + + global_declaration_instantiation_context( + &mut annex_b_function_names, + &self.inner.source, + &self.inner.realm.environment().compile_env(), + context, + )?; + let mut compiler = ByteCompiler::new( js_string!("
"), self.inner.source.strict(), false, self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(), - context, + context.interner_mut(), ); + + #[cfg(feature = "annex-b")] + { + compiler.annex_b_function_names = annex_b_function_names; + } + // TODO: move to `Script::evaluate` to make this operation infallible. compiler.global_declaration_instantiation( &self.inner.source, &self.inner.realm.environment().compile_env(), - )?; + ); compiler.compile_statement_list(self.inner.source.statements(), true, false); let cb = Gc::new(compiler.finish()); diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index 502f658c85f..2a486da098b 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -385,7 +385,11 @@ impl CodeBlock { Instruction::PushFloat { value } => ryu_js::Buffer::new().format(*value).to_string(), Instruction::PushDouble { value } => ryu_js::Buffer::new().format(*value).to_string(), Instruction::PushLiteral { index } - | Instruction::ThrowNewTypeError { message: index } => index.value().to_string(), + | Instruction::ThrowNewTypeError { message: index } + | Instruction::ThrowNewSyntaxError { message: index } + | Instruction::HasRestrictedGlobalProperty { index } + | Instruction::CanDeclareGlobalFunction { index } + | Instruction::CanDeclareGlobalVar { index } => index.value().to_string(), Instruction::PushRegExp { pattern_index: source_index, flags_index: flag_index, @@ -526,11 +530,15 @@ impl CodeBlock { format!("done: {done}") } Instruction::CreateGlobalFunctionBinding { - name_index, + index, + configurable, + } + | Instruction::CreateGlobalVarBinding { + index, configurable, } => { let name = self - .constant_string(name_index.value() as usize) + .constant_string(index.value() as usize) .to_std_string_escaped(); format!("name: {name}, configurable: {configurable}") } @@ -712,12 +720,7 @@ impl CodeBlock { | Instruction::Reserved51 | Instruction::Reserved52 | Instruction::Reserved53 - | Instruction::Reserved54 - | Instruction::Reserved55 - | Instruction::Reserved56 - | Instruction::Reserved57 - | Instruction::Reserved58 - | Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"), } } } diff --git a/core/engine/src/vm/flowgraph/mod.rs b/core/engine/src/vm/flowgraph/mod.rs index 2db51c7b4d7..273053d15b4 100644 --- a/core/engine/src/vm/flowgraph/mod.rs +++ b/core/engine/src/vm/flowgraph/mod.rs @@ -72,7 +72,11 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::PushLiteral { .. } | Instruction::PushRegExp { .. } => { + Instruction::PushLiteral { .. } + | Instruction::PushRegExp { .. } + | Instruction::HasRestrictedGlobalProperty { .. } + | Instruction::CanDeclareGlobalFunction { .. } + | Instruction::CanDeclareGlobalVar { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } @@ -281,7 +285,7 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::ThrowNewTypeError { .. } => { + Instruction::ThrowNewTypeError { .. } | Instruction::ThrowNewSyntaxError { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); if let Some((i, handler)) = self.find_handler(previous_pc as u32) { graph.add_edge( @@ -449,6 +453,7 @@ impl CodeBlock { | Instruction::CreateMappedArgumentsObject | Instruction::CreateUnmappedArgumentsObject | Instruction::CreateGlobalFunctionBinding { .. } + | Instruction::CreateGlobalVarBinding { .. } | Instruction::Nop => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); @@ -511,12 +516,7 @@ impl CodeBlock { | Instruction::Reserved51 | Instruction::Reserved52 | Instruction::Reserved53 - | Instruction::Reserved54 - | Instruction::Reserved55 - | Instruction::Reserved56 - | Instruction::Reserved57 - | Instruction::Reserved58 - | Instruction::Reserved59 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"), } } diff --git a/core/engine/src/vm/opcode/control_flow/throw.rs b/core/engine/src/vm/opcode/control_flow/throw.rs index bde9b6288f7..9261f29feb1 100644 --- a/core/engine/src/vm/opcode/control_flow/throw.rs +++ b/core/engine/src/vm/opcode/control_flow/throw.rs @@ -151,3 +151,41 @@ impl Operation for ThrowNewTypeError { Self::operation(context, index) } } + +/// `ThrowNewSyntaxError` implements the Opcode Operation for `Opcode::ThrowNewSyntaxError` +/// +/// Operation: +/// - Throws a `SyntaxError` exception. +#[derive(Debug, Clone, Copy)] +pub(crate) struct ThrowNewSyntaxError; + +impl ThrowNewSyntaxError { + fn operation(context: &mut Context, index: usize) -> JsResult { + let msg = context.vm.frame().code_block().constant_string(index); + let msg = msg + .to_std_string() + .expect("throw message must be an ASCII string"); + Err(JsNativeError::syntax().with_message(msg).into()) + } +} + +impl Operation for ThrowNewSyntaxError { + const NAME: &'static str = "ThrowNewSyntaxError"; + const INSTRUCTION: &'static str = "INST - ThrowNewSyntaxError"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } +} diff --git a/core/engine/src/vm/opcode/define/mod.rs b/core/engine/src/vm/opcode/define/mod.rs index 0b6465f8565..5c21d2b5cee 100644 --- a/core/engine/src/vm/opcode/define/mod.rs +++ b/core/engine/src/vm/opcode/define/mod.rs @@ -137,56 +137,3 @@ impl Operation for PutLexicalValue { Self::operation(context, index as usize) } } - -/// `CreateGlobalFunctionBinding` implements the Opcode Operation for `Opcode::CreateGlobalFunctionBinding` -/// -/// Operation: -/// - Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding -#[derive(Debug, Clone, Copy)] -pub(crate) struct CreateGlobalFunctionBinding; - -impl CreateGlobalFunctionBinding { - #[allow(clippy::unnecessary_wraps)] - fn operation( - context: &mut Context, - index: usize, - configurable: bool, - ) -> JsResult { - let name = context.vm.frame().code_block().constant_string(index); - let value = context.vm.pop(); - - let function = value - .as_object() - .expect("valeu should be an function") - .clone(); - context.create_global_function_binding(name, function, configurable)?; - - Ok(CompletionType::Normal) - } -} - -impl Operation for CreateGlobalFunctionBinding { - const NAME: &'static str = "CreateGlobalFunctionBinding"; - const INSTRUCTION: &'static str = "INST - CreateGlobalFunctionBinding"; - const COST: u8 = 2; - - fn execute(context: &mut Context) -> JsResult { - let configurable = context.vm.read::() != 0; - let index = context.vm.read::() as usize; - Self::operation(context, index, configurable) - } - - fn execute_with_u16_operands(context: &mut Context) -> JsResult { - let configurable = context.vm.read::() != 0; - let index = context.vm.read::() as usize; - Self::operation(context, index, configurable) - } - - fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let configurable = context.vm.read::() != 0; - let index = context.vm.read::() as usize; - Self::operation(context, index, configurable) - } -} diff --git a/core/engine/src/vm/opcode/global.rs b/core/engine/src/vm/opcode/global.rs new file mode 100644 index 00000000000..49666dc4373 --- /dev/null +++ b/core/engine/src/vm/opcode/global.rs @@ -0,0 +1,220 @@ +use crate::{vm::CompletionType, Context, JsResult}; + +use super::Operation; + +/// `HasRestrictedGlobalProperty` implements the Opcode Operation for `Opcode::HasRestrictedGlobalProperty` +/// +/// Operation: +/// - Performs [`HasRestrictedGlobalProperty ( N )`][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty +#[derive(Debug, Clone, Copy)] +pub(crate) struct HasRestrictedGlobalProperty; + +impl HasRestrictedGlobalProperty { + fn operation(context: &mut Context, index: usize) -> JsResult { + let name = &context.vm.frame().code_block().constant_string(index); + let value = context.has_restricted_global_property(name)?; + context.vm.push(value); + Ok(CompletionType::Normal) + } +} + +impl Operation for HasRestrictedGlobalProperty { + const NAME: &'static str = "HasRestrictedGlobalProperty"; + const INSTRUCTION: &'static str = "INST - HasRestrictedGlobalProperty"; + const COST: u8 = 4; + + fn execute(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } +} + +/// `CanDeclareGlobalFunction` implements the Opcode Operation for `Opcode::CanDeclareGlobalFunction` +/// +/// Operation: +/// - Performs [`CanDeclareGlobalFunction ( N )`][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction +#[derive(Debug, Clone, Copy)] +pub(crate) struct CanDeclareGlobalFunction; + +impl CanDeclareGlobalFunction { + fn operation(context: &mut Context, index: usize) -> JsResult { + let name = &context.vm.frame().code_block().constant_string(index); + let value = context.can_declare_global_function(name)?; + context.vm.push(value); + Ok(CompletionType::Normal) + } +} + +impl Operation for CanDeclareGlobalFunction { + const NAME: &'static str = "CanDeclareGlobalFunction"; + const INSTRUCTION: &'static str = "INST - CanDeclareGlobalFunction"; + const COST: u8 = 4; + + fn execute(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } +} + +/// `CanDeclareGlobalVar` implements the Opcode Operation for `Opcode::CanDeclareGlobalVar` +/// +/// Operation: +/// - Performs [`CanDeclareGlobalVar ( N )`][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar +#[derive(Debug, Clone, Copy)] +pub(crate) struct CanDeclareGlobalVar; + +impl CanDeclareGlobalVar { + fn operation(context: &mut Context, index: usize) -> JsResult { + let name = &context.vm.frame().code_block().constant_string(index); + let value = context.can_declare_global_var(name)?; + context.vm.push(value); + Ok(CompletionType::Normal) + } +} + +impl Operation for CanDeclareGlobalVar { + const NAME: &'static str = "CanDeclareGlobalVar"; + const INSTRUCTION: &'static str = "INST - CanDeclareGlobalVar"; + const COST: u8 = 4; + + fn execute(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } +} + +/// `CreateGlobalFunctionBinding` implements the Opcode Operation for `Opcode::CreateGlobalFunctionBinding` +/// +/// Operation: +/// - Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding +#[derive(Debug, Clone, Copy)] +pub(crate) struct CreateGlobalFunctionBinding; + +impl CreateGlobalFunctionBinding { + #[allow(clippy::unnecessary_wraps)] + fn operation( + context: &mut Context, + index: usize, + configurable: bool, + ) -> JsResult { + let name = context.vm.frame().code_block().constant_string(index); + let value = context.vm.pop(); + + let function = value + .as_object() + .expect("valeu should be an function") + .clone(); + context.create_global_function_binding(name, function, configurable)?; + + Ok(CompletionType::Normal) + } +} + +impl Operation for CreateGlobalFunctionBinding { + const NAME: &'static str = "CreateGlobalFunctionBinding"; + const INSTRUCTION: &'static str = "INST - CreateGlobalFunctionBinding"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let configurable = context.vm.read::() != 0; + let index = context.vm.read::() as usize; + Self::operation(context, index, configurable) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let configurable = context.vm.read::() != 0; + let index = context.vm.read::() as usize; + Self::operation(context, index, configurable) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let configurable = context.vm.read::() != 0; + let index = context.vm.read::() as usize; + Self::operation(context, index, configurable) + } +} + +/// `CreateGlobalVarBinding` implements the Opcode Operation for `Opcode::CreateGlobalVarBinding` +/// +/// Operation: +/// - Performs [`CreateGlobalVarBinding ( N, V, D )`][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding +#[derive(Debug, Clone, Copy)] +pub(crate) struct CreateGlobalVarBinding; + +impl CreateGlobalVarBinding { + #[allow(clippy::unnecessary_wraps)] + fn operation( + context: &mut Context, + index: usize, + configurable: bool, + ) -> JsResult { + let name = context.vm.frame().code_block().constant_string(index); + context.create_global_var_binding(name, configurable)?; + + Ok(CompletionType::Normal) + } +} + +impl Operation for CreateGlobalVarBinding { + const NAME: &'static str = "CreateGlobalVarBinding"; + const INSTRUCTION: &'static str = "INST - CreateGlobalVarBinding"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let configurable = context.vm.read::() != 0; + let index = context.vm.read::() as usize; + Self::operation(context, index, configurable) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let configurable = context.vm.read::() != 0; + let index = context.vm.read::() as usize; + Self::operation(context, index, configurable) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let configurable = context.vm.read::() != 0; + let index = context.vm.read::() as usize; + Self::operation(context, index, configurable) + } +} diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index 4458b786070..3f54a0080f8 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -17,6 +17,7 @@ mod dup; mod environment; mod generator; mod get; +mod global; mod iteration; mod meta; mod modifier; @@ -60,6 +61,8 @@ pub(crate) use generator::*; #[doc(inline)] pub(crate) use get::*; #[doc(inline)] +pub(crate) use global::*; +#[doc(inline)] pub(crate) use iteration::*; #[doc(inline)] pub(crate) use meta::*; @@ -1590,6 +1593,13 @@ generate_opcodes! { /// Stack: **=>** ThrowNewTypeError { message: VaryingOperand }, + /// Throw a new `SyntaxError` exception + /// + /// Operands: message: u32 + /// + /// Stack: **=>** + ThrowNewSyntaxError { message: VaryingOperand }, + /// Pops value converts it to boolean and pushes it back. /// /// Operands: @@ -2070,14 +2080,50 @@ generate_opcodes! { /// Stack: **=>** `arguments` CreateUnmappedArgumentsObject, + /// Performs [`HasRestrictedGlobalProperty ( N )`][spec] + /// + /// Operands: `index`: u32 + /// + /// Stack: **=>** + /// + /// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty + HasRestrictedGlobalProperty { index: VaryingOperand }, + + /// Performs [`CanDeclareGlobalFunction ( N )`][spec] + /// + /// Operands: `index`: u32 + /// + /// Stack: **=>** + /// + /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction + CanDeclareGlobalFunction { index: VaryingOperand }, + + /// Performs [`CanDeclareGlobalVar ( N )`][spec] + /// + /// Operands: `index`: u32 + /// + /// Stack: **=>** + /// + /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar + CanDeclareGlobalVar { index: VaryingOperand }, + /// Performs [`CreateGlobalFunctionBinding ( N, V, D )`][spec] /// - /// Operands: configurable: `bool`, `name_index`: `VaryingOperand` + /// Operands: configurable: `bool`, `index`: `VaryingOperand` /// - /// Stack: `value` **=>** + /// Stack: `function` **=>** /// /// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding - CreateGlobalFunctionBinding { configurable: bool, name_index: VaryingOperand }, + CreateGlobalFunctionBinding { configurable: bool, index: VaryingOperand }, + + /// Performs [`CreateGlobalVarBinding ( N, V, D )`][spec] + /// + /// Operands: configurable: `bool`, `index`: `VaryingOperand` + /// + /// Stack: **=>** + /// + /// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding + CreateGlobalVarBinding { configurable: bool, index: VaryingOperand }, /// No-operation instruction, does nothing. /// @@ -2208,16 +2254,6 @@ generate_opcodes! { Reserved53 => Reserved, /// Reserved [`Opcode`]. Reserved54 => Reserved, - /// Reserved [`Opcode`]. - Reserved55 => Reserved, - /// Reserved [`Opcode`]. - Reserved56 => Reserved, - /// Reserved [`Opcode`]. - Reserved57 => Reserved, - /// Reserved [`Opcode`]. - Reserved58 => Reserved, - /// Reserved [`Opcode`]. - Reserved59 => Reserved, } /// Specific opcodes for bindings. From f469096390740b925e469ed6148449b843127422 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:16:29 -0600 Subject: [PATCH 011/212] Bump either from 1.10.0 to 1.11.0 (#3835) Bumps [either](https://github.com/rayon-rs/either) from 1.10.0 to 1.11.0. - [Commits](https://github.com/rayon-rs/either/compare/1.10.0...1.11.0) --- updated-dependencies: - dependency-name: either dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 562face6db4..89997a2af19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1237,9 +1237,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elsa" From 1c8400ad046503cf232dced6a4a1092dda245960 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:16:38 -0600 Subject: [PATCH 012/212] Bump hashbrown from 0.14.3 to 0.14.5 (#3834) Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.14.3 to 0.14.5. - [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/hashbrown/compare/v0.14.3...v0.14.5) --- updated-dependencies: - dependency-name: hashbrown dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89997a2af19..b95ad844b38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,7 +404,7 @@ dependencies = [ "fixed_decimal", "float-cmp", "futures-lite 2.3.0", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "icu_calendar", "icu_casemap", "icu_collator", @@ -475,7 +475,7 @@ version = "0.18.0" dependencies = [ "boa_macros", "boa_profiler", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "icu_locid", "thin-vec", ] @@ -497,7 +497,7 @@ dependencies = [ "arbitrary", "boa_gc", "boa_macros", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "indexmap 2.2.6", "once_cell", "phf", @@ -1118,7 +1118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1657,9 +1657,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", "allocator-api2", @@ -2127,7 +2127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -3101,7 +3101,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eae2a1ebfecc58aff952ef8ccd364329abe627762f5bf09ff42eb9d98522479" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 4890c3f72b6..5dec2a4e1ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ bitflags = "2.5.0" clap = "4.5.4" colored = "2.1.0" fast-float = "0.2.0" -hashbrown = { version = "0.14.3", default-features = false } +hashbrown = { version = "0.14.5", default-features = false } indexmap = { version = "2.2.6", default-features = false } indoc = "2.0.5" jemallocator = "0.5.4" From 4452c03bd6260c3be5fdf9eef37a82e7a80dd672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:16:50 +0000 Subject: [PATCH 013/212] Bump serde from 1.0.198 to 1.0.199 (#3833) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.198 to 1.0.199. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.198...v1.0.199) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b95ad844b38..96a62deae14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3346,9 +3346,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] @@ -3374,9 +3374,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 5dec2a4e1ea..155025aa701 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ regex = "1.10.4" regress = { version="0.9.1", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } serde_json = "1.0.116" -serde = "1.0.198" +serde = "1.0.199" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" From 6febf7eda0bcb660965427c2904c456270e88a97 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Tue, 30 Apr 2024 17:05:38 +0200 Subject: [PATCH 014/212] Implement object keys access (#3832) --- core/engine/src/object/operations.rs | 10 +++++++++ examples/src/bin/properties.rs | 31 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 examples/src/bin/properties.rs diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index a607f70fe06..6cee0140e11 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -343,6 +343,16 @@ impl JsObject { Ok(desc.is_some()) } + /// Get all the keys of the properties of this object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys + pub fn own_property_keys(&self, context: &mut Context) -> JsResult> { + self.__own_property_keys__(context) + } + /// `Call ( F, V [ , argumentsList ] )` /// /// # Panics diff --git a/examples/src/bin/properties.rs b/examples/src/bin/properties.rs new file mode 100644 index 00000000000..ab92e7789af --- /dev/null +++ b/examples/src/bin/properties.rs @@ -0,0 +1,31 @@ +// This example shows how to access the keys and values of a `JsObject` + +use boa_engine::{ + js_string, property::PropertyKey, Context, JsError, JsNativeError, JsValue, Source, +}; + +fn main() -> Result<(), JsError> { + // We create a new `Context` to create a new Javascript executor. + let mut context = Context::default(); + + let value = context.eval(Source::from_bytes("({ x: 10, '1': 20 })"))?; + let object = value + .as_object() + .ok_or_else(|| JsNativeError::typ().with_message("Expected object"))?; + + let keys = object.own_property_keys(&mut context)?; + + assert_eq!( + keys, + &[PropertyKey::from(1), PropertyKey::from(js_string!("x"))] + ); + + let mut values = Vec::new(); + for key in keys { + values.push(object.get(key, &mut context)?); + } + + assert_eq!(values, &[JsValue::from(20), JsValue::from(10)]); + + Ok(()) +} From 3f6ee2276c00a73a8ef67aa8b4357919e08d1902 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Wed, 1 May 2024 20:33:49 +0200 Subject: [PATCH 015/212] Implement Latin1 JsString (#3450) --- cli/src/debug/function.rs | 9 +- cli/src/debug/limits.rs | 20 +- cli/src/debug/mod.rs | 25 +- cli/src/debug/optimizer.rs | 6 +- cli/src/debug/string.rs | 93 ++ core/engine/src/bigint.rs | 18 +- .../src/builtins/array/array_iterator.rs | 3 +- core/engine/src/builtins/array/mod.rs | 56 +- core/engine/src/builtins/array/tests.rs | 17 +- .../src/builtins/async_generator/mod.rs | 10 +- core/engine/src/builtins/atomics/mod.rs | 7 +- core/engine/src/builtins/bigint/tests.rs | 12 +- core/engine/src/builtins/builder.rs | 18 +- core/engine/src/builtins/date/mod.rs | 124 +- core/engine/src/builtins/date/tests.rs | 11 +- core/engine/src/builtins/date/utils.rs | 179 +-- core/engine/src/builtins/error/aggregate.rs | 11 +- core/engine/src/builtins/error/eval.rs | 9 +- core/engine/src/builtins/error/mod.rs | 21 +- core/engine/src/builtins/error/range.rs | 9 +- core/engine/src/builtins/error/reference.rs | 9 +- core/engine/src/builtins/error/syntax.rs | 9 +- core/engine/src/builtins/error/tests.rs | 58 +- core/engine/src/builtins/error/type.rs | 11 +- core/engine/src/builtins/error/uri.rs | 9 +- core/engine/src/builtins/escape/mod.rs | 8 +- core/engine/src/builtins/eval/mod.rs | 3 +- core/engine/src/builtins/function/mod.rs | 51 +- core/engine/src/builtins/function/tests.rs | 9 +- core/engine/src/builtins/intl/collator/mod.rs | 62 +- .../src/builtins/intl/date_time_format.rs | 33 +- .../src/builtins/intl/list_format/mod.rs | 31 +- core/engine/src/builtins/intl/locale/mod.rs | 25 +- core/engine/src/builtins/intl/locale/utils.rs | 4 +- .../src/builtins/intl/number_format/mod.rs | 14 +- .../builtins/intl/number_format/options.rs | 56 +- core/engine/src/builtins/intl/options.rs | 4 +- .../src/builtins/intl/plural_rules/mod.rs | 60 +- .../src/builtins/intl/segmenter/iterator.rs | 30 +- .../engine/src/builtins/intl/segmenter/mod.rs | 40 +- .../src/builtins/intl/segmenter/segments.rs | 2 +- .../iterable/async_from_sync_iterator.rs | 12 +- core/engine/src/builtins/iterable/mod.rs | 10 +- core/engine/src/builtins/json/mod.rs | 31 +- core/engine/src/builtins/json/tests.rs | 18 +- core/engine/src/builtins/map/mod.rs | 7 +- core/engine/src/builtins/map/tests.rs | 19 +- core/engine/src/builtins/mod.rs | 10 +- core/engine/src/builtins/number/globals.rs | 228 ++-- core/engine/src/builtins/number/mod.rs | 9 +- core/engine/src/builtins/number/tests.rs | 350 +++-- core/engine/src/builtins/object/mod.rs | 49 +- core/engine/src/builtins/object/tests.rs | 27 +- core/engine/src/builtins/options.rs | 4 +- core/engine/src/builtins/promise/mod.rs | 38 +- core/engine/src/builtins/proxy/mod.rs | 33 +- core/engine/src/builtins/reflect/tests.rs | 10 +- core/engine/src/builtins/regexp/mod.rs | 199 +-- .../builtins/regexp/regexp_string_iterator.rs | 6 +- core/engine/src/builtins/regexp/tests.rs | 17 +- core/engine/src/builtins/set/mod.rs | 11 +- core/engine/src/builtins/string/mod.rs | 212 +-- core/engine/src/builtins/string/tests.rs | 110 +- core/engine/src/builtins/symbol/mod.rs | 10 +- core/engine/src/builtins/symbol/tests.rs | 5 +- .../src/builtins/temporal/calendar/mod.rs | 22 +- .../src/builtins/temporal/calendar/object.rs | 46 +- .../src/builtins/temporal/duration/mod.rs | 59 +- .../src/builtins/temporal/instant/mod.rs | 35 +- core/engine/src/builtins/temporal/mod.rs | 4 +- core/engine/src/builtins/temporal/options.rs | 6 +- .../src/builtins/temporal/plain_date/mod.rs | 38 +- .../builtins/temporal/plain_date_time/mod.rs | 43 +- .../src/builtins/temporal/plain_time/mod.rs | 39 +- .../builtins/temporal/plain_year_month/mod.rs | 18 +- core/engine/src/builtins/temporal/tests.rs | 10 +- .../src/builtins/temporal/time_zone/custom.rs | 7 +- .../src/builtins/temporal/time_zone/mod.rs | 4 +- .../src/builtins/typed_array/builtin.rs | 20 +- core/engine/src/builtins/typed_array/mod.rs | 5 +- .../engine/src/builtins/typed_array/object.rs | 4 +- core/engine/src/builtins/uri/mod.rs | 26 +- core/engine/src/builtins/weak_map/mod.rs | 5 +- core/engine/src/builtins/weak_set/mod.rs | 5 +- core/engine/src/bytecompiler/declarations.rs | 6 +- .../engine/src/bytecompiler/expression/mod.rs | 20 +- core/engine/src/bytecompiler/mod.rs | 18 +- core/engine/src/class.rs | 12 +- core/engine/src/context/intrinsics.rs | 19 +- core/engine/src/context/mod.rs | 16 +- core/engine/src/error.rs | 22 +- core/engine/src/lib.rs | 11 +- core/engine/src/module/source.rs | 6 +- core/engine/src/object/builtins/jsdate.rs | 4 +- core/engine/src/object/builtins/jsmap.rs | 68 +- core/engine/src/object/builtins/jspromise.rs | 12 +- core/engine/src/object/builtins/jsproxy.rs | 34 +- .../src/object/internal_methods/string.rs | 3 +- core/engine/src/object/jsobject.rs | 32 +- core/engine/src/object/mod.rs | 11 +- .../src/optimizer/pass/constant_folding.rs | 14 +- core/engine/src/property/mod.rs | 19 +- core/engine/src/string/common.rs | 1198 ++++++++++------- core/engine/src/string/iter.rs | 95 ++ core/engine/src/string/mod.rs | 687 ++++++---- core/engine/src/string/str.rs | 358 +++++ core/engine/src/symbol.rs | 11 +- core/engine/src/tests/control_flow/loops.rs | 21 +- core/engine/src/tests/control_flow/mod.rs | 23 +- core/engine/src/tests/env.rs | 7 +- core/engine/src/tests/function.rs | 13 +- core/engine/src/tests/mod.rs | 7 +- core/engine/src/tests/operators.rs | 32 +- core/engine/src/tests/spread.rs | 9 +- core/engine/src/value/conversions/mod.rs | 10 +- .../src/value/conversions/serde_json.rs | 23 +- core/engine/src/value/display.rs | 14 +- core/engine/src/value/equality.rs | 12 +- core/engine/src/value/mod.rs | 33 +- core/engine/src/value/operations.rs | 6 +- core/engine/src/value/tests.rs | 38 +- core/engine/src/vm/flowgraph/mod.rs | 4 +- core/engine/src/vm/opcode/await/mod.rs | 5 +- core/engine/src/vm/opcode/concat/mod.rs | 5 +- .../src/vm/opcode/define/class/getter.rs | 12 +- .../src/vm/opcode/define/class/setter.rs | 12 +- core/engine/src/vm/opcode/generator/mod.rs | 6 +- core/engine/src/vm/opcode/iteration/for_in.rs | 4 +- .../src/vm/opcode/iteration/iterator.rs | 9 +- .../src/vm/opcode/push/class/private.rs | 7 +- core/engine/src/vm/opcode/set/private.rs | 5 +- core/engine/src/vm/opcode/set/property.rs | 8 +- core/engine/src/vm/opcode/templates/mod.rs | 4 +- core/engine/src/vm/tests.rs | 25 +- core/macros/src/lib.rs | 27 + core/runtime/src/console/mod.rs | 12 +- docs/boa_object.md | 31 + examples/src/bin/classes.rs | 11 +- examples/src/bin/closures.rs | 25 +- examples/src/bin/futures.rs | 6 +- examples/src/bin/host_defined.rs | 6 +- examples/src/bin/jsarray.rs | 18 +- examples/src/bin/jsarraybuffer.rs | 4 +- examples/src/bin/jsmap.rs | 2 +- examples/src/bin/jstypedarray.rs | 14 +- examples/src/bin/module_fetch.rs | 11 +- examples/src/bin/modulehandler.rs | 20 +- examples/src/bin/modules.rs | 11 +- examples/src/bin/synthetic.rs | 22 +- tests/tester/src/exec/js262.rs | 2 +- tests/tester/src/exec/mod.rs | 14 +- 151 files changed, 3778 insertions(+), 2495 deletions(-) create mode 100644 cli/src/debug/string.rs create mode 100644 core/engine/src/string/iter.rs create mode 100644 core/engine/src/string/str.rs diff --git a/cli/src/debug/function.rs b/cli/src/debug/function.rs index 8337ba708b7..71326c1294c 100644 --- a/cli/src/debug/function.rs +++ b/cli/src/debug/function.rs @@ -1,6 +1,6 @@ use boa_engine::{ builtins::function::OrdinaryFunction, - js_string, + js_str, js_string, object::ObjectInitializer, vm::flowgraph::{Direction, Graph}, Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, @@ -68,10 +68,9 @@ fn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResu let mut direction = Direction::LeftToRight; if let Some(arguments) = args.get(1) { if let Some(arguments) = arguments.as_object() { - format = flowgraph_parse_format_option(&arguments.get(js_string!("format"), context)?)?; - direction = flowgraph_parse_direction_option( - &arguments.get(js_string!("direction"), context)?, - )?; + format = flowgraph_parse_format_option(&arguments.get(js_str!("format"), context)?)?; + direction = + flowgraph_parse_direction_option(&arguments.get(js_str!("direction"), context)?)?; } else if value.is_string() { format = flowgraph_parse_format_option(value)?; } else { diff --git a/cli/src/debug/limits.rs b/cli/src/debug/limits.rs index 1f1868454e9..e2be688c745 100644 --- a/cli/src/debug/limits.rs +++ b/cli/src/debug/limits.rs @@ -1,5 +1,5 @@ use boa_engine::{ - js_string, + js_str, object::{FunctionObjectBuilder, ObjectInitializer}, property::Attribute, Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, @@ -51,51 +51,51 @@ fn set_recursion(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResu pub(super) fn create_object(context: &mut Context) -> JsObject { let get_loop = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_loop)) - .name("get loop") + .name(js_str!("get loop")) .length(0) .build(); let set_loop = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_loop)) - .name("set loop") + .name(js_str!("set loop")) .length(1) .build(); let get_stack = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_stack)) - .name("get stack") + .name(js_str!("get stack")) .length(0) .build(); let set_stack = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_stack)) - .name("set stack") + .name(js_str!("set stack")) .length(1) .build(); let get_recursion = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_recursion)) - .name("get recursion") + .name(js_str!("get recursion")) .length(0) .build(); let set_recursion = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_recursion)) - .name("set recursion") + .name(js_str!("set recursion")) .length(1) .build(); ObjectInitializer::new(context) .accessor( - js_string!("loop"), + js_str!("loop"), Some(get_loop), Some(set_loop), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, ) .accessor( - js_string!("stack"), + js_str!("stack"), Some(get_stack), Some(set_stack), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, ) .accessor( - js_string!("recursion"), + js_str!("recursion"), Some(get_recursion), Some(set_recursion), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, diff --git a/cli/src/debug/mod.rs b/cli/src/debug/mod.rs index 0e62caf3c8e..a093f76a40a 100644 --- a/cli/src/debug/mod.rs +++ b/cli/src/debug/mod.rs @@ -1,7 +1,7 @@ // Allow lint so it, doesn't warn about `JsResult<>` unneeded return on functions. #![allow(clippy::unnecessary_wraps)] -use boa_engine::{js_string, object::ObjectInitializer, property::Attribute, Context, JsObject}; +use boa_engine::{js_str, object::ObjectInitializer, property::Attribute, Context, JsObject}; mod function; mod gc; @@ -10,6 +10,7 @@ mod object; mod optimizer; mod realm; mod shape; +mod string; fn create_boa_object(context: &mut Context) -> JsObject { let function_module = function::create_object(context); @@ -19,43 +20,49 @@ fn create_boa_object(context: &mut Context) -> JsObject { let gc_module = gc::create_object(context); let realm_module = realm::create_object(context); let limits_module = limits::create_object(context); + let string_module = string::create_string(context); ObjectInitializer::new(context) .property( - js_string!("function"), + js_str!("function"), function_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - js_string!("object"), + js_str!("object"), object_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - js_string!("shape"), + js_str!("shape"), shape_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - js_string!("optimizer"), + js_str!("optimizer"), optimizer_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - js_string!("gc"), + js_str!("gc"), gc_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - js_string!("realm"), + js_str!("realm"), realm_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - js_string!("limits"), + js_str!("limits"), limits_module, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) + .property( + js_str!("string"), + string_module, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) .build() } @@ -64,7 +71,7 @@ pub(crate) fn init_boa_debug_object(context: &mut Context) { let boa_object = create_boa_object(context); context .register_global_property( - js_string!("$boa"), + js_str!("$boa"), boa_object, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) diff --git a/cli/src/debug/optimizer.rs b/cli/src/debug/optimizer.rs index 92793e93ba0..fdb93e17ec9 100644 --- a/cli/src/debug/optimizer.rs +++ b/cli/src/debug/optimizer.rs @@ -1,5 +1,5 @@ use boa_engine::{ - js_string, + js_str, object::{FunctionObjectBuilder, ObjectInitializer}, optimizer::OptimizerOptions, property::Attribute, @@ -64,13 +64,13 @@ pub(super) fn create_object(context: &mut Context) -> JsObject { .build(); ObjectInitializer::new(context) .accessor( - js_string!("constantFolding"), + js_str!("constantFolding"), Some(get_constant_folding), Some(set_constant_folding), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, ) .accessor( - js_string!("statistics"), + js_str!("statistics"), Some(get_statistics), Some(set_statistics), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, diff --git a/cli/src/debug/string.rs b/cli/src/debug/string.rs new file mode 100644 index 00000000000..ce064ed2af9 --- /dev/null +++ b/cli/src/debug/string.rs @@ -0,0 +1,93 @@ +use boa_engine::{ + js_string, object::ObjectInitializer, property::Attribute, string::JsStrVariant, Context, + JsNativeError, JsObject, JsResult, JsValue, NativeFunction, +}; + +fn storage(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { + let Some(value) = args.first() else { + return Err(JsNativeError::typ() + .with_message("expected string argument") + .into()); + }; + + let Some(string) = value.as_string() else { + return Err(JsNativeError::typ() + .with_message(format!("expected string, got {}", value.type_of())) + .into()); + }; + + let storage = if string.is_static() { "static" } else { "heap" }; + Ok(js_string!(storage).into()) +} + +fn encoding(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult { + let Some(value) = args.first() else { + return Err(JsNativeError::typ() + .with_message("expected string argument") + .into()); + }; + + let Some(string) = value.as_string() else { + return Err(JsNativeError::typ() + .with_message(format!("expected string, got {}", value.type_of())) + .into()); + }; + + let str = string.as_str(); + let encoding = match str.variant() { + JsStrVariant::Latin1(_) => "latin1", + JsStrVariant::Utf16(_) => "utf16", + }; + Ok(js_string!(encoding).into()) +} + +fn summary(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let Some(value) = args.first() else { + return Err(JsNativeError::typ() + .with_message("expected string argument") + .into()); + }; + + let Some(string) = value.as_string() else { + return Err(JsNativeError::typ() + .with_message(format!("expected string, got {}", value.type_of())) + .into()); + }; + + let storage = if string.is_static() { "static" } else { "heap" }; + let encoding = match string.as_str().variant() { + JsStrVariant::Latin1(_) => "latin1", + JsStrVariant::Utf16(_) => "utf16", + }; + + let summary = ObjectInitializer::new(context) + .property(js_string!("storage"), js_string!(storage), Attribute::all()) + .property( + js_string!("encoding"), + js_string!(encoding), + Attribute::all(), + ) + .build(); + + Ok(summary.into()) +} + +pub(super) fn create_string(context: &mut Context) -> JsObject { + ObjectInitializer::new(context) + .function( + NativeFunction::from_fn_ptr(storage), + js_string!("storage"), + 1, + ) + .function( + NativeFunction::from_fn_ptr(encoding), + js_string!("encoding"), + 1, + ) + .function( + NativeFunction::from_fn_ptr(summary), + js_string!("summary"), + 1, + ) + .build() +} diff --git a/core/engine/src/bigint.rs b/core/engine/src/bigint.rs index 367ddeda75c..87584fa2b9c 100644 --- a/core/engine/src/bigint.rs +++ b/core/engine/src/bigint.rs @@ -1,6 +1,6 @@ //! Boa's implementation of ECMAScript's bigint primitive type. -use crate::{builtins::Number, error::JsNativeError, JsData, JsResult}; +use crate::{builtins::Number, error::JsNativeError, JsData, JsResult, JsString}; use boa_gc::{Finalize, Trace}; use num_integer::Integer; use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero}; @@ -89,6 +89,22 @@ impl JsBigInt { }) } + /// Abstract operation `StringToBigInt ( str )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint + pub(crate) fn from_js_string(string: &JsString) -> Option { + // 1. Let text be ! StringToCodePoints(str). + // 2. Let literal be ParseText(text, StringIntegerLiteral). + // 3. If literal is a List of errors, return undefined. + // 4. Let mv be the MV of literal. + // 5. Assert: mv is an integer. + // 6. Return ℤ(mv). + JsBigInt::from_string(string.to_std_string().ok().as_ref()?) + } + /// This function takes a string and converts it to `BigInt` type. /// /// More information: diff --git a/core/engine/src/builtins/array/array_iterator.rs b/core/engine/src/builtins/array/array_iterator.rs index d323a7ad60a..70393e93406 100644 --- a/core/engine/src/builtins/array/array_iterator.rs +++ b/core/engine/src/builtins/array/array_iterator.rs @@ -20,6 +20,7 @@ use crate::{ Context, JsData, JsResult, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; /// The Array Iterator object represents an iteration over an array. It implements the iterator protocol. @@ -52,7 +53,7 @@ impl IntrinsicObject for ArrayIterator { .static_method(Self::next, js_string!("next"), 0) .static_property( JsSymbol::to_string_tag(), - js_string!("Array Iterator"), + js_str!("Array Iterator"), Attribute::CONFIGURABLE, ) .build(); diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index 06185d69546..aa962470408 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -10,7 +10,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array use boa_gc::{Finalize, Trace}; -use boa_macros::utf16; +use boa_macros::js_str; use boa_profiler::Profiler; use thin_vec::ThinVec; @@ -161,12 +161,12 @@ impl IntrinsicObject for Array { .method(Self::unshift, js_string!("unshift"), 1) .method(Self::with, js_string!("with"), 2) .property( - utf16!("toString"), + js_string!("toString"), to_string_function, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .property( - utf16!("values"), + js_string!("values"), values_function.clone(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) @@ -1012,7 +1012,7 @@ impl Array { for k in 0..len { // a. If k > 0, set R to the string-concatenation of R and sep. if k > 0 { - r.extend_from_slice(&separator); + r.push(separator.clone()); } // b. Let element be ? Get(O, ! ToString(𝔽(k))). let element = o.get(k, context)?; @@ -1023,7 +1023,7 @@ impl Array { element.to_string(context)? }; // d. Set R to the string-concatenation of R and next. - r.extend_from_slice(&next); + r.push(next.clone()); // e. Set k to k + 1. } // 8. Return R. @@ -1051,7 +1051,7 @@ impl Array { // 1. Let array be ? ToObject(this value). let array = this.to_object(context)?; // 2. Let func be ? Get(array, "join"). - let func = array.get(utf16!("join"), context)?; + let func = array.get(js_string!("join"), context)?; // 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%. // 4. Return ? Call(func, array). if let Some(func) = func.as_callable() { @@ -2203,12 +2203,12 @@ impl Array { #[cfg(feature = "intl")] { // TODO: this should eventually return a locale-sensitive separator. - utf16!(", ") + js_str!(", ") } #[cfg(not(feature = "intl"))] { - utf16!(", ") + js_str!(", ") } }; @@ -2221,7 +2221,7 @@ impl Array { // a. If k > 0, then if k > 0 { // i. Set R to the string-concatenation of R and separator. - r.extend_from_slice(separator); + r.extend(separator.iter()); } // b. Let nextElement be ? Get(array, ! ToString(k)). @@ -2231,16 +2231,16 @@ impl Array { if !next.is_null_or_undefined() { // i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)). let s = next - .invoke(utf16!("toLocaleString"), args, context)? + .invoke(js_str!("toLocaleString"), args, context)? .to_string(context)?; // ii. Set R to the string-concatenation of R and S. - r.extend_from_slice(&s); + r.extend(s.iter()); } // d. Increase k by 1. } // 7. Return R. - Ok(js_string!(r).into()) + Ok(js_string!(&r[..]).into()) } /// Gets the delete count of a splice operation. @@ -3304,37 +3304,37 @@ impl Array { { let mut obj = unscopable_list.borrow_mut(); // 2. Perform ! CreateDataPropertyOrThrow(unscopableList, "at", true). - obj.insert(utf16!("at"), true_prop.clone()); + obj.insert(js_str!("at"), true_prop.clone()); // 3. Perform ! CreateDataPropertyOrThrow(unscopableList, "copyWithin", true). - obj.insert(utf16!("copyWithin"), true_prop.clone()); + obj.insert(js_str!("copyWithin"), true_prop.clone()); // 4. Perform ! CreateDataPropertyOrThrow(unscopableList, "entries", true). - obj.insert(utf16!("entries"), true_prop.clone()); + obj.insert(js_str!("entries"), true_prop.clone()); // 5. Perform ! CreateDataPropertyOrThrow(unscopableList, "fill", true). - obj.insert(utf16!("fill"), true_prop.clone()); + obj.insert(js_str!("fill"), true_prop.clone()); // 6. Perform ! CreateDataPropertyOrThrow(unscopableList, "find", true). - obj.insert(utf16!("find"), true_prop.clone()); + obj.insert(js_str!("find"), true_prop.clone()); // 7. Perform ! CreateDataPropertyOrThrow(unscopableList, "findIndex", true). - obj.insert(utf16!("findIndex"), true_prop.clone()); + obj.insert(js_str!("findIndex"), true_prop.clone()); // 8. Perform ! CreateDataPropertyOrThrow(unscopableList, "findLast", true). - obj.insert(utf16!("findLast"), true_prop.clone()); + obj.insert(js_str!("findLast"), true_prop.clone()); // 9. Perform ! CreateDataPropertyOrThrow(unscopableList, "findLastIndex", true). - obj.insert(utf16!("findLastIndex"), true_prop.clone()); + obj.insert(js_str!("findLastIndex"), true_prop.clone()); // 10. Perform ! CreateDataPropertyOrThrow(unscopableList, "flat", true). - obj.insert(utf16!("flat"), true_prop.clone()); + obj.insert(js_str!("flat"), true_prop.clone()); // 11. Perform ! CreateDataPropertyOrThrow(unscopableList, "flatMap", true). - obj.insert(utf16!("flatMap"), true_prop.clone()); + obj.insert(js_str!("flatMap"), true_prop.clone()); // 12. Perform ! CreateDataPropertyOrThrow(unscopableList, "includes", true). - obj.insert(utf16!("includes"), true_prop.clone()); + obj.insert(js_str!("includes"), true_prop.clone()); // 13. Perform ! CreateDataPropertyOrThrow(unscopableList, "keys", true). - obj.insert(utf16!("keys"), true_prop.clone()); + obj.insert(js_str!("keys"), true_prop.clone()); // 14. Perform ! CreateDataPropertyOrThrow(unscopableList, "toReversed", true). - obj.insert(utf16!("toReversed"), true_prop.clone()); + obj.insert(js_str!("toReversed"), true_prop.clone()); // 15. Perform ! CreateDataPropertyOrThrow(unscopableList, "toSorted", true). - obj.insert(utf16!("toSorted"), true_prop.clone()); + obj.insert(js_str!("toSorted"), true_prop.clone()); // 16. Perform ! CreateDataPropertyOrThrow(unscopableList, "toSpliced", true). - obj.insert(utf16!("toSpliced"), true_prop.clone()); + obj.insert(js_str!("toSpliced"), true_prop.clone()); // 17. Perform ! CreateDataPropertyOrThrow(unscopableList, "values", true). - obj.insert(utf16!("values"), true_prop); + obj.insert(js_str!("values"), true_prop); } // 13. Return unscopableList. diff --git a/core/engine/src/builtins/array/tests.rs b/core/engine/src/builtins/array/tests.rs index 9f883439f54..4df066ca815 100644 --- a/core/engine/src/builtins/array/tests.rs +++ b/core/engine/src/builtins/array/tests.rs @@ -2,6 +2,7 @@ use super::Array; use crate::{ builtins::Number, js_string, run_test_actions, Context, JsNativeErrorKind, JsValue, TestAction, }; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -38,7 +39,7 @@ fn of() { TestAction::assert("arrayEquals(Array.of(), [])"), TestAction::run("let a = Array.of.call(Date, 'a', undefined, 3);"), TestAction::assert("a instanceof Date"), - TestAction::assert_eq("a[0]", js_string!("a")), + TestAction::assert_eq("a[0]", js_str!("a")), TestAction::assert_eq("a[1]", JsValue::undefined()), TestAction::assert_eq("a[2]", 3), TestAction::assert_eq("a.length", 3), @@ -75,9 +76,9 @@ fn copy_within() { fn join() { run_test_actions([ TestAction::assert_eq("[].join('.')", js_string!()), - TestAction::assert_eq("['a'].join('.')", js_string!("a")), - TestAction::assert_eq("['a', 'b', 'c'].join('.')", js_string!("a.b.c")), - TestAction::assert_eq("let a=[];a[0]=a;a[1]=a;a[2]=a;a.join()", js_string!(",,")), + TestAction::assert_eq("['a'].join('.')", js_str!("a")), + TestAction::assert_eq("['a', 'b', 'c'].join('.')", js_str!("a.b.c")), + TestAction::assert_eq("let a=[];a[0]=a;a[1]=a;a[2]=a;a.join()", js_str!(",,")), ]); } @@ -85,8 +86,8 @@ fn join() { fn to_string() { run_test_actions([ TestAction::assert_eq("[].toString()", js_string!()), - TestAction::assert_eq("['a'].toString()", js_string!("a")), - TestAction::assert_eq("['a', 'b', 'c'].toString()", js_string!("a,b,c")), + TestAction::assert_eq("['a'].toString()", js_str!("a")), + TestAction::assert_eq("['a', 'b', 'c'].toString()", js_str!("a,b,c")), ]); } @@ -116,7 +117,7 @@ fn every() { fn find() { run_test_actions([TestAction::assert_eq( "['a', 'b', 'c'].find(e => e == 'a')", - js_string!("a"), + js_str!("a"), )]); } @@ -350,7 +351,7 @@ fn fill_obj_ref() { let a = new Array(3).fill(obj); obj.hi = 'hi' "#}), - TestAction::assert_eq("a[2].hi", js_string!("hi")), + TestAction::assert_eq("a[2].hi", js_str!("hi")), ]); } diff --git a/core/engine/src/builtins/async_generator/mod.rs b/core/engine/src/builtins/async_generator/mod.rs index 2752f82185d..976dab93cfe 100644 --- a/core/engine/src/builtins/async_generator/mod.rs +++ b/core/engine/src/builtins/async_generator/mod.rs @@ -7,8 +7,10 @@ use crate::{ builtins::{ - generator::GeneratorContext, iterable::create_iter_result_object, - promise::if_abrupt_reject_promise, promise::PromiseCapability, Promise, + generator::GeneratorContext, + iterable::create_iter_result_object, + promise::{if_abrupt_reject_promise, PromiseCapability}, + Promise, }, context::intrinsics::Intrinsics, error::JsNativeError, @@ -576,7 +578,7 @@ impl AsyncGenerator { generator.clone(), ), ) - .name("") + .name(js_string!("")) .length(1) .build(); @@ -611,7 +613,7 @@ impl AsyncGenerator { generator, ), ) - .name("") + .name(js_string!("")) .length(1) .build(); diff --git a/core/engine/src/builtins/atomics/mod.rs b/core/engine/src/builtins/atomics/mod.rs index bca45095624..db74e6fbca8 100644 --- a/core/engine/src/builtins/atomics/mod.rs +++ b/core/engine/src/builtins/atomics/mod.rs @@ -21,6 +21,7 @@ use crate::{ JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{ @@ -463,9 +464,9 @@ impl Atomics { }; Ok(match result { - futex::AtomicsWaitResult::NotEqual => js_string!("not-equal"), - futex::AtomicsWaitResult::TimedOut => js_string!("timed-out"), - futex::AtomicsWaitResult::Ok => js_string!("ok"), + futex::AtomicsWaitResult::NotEqual => js_str!("not-equal"), + futex::AtomicsWaitResult::TimedOut => js_str!("timed-out"), + futex::AtomicsWaitResult::Ok => js_str!("ok"), } .into()) } diff --git a/core/engine/src/builtins/bigint/tests.rs b/core/engine/src/builtins/bigint/tests.rs index 8200f2cdef0..17ab0db1bbf 100644 --- a/core/engine/src/builtins/bigint/tests.rs +++ b/core/engine/src/builtins/bigint/tests.rs @@ -1,4 +1,6 @@ -use crate::{js_string, run_test_actions, JsBigInt, JsNativeErrorKind, TestAction}; +use boa_macros::js_str; + +use crate::{run_test_actions, JsBigInt, JsNativeErrorKind, TestAction}; #[test] fn equality() { @@ -147,10 +149,10 @@ fn operations() { #[test] fn to_string() { run_test_actions([ - TestAction::assert_eq("1000n.toString()", js_string!("1000")), - TestAction::assert_eq("1000n.toString(2)", js_string!("1111101000")), - TestAction::assert_eq("255n.toString(16)", js_string!("ff")), - TestAction::assert_eq("1000n.toString(36)", js_string!("rs")), + TestAction::assert_eq("1000n.toString()", js_str!("1000")), + TestAction::assert_eq("1000n.toString(2)", js_str!("1111101000")), + TestAction::assert_eq("255n.toString(16)", js_str!("ff")), + TestAction::assert_eq("1000n.toString(36)", js_str!("rs")), ]); } diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index eb6fc0a4aa7..d02627a9911 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -1,4 +1,4 @@ -use boa_macros::utf16; +use boa_macros::js_str; use crate::{ js_string, @@ -115,7 +115,7 @@ impl ApplyToObject for Callable { .configurable(true), ); object.insert( - utf16!("name"), + js_str!("name"), PropertyDescriptor::builder() .value(self.name) .writable(false) @@ -363,8 +363,8 @@ impl BuiltInConstructorWithPrototype<'_> { let length = self.length; let name = self.name.clone(); let prototype = self.prototype.clone(); - self = self.static_property(js_string!("length"), length, Attribute::CONFIGURABLE); - self = self.static_property(js_string!("name"), name, Attribute::CONFIGURABLE); + self = self.static_property(js_str!("length"), length, Attribute::CONFIGURABLE); + self = self.static_property(js_str!("name"), name, Attribute::CONFIGURABLE); self = self.static_property(PROTOTYPE, prototype, Attribute::empty()); let attributes = self.attributes; @@ -411,8 +411,8 @@ impl BuiltInConstructorWithPrototype<'_> { pub(crate) fn build_without_prototype(mut self) { let length = self.length; let name = self.name.clone(); - self = self.static_property(js_string!("length"), length, Attribute::CONFIGURABLE); - self = self.static_property(js_string!("name"), name, Attribute::CONFIGURABLE); + self = self.static_property(js_str!("length"), length, Attribute::CONFIGURABLE); + self = self.static_property(js_str!("name"), name, Attribute::CONFIGURABLE); let mut object = self.object.borrow_mut(); let function = object @@ -483,7 +483,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { realm, function, length: 0, - name: js_string!(""), + name: js_string!(), } } @@ -496,7 +496,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { object: I::get(realm.intrinsics()), kind: Callable { function, - name: js_string!(""), + name: js_string!(), length: 0, kind: OrdinaryFunction, realm: realm.clone(), @@ -515,7 +515,7 @@ impl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> { object, kind: Callable { function, - name: js_string!(""), + name: js_string!(), length: 0, kind: OrdinaryFunction, realm: realm.clone(), diff --git a/core/engine/src/builtins/date/mod.rs b/core/engine/src/builtins/date/mod.rs index 4987fe78275..49be23c5f88 100644 --- a/core/engine/src/builtins/date/mod.rs +++ b/core/engine/src/builtins/date/mod.rs @@ -27,12 +27,13 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, value::{JsValue, PreferredType}, Context, JsArgs, JsData, JsError, JsResult, JsString, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; pub(crate) mod utils; @@ -1416,34 +1417,40 @@ impl Date { // including all format elements and the UTC offset representation "Z". let year = year_from_time(tv); let year = if year.is_positive() && year >= 10000 { - js_string!(utf16!("+"), &pad_six(year.unsigned_abs())) + js_string!(js_str!("+"), pad_six(year.unsigned_abs(), &mut [0; 6])) } else if year.is_positive() { - JsString::from(&pad_four(year.unsigned_abs())) + pad_four(year.unsigned_abs(), &mut [0; 4]).into() } else { - js_string!(utf16!("-"), &pad_six(year.unsigned_abs())) + js_string!(js_str!("-"), pad_six(year.unsigned_abs(), &mut [0; 6])) }; - let month = pad_two(month_from_time(tv) + 1); - let day = pad_two(date_from_time(tv)); - let hour = pad_two(hour_from_time(tv)); - let minute = pad_two(min_from_time(tv)); - let second = pad_two(sec_from_time(tv)); - let millisecond = pad_three(ms_from_time(tv)); + let mut binding = [0; 2]; + let month = pad_two(month_from_time(tv) + 1, &mut binding); + let mut binding = [0; 2]; + let day = pad_two(date_from_time(tv), &mut binding); + let mut binding = [0; 2]; + let hour = pad_two(hour_from_time(tv), &mut binding); + let mut binding = [0; 2]; + let minute = pad_two(min_from_time(tv), &mut binding); + let mut binding = [0; 2]; + let second = pad_two(sec_from_time(tv), &mut binding); + let mut binding = [0; 3]; + let millisecond = pad_three(ms_from_time(tv), &mut binding); Ok(JsValue::from(js_string!( &year, - utf16!("-"), - &month, - utf16!("-"), - &day, - utf16!("T"), - &hour, - utf16!(":"), - &minute, - utf16!(":"), - &second, - utf16!("."), - &millisecond, - utf16!("Z") + js_str!("-"), + month, + js_str!("-"), + day, + js_str!("T"), + hour, + js_str!(":"), + minute, + js_str!(":"), + second, + js_str!("."), + millisecond, + js_str!("Z") ))) } @@ -1473,7 +1480,7 @@ impl Date { } // 4. Return ? Invoke(O, "toISOString"). - let func = o.get(utf16!("toISOString"), context)?; + let func = o.get(js_string!("toISOString"), context)?; func.call(this, &[], context) } @@ -1632,50 +1639,51 @@ impl Date { // 5. Let weekday be the Name of the entry in Table 63 with the Number WeekDay(tv). let weekday = match week_day(tv) { - 0 => utf16!("Sun"), - 1 => utf16!("Mon"), - 2 => utf16!("Tue"), - 3 => utf16!("Wed"), - 4 => utf16!("Thu"), - 5 => utf16!("Fri"), - 6 => utf16!("Sat"), + 0 => js_str!("Sun"), + 1 => js_str!("Mon"), + 2 => js_str!("Tue"), + 3 => js_str!("Wed"), + 4 => js_str!("Thu"), + 5 => js_str!("Fri"), + 6 => js_str!("Sat"), _ => unreachable!(), }; // 6. Let month be the Name of the entry in Table 64 with the Number MonthFromTime(tv). let month = match month_from_time(tv) { - 0 => utf16!("Jan"), - 1 => utf16!("Feb"), - 2 => utf16!("Mar"), - 3 => utf16!("Apr"), - 4 => utf16!("May"), - 5 => utf16!("Jun"), - 6 => utf16!("Jul"), - 7 => utf16!("Aug"), - 8 => utf16!("Sep"), - 9 => utf16!("Oct"), - 10 => utf16!("Nov"), - 11 => utf16!("Dec"), + 0 => js_str!("Jan"), + 1 => js_str!("Feb"), + 2 => js_str!("Mar"), + 3 => js_str!("Apr"), + 4 => js_str!("May"), + 5 => js_str!("Jun"), + 6 => js_str!("Jul"), + 7 => js_str!("Aug"), + 8 => js_str!("Sep"), + 9 => js_str!("Oct"), + 10 => js_str!("Nov"), + 11 => js_str!("Dec"), _ => unreachable!(), }; // 7. Let day be ToZeroPaddedDecimalString(ℝ(DateFromTime(tv)), 2). - let day = pad_two(date_from_time(tv)); + let mut binding = [0; 2]; + let day = pad_two(date_from_time(tv), &mut binding); // 8. Let yv be YearFromTime(tv). let yv = year_from_time(tv); // 9. If yv is +0𝔽 or yv > +0𝔽, let yearSign be the empty String; otherwise, let yearSign be "-". - let year_sign = if yv >= 0 { utf16!("") } else { utf16!("-") }; + let year_sign = if yv >= 0 { js_str!("") } else { js_str!("-") }; // 10. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4). let yv = yv.unsigned_abs(); - let padded_year = if yv >= 100_000 { - js_string!(&pad_six(yv)) + let padded_year: JsString = if yv >= 100_000 { + pad_six(yv, &mut [0; 6]).into() } else if yv >= 10000 { - js_string!(&pad_five(yv)) + pad_five(yv, &mut [0; 5]).into() } else { - js_string!(&pad_four(yv)) + pad_four(yv, &mut [0; 4]).into() }; // 11. Return the string-concatenation of @@ -1692,15 +1700,15 @@ impl Date { // and TimeString(tv). Ok(JsValue::from(js_string!( weekday, - utf16!(","), - utf16!(" "), - &day, - utf16!(" "), + js_str!(","), + js_str!(" "), + day, + js_str!(" "), month, - utf16!(" "), + js_str!(" "), year_sign, &padded_year, - utf16!(" "), + js_str!(" "), &time_string(tv) ))) } @@ -1755,12 +1763,10 @@ impl Date { let try_first = match hint.as_string() { // 3. If hint is "string" or "default", then // a. Let tryFirst be string. - Some(string) if string == utf16!("string") || string == utf16!("default") => { - PreferredType::String - } + Some(string) if string == "string" || string == "default" => PreferredType::String, // 4. Else if hint is "number", then // a. Let tryFirst be number. - Some(number) if number == utf16!("number") => PreferredType::Number, + Some(number) if number == "number" => PreferredType::Number, // 5. Else, throw a TypeError exception. _ => { return Err(JsNativeError::typ() diff --git a/core/engine/src/builtins/date/tests.rs b/core/engine/src/builtins/date/tests.rs index cf7497119d9..7950722408c 100644 --- a/core/engine/src/builtins/date/tests.rs +++ b/core/engine/src/builtins/date/tests.rs @@ -1,4 +1,5 @@ use crate::{js_string, run_test_actions, JsNativeErrorKind, TestAction}; +use boa_macros::js_str; use indoc::indoc; use time::{macros::format_description, util::local_offset, OffsetDateTime}; @@ -787,7 +788,7 @@ fn date_proto_set_utc_seconds() { fn date_proto_to_date_string() { run_test_actions([TestAction::assert_eq( "new Date(2020, 6, 8, 9, 16, 15, 779).toDateString()", - js_string!("Wed Jul 08 2020"), + js_str!("Wed Jul 08 2020"), )]); } @@ -795,7 +796,7 @@ fn date_proto_to_date_string() { fn date_proto_to_gmt_string() { run_test_actions([TestAction::assert_eq( "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toGMTString()", - js_string!("Wed, 08 Jul 2020 09:16:15 GMT"), + js_str!("Wed, 08 Jul 2020 09:16:15 GMT"), )]); } @@ -803,7 +804,7 @@ fn date_proto_to_gmt_string() { fn date_proto_to_iso_string() { run_test_actions([TestAction::assert_eq( "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toISOString()", - js_string!("2020-07-08T09:16:15.779Z"), + js_str!("2020-07-08T09:16:15.779Z"), )]); } @@ -811,7 +812,7 @@ fn date_proto_to_iso_string() { fn date_proto_to_json() { run_test_actions([TestAction::assert_eq( "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toJSON()", - js_string!("2020-07-08T09:16:15.779Z"), + js_str!("2020-07-08T09:16:15.779Z"), )]); } @@ -849,7 +850,7 @@ fn date_proto_to_time_string() { fn date_proto_to_utc_string() { run_test_actions([TestAction::assert_eq( "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toUTCString()", - js_string!("Wed, 08 Jul 2020 09:16:15 GMT"), + js_str!("Wed, 08 Jul 2020 09:16:15 GMT"), )]); } diff --git a/core/engine/src/builtins/date/utils.rs b/core/engine/src/builtins/date/utils.rs index fdabb5d0d18..b8c8f5ebaaa 100644 --- a/core/engine/src/builtins/date/utils.rs +++ b/core/engine/src/builtins/date/utils.rs @@ -1,5 +1,5 @@ -use crate::{context::HostHooks, js_string, value::IntegerOrInfinity, JsString}; -use boa_macros::utf16; +use crate::{context::HostHooks, js_string, value::IntegerOrInfinity, JsStr, JsString}; +use boa_macros::js_str; use std::{iter::Peekable, str::Chars}; use time::{macros::format_description, OffsetDateTime, PrimitiveDateTime}; @@ -498,13 +498,16 @@ pub(crate) fn time_clip(time: f64) -> f64 { /// [spec]: https://tc39.es/ecma262/#sec-timestring pub(super) fn time_string(tv: f64) -> JsString { // 1. Let hour be ToZeroPaddedDecimalString(ℝ(HourFromTime(tv)), 2). - let hour = pad_two(hour_from_time(tv)); + let mut binding = [0; 2]; + let hour = pad_two(hour_from_time(tv), &mut binding); // 2. Let minute be ToZeroPaddedDecimalString(ℝ(MinFromTime(tv)), 2). - let minute = pad_two(min_from_time(tv)); + let mut binding = [0; 2]; + let minute = pad_two(min_from_time(tv), &mut binding); - // 3. Let second be ToZeroPaddedDecimalString(ℝ(SecFromTime(tv)), 2). - let second = pad_two(sec_from_time(tv)); + // 3. Let second be ToZeroPaddedDecimalStringbindingFromTime(tv)), 2). + let mut binding = [0; 2]; + let second = pad_two(sec_from_time(tv), &mut binding); // 4. Return the string-concatenation of // hour, @@ -515,12 +518,12 @@ pub(super) fn time_string(tv: f64) -> JsString { // the code unit 0x0020 (SPACE), // and "GMT". js_string!( - &hour, - utf16!(":"), - &minute, - utf16!(":"), - &second, - utf16!(" GMT") + hour, + js_str!(":"), + minute, + js_str!(":"), + second, + js_str!(" GMT") ) } @@ -533,50 +536,51 @@ pub(super) fn time_string(tv: f64) -> JsString { pub(super) fn date_string(tv: f64) -> JsString { // 1. Let weekday be the Name of the entry in Table 63 with the Number WeekDay(tv). let weekday = match week_day(tv) { - 0 => utf16!("Sun"), - 1 => utf16!("Mon"), - 2 => utf16!("Tue"), - 3 => utf16!("Wed"), - 4 => utf16!("Thu"), - 5 => utf16!("Fri"), - 6 => utf16!("Sat"), + 0 => js_str!("Sun"), + 1 => js_str!("Mon"), + 2 => js_str!("Tue"), + 3 => js_str!("Wed"), + 4 => js_str!("Thu"), + 5 => js_str!("Fri"), + 6 => js_str!("Sat"), _ => unreachable!(), }; // 2. Let month be the Name of the entry in Table 64 with the Number MonthFromTime(tv). let month = match month_from_time(tv) { - 0 => utf16!("Jan"), - 1 => utf16!("Feb"), - 2 => utf16!("Mar"), - 3 => utf16!("Apr"), - 4 => utf16!("May"), - 5 => utf16!("Jun"), - 6 => utf16!("Jul"), - 7 => utf16!("Aug"), - 8 => utf16!("Sep"), - 9 => utf16!("Oct"), - 10 => utf16!("Nov"), - 11 => utf16!("Dec"), + 0 => js_str!("Jan"), + 1 => js_str!("Feb"), + 2 => js_str!("Mar"), + 3 => js_str!("Apr"), + 4 => js_str!("May"), + 5 => js_str!("Jun"), + 6 => js_str!("Jul"), + 7 => js_str!("Aug"), + 8 => js_str!("Sep"), + 9 => js_str!("Oct"), + 10 => js_str!("Nov"), + 11 => js_str!("Dec"), _ => unreachable!(), }; // 3. Let day be ToZeroPaddedDecimalString(ℝ(DateFromTime(tv)), 2). - let day = pad_two(date_from_time(tv)); + let mut binding = [0; 2]; + let day = pad_two(date_from_time(tv), &mut binding); // 4. Let yv be YearFromTime(tv). let yv = year_from_time(tv); // 5. If yv is +0𝔽 or yv > +0𝔽, let yearSign be the empty String; otherwise, let yearSign be "-". - let year_sign = if yv >= 0 { utf16!("") } else { utf16!("-") }; + let year_sign = if yv >= 0 { js_str!("") } else { js_str!("-") }; // 6. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4). let yv = yv.unsigned_abs(); - let padded_year = if yv >= 100_000 { - js_string!(&pad_six(yv)) + let padded_year: JsString = if yv >= 100_000 { + pad_six(yv, &mut [0; 6]).into() } else if yv >= 10000 { - js_string!(&pad_five(yv)) + pad_five(yv, &mut [0; 5]).into() } else { - js_string!(&pad_four(yv)) + pad_four(yv, &mut [0; 4]).into() }; // 7. Return the string-concatenation of @@ -590,11 +594,11 @@ pub(super) fn date_string(tv: f64) -> JsString { // and paddedYear. js_string!( weekday, - utf16!(" "), + js_str!(" "), month, - utf16!(" "), - &day, - utf16!(" "), + js_str!(" "), + day, + js_str!(" "), year_sign, &padded_year ) @@ -620,26 +624,28 @@ pub(super) fn time_zone_string(t: f64, hooks: &dyn HostHooks) -> JsString { let (offset_sign, abs_offset) = if offset >= 0.0 { // a. Let offsetSign be "+". // b. Let absOffset be offset. - (utf16!("+"), offset) + (js_str!("+"), offset) } // 6. Else, else { // a. Let offsetSign be "-". // b. Let absOffset be -offset. - (utf16!("-"), -offset) + (js_str!("-"), -offset) }; // 7. Let offsetMin be ToZeroPaddedDecimalString(ℝ(MinFromTime(absOffset)), 2). - let offset_min = pad_two(min_from_time(abs_offset)); + let mut binding = [0; 2]; + let offset_min = pad_two(min_from_time(abs_offset), &mut binding); // 8. Let offsetHour be ToZeroPaddedDecimalString(ℝ(HourFromTime(absOffset)), 2). - let offset_hour = pad_two(hour_from_time(abs_offset)); + let mut binding = [0; 2]; + let offset_hour = pad_two(hour_from_time(abs_offset), &mut binding); // 9. Let tzName be an implementation-defined string that is either the empty String or the // string-concatenation of the code unit 0x0020 (SPACE), the code unit 0x0028 (LEFT PARENTHESIS), // an implementation-defined timezone name, and the code unit 0x0029 (RIGHT PARENTHESIS). // 10. Return the string-concatenation of offsetSign, offsetHour, offsetMin, and tzName. - js_string!(offset_sign, &offset_hour, &offset_min) + js_string!(offset_sign, offset_hour, offset_min) } /// Abstract operation `ToDateString ( tv )` @@ -651,7 +657,7 @@ pub(super) fn time_zone_string(t: f64, hooks: &dyn HostHooks) -> JsString { pub(super) fn to_date_string_t(tv: f64, hooks: &dyn HostHooks) -> JsString { // 1. If tv is NaN, return "Invalid Date". if tv.is_nan() { - return JsString::from("Invalid Date"); + return js_string!("Invalid Date"); } // 2. Let t be LocalTime(tv). @@ -664,7 +670,7 @@ pub(super) fn to_date_string_t(tv: f64, hooks: &dyn HostHooks) -> JsString { // and TimeZoneString(tv). js_string!( &date_string(t), - utf16!(" "), + js_str!(" "), &time_string(t), &time_zone_string(t, hooks) ) @@ -676,46 +682,61 @@ fn local_timezone_offset_seconds(t: f64, hooks: &dyn HostHooks) -> i32 { hooks.local_timezone_offset_seconds(seconds) } -pub(super) fn pad_two(t: u8) -> [u16; 2] { - if t < 10 { - [0x30, 0x30 + u16::from(t)] +pub(super) fn pad_two(t: u8, output: &mut [u8; 2]) -> JsStr<'_> { + *output = if t < 10 { + [b'0', b'0' + t] } else { - [0x30 + (u16::from(t) / 10), 0x30 + (u16::from(t) % 10)] - } + [b'0' + (t / 10), b'0' + (t % 10)] + }; + debug_assert!(output.is_ascii()); + + JsStr::latin1(output) } -pub(super) fn pad_three(t: u16) -> [u16; 3] { - [0x30 + t / 100, 0x30 + ((t / 10) % 10), 0x30 + (t % 10)] +pub(super) fn pad_three(t: u16, output: &mut [u8; 3]) -> JsStr<'_> { + *output = [ + b'0' + (t / 100) as u8, + b'0' + ((t / 10) % 10) as u8, + b'0' + (t % 10) as u8, + ]; + + JsStr::latin1(output) } -pub(super) fn pad_four(t: u32) -> [u16; 4] { - [ - 0x30 + (t / 1000) as u16, - 0x30 + ((t / 100) % 10) as u16, - 0x30 + ((t / 10) % 10) as u16, - 0x30 + (t % 10) as u16, - ] +pub(super) fn pad_four(t: u32, output: &mut [u8; 4]) -> JsStr<'_> { + *output = [ + b'0' + (t / 1000) as u8, + b'0' + ((t / 100) % 10) as u8, + b'0' + ((t / 10) % 10) as u8, + b'0' + (t % 10) as u8, + ]; + + JsStr::latin1(output) } -pub(super) fn pad_five(t: u32) -> [u16; 5] { - [ - 0x30 + (t / 10_000) as u16, - 0x30 + ((t / 1000) % 10) as u16, - 0x30 + ((t / 100) % 10) as u16, - 0x30 + ((t / 10) % 10) as u16, - 0x30 + (t % 10) as u16, - ] +pub(super) fn pad_five(t: u32, output: &mut [u8; 5]) -> JsStr<'_> { + *output = [ + b'0' + (t / 10_000) as u8, + b'0' + ((t / 1000) % 10) as u8, + b'0' + ((t / 100) % 10) as u8, + b'0' + ((t / 10) % 10) as u8, + b'0' + (t % 10) as u8, + ]; + + JsStr::latin1(output) } -pub(super) fn pad_six(t: u32) -> [u16; 6] { - [ - 0x30 + (t / 100_000) as u16, - 0x30 + ((t / 10_000) % 10) as u16, - 0x30 + ((t / 1000) % 10) as u16, - 0x30 + ((t / 100) % 10) as u16, - 0x30 + ((t / 10) % 10) as u16, - 0x30 + (t % 10) as u16, - ] +pub(super) fn pad_six(t: u32, output: &mut [u8; 6]) -> JsStr<'_> { + *output = [ + b'0' + (t / 100_000) as u8, + b'0' + ((t / 10_000) % 10) as u8, + b'0' + ((t / 1000) % 10) as u8, + b'0' + ((t / 100) % 10) as u8, + b'0' + ((t / 10) % 10) as u8, + b'0' + (t % 10) as u8, + ]; + + JsStr::latin1(output) } /// Parse a date string according to the steps specified in [`Date.parse`][spec]. diff --git a/core/engine/src/builtins/error/aggregate.rs b/core/engine/src/builtins/error/aggregate.rs index 8abeefd3bc3..bc4b9c32928 100644 --- a/core/engine/src/builtins/error/aggregate.rs +++ b/core/engine/src/builtins/error/aggregate.rs @@ -17,9 +17,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyDescriptorBuilder}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -35,8 +36,8 @@ impl IntrinsicObject for AggregateError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -95,7 +96,7 @@ impl BuiltInConstructor for AggregateError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). @@ -112,7 +113,7 @@ impl BuiltInConstructor for AggregateError { // [[Value]]: CreateArrayFromList(errorsList) // }). o.define_property_or_throw( - utf16!("errors"), + js_str!("errors"), PropertyDescriptorBuilder::new() .configurable(true) .enumerable(false) diff --git a/core/engine/src/builtins/error/eval.rs b/core/engine/src/builtins/error/eval.rs index ece3277f4a4..266fd820530 100644 --- a/core/engine/src/builtins/error/eval.rs +++ b/core/engine/src/builtins/error/eval.rs @@ -18,9 +18,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -37,8 +38,8 @@ impl IntrinsicObject for EvalError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -94,7 +95,7 @@ impl BuiltInConstructor for EvalError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). diff --git a/core/engine/src/builtins/error/mod.rs b/core/engine/src/builtins/error/mod.rs index ebc57adee12..ed005d0e380 100644 --- a/core/engine/src/builtins/error/mod.rs +++ b/core/engine/src/builtins/error/mod.rs @@ -18,10 +18,11 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; pub(crate) mod aggregate; @@ -136,8 +137,8 @@ impl IntrinsicObject for Error { let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; BuiltInBuilder::from_standard_constructor::(realm) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_string!("name"), Self::NAME, attribute) + .property(js_string!("message"), js_string!(), attribute) .method(Self::to_string, js_string!("toString"), 0) .build(); } @@ -191,7 +192,7 @@ impl BuiltInConstructor for Error { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_string!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). @@ -210,12 +211,12 @@ impl Error { ) -> JsResult<()> { // 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then if let Some(options) = options.as_object() { - if options.has_property(utf16!("cause"), context)? { + if options.has_property(js_str!("cause"), context)? { // a. Let cause be ? Get(options, "cause"). - let cause = options.get(utf16!("cause"), context)?; + let cause = options.get(js_str!("cause"), context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). - o.create_non_enumerable_data_property_or_throw(utf16!("cause"), cause, context); + o.create_non_enumerable_data_property_or_throw(js_str!("cause"), cause, context); } } @@ -246,7 +247,7 @@ impl Error { .ok_or_else(|| JsNativeError::typ().with_message("'this' is not an Object"))?; // 3. Let name be ? Get(O, "name"). - let name = o.get(js_string!("name"), context)?; + let name = o.get(js_str!("name"), context)?; // 4. If name is undefined, set name to "Error"; otherwise set name to ? ToString(name). let name = if name.is_undefined() { @@ -256,7 +257,7 @@ impl Error { }; // 5. Let msg be ? Get(O, "message"). - let msg = o.get(js_string!("message"), context)?; + let msg = o.get(js_str!("message"), context)?; // 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg). let msg = if msg.is_undefined() { @@ -277,6 +278,6 @@ impl Error { // 9. Return the string-concatenation of name, the code unit 0x003A (COLON), // the code unit 0x0020 (SPACE), and msg. - Ok(js_string!(&name, utf16!(": "), &msg).into()) + Ok(js_string!(&name, js_str!(": "), &msg).into()) } } diff --git a/core/engine/src/builtins/error/range.rs b/core/engine/src/builtins/error/range.rs index 985269df825..cc192396c33 100644 --- a/core/engine/src/builtins/error/range.rs +++ b/core/engine/src/builtins/error/range.rs @@ -16,9 +16,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -35,8 +36,8 @@ impl IntrinsicObject for RangeError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -92,7 +93,7 @@ impl BuiltInConstructor for RangeError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). diff --git a/core/engine/src/builtins/error/reference.rs b/core/engine/src/builtins/error/reference.rs index 5785bda863a..eb8a1158623 100644 --- a/core/engine/src/builtins/error/reference.rs +++ b/core/engine/src/builtins/error/reference.rs @@ -16,9 +16,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -34,8 +35,8 @@ impl IntrinsicObject for ReferenceError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(js_string!("name"), Self::NAME, attribute) - .property(js_string!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -94,7 +95,7 @@ impl BuiltInConstructor for ReferenceError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). diff --git a/core/engine/src/builtins/error/syntax.rs b/core/engine/src/builtins/error/syntax.rs index cf35acc44c0..9074856694d 100644 --- a/core/engine/src/builtins/error/syntax.rs +++ b/core/engine/src/builtins/error/syntax.rs @@ -18,9 +18,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -37,8 +38,8 @@ impl IntrinsicObject for SyntaxError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -97,7 +98,7 @@ impl BuiltInConstructor for SyntaxError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). diff --git a/core/engine/src/builtins/error/tests.rs b/core/engine/src/builtins/error/tests.rs index ea3950b3041..f5e4a060512 100644 --- a/core/engine/src/builtins/error/tests.rs +++ b/core/engine/src/builtins/error/tests.rs @@ -1,42 +1,34 @@ -use crate::{js_string, run_test_actions, TestAction}; +use crate::{run_test_actions, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] fn error_to_string() { run_test_actions([ - TestAction::assert_eq("(new Error('1')).toString()", js_string!("Error: 1")), - TestAction::assert_eq( - "(new RangeError('2')).toString()", - js_string!("RangeError: 2"), - ), + TestAction::assert_eq("(new Error('1')).toString()", js_str!("Error: 1")), + TestAction::assert_eq("(new RangeError('2')).toString()", js_str!("RangeError: 2")), TestAction::assert_eq( "(new ReferenceError('3')).toString()", - js_string!("ReferenceError: 3"), + js_str!("ReferenceError: 3"), ), TestAction::assert_eq( "(new SyntaxError('4')).toString()", - js_string!("SyntaxError: 4"), - ), - TestAction::assert_eq( - "(new TypeError('5')).toString()", - js_string!("TypeError: 5"), - ), - TestAction::assert_eq( - "(new EvalError('6')).toString()", - js_string!("EvalError: 6"), + js_str!("SyntaxError: 4"), ), - TestAction::assert_eq("(new URIError('7')).toString()", js_string!("URIError: 7")), + TestAction::assert_eq("(new TypeError('5')).toString()", js_str!("TypeError: 5")), + TestAction::assert_eq("(new EvalError('6')).toString()", js_str!("EvalError: 6")), + TestAction::assert_eq("(new URIError('7')).toString()", js_str!("URIError: 7")), // no message - TestAction::assert_eq("(new Error()).toString()", js_string!("Error")), - TestAction::assert_eq("(new RangeError()).toString()", js_string!("RangeError")), + TestAction::assert_eq("(new Error()).toString()", js_str!("Error")), + TestAction::assert_eq("(new RangeError()).toString()", js_str!("RangeError")), TestAction::assert_eq( "(new ReferenceError()).toString()", - js_string!("ReferenceError"), + js_str!("ReferenceError"), ), - TestAction::assert_eq("(new SyntaxError()).toString()", js_string!("SyntaxError")), - TestAction::assert_eq("(new TypeError()).toString()", js_string!("TypeError")), - TestAction::assert_eq("(new EvalError()).toString()", js_string!("EvalError")), - TestAction::assert_eq("(new URIError()).toString()", js_string!("URIError")), + TestAction::assert_eq("(new SyntaxError()).toString()", js_str!("SyntaxError")), + TestAction::assert_eq("(new TypeError()).toString()", js_str!("TypeError")), + TestAction::assert_eq("(new EvalError()).toString()", js_str!("EvalError")), + TestAction::assert_eq("(new URIError()).toString()", js_str!("URIError")), // no name TestAction::assert_eq( indoc! {r#" @@ -44,7 +36,7 @@ fn error_to_string() { message.name = ''; message.toString() "#}, - js_string!("message"), + js_str!("message"), ), ]); } @@ -52,14 +44,14 @@ fn error_to_string() { #[test] fn error_names() { run_test_actions([ - TestAction::assert_eq("Error.name", js_string!("Error")), - TestAction::assert_eq("EvalError.name", js_string!("EvalError")), - TestAction::assert_eq("RangeError.name", js_string!("RangeError")), - TestAction::assert_eq("ReferenceError.name", js_string!("ReferenceError")), - TestAction::assert_eq("SyntaxError.name", js_string!("SyntaxError")), - TestAction::assert_eq("URIError.name", js_string!("URIError")), - TestAction::assert_eq("TypeError.name", js_string!("TypeError")), - TestAction::assert_eq("AggregateError.name", js_string!("AggregateError")), + TestAction::assert_eq("Error.name", js_str!("Error")), + TestAction::assert_eq("EvalError.name", js_str!("EvalError")), + TestAction::assert_eq("RangeError.name", js_str!("RangeError")), + TestAction::assert_eq("ReferenceError.name", js_str!("ReferenceError")), + TestAction::assert_eq("SyntaxError.name", js_str!("SyntaxError")), + TestAction::assert_eq("URIError.name", js_str!("URIError")), + TestAction::assert_eq("TypeError.name", js_str!("TypeError")), + TestAction::assert_eq("AggregateError.name", js_str!("AggregateError")), ]); } diff --git a/core/engine/src/builtins/error/type.rs b/core/engine/src/builtins/error/type.rs index 02fea36f851..3b3223d0fb0 100644 --- a/core/engine/src/builtins/error/type.rs +++ b/core/engine/src/builtins/error/type.rs @@ -24,9 +24,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, NativeFunction, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -43,8 +44,8 @@ impl IntrinsicObject for TypeError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -100,7 +101,7 @@ impl BuiltInConstructor for TypeError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). @@ -119,7 +120,7 @@ impl IntrinsicObject for ThrowTypeError { let obj = BuiltInBuilder::with_intrinsic::(realm) .prototype(realm.intrinsics().constructors().function().prototype()) .static_property(StaticJsStrings::LENGTH, 0, Attribute::empty()) - .static_property(utf16!("name"), js_string!(), Attribute::empty()) + .static_property(js_str!("name"), js_string!(), Attribute::empty()) .build(); let mut obj = obj.borrow_mut(); diff --git a/core/engine/src/builtins/error/uri.rs b/core/engine/src/builtins/error/uri.rs index 73e41ee9e45..fcf7b1d208b 100644 --- a/core/engine/src/builtins/error/uri.rs +++ b/core/engine/src/builtins/error/uri.rs @@ -17,9 +17,10 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use super::{Error, ErrorObject}; @@ -36,8 +37,8 @@ impl IntrinsicObject for UriError { BuiltInBuilder::from_standard_constructor::(realm) .prototype(realm.intrinsics().constructors().error().constructor()) .inherits(Some(realm.intrinsics().constructors().error().prototype())) - .property(utf16!("name"), Self::NAME, attribute) - .property(utf16!("message"), js_string!(), attribute) + .property(js_str!("name"), Self::NAME, attribute) + .property(js_str!("message"), js_string!(), attribute) .build(); } @@ -93,7 +94,7 @@ impl BuiltInConstructor for UriError { let msg = message.to_string(context)?; // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - o.create_non_enumerable_data_property_or_throw(utf16!("message"), msg, context); + o.create_non_enumerable_data_property_or_throw(js_str!("message"), msg, context); } // 4. Perform ? InstallErrorCause(O, options). diff --git a/core/engine/src/builtins/escape/mod.rs b/core/engine/src/builtins/escape/mod.rs index fa8c8552041..e3fcf1cb2b0 100644 --- a/core/engine/src/builtins/escape/mod.rs +++ b/core/engine/src/builtins/escape/mod.rs @@ -59,7 +59,7 @@ fn escape(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult JsResult JsResult>::new(string.iter().copied()); + let mut codepoints = >::new(string.iter()); // 2. Let len be the length of string. // 4. Let k be 0. @@ -192,7 +192,7 @@ fn unescape(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult + let parameters = itertools::Itertools::intersperse( + param_list.iter().map(JsString::iter), + js_str!(",").iter(), + ) + .flatten() + .collect::>(); let mut parser = Parser::new(Source::from_utf16(¶meters)); parser.set_identifier(context.next_parser_identifier()); @@ -518,7 +529,7 @@ impl BuiltInFunctionObject { // 14. Let bodyParseString be the string-concatenation of 0x000A (LINE FEED), bodyString, and 0x000A (LINE FEED). let mut body_parse = Vec::with_capacity(body.len()); body_parse.push(u16::from(b'\n')); - body_parse.extend_from_slice(&body); + body_parse.extend(body.iter()); body_parse.push(u16::from(b'\n')); // 19. Let body be ParseText(StringToCodePoints(bodyParseString), bodySym). @@ -743,7 +754,7 @@ impl BuiltInFunctionObject { .expect("defining the `length` property for a new object should not fail"); // 8. Let targetName be ? Get(Target, "name"). - let target_name = target.get(utf16!("name"), context)?; + let target_name = target.get(js_str!("name"), context)?; // 9. If Type(targetName) is not String, set targetName to the empty String. let target_name = target_name @@ -751,7 +762,7 @@ impl BuiltInFunctionObject { .map_or_else(JsString::default, Clone::clone); // 10. Perform SetFunctionName(F, targetName, "bound"). - set_function_name(&f, &target_name.into(), Some(js_string!("bound")), context); + set_function_name(&f, &target_name.into(), Some(js_str!("bound")), context); // 11. Return F. Ok(f.into()) @@ -816,7 +827,7 @@ impl BuiltInFunctionObject { let name = { // Is there a case here where if there is no name field on a value // name should default to None? Do all functions have names set? - let value = object.get(utf16!("name"), &mut *context)?; + let value = object.get(js_str!("name"), &mut *context)?; if value.is_null_or_undefined() { js_string!() } else { @@ -824,10 +835,10 @@ impl BuiltInFunctionObject { } }; return Ok( - js_string!(utf16!("function "), &name, utf16!("() { [native code] }")).into(), + js_string!(js_str!("function "), &name, js_str!("() { [native code] }")).into(), ); } else if object_borrow.is::() || object_borrow.is::() { - return Ok(js_string!(utf16!("function () { [native code] }")).into()); + return Ok(js_string!("function () { [native code] }").into()); } let function = object_borrow @@ -837,9 +848,9 @@ impl BuiltInFunctionObject { let code = function.codeblock(); Ok(js_string!( - utf16!("function "), + js_str!("function "), code.name(), - utf16!("() { [native code] }") + js_str!("() { [native code] }") ) .into()) } @@ -871,7 +882,7 @@ impl BuiltInFunctionObject { pub(crate) fn set_function_name( function: &JsObject, name: &PropertyKey, - prefix: Option, + prefix: Option>, context: &mut Context, ) { // 1. Assert: F is an extensible object that does not have a "name" own property. @@ -883,7 +894,7 @@ pub(crate) fn set_function_name( // c. Else, set name to the string-concatenation of "[", description, and "]". sym.description().map_or_else( || js_string!(), - |desc| js_string!(utf16!("["), &desc, utf16!("]")), + |desc| js_string!(js_str!("["), &desc, js_str!("]")), ) } PropertyKey::String(string) => string.clone(), @@ -900,7 +911,7 @@ pub(crate) fn set_function_name( // 5. If prefix is present, then if let Some(prefix) = prefix { - name = js_string!(&prefix, utf16!(" "), &name); + name = js_string!(prefix, js_str!(" "), &name); // b. If F has an [[InitialName]] internal slot, then // i. Optionally, set F.[[InitialName]] to name. // todo: implement [[InitialName]] for builtins @@ -910,7 +921,7 @@ pub(crate) fn set_function_name( // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). function .define_property_or_throw( - utf16!("name"), + js_str!("name"), PropertyDescriptor::builder() .value(name) .writable(false) diff --git a/core/engine/src/builtins/function/tests.rs b/core/engine/src/builtins/function/tests.rs index d28f663a052..fc59ced992c 100644 --- a/core/engine/src/builtins/function/tests.rs +++ b/core/engine/src/builtins/function/tests.rs @@ -6,6 +6,7 @@ use crate::{ property::{Attribute, PropertyDescriptor}, run_test_actions, JsNativeErrorKind, JsValue, TestAction, }; +use boa_macros::js_str; use indoc::indoc; #[allow(clippy::float_cmp)] @@ -69,7 +70,7 @@ fn function_prototype() { fn function_prototype_call() { run_test_actions([TestAction::assert_eq( "Object.prototype.toString.call(new Error())", - js_string!("[object Error]"), + js_str!("[object Error]"), )]); } @@ -168,13 +169,13 @@ fn closure_capture_clone() { (string, object), ), ) - .name("closure") + .name(js_str!("closure")) .build(); - ctx.register_global_property(js_string!("closure"), func, Attribute::default()) + ctx.register_global_property(js_str!("closure"), func, Attribute::default()) .unwrap(); }), - TestAction::assert_eq("closure()", js_string!("Hello world!")), + TestAction::assert_eq("closure()", js_str!("Hello world!")), ]); } diff --git a/core/engine/src/builtins/intl/collator/mod.rs b/core/engine/src/builtins/intl/collator/mod.rs index b1817b762e1..a99aec57a68 100644 --- a/core/engine/src/builtins/intl/collator/mod.rs +++ b/core/engine/src/builtins/intl/collator/mod.rs @@ -1,4 +1,5 @@ use boa_gc::{custom_trace, Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use icu_collator::{ provider::CollationMetadataV1Marker, AlternateHandling, CaseFirst, Collator as NativeCollator, @@ -28,7 +29,7 @@ use crate::{ }, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsData, JsNativeError, JsResult, JsString, JsValue, }; @@ -241,28 +242,28 @@ impl BuiltInConstructor for Collator { // a. Let localeData be %Collator%.[[SortLocaleData]]. // 6. Else, // a. Let localeData be %Collator%.[[SearchLocaleData]]. - let usage = get_option(&options, utf16!("usage"), context)?.unwrap_or_default(); + let usage = get_option(&options, js_str!("usage"), context)?.unwrap_or_default(); // 7. Let opt be a new Record. // 8. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 9. Set opt.[[localeMatcher]] to matcher. - let matcher = get_option(&options, utf16!("localeMatcher"), context)?.unwrap_or_default(); + let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); // 10. Let collation be ? GetOption(options, "collation", string, empty, undefined). // 11. If collation is not undefined, then // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. // 12. Set opt.[[co]] to collation. - let collation = get_option(&options, utf16!("collation"), context)?; + let collation = get_option(&options, js_str!("collation"), context)?; // 13. Let numeric be ? GetOption(options, "numeric", boolean, empty, undefined). // 14. If numeric is not undefined, then // a. Let numeric be ! ToString(numeric). // 15. Set opt.[[kn]] to numeric. - let numeric = get_option(&options, utf16!("numeric"), context)?; + let numeric = get_option(&options, js_str!("numeric"), context)?; // 16. Let caseFirst be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined). // 17. Set opt.[[kf]] to caseFirst. - let case_first = get_option(&options, utf16!("caseFirst"), context)?; + let case_first = get_option(&options, js_str!("caseFirst"), context)?; let mut intl_options = IntlOptions { matcher, @@ -314,7 +315,7 @@ impl BuiltInConstructor for Collator { // 26. Let sensitivity be ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », undefined). // 28. Set collator.[[Sensitivity]] to sensitivity. - let sensitivity = get_option(&options, utf16!("sensitivity"), context)? + let sensitivity = get_option(&options, js_str!("sensitivity"), context)? // 27. If sensitivity is undefined, then // a. If usage is "sort", then // i. Let sensitivity be "variant". @@ -327,7 +328,7 @@ impl BuiltInConstructor for Collator { // 29. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", boolean, empty, false). // 30. Set collator.[[IgnorePunctuation]] to ignorePunctuation. let ignore_punctuation: bool = - get_option(&options, utf16!("ignorePunctuation"), context)?.unwrap_or_default(); + get_option(&options, js_str!("ignorePunctuation"), context)?.unwrap_or_default(); let (strength, case_level) = sensitivity.map(Sensitivity::to_collator_options).unzip(); @@ -442,13 +443,22 @@ impl Collator { // 3. If x is not provided, let x be undefined. // 5. Let X be ? ToString(x). - let x = args.get_or_undefined(0).to_string(context)?; + let x = args + .get_or_undefined(0) + .to_string(context)? + .iter() + .collect::>(); // 4. If y is not provided, let y be undefined. // 6. Let Y be ? ToString(y). - let y = args.get_or_undefined(1).to_string(context)?; + let y = args + .get_or_undefined(1) + .to_string(context)? + .iter() + .collect::>(); // 7. Return CompareStrings(collator, X, Y). + let result = collator.collator.compare_utf16(&x, &y) as i32; Ok(result.into()) @@ -507,58 +517,58 @@ impl Collator { // 5. Return options. options .create_data_property_or_throw( - utf16!("locale"), + js_str!("locale"), js_string!(collator.locale.to_string()), context, ) .expect("operation must not fail per the spec"); options .create_data_property_or_throw( - utf16!("usage"), + js_str!("usage"), match collator.usage { - Usage::Search => js_string!("search"), - Usage::Sort => js_string!("sort"), + Usage::Search => js_str!("search"), + Usage::Sort => js_str!("sort"), }, context, ) .expect("operation must not fail per the spec"); options .create_data_property_or_throw( - utf16!("sensitivity"), + js_str!("sensitivity"), match collator.sensitivity { - Sensitivity::Base => js_string!("base"), - Sensitivity::Accent => js_string!("accent"), - Sensitivity::Case => js_string!("case"), - Sensitivity::Variant => js_string!("variant"), + Sensitivity::Base => js_str!("base"), + Sensitivity::Accent => js_str!("accent"), + Sensitivity::Case => js_str!("case"), + Sensitivity::Variant => js_str!("variant"), }, context, ) .expect("operation must not fail per the spec"); options .create_data_property_or_throw( - js_string!("ignorePunctuation"), + js_str!("ignorePunctuation"), collator.ignore_punctuation, context, ) .expect("operation must not fail per the spec"); options .create_data_property_or_throw( - js_string!("collation"), + js_str!("collation"), js_string!(collator.collation.to_string()), context, ) .expect("operation must not fail per the spec"); options - .create_data_property_or_throw(utf16!("numeric"), collator.numeric, context) + .create_data_property_or_throw(js_str!("numeric"), collator.numeric, context) .expect("operation must not fail per the spec"); if let Some(kf) = collator.case_first { options .create_data_property_or_throw( - js_string!("caseFirst"), + js_str!("caseFirst"), match kf { - CaseFirst::Off => js_string!("false"), - CaseFirst::LowerFirst => js_string!("lower"), - CaseFirst::UpperFirst => js_string!("upper"), + CaseFirst::Off => js_str!("false"), + CaseFirst::LowerFirst => js_str!("lower"), + CaseFirst::UpperFirst => js_str!("upper"), _ => unreachable!(), }, context, diff --git a/core/engine/src/builtins/intl/date_time_format.rs b/core/engine/src/builtins/intl/date_time_format.rs index 01c8d84fc86..0dff2fe4dd8 100644 --- a/core/engine/src/builtins/intl/date_time_format.rs +++ b/core/engine/src/builtins/intl/date_time_format.rs @@ -17,11 +17,12 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsData, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use icu_datetime::options::preferences::HourCycle; @@ -205,10 +206,10 @@ pub(crate) fn to_date_time_options( if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(required) { // a. For each property name prop of « "weekday", "year", "month", "day" », do for property in [ - utf16!("weekday"), - utf16!("year"), - utf16!("month"), - utf16!("day"), + js_str!("weekday"), + js_str!("year"), + js_str!("month"), + js_str!("day"), ] { // i. Let value be ? Get(options, prop). let value = options.get(property, context)?; @@ -225,11 +226,11 @@ pub(crate) fn to_date_time_options( // a. For each property name prop of « "dayPeriod", "hour", "minute", "second", // "fractionalSecondDigits" », do for property in [ - utf16!("dayPeriod"), - utf16!("hour"), - utf16!("minute"), - utf16!("second"), - utf16!("fractionalSecondDigits"), + js_str!("dayPeriod"), + js_str!("hour"), + js_str!("minute"), + js_str!("second"), + js_str!("fractionalSecondDigits"), ] { // i. Let value be ? Get(options, prop). let value = options.get(property, context)?; @@ -242,10 +243,10 @@ pub(crate) fn to_date_time_options( } // 6. Let dateStyle be ? Get(options, "dateStyle"). - let date_style = options.get(utf16!("dateStyle"), context)?; + let date_style = options.get(js_str!("dateStyle"), context)?; // 7. Let timeStyle be ? Get(options, "timeStyle"). - let time_style = options.get(utf16!("timeStyle"), context)?; + let time_style = options.get(js_str!("timeStyle"), context)?; // 8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false. if !date_style.is_undefined() || !time_style.is_undefined() { @@ -271,18 +272,18 @@ pub(crate) fn to_date_time_options( // 11. If needDefaults is true and defaults is either "date" or "all", then if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(defaults) { // a. For each property name prop of « "year", "month", "day" », do - for property in [utf16!("year"), utf16!("month"), utf16!("day")] { + for property in [js_str!("year"), js_str!("month"), js_str!("day")] { // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). - options.create_data_property_or_throw(property, js_string!("numeric"), context)?; + options.create_data_property_or_throw(property, js_str!("numeric"), context)?; } } // 12. If needDefaults is true and defaults is either "time" or "all", then if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(defaults) { // a. For each property name prop of « "hour", "minute", "second" », do - for property in [utf16!("hour"), utf16!("minute"), utf16!("second")] { + for property in [js_str!("hour"), js_str!("minute"), js_str!("second")] { // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). - options.create_data_property_or_throw(property, js_string!("numeric"), context)?; + options.create_data_property_or_throw(property, js_str!("numeric"), context)?; } } diff --git a/core/engine/src/builtins/intl/list_format/mod.rs b/core/engine/src/builtins/intl/list_format/mod.rs index 55c2105322e..c76889ffaed 100644 --- a/core/engine/src/builtins/intl/list_format/mod.rs +++ b/core/engine/src/builtins/intl/list_format/mod.rs @@ -1,6 +1,7 @@ use std::fmt::Write; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use icu_list::{provider::AndListV1Marker, ListFormatter, ListLength}; use icu_locid::Locale; @@ -16,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsData, JsNativeError, JsResult, JsString, JsValue, }; @@ -114,7 +115,7 @@ impl BuiltInConstructor for ListFormat { // 5. Let opt be a new Record. // 6. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). - let matcher = get_option(&options, utf16!("localeMatcher"), context)?.unwrap_or_default(); + let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); // 7. Set opt.[[localeMatcher]] to matcher. // 8. Let localeData be %ListFormat%.[[LocaleData]]. @@ -131,11 +132,11 @@ impl BuiltInConstructor for ListFormat { // 11. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction"). // 12. Set listFormat.[[Type]] to type. - let typ = get_option(&options, utf16!("type"), context)?.unwrap_or_default(); + let typ = get_option(&options, js_str!("type"), context)?.unwrap_or_default(); // 13. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long"). // 14. Set listFormat.[[Style]] to style. - let style = get_option(&options, utf16!("style"), context)?.unwrap_or(ListLength::Wide); + let style = get_option(&options, js_str!("style"), context)?.unwrap_or(ListLength::Wide); // 15. Let dataLocale be r.[[dataLocale]]. // 16. Let dataLocaleData be localeData.[[]]. @@ -380,11 +381,11 @@ impl ListFormat { .create(OrdinaryObject, vec![]); // b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]). - o.create_data_property_or_throw(utf16!("type"), js_string!(part.typ()), context) + o.create_data_property_or_throw(js_str!("type"), js_string!(part.typ()), context) .expect("operation must not fail per the spec"); // c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]). - o.create_data_property_or_throw(utf16!("value"), js_string!(part.value()), context) + o.create_data_property_or_throw(js_str!("value"), js_string!(part.value()), context) .expect("operation must not fail per the spec"); // d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O). @@ -435,29 +436,29 @@ impl ListFormat { // d. Perform ! CreateDataPropertyOrThrow(options, p, v). options .create_data_property_or_throw( - utf16!("locale"), + js_str!("locale"), js_string!(lf.locale.to_string()), context, ) .expect("operation must not fail per the spec"); options .create_data_property_or_throw( - js_string!("type"), + js_str!("type"), match lf.typ { - ListFormatType::Conjunction => js_string!("conjunction"), - ListFormatType::Disjunction => js_string!("disjunction"), - ListFormatType::Unit => js_string!("unit"), + ListFormatType::Conjunction => js_str!("conjunction"), + ListFormatType::Disjunction => js_str!("disjunction"), + ListFormatType::Unit => js_str!("unit"), }, context, ) .expect("operation must not fail per the spec"); options .create_data_property_or_throw( - js_string!("style"), + js_str!("style"), match lf.style { - ListLength::Wide => js_string!("long"), - ListLength::Short => js_string!("short"), - ListLength::Narrow => js_string!("narrow"), + ListLength::Wide => js_str!("long"), + ListLength::Short => js_str!("short"), + ListLength::Narrow => js_str!("narrow"), _ => unreachable!(), }, context, diff --git a/core/engine/src/builtins/intl/locale/mod.rs b/core/engine/src/builtins/intl/locale/mod.rs index 8f5a288e665..a70ab770f63 100644 --- a/core/engine/src/builtins/intl/locale/mod.rs +++ b/core/engine/src/builtins/intl/locale/mod.rs @@ -1,8 +1,5 @@ -use crate::{ - builtins::options::get_option, - realm::Realm, - string::{common::StaticJsStrings, utf16}, -}; +use crate::{builtins::options::get_option, realm::Realm, string::common::StaticJsStrings}; +use boa_macros::js_str; use boa_profiler::Profiler; use icu_collator::CaseFirst; use icu_datetime::options::preferences::HourCycle; @@ -236,17 +233,17 @@ impl BuiltInConstructor for Locale { // 4. Let language be ? GetOption(options, "language", string, empty, undefined). // 5. If language is not undefined, then // a. If language does not match the unicode_language_subtag production, throw a RangeError exception. - let language = get_option(options, utf16!("language"), context)?; + let language = get_option(options, js_str!("language"), context)?; // 6. Let script be ? GetOption(options, "script", string, empty, undefined). // 7. If script is not undefined, then // a. If script does not match the unicode_script_subtag production, throw a RangeError exception. - let script = get_option(options, utf16!("script"), context)?; + let script = get_option(options, js_str!("script"), context)?; // 8. Let region be ? GetOption(options, "region", string, empty, undefined). // 9. If region is not undefined, then // a. If region does not match the unicode_region_subtag production, throw a RangeError exception. - let region = get_option(options, utf16!("region"), context)?; + let region = get_option(options, js_str!("region"), context)?; // 10. Set tag to ! CanonicalizeUnicodeLocaleId(tag). context @@ -294,17 +291,17 @@ impl BuiltInConstructor for Locale { // 14. If calendar is not undefined, then // 15. Set opt.[[ca]] to calendar. // a. If calendar does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. - let ca = get_option(options, utf16!("calendar"), context)?; + let ca = get_option(options, js_str!("calendar"), context)?; // 16. Let collation be ? GetOption(options, "collation", string, empty, undefined). // 17. If collation is not undefined, then // 18. Set opt.[[co]] to collation. // a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. - let co = get_option(options, utf16!("collation"), context)?; + let co = get_option(options, js_str!("collation"), context)?; // 19. Let hc be ? GetOption(options, "hourCycle", string, « "h11", "h12", "h23", "h24" », undefined). // 20. Set opt.[[hc]] to hc. - let hc = get_option(options, utf16!("hourCycle"), context)?.map(|hc| match hc { + let hc = get_option(options, js_str!("hourCycle"), context)?.map(|hc| match hc { HourCycle::H24 => value!("h24"), HourCycle::H23 => value!("h23"), HourCycle::H12 => value!("h12"), @@ -313,7 +310,7 @@ impl BuiltInConstructor for Locale { // 21. Let kf be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined). // 22. Set opt.[[kf]] to kf. - let kf = get_option(options, utf16!("caseFirst"), context)?.map(|kf| match kf { + let kf = get_option(options, js_str!("caseFirst"), context)?.map(|kf| match kf { CaseFirst::UpperFirst => value!("upper"), CaseFirst::LowerFirst => value!("lower"), CaseFirst::Off => value!("false"), @@ -323,7 +320,7 @@ impl BuiltInConstructor for Locale { // 23. Let kn be ? GetOption(options, "numeric", boolean, empty, undefined). // 24. If kn is not undefined, set kn to ! ToString(kn). // 25. Set opt.[[kn]] to kn. - let kn = get_option(options, utf16!("numeric"), context)?.map(|b| { + let kn = get_option(options, js_str!("numeric"), context)?.map(|b| { if b { value!("true") } else { @@ -335,7 +332,7 @@ impl BuiltInConstructor for Locale { // 27. If numberingSystem is not undefined, then // 28. Set opt.[[nu]] to numberingSystem. // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception. - let nu = get_option(options, utf16!("numberingSystem"), context)?; + let nu = get_option(options, js_str!("numberingSystem"), context)?; // 29. Let r be ! ApplyUnicodeExtensionToTag(tag, opt, relevantExtensionKeys). // 30. Set locale.[[Locale]] to r.[[locale]]. diff --git a/core/engine/src/builtins/intl/locale/utils.rs b/core/engine/src/builtins/intl/locale/utils.rs index 423908cd461..24f4c0ad2c5 100644 --- a/core/engine/src/builtins/intl/locale/utils.rs +++ b/core/engine/src/builtins/intl/locale/utils.rs @@ -10,10 +10,10 @@ use crate::{ context::icu::IntlProvider, js_string, object::JsObject, - string::utf16, Context, JsNativeError, JsResult, JsValue, }; +use boa_macros::js_str; use icu_collator::provider::CollationMetadataV1Marker; use icu_locid::{ extensions::unicode::{Key, Value}, @@ -551,7 +551,7 @@ where let options = coerce_options_to_object(options, context)?; // 2. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). - let matcher = get_option(&options, utf16!("localeMatcher"), context)?.unwrap_or_default(); + let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); let elements = match matcher { // 4. Else, diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index 66bbea62832..3b63fca375d 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use boa_gc::{Finalize, Trace}; -use boa_macros::utf16; +use boa_macros::js_str; use boa_profiler::Profiler; use fixed_decimal::{FixedDecimal, FloatPrecision, SignDisplay}; use icu_decimal::{ @@ -221,13 +221,13 @@ impl BuiltInConstructor for NumberFormat { // 4. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 5. Set opt.[[localeMatcher]] to matcher. - let matcher = get_option(&options, utf16!("localeMatcher"), context)?.unwrap_or_default(); + let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); // 6. Let numberingSystem be ? GetOption(options, "numberingSystem", string, empty, undefined). // 7. If numberingSystem is not undefined, then // a. If numberingSystem cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception. // 8. Set opt.[[nu]] to numberingSystem. - let numbering_system = get_option(&options, utf16!("numberingSystem"), context)?; + let numbering_system = get_option(&options, js_str!("numberingSystem"), context)?; let mut intl_options = IntlOptions { matcher, @@ -277,7 +277,7 @@ impl BuiltInConstructor for NumberFormat { // 18. Let notation be ? GetOption(options, "notation", string, « "standard", "scientific", "engineering", "compact" », "standard"). // 19. Set numberFormat.[[Notation]] to notation. - let notation = get_option(&options, utf16!("notation"), context)?.unwrap_or_default(); + let notation = get_option(&options, js_str!("notation"), context)?.unwrap_or_default(); // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation). let digit_options = DigitFormatOptions::from_options( @@ -290,7 +290,7 @@ impl BuiltInConstructor for NumberFormat { // 21. Let compactDisplay be ? GetOption(options, "compactDisplay", string, « "short", "long" », "short"). let compact_display = - get_option(&options, utf16!("compactDisplay"), context)?.unwrap_or_default(); + get_option(&options, js_str!("compactDisplay"), context)?.unwrap_or_default(); // 22. Let defaultUseGrouping be "auto". let mut default_use_grouping = GroupingStrategy::Auto; @@ -325,7 +325,7 @@ impl BuiltInConstructor for NumberFormat { // // 1. Let value be ? Get(options, property). - let value = options.get(utf16!("useGrouping"), context)?; + let value = options.get(js_str!("useGrouping"), context)?; // 2. If value is undefined, return fallback. if value.is_undefined() { @@ -363,7 +363,7 @@ impl BuiltInConstructor for NumberFormat { // 29. Let signDisplay be ? GetOption(options, "signDisplay", string, « "auto", "never", "always", "exceptZero", "negative" », "auto"). // 30. Set numberFormat.[[SignDisplay]] to signDisplay. let sign_display = - get_option(&options, utf16!("signDisplay"), context)?.unwrap_or(SignDisplay::Auto); + get_option(&options, js_str!("signDisplay"), context)?.unwrap_or(SignDisplay::Auto); let formatter = FixedDecimalFormatter::try_new_unstable( context.intl_provider(), diff --git a/core/engine/src/builtins/intl/number_format/options.rs b/core/engine/src/builtins/intl/number_format/options.rs index 74dac331941..b5e85ce4d48 100644 --- a/core/engine/src/builtins/intl/number_format/options.rs +++ b/core/engine/src/builtins/intl/number_format/options.rs @@ -2,7 +2,7 @@ use std::fmt; use fixed_decimal::{FixedDecimal, FloatPrecision, RoundingIncrement as BaseMultiple, SignDisplay}; -use boa_macros::utf16; +use boa_macros::js_str; use tinystr::TinyAsciiStr; use crate::{ @@ -10,7 +10,7 @@ use crate::{ intl::options::{default_number_option, get_number_option}, options::{get_option, OptionType, ParsableOptionType, RoundingMode}, }, - js_string, Context, JsNativeError, JsObject, JsResult, JsString, + js_string, Context, JsNativeError, JsObject, JsResult, JsStr, JsString, }; #[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] @@ -245,9 +245,9 @@ impl ParsableOptionType for Currency {} #[derive(Debug, Eq, PartialEq)] pub(crate) struct Unit { // INVARIANT: `numerator` must only contain ASCII lowercase alphabetic letters or `-`. - numerator: &'static str, + numerator: JsStr<'static>, // INVARIANT: if `denominator` is not empty, it must only contain ASCII lowercase alphabetic letters or `-` - denominator: &'static str, + denominator: JsStr<'static>, } impl Unit { @@ -258,9 +258,7 @@ impl Unit { } else { // TODO: this is not optimal for now, but the new JS strings should // allow us to optimize this to simple casts from ASCII to JsString. - let numerator: Vec = self.numerator.encode_utf16().collect(); - let denominator: Vec = self.denominator.encode_utf16().collect(); - js_string!(&numerator, utf16!("-per-"), &denominator) + js_string!(self.numerator, js_str!("-per-"), self.denominator) } } } @@ -281,7 +279,7 @@ impl std::str::FromStr for Unit { /// /// [spec]: https://tc39.es/ecma402/#sec-iswellformedunitidentifier fn from_str(s: &str) -> Result { - static SANCTIONED_UNITS: [&str; 45] = [ + const SANCTIONED_UNITS: [&str; 45] = [ "acre", "bit", "byte", @@ -339,13 +337,17 @@ impl std::str::FromStr for Unit { .map(|i| SANCTIONED_UNITS[i]) .map_err(|_| ParseUnitError)?; + let num = JsStr::latin1(num.as_bytes()); + let den = if den.is_empty() { - "" + JsStr::EMPTY } else { - SANCTIONED_UNITS + let value = SANCTIONED_UNITS .binary_search(&den) .map(|i| SANCTIONED_UNITS[i]) - .map_err(|_| ParseUnitError)? + .map_err(|_| ParseUnitError)?; + + JsStr::latin1(value.as_bytes()) }; Ok(Self { @@ -390,12 +392,12 @@ impl UnitFormatOptions { pub(crate) fn from_options(options: &JsObject, context: &mut Context) -> JsResult { // 1. Let style be ? GetOption(options, "style", string, « "decimal", "percent", "currency", "unit" », "decimal"). // 2. Set intlObj.[[Style]] to style. - let style: Style = get_option(options, utf16!("style"), context)?.unwrap_or_default(); + let style: Style = get_option(options, js_str!("style"), context)?.unwrap_or_default(); // 3. Let currency be ? GetOption(options, "currency", string, empty, undefined). // 5. Else, // a. If IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception. - let currency = get_option(options, utf16!("currency"), context)?; + let currency = get_option(options, js_str!("currency"), context)?; // 4. If currency is undefined, then if currency.is_none() { @@ -411,16 +413,16 @@ impl UnitFormatOptions { // 6. Let currencyDisplay be ? GetOption(options, "currencyDisplay", string, « "code", "symbol", "narrowSymbol", "name" », "symbol"). let currency_display = - get_option(options, utf16!("currencyDisplay"), context)?.unwrap_or_default(); + get_option(options, js_str!("currencyDisplay"), context)?.unwrap_or_default(); // 7. Let currencySign be ? GetOption(options, "currencySign", string, « "standard", "accounting" », "standard"). let currency_sign = - get_option(options, utf16!("currencySign"), context)?.unwrap_or_default(); + get_option(options, js_str!("currencySign"), context)?.unwrap_or_default(); // 8. Let unit be ? GetOption(options, "unit", string, empty, undefined). // 10. Else, // a. If IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception. - let unit = get_option(options, utf16!("unit"), context)?; + let unit = get_option(options, js_str!("unit"), context)?; // 9. If unit is undefined, then if unit.is_none() { // a. If style is "unit", throw a TypeError exception. @@ -434,7 +436,8 @@ impl UnitFormatOptions { } // 11. Let unitDisplay be ? GetOption(options, "unitDisplay", string, « "short", "narrow", "long" », "short"). - let unit_display = get_option(options, utf16!("unitDisplay"), context)?.unwrap_or_default(); + let unit_display = + get_option(options, js_str!("unitDisplay"), context)?.unwrap_or_default(); // 14. Return unused. Ok(match style { @@ -489,25 +492,26 @@ impl DigitFormatOptions { ) -> JsResult { // 1. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1). let minimum_integer_digits = - get_number_option(options, utf16!("minimumIntegerDigits"), 1, 21, context)? + get_number_option(options, js_str!("minimumIntegerDigits"), 1, 21, context)? .unwrap_or(1); // 2. Let mnfd be ? Get(options, "minimumFractionDigits"). - let min_float_digits = options.get(utf16!("minimumFractionDigits"), context)?; + let min_float_digits = options.get(js_str!("minimumFractionDigits"), context)?; // 3. Let mxfd be ? Get(options, "maximumFractionDigits"). - let max_float_digits = options.get(utf16!("maximumFractionDigits"), context)?; + let max_float_digits = options.get(js_str!("maximumFractionDigits"), context)?; // 4. Let mnsd be ? Get(options, "minimumSignificantDigits"). - let min_sig_digits = options.get(utf16!("minimumSignificantDigits"), context)?; + let min_sig_digits = options.get(js_str!("minimumSignificantDigits"), context)?; // 5. Let mxsd be ? Get(options, "maximumSignificantDigits"). - let max_sig_digits = options.get(utf16!("maximumSignificantDigits"), context)?; + let max_sig_digits = options.get(js_str!("maximumSignificantDigits"), context)?; // 7. Let roundingPriority be ? GetOption(options, "roundingPriority", string, « "auto", "morePrecision", "lessPrecision" », "auto"). let mut rounding_priority = - get_option(options, utf16!("roundingPriority"), context)?.unwrap_or_default(); + get_option(options, js_str!("roundingPriority"), context)?.unwrap_or_default(); // 8. Let roundingIncrement be ? GetNumberOption(options, "roundingIncrement", 1, 5000, 1). // 9. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception. let rounding_increment = - get_number_option(options, utf16!("roundingIncrement"), 1, 5000, context)?.unwrap_or(1); + get_number_option(options, js_str!("roundingIncrement"), 1, 5000, context)? + .unwrap_or(1); let rounding_increment = RoundingIncrement::from_u16(rounding_increment).ok_or_else(|| { @@ -516,11 +520,11 @@ impl DigitFormatOptions { // 10. Let roundingMode be ? GetOption(options, "roundingMode", string, « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven" », "halfExpand"). let rounding_mode = - get_option(options, utf16!("roundingMode"), context)?.unwrap_or_default(); + get_option(options, js_str!("roundingMode"), context)?.unwrap_or_default(); // 11. Let trailingZeroDisplay be ? GetOption(options, "trailingZeroDisplay", string, « "auto", "stripIfInteger" », "auto"). let trailing_zero_display = - get_option(options, utf16!("trailingZeroDisplay"), context)?.unwrap_or_default(); + get_option(options, js_str!("trailingZeroDisplay"), context)?.unwrap_or_default(); // 12. NOTE: All fields required by SetNumberFormatDigitOptions have now been read from options. The remainder of this AO interprets the options and may throw exceptions. diff --git a/core/engine/src/builtins/intl/options.rs b/core/engine/src/builtins/intl/options.rs index bef7c8b3326..da0803126c2 100644 --- a/core/engine/src/builtins/intl/options.rs +++ b/core/engine/src/builtins/intl/options.rs @@ -5,7 +5,7 @@ use num_traits::FromPrimitive; use crate::{ builtins::{options::ParsableOptionType, OrdinaryObject}, object::JsObject, - Context, JsNativeError, JsResult, JsValue, + Context, JsNativeError, JsResult, JsStr, JsValue, }; /// `IntlOptions` aggregates the `locale_matcher` selector and any other object @@ -60,7 +60,7 @@ impl ParsableOptionType for LocaleMatcher {} /// [spec]: https://tc39.es/ecma402/#sec-getnumberoption pub(super) fn get_number_option( options: &JsObject, - property: &[u16], + property: JsStr<'_>, minimum: T, maximum: T, context: &mut Context, diff --git a/core/engine/src/builtins/intl/plural_rules/mod.rs b/core/engine/src/builtins/intl/plural_rules/mod.rs index 74a07c736ef..d0dad83b308 100644 --- a/core/engine/src/builtins/intl/plural_rules/mod.rs +++ b/core/engine/src/builtins/intl/plural_rules/mod.rs @@ -1,7 +1,7 @@ mod options; use boa_gc::{Finalize, Trace}; -use boa_macros::utf16; +use boa_macros::js_str; use boa_profiler::Profiler; use fixed_decimal::FixedDecimal; use icu_locid::Locale; @@ -22,7 +22,7 @@ use crate::{ property::Attribute, realm::Realm, string::common::StaticJsStrings, - Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, + Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsStr, JsString, JsSymbol, JsValue, }; use super::{ @@ -121,12 +121,12 @@ impl BuiltInConstructor for PluralRules { // 3. Let opt be a new Record. // 4. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 5. Set opt.[[localeMatcher]] to matcher. - let matcher = get_option(&options, utf16!("localeMatcher"), context)?.unwrap_or_default(); + let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); // 6. Let t be ? GetOption(options, "type", string, « "cardinal", "ordinal" », "cardinal"). // 7. Set pluralRules.[[Type]] to t. let rule_type = - get_option(&options, utf16!("type"), context)?.unwrap_or(PluralRuleType::Cardinal); + get_option(&options, js_str!("type"), context)?.unwrap_or(PluralRuleType::Cardinal); // 8. Perform ? SetNumberFormatDigitOptions(pluralRules, options, +0𝔽, 3𝔽, "standard"). let format_options = @@ -330,21 +330,21 @@ impl PluralRules { let mut options = ObjectInitializer::new(context); options .property( - js_string!("locale"), + js_str!("locale"), js_string!(plural_rules.locale.to_string()), Attribute::all(), ) .property( - js_string!("type"), + js_str!("type"), match plural_rules.rule_type { - PluralRuleType::Cardinal => js_string!("cardinal"), - PluralRuleType::Ordinal => js_string!("ordinal"), - _ => js_string!("unknown"), + PluralRuleType::Cardinal => js_str!("cardinal"), + PluralRuleType::Ordinal => js_str!("ordinal"), + _ => js_str!("unknown"), }, Attribute::all(), ) .property( - js_string!("minimumIntegerDigits"), + js_str!("minimumIntegerDigits"), plural_rules.format_options.minimum_integer_digits, Attribute::all(), ); @@ -353,16 +353,8 @@ impl PluralRules { plural_rules.format_options.rounding_type.fraction_digits() { options - .property( - js_string!("minimumFractionDigits"), - minimum, - Attribute::all(), - ) - .property( - js_string!("maximumFractionDigits"), - maximum, - Attribute::all(), - ); + .property(js_str!("minimumFractionDigits"), minimum, Attribute::all()) + .property(js_str!("maximumFractionDigits"), maximum, Attribute::all()); } if let Some(Extrema { minimum, maximum }) = plural_rules @@ -372,12 +364,12 @@ impl PluralRules { { options .property( - js_string!("minimumSignificantDigits"), + js_str!("minimumSignificantDigits"), minimum, Attribute::all(), ) .property( - js_string!("maximumSignificantDigits"), + js_str!("maximumSignificantDigits"), maximum, Attribute::all(), ); @@ -385,17 +377,17 @@ impl PluralRules { options .property( - js_string!("roundingMode"), + js_str!("roundingMode"), js_string!(plural_rules.format_options.rounding_mode.to_js_string()), Attribute::all(), ) .property( - js_string!("roundingIncrement"), + js_str!("roundingIncrement"), plural_rules.format_options.rounding_increment.to_u16(), Attribute::all(), ) .property( - js_string!("trailingZeroDisplay"), + js_str!("trailingZeroDisplay"), plural_rules .format_options .trailing_zero_display @@ -416,7 +408,7 @@ impl PluralRules { // 6. Perform ! CreateDataProperty(options, "pluralCategories", CreateArrayFromList(pluralCategories)). options.property( - js_string!("pluralCategories"), + js_str!("pluralCategories"), plural_categories, Attribute::all(), ); @@ -428,7 +420,7 @@ impl PluralRules { // 9. Else, // a. Perform ! CreateDataPropertyOrThrow(options, "roundingPriority", "auto"). options.property( - js_string!("roundingPriority"), + js_str!("roundingPriority"), js_string!(plural_rules.format_options.rounding_priority.to_js_string()), Attribute::all(), ); @@ -480,13 +472,13 @@ fn resolve_plural(plural_rules: &PluralRules, n: f64) -> ResolvedPlural { } } -fn plural_category_to_js_string(category: PluralCategory) -> JsString { +fn plural_category_to_js_string(category: PluralCategory) -> JsStr<'static> { match category { - PluralCategory::Zero => js_string!("zero"), - PluralCategory::One => js_string!("one"), - PluralCategory::Two => js_string!("two"), - PluralCategory::Few => js_string!("few"), - PluralCategory::Many => js_string!("many"), - PluralCategory::Other => js_string!("other"), + PluralCategory::Zero => js_str!("zero"), + PluralCategory::One => js_str!("one"), + PluralCategory::Two => js_str!("two"), + PluralCategory::Few => js_str!("few"), + PluralCategory::Many => js_str!("many"), + PluralCategory::Other => js_str!("other"), } } diff --git a/core/engine/src/builtins/intl/segmenter/iterator.rs b/core/engine/src/builtins/intl/segmenter/iterator.rs index b7e6dfe133f..7ed7ef663c2 100644 --- a/core/engine/src/builtins/intl/segmenter/iterator.rs +++ b/core/engine/src/builtins/intl/segmenter/iterator.rs @@ -1,7 +1,9 @@ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use icu_segmenter::{ - GraphemeClusterBreakIteratorUtf16, SentenceBreakIteratorUtf16, WordBreakIteratorUtf16, + GraphemeClusterBreakIteratorLatin1, GraphemeClusterBreakIteratorUtf16, + SentenceBreakIteratorLatin1, SentenceBreakIteratorUtf16, WordBreakIteratorLatin1, + WordBreakIteratorUtf16, }; use crate::{ @@ -16,9 +18,12 @@ use crate::{ use super::{create_segment_data_object, Segmenter}; pub(crate) enum NativeSegmentIterator<'l, 's> { - Grapheme(GraphemeClusterBreakIteratorUtf16<'l, 's>), - Word(WordBreakIteratorUtf16<'l, 's>), - Sentence(SentenceBreakIteratorUtf16<'l, 's>), + GraphemeUtf16(GraphemeClusterBreakIteratorUtf16<'l, 's>), + WordUtf16(WordBreakIteratorUtf16<'l, 's>), + SentenceUtf16(SentenceBreakIteratorUtf16<'l, 's>), + GraphemeLatin1(GraphemeClusterBreakIteratorLatin1<'l, 's>), + WordLatin1(WordBreakIteratorLatin1<'l, 's>), + SentenceLatin1(SentenceBreakIteratorLatin1<'l, 's>), } impl Iterator for NativeSegmentIterator<'_, '_> { @@ -26,9 +31,12 @@ impl Iterator for NativeSegmentIterator<'_, '_> { fn next(&mut self) -> Option { match self { - NativeSegmentIterator::Grapheme(g) => g.next(), - NativeSegmentIterator::Word(w) => w.next(), - NativeSegmentIterator::Sentence(s) => s.next(), + NativeSegmentIterator::GraphemeUtf16(g) => g.next(), + NativeSegmentIterator::WordUtf16(w) => w.next(), + NativeSegmentIterator::SentenceUtf16(s) => s.next(), + NativeSegmentIterator::GraphemeLatin1(g) => g.next(), + NativeSegmentIterator::WordLatin1(w) => w.next(), + NativeSegmentIterator::SentenceLatin1(s) => s.next(), } } } @@ -37,10 +45,10 @@ impl NativeSegmentIterator<'_, '_> { /// If the iterator is a word break iterator, returns `Some(true)` when the segment preceding /// the current boundary is word-like. pub(crate) fn is_word_like(&self) -> Option { - if let Self::Word(w) = self { - Some(w.is_word_like()) - } else { - None + match self { + Self::WordLatin1(w) => Some(w.is_word_like()), + Self::WordUtf16(w) => Some(w.is_word_like()), + _ => None, } } } diff --git a/core/engine/src/builtins/intl/segmenter/mod.rs b/core/engine/src/builtins/intl/segmenter/mod.rs index 2ff7f03e56d..517e359b688 100644 --- a/core/engine/src/builtins/intl/segmenter/mod.rs +++ b/core/engine/src/builtins/intl/segmenter/mod.rs @@ -1,7 +1,7 @@ use std::ops::Range; use boa_gc::{Finalize, Trace}; -use boa_macros::utf16; +use boa_macros::js_str; use boa_profiler::Profiler; use icu_locid::Locale; use icu_segmenter::{ @@ -19,7 +19,7 @@ use crate::{ property::Attribute, realm::Realm, string::common::StaticJsStrings, - Context, JsArgs, JsData, JsNativeError, JsResult, JsString, JsSymbol, JsValue, + Context, JsArgs, JsData, JsNativeError, JsResult, JsStr, JsString, JsSymbol, JsValue, }; mod iterator; @@ -62,11 +62,18 @@ impl NativeSegmenter { /// Segment the passed string, returning an iterator with the index boundaries /// of the segments. - pub(crate) fn segment<'l, 's>(&'l self, input: &'s [u16]) -> NativeSegmentIterator<'l, 's> { - match self { - Self::Grapheme(g) => NativeSegmentIterator::Grapheme(g.segment_utf16(input)), - Self::Word(w) => NativeSegmentIterator::Word(w.segment_utf16(input)), - Self::Sentence(s) => NativeSegmentIterator::Sentence(s.segment_utf16(input)), + pub(crate) fn segment<'l, 's>(&'l self, input: JsStr<'s>) -> NativeSegmentIterator<'l, 's> { + match input.variant() { + crate::string::JsStrVariant::Latin1(input) => match self { + Self::Grapheme(g) => NativeSegmentIterator::GraphemeLatin1(g.segment_latin1(input)), + Self::Word(w) => NativeSegmentIterator::WordLatin1(w.segment_latin1(input)), + Self::Sentence(s) => NativeSegmentIterator::SentenceLatin1(s.segment_latin1(input)), + }, + crate::string::JsStrVariant::Utf16(input) => match self { + Self::Grapheme(g) => NativeSegmentIterator::GraphemeUtf16(g.segment_utf16(input)), + Self::Word(w) => NativeSegmentIterator::WordUtf16(w.segment_utf16(input)), + Self::Sentence(s) => NativeSegmentIterator::SentenceUtf16(s.segment_utf16(input)), + }, } } } @@ -134,7 +141,7 @@ impl BuiltInConstructor for Segmenter { // 6. Let opt be a new Record. // 7. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). - let matcher = get_option(&options, utf16!("localeMatcher"), context)?.unwrap_or_default(); + let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); // 8. Set opt.[[localeMatcher]] to matcher. // 9. Let localeData be %Segmenter%.[[LocaleData]]. @@ -150,7 +157,8 @@ impl BuiltInConstructor for Segmenter { ); // 12. Let granularity be ? GetOption(options, "granularity", string, « "grapheme", "word", "sentence" », "grapheme"). - let granularity = get_option(&options, utf16!("granularity"), context)?.unwrap_or_default(); + let granularity = + get_option(&options, js_str!("granularity"), context)?.unwrap_or_default(); // 13. Set segmenter.[[SegmenterGranularity]] to granularity. let native = match granularity { @@ -241,12 +249,12 @@ impl Segmenter { // d. Perform ! CreateDataPropertyOrThrow(options, p, v). let options = ObjectInitializer::new(context) .property( - js_string!("locale"), + js_str!("locale"), js_string!(segmenter.locale.to_string()), Attribute::all(), ) .property( - js_string!("granularity"), + js_str!("granularity"), js_string!(segmenter.native.granularity().to_string()), Attribute::all(), ) @@ -301,25 +309,25 @@ fn create_segment_data_object( let start = range.start; // 6. Let segment be the substring of string from startIndex to endIndex. - let segment = js_string!(&string[range]); + let segment = string.get(range).expect("range already checked"); // 5. Let result be OrdinaryObjectCreate(%Object.prototype%). let object = &mut ObjectInitializer::new(context); object // 7. Perform ! CreateDataPropertyOrThrow(result, "segment", segment). - .property(js_string!("segment"), segment, Attribute::all()) + .property(js_str!("segment"), segment, Attribute::all()) // 8. Perform ! CreateDataPropertyOrThrow(result, "index", 𝔽(startIndex)). - .property(js_string!("index"), start, Attribute::all()) + .property(js_str!("index"), start, Attribute::all()) // 9. Perform ! CreateDataPropertyOrThrow(result, "input", string). - .property(js_string!("input"), string, Attribute::all()); + .property(js_str!("input"), string, Attribute::all()); // 10. Let granularity be segmenter.[[SegmenterGranularity]]. // 11. If granularity is "word", then if let Some(is_word_like) = is_word_like { // a. Let isWordLike be a Boolean value indicating whether the segment in string is "word-like" according to locale segmenter.[[Locale]]. // b. Perform ! CreateDataPropertyOrThrow(result, "isWordLike", isWordLike). - object.property(js_string!("isWordLike"), is_word_like, Attribute::all()); + object.property(js_str!("isWordLike"), is_word_like, Attribute::all()); } // 12. Return result. diff --git a/core/engine/src/builtins/intl/segmenter/segments.rs b/core/engine/src/builtins/intl/segmenter/segments.rs index 7710904cff0..65487c362d9 100644 --- a/core/engine/src/builtins/intl/segmenter/segments.rs +++ b/core/engine/src/builtins/intl/segmenter/segments.rs @@ -89,7 +89,7 @@ impl Segments { // 8. Let startIndex be ! FindBoundary(segmenter, string, n, before). // 9. Let endIndex be ! FindBoundary(segmenter, string, n, after). let (range, is_word_like) = { - let mut segments = segmenter.native.segment(&segments.string); + let mut segments = segmenter.native.segment(segments.string.as_str()); std::iter::from_fn(|| segments.next().map(|i| (i, segments.is_word_like()))) .tuple_windows() .find(|((i, _), (j, _))| (*i..*j).contains(&n)) diff --git a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs index 7ccf4bb5d09..8854bdf00e6 100644 --- a/core/engine/src/builtins/iterable/async_from_sync_iterator.rs +++ b/core/engine/src/builtins/iterable/async_from_sync_iterator.rs @@ -9,10 +9,10 @@ use crate::{ native_function::NativeFunction, object::{FunctionObjectBuilder, JsObject}, realm::Realm, - string::utf16, Context, JsArgs, JsData, JsError, JsNativeError, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; /// `%AsyncFromSyncIteratorPrototype%` object. @@ -80,7 +80,7 @@ impl AsyncFromSyncIterator { // 3. Let nextMethod be ! Get(asyncIterator, "next"). let next_method = async_iterator - .get(utf16!("next"), context) + .get(js_str!("next"), context) .expect("async from sync iterator prototype must have next method"); // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }. @@ -166,7 +166,7 @@ impl AsyncFromSyncIterator { .expect("cannot fail with promise constructor"); // 6. Let return be Completion(GetMethod(syncIterator, "return")). - let r#return = sync_iterator.get_method(utf16!("return"), context); + let r#return = sync_iterator.get_method(js_str!("return"), context); // 7. IfAbruptRejectPromise(return, promiseCapability). let r#return = if_abrupt_reject_promise!(r#return, promise_capability, context); @@ -243,7 +243,7 @@ impl AsyncFromSyncIterator { .expect("cannot fail with promise constructor"); // 6. Let throw be Completion(GetMethod(syncIterator, "throw")). - let throw = sync_iterator.get_method(utf16!("throw"), context); + let throw = sync_iterator.get_method(js_str!("throw"), context); // 7. IfAbruptRejectPromise(throw, promiseCapability). let throw = if_abrupt_reject_promise!(throw, promise_capability, context); @@ -368,7 +368,7 @@ impl AsyncFromSyncIterator { )) }), ) - .name("") + .name(js_str!("")) .length(1) .build(); @@ -401,7 +401,7 @@ impl AsyncFromSyncIterator { sync_iterator_record, ), ) - .name("") + .name(js_str!("")) .length(1) .build(), ) diff --git a/core/engine/src/builtins/iterable/mod.rs b/core/engine/src/builtins/iterable/mod.rs index 2d15325171e..63b21a87b46 100644 --- a/core/engine/src/builtins/iterable/mod.rs +++ b/core/engine/src/builtins/iterable/mod.rs @@ -4,13 +4,13 @@ use crate::{ builtins::{BuiltInBuilder, IntrinsicObject}, context::intrinsics::Intrinsics, error::JsNativeError, - js_string, object::JsObject, realm::Realm, symbol::JsSymbol, Context, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; mod async_from_sync_iterator; @@ -282,7 +282,7 @@ impl JsValue { })?; // 5. Let nextMethod be ? GetV(iterator, "next"). - let next_method = iterator.get_v(js_string!("next"), context)?; + let next_method = iterator.get_v(js_str!("next"), context)?; // 6. Let iteratorRecord be the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. // 7. Return iteratorRecord. @@ -326,7 +326,7 @@ impl IteratorResult { #[inline] pub fn complete(&self, context: &mut Context) -> JsResult { // 1. Return ToBoolean(? Get(iterResult, "done")). - Ok(self.object.get(js_string!("done"), context)?.to_boolean()) + Ok(self.object.get(js_str!("done"), context)?.to_boolean()) } /// `IteratorValue ( iterResult )` @@ -342,7 +342,7 @@ impl IteratorResult { #[inline] pub fn value(&self, context: &mut Context) -> JsResult { // 1. Return ? Get(iterResult, "value"). - self.object.get(js_string!("value"), context) + self.object.get(js_str!("value"), context) } } @@ -524,7 +524,7 @@ impl IteratorRecord { let iterator = &self.iterator; // 3. Let innerResult be Completion(GetMethod(iterator, "return")). - let inner_result = iterator.get_method(js_string!("return"), context); + let inner_result = iterator.get_method(js_str!("return"), context); // 4. If innerResult.[[Type]] is normal, then let inner_result = match inner_result { diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index 3ba82ce549c..af6a4513baa 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -15,6 +15,7 @@ use std::{borrow::Cow, iter::once}; +use boa_macros::{js_str, utf16}; use itertools::Itertools; use crate::{ @@ -26,7 +27,7 @@ use crate::{ object::{internal_methods::InternalMethodContext, JsObject}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::{common::StaticJsStrings, utf16, CodePoint}, + string::{common::StaticJsStrings, CodePoint}, symbol::JsSymbol, value::IntegerOrInfinity, vm::{CallFrame, CallFrameFlags}, @@ -148,11 +149,11 @@ impl Json { // b. Let rootName be the empty String. // c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered). - root.create_data_property_or_throw(utf16!(""), unfiltered, context) + root.create_data_property_or_throw(js_str!(""), unfiltered, context) .expect("CreateDataPropertyOrThrow should never throw here"); // d. Return ? InternalizeJSONProperty(root, rootName, reviver). - Self::internalize_json_property(&root, "".into(), obj, context) + Self::internalize_json_property(&root, js_string!(), obj, context) } else { // 12. Else, // a. Return unfiltered. @@ -368,7 +369,7 @@ impl Json { // 7. Else if Type(space) is String, then } else if let Some(s) = space.as_string() { // a. If the length of space is 10 or less, let gap be space; otherwise let gap be the substring of space from 0 to 10. - js_string!(s.get(..10).unwrap_or(s)) + js_string!(s.get(..10).unwrap_or(s.as_str())) // 8. Else, } else { // a. Let gap be the empty String. @@ -380,7 +381,7 @@ impl Json { // 10. Perform ! CreateDataPropertyOrThrow(wrapper, the empty String, value). wrapper - .create_data_property_or_throw(utf16!(""), args.get_or_undefined(0).clone(), context) + .create_data_property_or_throw(js_str!(""), args.get_or_undefined(0).clone(), context) .expect("CreateDataPropertyOrThrow should never fail here"); // 11. Let state be the Record { [[ReplacerFunction]]: ReplacerFunction, [[Stack]]: stack, [[Indent]]: indent, [[Gap]]: gap, [[PropertyList]]: PropertyList }. @@ -418,7 +419,7 @@ impl Json { // 2. If Type(value) is Object or BigInt, then if value.is_object() || value.is_bigint() { // a. Let toJSON be ? GetV(value, "toJSON"). - let to_json = value.get_v(utf16!("toJSON"), context)?; + let to_json = value.get_v(js_str!("toJSON"), context)?; // b. If IsCallable(toJSON) is true, then if let Some(obj) = to_json.as_object() { @@ -627,7 +628,7 @@ impl Json { // b. If strP is not undefined, then if let Some(str_p) = str_p { // i. Let member be QuoteJSONString(P). - let mut member = Self::quote_json_string(p).to_vec(); + let mut member = Self::quote_json_string(p).iter().collect::>(); // ii. Set member to the string-concatenation of member and ":". member.push(':' as u16); @@ -639,7 +640,7 @@ impl Json { } // iv. Set member to the string-concatenation of member and strP. - member.extend_from_slice(&str_p); + member.extend(str_p.iter()); // v. Append member to partial. partial.push(member); @@ -674,20 +675,20 @@ impl Json { // i. Let separator be the string-concatenation of the code unit 0x002C (COMMA), // the code unit 0x000A (LINE FEED), and state.[[Indent]]. let mut separator = utf16!(",\n").to_vec(); - separator.extend_from_slice(&state.indent); + separator.extend(state.indent.iter()); // ii. Let properties be the String value formed by concatenating all the element Strings of partial // with each adjacent pair of Strings separated with separator. // The separator String is not inserted either before the first String or after the last String. // iii. Let final be the string-concatenation of "{", the code // unit 0x000A (LINE FEED), state.[[Indent]], properties, // the code unit 0x000A (LINE FEED), stepback, and "}". - let result = [utf16!("{\n"), &state.indent[..]] + let result = [utf16!("{\n"), &state.indent.to_vec()[..]] .into_iter() .chain(Itertools::intersperse( partial.iter().map(Vec::as_slice), &separator, )) - .chain([utf16!("\n"), &stepback[..], utf16!("}")]) + .chain([utf16!("\n"), &stepback.to_vec()[..], utf16!("}")]) .flatten() .copied() .collect::>(); @@ -750,7 +751,7 @@ impl Json { // b. If strP is undefined, then if let Some(str_p) = str_p { // i. Append strP to partial. - partial.push(Cow::Owned(str_p.to_vec())); + partial.push(Cow::Owned(str_p.iter().collect::<_>())); // c. Else, } else { // i. Append "null" to partial. @@ -789,18 +790,18 @@ impl Json { // i. Let separator be the string-concatenation of the code unit 0x002C (COMMA), // the code unit 0x000A (LINE FEED), and state.[[Indent]]. let mut separator = utf16!(",\n").to_vec(); - separator.extend_from_slice(&state.indent); + separator.extend(state.indent.iter()); // ii. Let properties be the String value formed by concatenating all the element Strings of partial // with each adjacent pair of Strings separated with separator. // The separator String is not inserted either before the first String or after the last String. // iii. Let final be the string-concatenation of "[", the code unit 0x000A (LINE FEED), state.[[Indent]], properties, the code unit 0x000A (LINE FEED), stepback, and "]". - let result = [utf16!("[\n"), &state.indent[..]] + let result = [utf16!("[\n"), &state.indent.to_vec()[..]] .into_iter() .chain(Itertools::intersperse( partial.iter().map(Cow::as_ref), &separator, )) - .chain([utf16!("\n"), &stepback[..], utf16!("]")]) + .chain([utf16!("\n"), &stepback.to_vec()[..], utf16!("]")]) .flatten() .copied() .collect::>(); diff --git a/core/engine/src/builtins/json/tests.rs b/core/engine/src/builtins/json/tests.rs index 2300260036b..805c0b8c651 100644 --- a/core/engine/src/builtins/json/tests.rs +++ b/core/engine/src/builtins/json/tests.rs @@ -1,3 +1,4 @@ +use boa_macros::js_str; use indoc::indoc; use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; @@ -5,7 +6,7 @@ use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction} #[test] fn json_sanity() { run_test_actions([ - TestAction::assert_eq(r#"JSON.parse('{"aaa":"bbb"}').aaa"#, js_string!("bbb")), + TestAction::assert_eq(r#"JSON.parse('{"aaa":"bbb"}').aaa"#, js_str!("bbb")), TestAction::assert_eq( r#"JSON.stringify({aaa: 'bbb'})"#, js_string!(r#"{"aaa":"bbb"}"#), @@ -89,7 +90,7 @@ fn json_stringify_object_array() { fn json_stringify_array_converts_undefined_to_null() { run_test_actions([TestAction::assert_eq( "JSON.stringify([undefined])", - js_string!("[null]"), + js_str!("[null]"), )]); } @@ -97,7 +98,7 @@ fn json_stringify_array_converts_undefined_to_null() { fn json_stringify_array_converts_function_to_null() { run_test_actions([TestAction::assert_eq( "JSON.stringify([() => {}])", - js_string!("[null]"), + js_str!("[null]"), )]); } @@ -105,7 +106,7 @@ fn json_stringify_array_converts_function_to_null() { fn json_stringify_array_converts_symbol_to_null() { run_test_actions([TestAction::assert_eq( "JSON.stringify([Symbol()])", - js_string!("[null]"), + js_str!("[null]"), )]); } #[test] @@ -150,10 +151,7 @@ fn json_stringify_no_args() { #[test] fn json_stringify_fractional_numbers() { - run_test_actions([TestAction::assert_eq( - "JSON.stringify(1.2)", - js_string!("1.2"), - )]); + run_test_actions([TestAction::assert_eq("JSON.stringify(1.2)", js_str!("1.2"))]); } #[test] @@ -278,8 +276,8 @@ fn json_parse_object_with_reviver() { var jsonObj = JSON.parse(jsonString, dataReviver); "#}), - TestAction::assert_eq("jsonObj.firstname", js_string!("boa")), - TestAction::assert_eq("jsonObj.lastname", js_string!("interpreter")), + TestAction::assert_eq("jsonObj.firstname", js_str!("boa")), + TestAction::assert_eq("jsonObj.lastname", js_str!("interpreter")), ]); } diff --git a/core/engine/src/builtins/map/mod.rs b/core/engine/src/builtins/map/mod.rs index 5d5ba03a99f..c5c4edee468 100644 --- a/core/engine/src/builtins/map/mod.rs +++ b/core/engine/src/builtins/map/mod.rs @@ -18,10 +18,11 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use num_traits::Zero; @@ -63,7 +64,7 @@ impl IntrinsicObject for Map { Attribute::CONFIGURABLE, ) .property( - utf16!("entries"), + js_string!("entries"), entries_function.clone(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) @@ -148,7 +149,7 @@ impl BuiltInConstructor for Map { }; // 5. Let adder be ? Get(map, "set"). - let adder = map.get(utf16!("set"), context)?; + let adder = map.get(js_str!("set"), context)?; // 6. Return ? AddEntriesFromIterable(map, iterable, adder). add_entries_from_iterable(&map, iterable, &adder, context) diff --git a/core/engine/src/builtins/map/tests.rs b/core/engine/src/builtins/map/tests.rs index 89d2e5cb842..cbb27227919 100644 --- a/core/engine/src/builtins/map/tests.rs +++ b/core/engine/src/builtins/map/tests.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -74,7 +75,7 @@ fn merge() { let merged2 = new Map([...second, ...third]); "#}), TestAction::assert_eq("merged1.size", 3), - TestAction::assert_eq("merged1.get('2')", js_string!("second two")), + TestAction::assert_eq("merged1.get('2')", js_str!("second two")), TestAction::assert_eq("merged2.size", 4), ]); } @@ -85,8 +86,8 @@ fn get() { TestAction::run(indoc! {r#" let map = new Map([["1", "one"], ["2", "two"]]); "#}), - TestAction::assert_eq("map.get('1')", js_string!("one")), - TestAction::assert_eq("map.get('2')", js_string!("two")), + TestAction::assert_eq("map.get('1')", js_str!("one")), + TestAction::assert_eq("map.get('2')", js_str!("two")), TestAction::assert_eq("map.get('3')", JsValue::undefined()), TestAction::assert_eq("map.get()", JsValue::undefined()), ]); @@ -98,7 +99,7 @@ fn set() { TestAction::run("let map = new Map();"), TestAction::assert("map.set(); map.has(undefined)"), TestAction::assert_eq("map.get()", JsValue::undefined()), - TestAction::assert_eq("map.set('1', 'one'); map.get('1')", js_string!("one")), + TestAction::assert_eq("map.set('1', 'one'); map.get('1')", js_str!("one")), TestAction::assert("map.set('2'); map.has('2')"), TestAction::assert_eq("map.get('2')", JsValue::undefined()), ]); @@ -148,7 +149,7 @@ fn keys() { let item2 = keysIterator.next(); let item3 = keysIterator.next(); "#}), - TestAction::assert_eq("item1.value", js_string!("0")), + TestAction::assert_eq("item1.value", js_str!("0")), TestAction::assert_eq("item2.value", 1), TestAction::assert("item3.done"), ]); @@ -187,8 +188,8 @@ fn values() { let item2 = valuesIterator.next(); let item3 = valuesIterator.next(); "#}), - TestAction::assert_eq("item1.value", js_string!("foo")), - TestAction::assert_eq("item2.value", js_string!("bar")), + TestAction::assert_eq("item1.value", js_str!("foo")), + TestAction::assert_eq("item2.value", js_str!("bar")), TestAction::assert("item3.done"), ]); } @@ -201,7 +202,7 @@ fn modify_key() { let map = new Map([[obj, "one"]]); obj.field = "Value"; "#}), - TestAction::assert_eq("map.get(obj)", js_string!("one")), + TestAction::assert_eq("map.get(obj)", js_str!("one")), ]); } diff --git a/core/engine/src/builtins/mod.rs b/core/engine/src/builtins/mod.rs index 0cbe765894d..17e723ce5d3 100644 --- a/core/engine/src/builtins/mod.rs +++ b/core/engine/src/builtins/mod.rs @@ -36,6 +36,7 @@ pub mod weak_set; mod builder; +use boa_macros::js_str; use builder::BuiltInBuilder; #[cfg(feature = "annex-b")] @@ -108,7 +109,6 @@ use crate::{ object::JsObject, property::{Attribute, PropertyDescriptor}, realm::Realm, - string::utf16, Context, JsResult, JsString, JsValue, }; @@ -307,7 +307,7 @@ pub(crate) fn set_default_global_bindings(context: &mut Context) -> JsResult<()> let global_object = context.global_object(); global_object.define_property_or_throw( - utf16!("globalThis"), + js_str!("globalThis"), PropertyDescriptor::builder() .value(context.realm().global_this().clone()) .writable(true) @@ -320,17 +320,17 @@ pub(crate) fn set_default_global_bindings(context: &mut Context) -> JsResult<()> .enumerable(false) .configurable(false); global_object.define_property_or_throw( - utf16!("Infinity"), + js_str!("Infinity"), restricted.clone().value(f64::INFINITY), context, )?; global_object.define_property_or_throw( - utf16!("NaN"), + js_str!("NaN"), restricted.clone().value(f64::NAN), context, )?; global_object.define_property_or_throw( - utf16!("undefined"), + js_str!("undefined"), restricted.value(JsValue::undefined()), context, )?; diff --git a/core/engine/src/builtins/number/globals.rs b/core/engine/src/builtins/number/globals.rs index 46c16a93232..1900fa850c0 100644 --- a/core/engine/src/builtins/number/globals.rs +++ b/core/engine/src/builtins/number/globals.rs @@ -1,15 +1,13 @@ -use boa_macros::utf16; - use crate::{ builtins::{string::is_trimmable_whitespace, BuiltInBuilder, BuiltInObject, IntrinsicObject}, context::intrinsics::Intrinsics, object::JsObject, realm::Realm, - string::{common::StaticJsStrings, Utf16Trim}, - Context, JsArgs, JsResult, JsString, JsValue, + string::common::StaticJsStrings, + Context, JsArgs, JsResult, JsStr, JsString, JsValue, }; -use num_traits::Num; +use boa_macros::js_str; /// Builtin javascript 'isFinite(number)' function. /// @@ -95,6 +93,55 @@ impl BuiltInObject for IsNaN { const NAME: JsString = StaticJsStrings::IS_NAN; } +fn from_js_str_radix(src: JsStr<'_>, radix: u8) -> Option { + /// Determines if a string of text of that length of that radix could be guaranteed to be + /// stored in the given type T. + /// Note that if the radix is known to the compiler, it is just the check of digits.len that + /// is done at runtime. + fn can_not_overflow(radix: u8, digits_len: usize) -> bool { + usize::from(radix) <= 16 && digits_len <= std::mem::size_of::() * 2 + } + + const fn to_digit(input: u8, radix: u8) -> Option { + // If not a digit, a number greater than radix will be created. + let mut digit = input.wrapping_sub(b'0'); + if radix > 10 { + debug_assert!(radix <= 36, "to_digit: radix is too high (maximum 36)"); + if digit < 10 { + return Some(digit); + } + // Force the 6th bit to be set to ensure ascii is lower case. + digit = (input | 0b10_0000).wrapping_sub(b'a').saturating_add(10); + } + // FIXME: once then_some is const fn, use it here + if digit < radix { + Some(digit) + } else { + None + } + } + + let src = src + .iter() + .map(|x| u8::try_from(x).expect("should be ascii string")); + + let result = if can_not_overflow(radix, src.len()) { + let mut result = 0; + for c in src { + result = result * u64::from(radix) + u64::from(to_digit(c, radix)?); + } + result as f64 + } else { + let mut result = 0f64; + for c in src { + result = result * f64::from(radix) + f64::from(to_digit(c, radix)?); + } + result + }; + + Some(result) +} + /// Builtin javascript 'parseInt(str, radix)' function. /// /// Parses the given string as an integer using the given radix as a base. @@ -110,107 +157,108 @@ impl BuiltInObject for IsNaN { /// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt pub(crate) fn parse_int(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - if let (Some(val), radix) = (args.first(), args.get_or_undefined(1)) { - // 1. Let inputString be ? ToString(string). - let input_string = val.to_string(context)?; - - // 2. Let S be ! TrimString(inputString, start). - let mut var_s = input_string.trim_start(); - - // 3. Let sign be 1. - // 4. If S is not empty and the first code unit of S is the code unit 0x002D (HYPHEN-MINUS), - // set sign to -1. - let sign = if !var_s.is_empty() && var_s.starts_with(utf16!("-")) { - -1 - } else { - 1 - }; + let (Some(val), radix) = (args.first(), args.get_or_undefined(1)) else { + // Not enough arguments to parseInt. + return Ok(JsValue::nan()); + }; - // 5. If S is not empty and the first code unit of S is the code unit 0x002B (PLUS SIGN) or - // the code unit 0x002D (HYPHEN-MINUS), remove the first code unit from S. - if !var_s.is_empty() && (var_s.starts_with(utf16!("+")) || var_s.starts_with(utf16!("-"))) { - var_s = &var_s[1..]; - } + // 1. Let inputString be ? ToString(string). + let input_string = val.to_string(context)?; - // 6. Let R be ℝ(? ToInt32(radix)). - let mut var_r = radix.to_i32(context)?; + // 2. Let S be ! TrimString(inputString, start). + let mut s = input_string.trim_start(); + // let mut - // 7. Let stripPrefix be true. - let mut strip_prefix = true; + // 3. Let sign be 1. + // 4. If S is not empty and the first code unit of S is the code unit 0x002D (HYPHEN-MINUS), + // set sign to -1. + let sign = if !s.is_empty() && s.starts_with(js_str!("-")) { + -1 + } else { + 1 + }; - // 8. If R ≠ 0, then - #[allow(clippy::if_not_else)] - if var_r != 0 { - // a. If R < 2 or R > 36, return NaN. - if !(2..=36).contains(&var_r) { - return Ok(JsValue::nan()); - } + // 5. If S is not empty and the first code unit of S is the code unit 0x002B (PLUS SIGN) or + // the code unit 0x002D (HYPHEN-MINUS), remove the first code unit from S. + if !s.is_empty() && (s.starts_with(js_str!("+")) || s.starts_with(js_str!("-"))) { + s = s.get(1..).expect("already checked that it's not empty"); + } - // b. If R ≠ 16, set stripPrefix to false. - if var_r != 16 { - strip_prefix = false; - } - } else { - // 9. Else, - // a. Set R to 10. - var_r = 10; - } + // 6. Let R be ℝ(? ToInt32(radix)). + let r = radix.to_i32(context)?; - // 10. If stripPrefix is true, then - // a. If the length of S is at least 2 and the first two code units of S are either "0x" or "0X", then - // i. Remove the first two code units from S. - // ii. Set R to 16. - if strip_prefix - && var_s.len() >= 2 - && (var_s.starts_with(utf16!("0x")) || var_s.starts_with(utf16!("0X"))) - { - var_s = &var_s[2..]; + // 7. Let stripPrefix be true. + let mut strip_prefix = true; - var_r = 16; + // 8. If R ≠ 0, then + #[allow(clippy::if_not_else)] + let mut r = if r != 0 { + // a. If R < 2 or R > 36, return NaN. + if !(2..=36).contains(&r) { + return Ok(JsValue::nan()); } - // 11. If S contains a code unit that is not a radix-R digit, let end be the index within S of the - // first such code unit; otherwise, let end be the length of S. - let end = char::decode_utf16(var_s.iter().copied()) - .position(|code| !code.is_ok_and(|c| c.is_digit(var_r as u32))) - .unwrap_or(var_s.len()); + // b. If R ≠ 16, set stripPrefix to false. + if r != 16 { + strip_prefix = false; + } + r as u8 + } else { + // 9. Else, + // a. Set R to 10. + 10 + }; + + // 10. If stripPrefix is true, then + // a. If the length of S is at least 2 and the first two code units of S are either "0x" or "0X", then + // i. Remove the first two code units from S. + // ii. Set R to 16. + if strip_prefix + && s.len() >= 2 + && (s.starts_with(js_str!("0x")) || s.starts_with(js_str!("0X"))) + { + s = s + .get(2..) + .expect("already checked that it contains at least two chars"); + + r = 16; + } - // 12. Let Z be the substring of S from 0 to end. - let var_z = String::from_utf16_lossy(&var_s[..end]); + // 11. If S contains a code unit that is not a radix-R digit, let end be the index within S of the + // first such code unit; otherwise, let end be the length of S. + let end = char::decode_utf16(s.iter()) + .position(|code| !code.is_ok_and(|c| c.is_digit(u32::from(r)))) + .unwrap_or(s.len()); - // 13. If Z is empty, return NaN. - if var_z.is_empty() { - return Ok(JsValue::nan()); - } + // 12. Let Z be the substring of S from 0 to end. + let z = s.get(..end).expect("should be in range"); - // 14. Let mathInt be the integer value that is represented by Z in radix-R notation, using the - // letters A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains - // more than 20 significant digits, every significant digit after the 20th may be replaced by a - // 0 digit, at the option of the implementation; and if R is not 2, 4, 8, 10, 16, or 32, then - // mathInt may be an implementation-approximated value representing the integer value that is - // represented by Z in radix-R notation.) - let math_int = u64::from_str_radix(&var_z, var_r as u32).map_or_else( - |_| f64::from_str_radix(&var_z, var_r as u32).expect("invalid_float_conversion"), - |i| i as f64, - ); - - // 15. If mathInt = 0, then - // a. If sign = -1, return -0𝔽. - // b. Return +0𝔽. - if math_int == 0_f64 { - if sign == -1 { - return Ok(JsValue::new(-0_f64)); - } + // 13. If Z is empty, return NaN. + if z.is_empty() { + return Ok(JsValue::nan()); + } - return Ok(JsValue::new(0_f64)); + // 14. Let mathInt be the integer value that is represented by Z in radix-R notation, using the + // letters A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains + // more than 20 significant digits, every significant digit after the 20th may be replaced by a + // 0 digit, at the option of the implementation; and if R is not 2, 4, 8, 10, 16, or 32, then + // mathInt may be an implementation-approximated value representing the integer value that is + // represented by Z in radix-R notation.) + let math_int = from_js_str_radix(z, r).expect("Already checked"); + + // 15. If mathInt = 0, then + // a. If sign = -1, return -0𝔽. + // b. Return +0𝔽. + if math_int == 0_f64 { + if sign == -1 { + return Ok(JsValue::new(-0_f64)); } - // 16. Return 𝔽(sign × mathInt). - Ok(JsValue::new(f64::from(sign) * math_int)) - } else { - // Not enough arguments to parseInt. - Ok(JsValue::nan()) + return Ok(JsValue::new(0_f64)); } + + // 16. Return 𝔽(sign × mathInt). + Ok(JsValue::new(f64::from(sign) * math_int)) } pub(crate) struct ParseInt; diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index 592f157b522..025c25acf6e 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -25,6 +25,7 @@ use crate::{ value::{AbstractRelation, IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, JsString, }; +use boa_macros::js_str; use boa_profiler::Profiler; use num_traits::float::FloatCore; @@ -690,13 +691,13 @@ impl Number { } if x == -0. { - return Ok(JsValue::new(js_string!("0"))); + return Ok(JsValue::new(js_str!("0"))); } else if x.is_nan() { - return Ok(JsValue::new(js_string!("NaN"))); + return Ok(JsValue::new(js_str!("NaN"))); } else if x.is_infinite() && x.is_sign_positive() { - return Ok(JsValue::new(js_string!("Infinity"))); + return Ok(JsValue::new(js_str!("Infinity"))); } else if x.is_infinite() && x.is_sign_negative() { - return Ok(JsValue::new(js_string!("-Infinity"))); + return Ok(JsValue::new(js_str!("-Infinity"))); } // This is a Optimization from the v8 source code to print values that can fit in a single character diff --git a/core/engine/src/builtins/number/tests.rs b/core/engine/src/builtins/number/tests.rs index 290c4fb9a20..7b2e1170f29 100644 --- a/core/engine/src/builtins/number/tests.rs +++ b/core/engine/src/builtins/number/tests.rs @@ -1,11 +1,12 @@ +use boa_macros::js_str; + use crate::{ - builtins::Number, js_string, run_test_actions, value::AbstractRelation, JsNativeErrorKind, - TestAction, + builtins::Number, run_test_actions, value::AbstractRelation, JsNativeErrorKind, TestAction, }; #[test] fn integer_number_primitive_to_number_object() { - run_test_actions([TestAction::assert_eq("(100).toString()", js_string!("100"))]); + run_test_actions([TestAction::assert_eq("(100).toString()", js_str!("100"))]); } #[test] @@ -25,136 +26,133 @@ fn call_number() { #[test] fn to_exponential() { run_test_actions([ - TestAction::assert_eq("Number().toExponential()", js_string!("0e+0")), - TestAction::assert_eq("Number(5).toExponential()", js_string!("5e+0")), - TestAction::assert_eq("Number(1.234).toExponential()", js_string!("1.234e+0")), - TestAction::assert_eq("Number(1234).toExponential()", js_string!("1.234e+3")), + TestAction::assert_eq("Number().toExponential()", js_str!("0e+0")), + TestAction::assert_eq("Number(5).toExponential()", js_str!("5e+0")), + TestAction::assert_eq("Number(1.234).toExponential()", js_str!("1.234e+0")), + TestAction::assert_eq("Number(1234).toExponential()", js_str!("1.234e+3")), TestAction::assert_eq( "Number('I am also not a number').toExponential()", - js_string!("NaN"), + js_str!("NaN"), ), - TestAction::assert_eq("Number('1.23e+2').toExponential()", js_string!("1.23e+2")), + TestAction::assert_eq("Number('1.23e+2').toExponential()", js_str!("1.23e+2")), ]); } #[test] fn to_fixed() { run_test_actions([ - TestAction::assert_eq("Number().toFixed()", js_string!("0")), - TestAction::assert_eq("Number('3.456e+4').toFixed()", js_string!("34560")), - TestAction::assert_eq("Number('3.456e-4').toFixed()", js_string!("0")), - TestAction::assert_eq("Number(5).toFixed()", js_string!("5")), - TestAction::assert_eq( - "Number('I am also not a number').toFixed()", - js_string!("NaN"), - ), - TestAction::assert_eq("(1.35).toFixed(1)", js_string!("1.4")), + TestAction::assert_eq("Number().toFixed()", js_str!("0")), + TestAction::assert_eq("Number('3.456e+4').toFixed()", js_str!("34560")), + TestAction::assert_eq("Number('3.456e-4').toFixed()", js_str!("0")), + TestAction::assert_eq("Number(5).toFixed()", js_str!("5")), + TestAction::assert_eq("Number('I am also not a number').toFixed()", js_str!("NaN")), + TestAction::assert_eq("(1.35).toFixed(1)", js_str!("1.4")), // Test cases from https://source.chromium.org/chromium/chromium/src/+/main:v8/test/mjsunit/number-tostring-func.js;l=157-240;drc=aa3518a0f37245ebe8f062dce97ee492e2a41652 - TestAction::assert_eq("(NaN).toFixed(2)", js_string!("NaN")), - TestAction::assert_eq("(1/0).toFixed(2)", js_string!("Infinity")), - TestAction::assert_eq("(-1/0).toFixed(2)", js_string!("-Infinity")), + TestAction::assert_eq("(NaN).toFixed(2)", js_str!("NaN")), + TestAction::assert_eq("(1/0).toFixed(2)", js_str!("Infinity")), + TestAction::assert_eq("(-1/0).toFixed(2)", js_str!("-Infinity")), TestAction::assert_eq( "(1111111111111111111111).toFixed(8)", - js_string!("1.1111111111111111e+21"), - ), - TestAction::assert_eq("(0.1).toFixed(1)", js_string!("0.1")), - TestAction::assert_eq("(0.1).toFixed(2)", js_string!("0.10")), - TestAction::assert_eq("(0.1).toFixed(3)", js_string!("0.100")), - TestAction::assert_eq("(0.01).toFixed(2)", js_string!("0.01")), - TestAction::assert_eq("(0.01).toFixed(3)", js_string!("0.010")), - TestAction::assert_eq("(0.01).toFixed(4)", js_string!("0.0100")), - TestAction::assert_eq("(0.001).toFixed(2)", js_string!("0.00")), - TestAction::assert_eq("(0.001).toFixed(3)", js_string!("0.001")), - TestAction::assert_eq("(0.001).toFixed(4)", js_string!("0.0010")), - TestAction::assert_eq("(1).toFixed(4)", js_string!("1.0000")), - TestAction::assert_eq("(1).toFixed(1)", js_string!("1.0")), - TestAction::assert_eq("(1).toFixed(0)", js_string!("1")), - TestAction::assert_eq("(12).toFixed(0)", js_string!("12")), - TestAction::assert_eq("(1.1).toFixed(0)", js_string!("1")), - TestAction::assert_eq("(12.1).toFixed(0)", js_string!("12")), - TestAction::assert_eq("(1.12).toFixed(0)", js_string!("1")), - TestAction::assert_eq("(12.12).toFixed(0)", js_string!("12")), - TestAction::assert_eq("(0.0000006).toFixed(7)", js_string!("0.0000006")), - TestAction::assert_eq("(0.00000006).toFixed(8)", js_string!("0.00000006")), - TestAction::assert_eq("(0.00000006).toFixed(9)", js_string!("0.000000060")), - TestAction::assert_eq("(0.00000006).toFixed(10)", js_string!("0.0000000600")), - TestAction::assert_eq("(0).toFixed(0)", js_string!("0")), - TestAction::assert_eq("(0).toFixed(1)", js_string!("0.0")), - TestAction::assert_eq("(0).toFixed(2)", js_string!("0.00")), + js_str!("1.1111111111111111e+21"), + ), + TestAction::assert_eq("(0.1).toFixed(1)", js_str!("0.1")), + TestAction::assert_eq("(0.1).toFixed(2)", js_str!("0.10")), + TestAction::assert_eq("(0.1).toFixed(3)", js_str!("0.100")), + TestAction::assert_eq("(0.01).toFixed(2)", js_str!("0.01")), + TestAction::assert_eq("(0.01).toFixed(3)", js_str!("0.010")), + TestAction::assert_eq("(0.01).toFixed(4)", js_str!("0.0100")), + TestAction::assert_eq("(0.001).toFixed(2)", js_str!("0.00")), + TestAction::assert_eq("(0.001).toFixed(3)", js_str!("0.001")), + TestAction::assert_eq("(0.001).toFixed(4)", js_str!("0.0010")), + TestAction::assert_eq("(1).toFixed(4)", js_str!("1.0000")), + TestAction::assert_eq("(1).toFixed(1)", js_str!("1.0")), + TestAction::assert_eq("(1).toFixed(0)", js_str!("1")), + TestAction::assert_eq("(12).toFixed(0)", js_str!("12")), + TestAction::assert_eq("(1.1).toFixed(0)", js_str!("1")), + TestAction::assert_eq("(12.1).toFixed(0)", js_str!("12")), + TestAction::assert_eq("(1.12).toFixed(0)", js_str!("1")), + TestAction::assert_eq("(12.12).toFixed(0)", js_str!("12")), + TestAction::assert_eq("(0.0000006).toFixed(7)", js_str!("0.0000006")), + TestAction::assert_eq("(0.00000006).toFixed(8)", js_str!("0.00000006")), + TestAction::assert_eq("(0.00000006).toFixed(9)", js_str!("0.000000060")), + TestAction::assert_eq("(0.00000006).toFixed(10)", js_str!("0.0000000600")), + TestAction::assert_eq("(0).toFixed(0)", js_str!("0")), + TestAction::assert_eq("(0).toFixed(1)", js_str!("0.0")), + TestAction::assert_eq("(0).toFixed(2)", js_str!("0.00")), TestAction::assert_eq( "(-1111111111111111111111).toFixed(8)", - js_string!("-1.1111111111111111e+21"), - ), - TestAction::assert_eq("(-0.1).toFixed(1)", js_string!("-0.1")), - TestAction::assert_eq("(-0.1).toFixed(2)", js_string!("-0.10")), - TestAction::assert_eq("(-0.1).toFixed(3)", js_string!("-0.100")), - TestAction::assert_eq("(-0.01).toFixed(2)", js_string!("-0.01")), - TestAction::assert_eq("(-0.01).toFixed(3)", js_string!("-0.010")), - TestAction::assert_eq("(-0.01).toFixed(4)", js_string!("-0.0100")), - TestAction::assert_eq("(-0.001).toFixed(2)", js_string!("-0.00")), - TestAction::assert_eq("(-0.001).toFixed(3)", js_string!("-0.001")), - TestAction::assert_eq("(-0.001).toFixed(4)", js_string!("-0.0010")), - TestAction::assert_eq("(-1).toFixed(4)", js_string!("-1.0000")), - TestAction::assert_eq("(-1).toFixed(1)", js_string!("-1.0")), - TestAction::assert_eq("(-1).toFixed(0)", js_string!("-1")), - TestAction::assert_eq("(-1.1).toFixed(0)", js_string!("-1")), - TestAction::assert_eq("(-12.1).toFixed(0)", js_string!("-12")), - TestAction::assert_eq("(-1.12).toFixed(0)", js_string!("-1")), - TestAction::assert_eq("(-12.12).toFixed(0)", js_string!("-12")), - TestAction::assert_eq("(-0.0000006).toFixed(7)", js_string!("-0.0000006")), - TestAction::assert_eq("(-0.00000006).toFixed(8)", js_string!("-0.00000006")), - TestAction::assert_eq("(-0.00000006).toFixed(9)", js_string!("-0.000000060")), - TestAction::assert_eq("(-0.00000006).toFixed(10)", js_string!("-0.0000000600")), - TestAction::assert_eq("(-0).toFixed(0)", js_string!("0")), - TestAction::assert_eq("(-0).toFixed(1)", js_string!("0.0")), - TestAction::assert_eq("(-0).toFixed(2)", js_string!("0.00")), - TestAction::assert_eq("(0.00001).toFixed(5)", js_string!("0.00001")), + js_str!("-1.1111111111111111e+21"), + ), + TestAction::assert_eq("(-0.1).toFixed(1)", js_str!("-0.1")), + TestAction::assert_eq("(-0.1).toFixed(2)", js_str!("-0.10")), + TestAction::assert_eq("(-0.1).toFixed(3)", js_str!("-0.100")), + TestAction::assert_eq("(-0.01).toFixed(2)", js_str!("-0.01")), + TestAction::assert_eq("(-0.01).toFixed(3)", js_str!("-0.010")), + TestAction::assert_eq("(-0.01).toFixed(4)", js_str!("-0.0100")), + TestAction::assert_eq("(-0.001).toFixed(2)", js_str!("-0.00")), + TestAction::assert_eq("(-0.001).toFixed(3)", js_str!("-0.001")), + TestAction::assert_eq("(-0.001).toFixed(4)", js_str!("-0.0010")), + TestAction::assert_eq("(-1).toFixed(4)", js_str!("-1.0000")), + TestAction::assert_eq("(-1).toFixed(1)", js_str!("-1.0")), + TestAction::assert_eq("(-1).toFixed(0)", js_str!("-1")), + TestAction::assert_eq("(-1.1).toFixed(0)", js_str!("-1")), + TestAction::assert_eq("(-12.1).toFixed(0)", js_str!("-12")), + TestAction::assert_eq("(-1.12).toFixed(0)", js_str!("-1")), + TestAction::assert_eq("(-12.12).toFixed(0)", js_str!("-12")), + TestAction::assert_eq("(-0.0000006).toFixed(7)", js_str!("-0.0000006")), + TestAction::assert_eq("(-0.00000006).toFixed(8)", js_str!("-0.00000006")), + TestAction::assert_eq("(-0.00000006).toFixed(9)", js_str!("-0.000000060")), + TestAction::assert_eq("(-0.00000006).toFixed(10)", js_str!("-0.0000000600")), + TestAction::assert_eq("(-0).toFixed(0)", js_str!("0")), + TestAction::assert_eq("(-0).toFixed(1)", js_str!("0.0")), + TestAction::assert_eq("(-0).toFixed(2)", js_str!("0.00")), + TestAction::assert_eq("(0.00001).toFixed(5)", js_str!("0.00001")), TestAction::assert_eq( "(0.0000000000000000001).toFixed(20)", - js_string!("0.00000000000000000010"), + js_str!("0.00000000000000000010"), ), - TestAction::assert_eq("(0.00001).toFixed(17)", js_string!("0.00001000000000000")), - TestAction::assert_eq("(1).toFixed(17)", js_string!("1.00000000000000000")), + TestAction::assert_eq("(0.00001).toFixed(17)", js_str!("0.00001000000000000")), + TestAction::assert_eq("(1).toFixed(17)", js_str!("1.00000000000000000")), TestAction::assert_eq( "(100000000000000128).toFixed(1)", - js_string!("100000000000000128.0"), + js_str!("100000000000000128.0"), ), TestAction::assert_eq( "(10000000000000128).toFixed(2)", - js_string!("10000000000000128.00"), + js_str!("10000000000000128.00"), ), TestAction::assert_eq( "(10000000000000128).toFixed(20)", - js_string!("10000000000000128.00000000000000000000"), + js_str!("10000000000000128.00000000000000000000"), ), - TestAction::assert_eq("(-42).toFixed(3)", js_string!("-42.000")), + TestAction::assert_eq("(-42).toFixed(3)", js_str!("-42.000")), TestAction::assert_eq( "(-0.0000000000000000001).toFixed(20)", - js_string!("-0.00000000000000000010"), + js_str!("-0.00000000000000000010"), ), TestAction::assert_eq( "(0.123123123123123).toFixed(20)", - js_string!("0.12312312312312299889"), + js_str!("0.12312312312312299889"), ), TestAction::assert_eq( "(-1000000000000000128).toFixed()", - js_string!("-1000000000000000128"), + js_str!("-1000000000000000128"), ), - TestAction::assert_eq("(0).toFixed()", js_string!("0")), + TestAction::assert_eq("(0).toFixed()", js_str!("0")), TestAction::assert_eq( "(1000000000000000128).toFixed()", - js_string!("1000000000000000128"), + js_str!("1000000000000000128"), ), - TestAction::assert_eq("(1000).toFixed()", js_string!("1000")), - TestAction::assert_eq("(0.00001).toFixed()", js_string!("0")), + TestAction::assert_eq("(1000).toFixed()", js_str!("1000")), + TestAction::assert_eq("(0.00001).toFixed()", js_str!("0")), // Test that we round up even when the last digit generated is even. // dtoa does not do this in its original form. - TestAction::assert_eq("(0.5).toFixed(0)", js_string!("1")), - TestAction::assert_eq("(-0.5).toFixed(0)", js_string!("-1")), - TestAction::assert_eq("(1.25).toFixed(1)", js_string!("1.3")), + TestAction::assert_eq("(0.5).toFixed(0)", js_str!("1")), + TestAction::assert_eq("(-0.5).toFixed(0)", js_str!("-1")), + TestAction::assert_eq("(1.25).toFixed(1)", js_str!("1.3")), // This is bizare, but Spidermonkey and KJS behave the same. - TestAction::assert_eq("(234.2040).toFixed(4)", js_string!("234.2040")), - TestAction::assert_eq("(234.2040506).toFixed(4)", js_string!("234.2041")), + TestAction::assert_eq("(234.2040).toFixed(4)", js_str!("234.2040")), + TestAction::assert_eq("(234.2040506).toFixed(4)", js_str!("234.2041")), ]); } @@ -162,8 +160,8 @@ fn to_fixed() { #[test] fn issue_2609() { run_test_actions([ - TestAction::assert_eq("(1.25).toFixed(1)", js_string!("1.3")), - TestAction::assert_eq("(1.35).toFixed(1)", js_string!("1.4")), + TestAction::assert_eq("(1.25).toFixed(1)", js_str!("1.3")), + TestAction::assert_eq("(1.35).toFixed(1)", js_str!("1.4")), ]); } @@ -172,10 +170,10 @@ fn to_locale_string() { // TODO: We don't actually do any locale checking here // To honor the spec we should print numbers according to user locale. run_test_actions([ - TestAction::assert_eq("Number().toLocaleString()", js_string!("0")), - TestAction::assert_eq("Number(5).toLocaleString()", js_string!("5")), - TestAction::assert_eq("Number('345600').toLocaleString()", js_string!("345600")), - TestAction::assert_eq("Number(-25).toLocaleString()", js_string!("-25")), + TestAction::assert_eq("Number().toLocaleString()", js_str!("0")), + TestAction::assert_eq("Number(5).toLocaleString()", js_str!("5")), + TestAction::assert_eq("Number('345600').toLocaleString()", js_str!("345600")), + TestAction::assert_eq("Number(-25).toLocaleString()", js_str!("-25")), ]); } @@ -183,21 +181,21 @@ fn to_locale_string() { fn to_precision() { const ERROR: &str = "precision must be an integer at least 1 and no greater than 100"; run_test_actions([ - TestAction::assert_eq("(1/0).toPrecision(3)", js_string!("Infinity")), - TestAction::assert_eq("Number().toPrecision()", js_string!("0")), - TestAction::assert_eq("Number().toPrecision(undefined)", js_string!("0")), - TestAction::assert_eq("(123456789).toPrecision(1)", js_string!("1e+8")), - TestAction::assert_eq("(123456789).toPrecision(4)", js_string!("1.235e+8")), - TestAction::assert_eq("(123456789).toPrecision(9)", js_string!("123456789")), - TestAction::assert_eq("(-123456789).toPrecision(4)", js_string!("-1.235e+8")), + TestAction::assert_eq("(1/0).toPrecision(3)", js_str!("Infinity")), + TestAction::assert_eq("Number().toPrecision()", js_str!("0")), + TestAction::assert_eq("Number().toPrecision(undefined)", js_str!("0")), + TestAction::assert_eq("(123456789).toPrecision(1)", js_str!("1e+8")), + TestAction::assert_eq("(123456789).toPrecision(4)", js_str!("1.235e+8")), + TestAction::assert_eq("(123456789).toPrecision(9)", js_str!("123456789")), + TestAction::assert_eq("(-123456789).toPrecision(4)", js_str!("-1.235e+8")), TestAction::assert_eq( "(123456789).toPrecision(50)", - js_string!("123456789.00000000000000000000000000000000000000000"), + js_str!("123456789.00000000000000000000000000000000000000000"), ), - TestAction::assert_eq("(0.1).toPrecision(4)", js_string!("0.1000")), + TestAction::assert_eq("(0.1).toPrecision(4)", js_str!("0.1000")), TestAction::assert_eq( "(1/3).toPrecision(60)", - js_string!("0.333333333333333314829616256247390992939472198486328125000000"), + js_str!("0.333333333333333314829616256247390992939472198486328125000000"), ), TestAction::assert_native_error("(1).toPrecision(101)", JsNativeErrorKind::Range, ERROR), TestAction::assert_native_error("(1).toPrecision(0)", JsNativeErrorKind::Range, ERROR), @@ -209,132 +207,126 @@ fn to_precision() { #[test] fn to_string() { run_test_actions([ - TestAction::assert_eq("Number(NaN).toString()", js_string!("NaN")), - TestAction::assert_eq("Number(1/0).toString()", js_string!("Infinity")), - TestAction::assert_eq("Number(-1/0).toString()", js_string!("-Infinity")), - TestAction::assert_eq("Number(0).toString()", js_string!("0")), - TestAction::assert_eq("Number(9).toString()", js_string!("9")), - TestAction::assert_eq("Number(90).toString()", js_string!("90")), - TestAction::assert_eq("Number(90.12).toString()", js_string!("90.12")), - TestAction::assert_eq("Number(0.1).toString()", js_string!("0.1")), - TestAction::assert_eq("Number(0.01).toString()", js_string!("0.01")), - TestAction::assert_eq("Number(0.0123).toString()", js_string!("0.0123")), - TestAction::assert_eq("Number(0.00001).toString()", js_string!("0.00001")), - TestAction::assert_eq("Number(0.000001).toString()", js_string!("0.000001")), - TestAction::assert_eq("Number(NaN).toString(16)", js_string!("NaN")), - TestAction::assert_eq("Number(1/0).toString(16)", js_string!("Infinity")), - TestAction::assert_eq("Number(-1/0).toString(16)", js_string!("-Infinity")), - TestAction::assert_eq("Number(0).toString(16)", js_string!("0")), - TestAction::assert_eq("Number(9).toString(16)", js_string!("9")), - TestAction::assert_eq("Number(90).toString(16)", js_string!("5a")), - TestAction::assert_eq("Number(90.12).toString(16)", js_string!("5a.1eb851eb852")), - TestAction::assert_eq("Number(0.1).toString(16)", js_string!("0.1999999999999a")), - TestAction::assert_eq("Number(0.01).toString(16)", js_string!("0.028f5c28f5c28f6")), - TestAction::assert_eq( - "Number(0.0123).toString(16)", - js_string!("0.032617c1bda511a"), - ), + TestAction::assert_eq("Number(NaN).toString()", js_str!("NaN")), + TestAction::assert_eq("Number(1/0).toString()", js_str!("Infinity")), + TestAction::assert_eq("Number(-1/0).toString()", js_str!("-Infinity")), + TestAction::assert_eq("Number(0).toString()", js_str!("0")), + TestAction::assert_eq("Number(9).toString()", js_str!("9")), + TestAction::assert_eq("Number(90).toString()", js_str!("90")), + TestAction::assert_eq("Number(90.12).toString()", js_str!("90.12")), + TestAction::assert_eq("Number(0.1).toString()", js_str!("0.1")), + TestAction::assert_eq("Number(0.01).toString()", js_str!("0.01")), + TestAction::assert_eq("Number(0.0123).toString()", js_str!("0.0123")), + TestAction::assert_eq("Number(0.00001).toString()", js_str!("0.00001")), + TestAction::assert_eq("Number(0.000001).toString()", js_str!("0.000001")), + TestAction::assert_eq("Number(NaN).toString(16)", js_str!("NaN")), + TestAction::assert_eq("Number(1/0).toString(16)", js_str!("Infinity")), + TestAction::assert_eq("Number(-1/0).toString(16)", js_str!("-Infinity")), + TestAction::assert_eq("Number(0).toString(16)", js_str!("0")), + TestAction::assert_eq("Number(9).toString(16)", js_str!("9")), + TestAction::assert_eq("Number(90).toString(16)", js_str!("5a")), + TestAction::assert_eq("Number(90.12).toString(16)", js_str!("5a.1eb851eb852")), + TestAction::assert_eq("Number(0.1).toString(16)", js_str!("0.1999999999999a")), + TestAction::assert_eq("Number(0.01).toString(16)", js_str!("0.028f5c28f5c28f6")), + TestAction::assert_eq("Number(0.0123).toString(16)", js_str!("0.032617c1bda511a")), TestAction::assert_eq( "Number(111111111111111111111).toString(16)", - js_string!("605f9f6dd18bc8000"), + js_str!("605f9f6dd18bc8000"), ), TestAction::assert_eq( "Number(1111111111111111111111).toString(16)", - js_string!("3c3bc3a4a2f75c0000"), + js_str!("3c3bc3a4a2f75c0000"), ), TestAction::assert_eq( "Number(11111111111111111111111).toString(16)", - js_string!("25a55a46e5da9a00000"), + js_str!("25a55a46e5da9a00000"), ), TestAction::assert_eq( "Number(0.00001).toString(16)", - js_string!("0.0000a7c5ac471b4788"), + js_str!("0.0000a7c5ac471b4788"), ), TestAction::assert_eq( "Number(0.000001).toString(16)", - js_string!("0.000010c6f7a0b5ed8d"), + js_str!("0.000010c6f7a0b5ed8d"), ), TestAction::assert_eq( "Number(0.0000001).toString(16)", - js_string!("0.000001ad7f29abcaf48"), + js_str!("0.000001ad7f29abcaf48"), ), TestAction::assert_eq( "Number(0.00000012).toString(16)", - js_string!("0.000002036565348d256"), + js_str!("0.000002036565348d256"), ), TestAction::assert_eq( "Number(0.000000123).toString(16)", - js_string!("0.0000021047ee22aa466"), + js_str!("0.0000021047ee22aa466"), ), TestAction::assert_eq( "Number(0.00000001).toString(16)", - js_string!("0.0000002af31dc4611874"), + js_str!("0.0000002af31dc4611874"), ), TestAction::assert_eq( "Number(0.000000012).toString(16)", - js_string!("0.000000338a23b87483be"), + js_str!("0.000000338a23b87483be"), ), TestAction::assert_eq( "Number(0.0000000123).toString(16)", - js_string!("0.00000034d3fe36aaa0a2"), + js_str!("0.00000034d3fe36aaa0a2"), ), - TestAction::assert_eq("Number(-0).toString(16)", js_string!("0")), - TestAction::assert_eq("Number(-9).toString(16)", js_string!("-9")), + TestAction::assert_eq("Number(-0).toString(16)", js_str!("0")), + TestAction::assert_eq("Number(-9).toString(16)", js_str!("-9")), // - TestAction::assert_eq("Number(-90).toString(16)", js_string!("-5a")), - TestAction::assert_eq("Number(-90.12).toString(16)", js_string!("-5a.1eb851eb852")), - TestAction::assert_eq("Number(-0.1).toString(16)", js_string!("-0.1999999999999a")), - TestAction::assert_eq( - "Number(-0.01).toString(16)", - js_string!("-0.028f5c28f5c28f6"), - ), + TestAction::assert_eq("Number(-90).toString(16)", js_str!("-5a")), + TestAction::assert_eq("Number(-90.12).toString(16)", js_str!("-5a.1eb851eb852")), + TestAction::assert_eq("Number(-0.1).toString(16)", js_str!("-0.1999999999999a")), + TestAction::assert_eq("Number(-0.01).toString(16)", js_str!("-0.028f5c28f5c28f6")), TestAction::assert_eq( "Number(-0.0123).toString(16)", - js_string!("-0.032617c1bda511a"), + js_str!("-0.032617c1bda511a"), ), TestAction::assert_eq( "Number(-111111111111111111111).toString(16)", - js_string!("-605f9f6dd18bc8000"), + js_str!("-605f9f6dd18bc8000"), ), TestAction::assert_eq( "Number(-1111111111111111111111).toString(16)", - js_string!("-3c3bc3a4a2f75c0000"), + js_str!("-3c3bc3a4a2f75c0000"), ), TestAction::assert_eq( "Number(-11111111111111111111111).toString(16)", - js_string!("-25a55a46e5da9a00000"), + js_str!("-25a55a46e5da9a00000"), ), TestAction::assert_eq( "Number(-0.00001).toString(16)", - js_string!("-0.0000a7c5ac471b4788"), + js_str!("-0.0000a7c5ac471b4788"), ), TestAction::assert_eq( "Number(-0.000001).toString(16)", - js_string!("-0.000010c6f7a0b5ed8d"), + js_str!("-0.000010c6f7a0b5ed8d"), ), TestAction::assert_eq( "Number(-0.0000001).toString(16)", - js_string!("-0.000001ad7f29abcaf48"), + js_str!("-0.000001ad7f29abcaf48"), ), TestAction::assert_eq( "Number(-0.00000012).toString(16)", - js_string!("-0.000002036565348d256"), + js_str!("-0.000002036565348d256"), ), TestAction::assert_eq( "Number(-0.000000123).toString(16)", - js_string!("-0.0000021047ee22aa466"), + js_str!("-0.0000021047ee22aa466"), ), TestAction::assert_eq( "Number(-0.00000001).toString(16)", - js_string!("-0.0000002af31dc4611874"), + js_str!("-0.0000002af31dc4611874"), ), TestAction::assert_eq( "Number(-0.000000012).toString(16)", - js_string!("-0.000000338a23b87483be"), + js_str!("-0.000000338a23b87483be"), ), TestAction::assert_eq( "Number(-0.0000000123).toString(16)", - js_string!("-0.00000034d3fe36aaa0a2"), + js_str!("-0.00000034d3fe36aaa0a2"), ), ]); } @@ -342,26 +334,26 @@ fn to_string() { #[test] fn num_to_string_exponential() { run_test_actions([ - TestAction::assert_eq("(0).toString()", js_string!("0")), - TestAction::assert_eq("(-0).toString()", js_string!("0")), + TestAction::assert_eq("(0).toString()", js_str!("0")), + TestAction::assert_eq("(-0).toString()", js_str!("0")), TestAction::assert_eq( "(111111111111111111111).toString()", - js_string!("111111111111111110000"), + js_str!("111111111111111110000"), ), TestAction::assert_eq( "(1111111111111111111111).toString()", - js_string!("1.1111111111111111e+21"), + js_str!("1.1111111111111111e+21"), ), TestAction::assert_eq( "(11111111111111111111111).toString()", - js_string!("1.1111111111111111e+22"), - ), - TestAction::assert_eq("(0.0000001).toString()", js_string!("1e-7")), - TestAction::assert_eq("(0.00000012).toString()", js_string!("1.2e-7")), - TestAction::assert_eq("(0.000000123).toString()", js_string!("1.23e-7")), - TestAction::assert_eq("(0.00000001).toString()", js_string!("1e-8")), - TestAction::assert_eq("(0.000000012).toString()", js_string!("1.2e-8")), - TestAction::assert_eq("(0.0000000123).toString()", js_string!("1.23e-8")), + js_str!("1.1111111111111111e+22"), + ), + TestAction::assert_eq("(0.0000001).toString()", js_str!("1e-7")), + TestAction::assert_eq("(0.00000012).toString()", js_str!("1.2e-7")), + TestAction::assert_eq("(0.000000123).toString()", js_str!("1.23e-7")), + TestAction::assert_eq("(0.00000001).toString()", js_str!("1e-8")), + TestAction::assert_eq("(0.000000012).toString()", js_str!("1.2e-8")), + TestAction::assert_eq("(0.0000000123).toString()", js_str!("1.23e-8")), ]); } @@ -642,11 +634,11 @@ fn issue_2717() { run_test_actions([ TestAction::assert_eq( "(0.1600057092765239).toString(36)", - js_string!("0.5rd85dm1ixq"), + js_str!("0.5rd85dm1ixq"), ), TestAction::assert_eq( "(0.23046743672210102).toString(36)", - js_string!("0.8aoosla2phj"), + js_str!("0.8aoosla2phj"), ), ]); } diff --git a/core/engine/src/builtins/object/mod.rs b/core/engine/src/builtins/object/mod.rs index 9476038ca7b..a0fe6351913 100644 --- a/core/engine/src/builtins/object/mod.rs +++ b/core/engine/src/builtins/object/mod.rs @@ -13,8 +13,6 @@ //! [spec]: https://tc39.es/ecma262/#sec-objects //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object -use std::ops::Deref; - use super::{ error::ErrorObject, Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp, }; @@ -30,12 +28,13 @@ use crate::{ }, property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsData, JsResult, JsString, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use tap::{Conv, Pipe}; @@ -63,7 +62,7 @@ impl IntrinsicObject for OrdinaryObject { BuiltInBuilder::from_standard_constructor::(realm) .inherits(None) .accessor( - utf16!("__proto__"), + js_string!("__proto__"), Some(legacy_proto_getter), Some(legacy_setter_proto), Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, @@ -574,42 +573,42 @@ impl OrdinaryObject { // 4. If Desc has a [[Value]] field, then if let Some(value) = desc.value() { // a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]). - obj.create_data_property_or_throw(utf16!("value"), value.clone(), context) + obj.create_data_property_or_throw(js_str!("value"), value.clone(), context) .expect("CreateDataPropertyOrThrow cannot fail here"); } // 5. If Desc has a [[Writable]] field, then if let Some(writable) = desc.writable() { // a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]). - obj.create_data_property_or_throw(utf16!("writable"), writable, context) + obj.create_data_property_or_throw(js_str!("writable"), writable, context) .expect("CreateDataPropertyOrThrow cannot fail here"); } // 6. If Desc has a [[Get]] field, then if let Some(get) = desc.get() { // a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]). - obj.create_data_property_or_throw(utf16!("get"), get.clone(), context) + obj.create_data_property_or_throw(js_str!("get"), get.clone(), context) .expect("CreateDataPropertyOrThrow cannot fail here"); } // 7. If Desc has a [[Set]] field, then if let Some(set) = desc.set() { // a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]). - obj.create_data_property_or_throw(utf16!("set"), set.clone(), context) + obj.create_data_property_or_throw(js_str!("set"), set.clone(), context) .expect("CreateDataPropertyOrThrow cannot fail here"); } // 8. If Desc has an [[Enumerable]] field, then if let Some(enumerable) = desc.enumerable() { // a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]). - obj.create_data_property_or_throw(utf16!("enumerable"), enumerable, context) + obj.create_data_property_or_throw(js_str!("enumerable"), enumerable, context) .expect("CreateDataPropertyOrThrow cannot fail here"); } // 9. If Desc has a [[Configurable]] field, then if let Some(configurable) = desc.configurable() { // a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]). - obj.create_data_property_or_throw(utf16!("configurable"), configurable, context) + obj.create_data_property_or_throw(js_str!("configurable"), configurable, context) .expect("CreateDataPropertyOrThrow cannot fail here"); } @@ -825,11 +824,11 @@ impl OrdinaryObject { pub fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { // 1. If the this value is undefined, return "[object Undefined]". if this.is_undefined() { - return Ok(js_string!("[object Undefined]").into()); + return Ok(js_str!("[object Undefined]").into()); } // 2. If the this value is null, return "[object Null]". if this.is_null() { - return Ok(js_string!("[object Null]").into()); + return Ok(js_str!("[object Null]").into()); } // 3. Let O be ! ToObject(this value). let o = this.to_object(context).expect("toObject cannot fail here"); @@ -837,37 +836,37 @@ impl OrdinaryObject { // 4. Let isArray be ? IsArray(O). // 5. If isArray is true, let builtinTag be "Array". let builtin_tag = if o.is_array_abstract()? { - utf16!("Array") + js_str!("Array") } else { let o_borrow = o.borrow(); if o_borrow.is_arguments() { // 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments". - utf16!("Arguments") + js_str!("Arguments") } else if o.is_callable() { // 7. Else if O has a [[Call]] internal method, let builtinTag be "Function". - utf16!("Function") + js_str!("Function") } else if o.is::() { // 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error". - utf16!("Error") + js_str!("Error") } else if o.is::() { // 9. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean". - utf16!("Boolean") + js_str!("Boolean") } else if o.is::() { // 10. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number". - utf16!("Number") + js_str!("Number") } else if o.is::() { // 11. Else if O has a [[StringData]] internal slot, let builtinTag be "String". - utf16!("String") + js_str!("String") } else if o.is::() { // 12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date". - utf16!("Date") + js_str!("Date") } else if o.is::() { // 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp". - utf16!("RegExp") + js_str!("RegExp") } else { // 14. Else, let builtinTag be "Object". - utf16!("Object") + js_str!("Object") } }; @@ -875,10 +874,10 @@ impl OrdinaryObject { let tag = o.get(JsSymbol::to_string_tag(), context)?; // 16. If Type(tag) is not String, set tag to builtinTag. - let tag_str = tag.as_string().map_or(builtin_tag, JsString::deref); + let tag_str = tag.as_string().map_or(builtin_tag, JsString::as_str); // 17. Return the string-concatenation of "[object ", tag, and "]". - Ok(js_string!(utf16!("[object "), tag_str, utf16!("]")).into()) + Ok(js_string!(js_str!("[object "), tag_str, js_str!("]")).into()) } /// `Object.prototype.toLocaleString( [ reserved1 [ , reserved2 ] ] )` @@ -897,7 +896,7 @@ impl OrdinaryObject { ) -> JsResult { // 1. Let O be the this value. // 2. Return ? Invoke(O, "toString"). - this.invoke(utf16!("toString"), &[], context) + this.invoke(js_str!("toString"), &[], context) } /// `Object.prototype.hasOwnProperty( property )` diff --git a/core/engine/src/builtins/object/tests.rs b/core/engine/src/builtins/object/tests.rs index 17c42c1100f..151f5995386 100644 --- a/core/engine/src/builtins/object/tests.rs +++ b/core/engine/src/builtins/object/tests.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -137,21 +138,21 @@ fn object_to_string() { "#}), TestAction::assert_eq( "Object.prototype.toString.call(undefined)", - js_string!("[object Undefined]"), + js_str!("[object Undefined]"), ), TestAction::assert_eq( "Object.prototype.toString.call(null)", - js_string!("[object Null]"), + js_str!("[object Null]"), ), - TestAction::assert_eq("[].toString()", js_string!("[object Array]")), - TestAction::assert_eq("(() => {}).toString()", js_string!("[object Function]")), - TestAction::assert_eq("(new Error('')).toString()", js_string!("[object Error]")), - TestAction::assert_eq("Boolean().toString()", js_string!("[object Boolean]")), - TestAction::assert_eq("Number(42).toString()", js_string!("[object Number]")), - TestAction::assert_eq("String('boa').toString()", js_string!("[object String]")), - TestAction::assert_eq("(new Date()).toString()", js_string!("[object Date]")), - TestAction::assert_eq("/boa/.toString()", js_string!("[object RegExp]")), - TestAction::assert_eq("({}).toString()", js_string!("[object Object]")), + TestAction::assert_eq("[].toString()", js_str!("[object Array]")), + TestAction::assert_eq("(() => {}).toString()", js_str!("[object Function]")), + TestAction::assert_eq("(new Error('')).toString()", js_str!("[object Error]")), + TestAction::assert_eq("Boolean().toString()", js_str!("[object Boolean]")), + TestAction::assert_eq("Number(42).toString()", js_str!("[object Number]")), + TestAction::assert_eq("String('boa').toString()", js_str!("[object String]")), + TestAction::assert_eq("(new Date()).toString()", js_str!("[object Date]")), + TestAction::assert_eq("/boa/.toString()", js_str!("[object RegExp]")), + TestAction::assert_eq("({}).toString()", js_str!("[object Object]")), ]); } @@ -163,7 +164,7 @@ fn define_symbol_property() { let sym = Symbol("key"); Object.defineProperty(obj, sym, { value: "val" }); "#}), - TestAction::assert_eq("obj[sym]", js_string!("val")), + TestAction::assert_eq("obj[sym]", js_str!("val")), ]); } diff --git a/core/engine/src/builtins/options.rs b/core/engine/src/builtins/options.rs index cae5b7f4830..6b6777a0cf5 100644 --- a/core/engine/src/builtins/options.rs +++ b/core/engine/src/builtins/options.rs @@ -2,7 +2,7 @@ use std::{fmt, str::FromStr}; -use crate::{object::JsObject, Context, JsNativeError, JsResult, JsString, JsValue}; +use crate::{object::JsObject, string::JsStr, Context, JsNativeError, JsResult, JsString, JsValue}; /// A type used as an option parameter for [`get_option`]. pub(crate) trait OptionType: Sized { @@ -50,7 +50,7 @@ where /// [spec]: https://tc39.es/ecma402/#sec-getoption pub(crate) fn get_option( options: &JsObject, - property: &[u16], + property: JsStr<'_>, context: &mut Context, ) -> JsResult> { // 1. Let value be ? Get(options, property). diff --git a/core/engine/src/builtins/promise/mod.rs b/core/engine/src/builtins/promise/mod.rs index c48ed69dec5..8d1b45beb67 100644 --- a/core/engine/src/builtins/promise/mod.rs +++ b/core/engine/src/builtins/promise/mod.rs @@ -17,13 +17,13 @@ use crate::{ }, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsError, JsResult, JsString, }; use boa_gc::{custom_trace, Finalize, Gc, GcRefCell, Trace}; -use boa_macros::JsData; +use boa_macros::{js_str, JsData}; use boa_profiler::Profiler; use std::{cell::Cell, rc::Rc}; use tap::{Conv, Pipe}; @@ -768,7 +768,7 @@ impl Promise { // s. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »). next_promise.invoke( - utf16!("then"), + js_str!("then"), &[ on_fulfilled.into(), result_capability.functions.reject.clone().into(), @@ -957,15 +957,15 @@ impl Promise { // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled"). obj.create_data_property_or_throw( - utf16!("status"), - js_string!("fulfilled"), + js_str!("status"), + js_str!("fulfilled"), context, ) .expect("cannot fail per spec"); // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). obj.create_data_property_or_throw( - utf16!("value"), + js_str!("value"), args.get_or_undefined(0).clone(), context, ) @@ -1047,15 +1047,15 @@ impl Promise { // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected"). obj.create_data_property_or_throw( - utf16!("status"), - js_string!("rejected"), + js_str!("status"), + js_str!("rejected"), context, ) .expect("cannot fail per spec"); // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). obj.create_data_property_or_throw( - utf16!("reason"), + js_str!("reason"), args.get_or_undefined(0).clone(), context, ) @@ -1107,7 +1107,7 @@ impl Promise { // ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). next_promise.invoke( - utf16!("then"), + js_str!("then"), &[on_fulfilled.into(), on_rejected.into()], context, )?; @@ -1343,7 +1343,7 @@ impl Promise { // s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »). next_promise.invoke( - utf16!("then"), + js_str!("then"), &[ result_capability.functions.resolve.clone().into(), on_rejected.into(), @@ -1466,7 +1466,7 @@ impl Promise { // i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »). next_promise.invoke( - utf16!("then"), + js_str!("then"), &[ result_capability.functions.resolve.clone().into(), result_capability.functions.reject.clone().into(), @@ -1621,7 +1621,7 @@ impl Promise { let promise = this; // 2. Return ? Invoke(promise, "then", « undefined, onRejected »). promise.invoke( - utf16!("then"), + js_str!("then"), &[JsValue::undefined(), on_rejected.clone()], context, ) @@ -1667,7 +1667,7 @@ impl Promise { // a. Let thenFinally be onFinally. // b. Let catchFinally be onFinally. // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). - let then = promise.get(utf16!("then"), context)?; + let then = promise.get(js_str!("then"), context)?; return then.call(this, &[on_finally.clone(), on_finally.clone()], context); }; @@ -1675,7 +1675,7 @@ impl Promise { Self::then_catch_finally_closures(c, on_finally, context); // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). - let then = promise.get(utf16!("then"), context)?; + let then = promise.get(js_str!("then"), context)?; then.call(this, &[then_finally.into(), catch_finally.into()], context) } @@ -1730,7 +1730,7 @@ impl Promise { let value_thunk = return_value.length(0).name("").build(); // v. Return ? Invoke(promise, "then", « valueThunk »). - promise.invoke(utf16!("then"), &[value_thunk.into()], context) + promise.invoke(js_str!("then"), &[value_thunk.into()], context) }, FinallyCaptures { on_finally: on_finally.clone(), @@ -1781,7 +1781,7 @@ impl Promise { let thrower = throw_reason.length(0).name("").build(); // v. Return ? Invoke(promise, "then", « thrower »). - promise.invoke(utf16!("then"), &[thrower.into()], context) + promise.invoke(js_str!("then"), &[thrower.into()], context) }, FinallyCaptures { on_finally, c }, ), @@ -1986,7 +1986,7 @@ impl Promise { context: &mut Context, ) -> JsResult { // 1. Let promiseResolve be ? Get(promiseConstructor, "resolve"). - let promise_resolve = promise_constructor.get(utf16!("resolve"), context)?; + let promise_resolve = promise_constructor.get(js_str!("resolve"), context)?; // 2. If IsCallable(promiseResolve) is false, throw a TypeError exception. promise_resolve.as_callable().cloned().ok_or_else(|| { @@ -2184,7 +2184,7 @@ impl Promise { }; // 9. Let then be Completion(Get(resolution, "then")). - let then_action = match then.get(utf16!("then"), context) { + let then_action = match then.get(js_str!("then"), context) { // 10. If then is an abrupt completion, then Err(e) => { // a. Perform RejectPromise(promise, then.[[Value]]). diff --git a/core/engine/src/builtins/proxy/mod.rs b/core/engine/src/builtins/proxy/mod.rs index 0d654c23aeb..ea0d0329107 100644 --- a/core/engine/src/builtins/proxy/mod.rs +++ b/core/engine/src/builtins/proxy/mod.rs @@ -26,11 +26,12 @@ use crate::{ }, property::{PropertyDescriptor, PropertyKey}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, value::Type, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, GcRefCell, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use rustc_hash::FxHashSet; @@ -233,12 +234,12 @@ impl Proxy { // 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p). result - .create_data_property_or_throw(utf16!("proxy"), p, context) + .create_data_property_or_throw(js_str!("proxy"), p, context) .expect("CreateDataPropertyOrThrow cannot fail here"); // 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker). result - .create_data_property_or_throw(utf16!("revoke"), revoker, context) + .create_data_property_or_throw(js_str!("revoke"), revoker, context) .expect("CreateDataPropertyOrThrow cannot fail here"); // 8. Return result. @@ -266,7 +267,7 @@ pub(crate) fn proxy_exotic_get_prototype_of( .try_data()?; // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). - let Some(trap) = handler.get_method(utf16!("getPrototypeOf"), context)? else { + let Some(trap) = handler.get_method(js_str!("getPrototypeOf"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[GetPrototypeOf]](). return target.__get_prototype_of__(context); @@ -327,7 +328,7 @@ pub(crate) fn proxy_exotic_set_prototype_of( .try_data()?; // 5. Let trap be ? GetMethod(handler, "setPrototypeOf"). - let Some(trap) = handler.get_method(utf16!("setPrototypeOf"), context)? else { + let Some(trap) = handler.get_method(js_str!("setPrototypeOf"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[SetPrototypeOf]](V). return target.__set_prototype_of__(val, context); @@ -386,7 +387,7 @@ pub(crate) fn proxy_exotic_is_extensible(obj: &JsObject, context: &mut Context) .try_data()?; // 5. Let trap be ? GetMethod(handler, "isExtensible"). - let Some(trap) = handler.get_method(utf16!("isExtensible"), context)? else { + let Some(trap) = handler.get_method(js_str!("isExtensible"), context)? else { // 6. If trap is undefined, then // a. Return ? IsExtensible(target). return target.is_extensible(context); @@ -431,7 +432,7 @@ pub(crate) fn proxy_exotic_prevent_extensions( .try_data()?; // 5. Let trap be ? GetMethod(handler, "preventExtensions"). - let Some(trap) = handler.get_method(utf16!("preventExtensions"), context)? else { + let Some(trap) = handler.get_method(js_str!("preventExtensions"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[PreventExtensions]](). return target.__prevent_extensions__(context); @@ -478,7 +479,7 @@ pub(crate) fn proxy_exotic_get_own_property( .try_data()?; // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). - let Some(trap) = handler.get_method(utf16!("getOwnPropertyDescriptor"), context)? else { + let Some(trap) = handler.get_method(js_str!("getOwnPropertyDescriptor"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[GetOwnProperty]](P). return target.__get_own_property__(key, context); @@ -603,7 +604,7 @@ pub(crate) fn proxy_exotic_define_own_property( .try_data()?; // 5. Let trap be ? GetMethod(handler, "defineProperty"). - let Some(trap) = handler.get_method(utf16!("defineProperty"), context)? else { + let Some(trap) = handler.get_method(js_str!("defineProperty"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[DefineOwnProperty]](P, Desc). return target.__define_own_property__(key, desc, context); @@ -715,7 +716,7 @@ pub(crate) fn proxy_exotic_has_property( .try_data()?; // 5. Let trap be ? GetMethod(handler, "has"). - let Some(trap) = handler.get_method(utf16!("has"), context)? else { + let Some(trap) = handler.get_method(js_str!("has"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[HasProperty]](P). return target.has_property(key.clone(), context); @@ -783,7 +784,7 @@ pub(crate) fn proxy_exotic_get( .try_data()?; // 5. Let trap be ? GetMethod(handler, "get"). - let Some(trap) = handler.get_method(utf16!("get"), context)? else { + let Some(trap) = handler.get_method(js_str!("get"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[Get]](P, Receiver). return target.__get__(key, receiver, context); @@ -853,7 +854,7 @@ pub(crate) fn proxy_exotic_set( .try_data()?; // 5. Let trap be ? GetMethod(handler, "set"). - let Some(trap) = handler.get_method(utf16!("set"), context)? else { + let Some(trap) = handler.get_method(js_str!("set"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[Set]](P, V, Receiver). return target.__set__(key, value, receiver, context); @@ -933,7 +934,7 @@ pub(crate) fn proxy_exotic_delete( .try_data()?; // 5. Let trap be ? GetMethod(handler, "deleteProperty"). - let Some(trap) = handler.get_method(utf16!("deleteProperty"), context)? else { + let Some(trap) = handler.get_method(js_str!("deleteProperty"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[Delete]](P). return target.__delete__(key, context); @@ -998,7 +999,7 @@ pub(crate) fn proxy_exotic_own_property_keys( .try_data()?; // 5. Let trap be ? GetMethod(handler, "ownKeys"). - let Some(trap) = handler.get_method(utf16!("ownKeys"), context)? else { + let Some(trap) = handler.get_method(js_str!("ownKeys"), context)? else { // 6. If trap is undefined, then // a. Return ? target.[[OwnPropertyKeys]](). return target.__own_property_keys__(context); @@ -1132,7 +1133,7 @@ fn proxy_exotic_call( .try_data()?; // 5. Let trap be ? GetMethod(handler, "apply"). - let Some(trap) = handler.get_method(utf16!("apply"), context)? else { + let Some(trap) = handler.get_method(js_str!("apply"), context)? else { // 6. If trap is undefined, then // a. Return ? Call(target, thisArgument, argumentsList). return Ok(target.__call__(argument_count)); @@ -1180,7 +1181,7 @@ fn proxy_exotic_construct( assert!(target.is_constructor()); // 6. Let trap be ? GetMethod(handler, "construct"). - let Some(trap) = handler.get_method(utf16!("construct"), context)? else { + let Some(trap) = handler.get_method(js_str!("construct"), context)? else { // 7. If trap is undefined, then // a. Return ? Construct(target, argumentsList, newTarget). return Ok(target.__construct__(argument_count)); diff --git a/core/engine/src/builtins/reflect/tests.rs b/core/engine/src/builtins/reflect/tests.rs index 41798586c52..320f63da9d7 100644 --- a/core/engine/src/builtins/reflect/tests.rs +++ b/core/engine/src/builtins/reflect/tests.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsValue, TestAction}; +use crate::{run_test_actions, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -68,10 +69,7 @@ fn get_prototype_of() { function F() { this.p = 42 }; let f = new F(); "#}), - TestAction::assert_eq( - "Reflect.getPrototypeOf(f).constructor.name", - js_string!("F"), - ), + TestAction::assert_eq("Reflect.getPrototypeOf(f).constructor.name", js_str!("F")), ]); } @@ -134,6 +132,6 @@ fn set_prototype_of() { let obj = {} Reflect.setPrototypeOf(obj, F); "#}), - TestAction::assert_eq("Reflect.getPrototypeOf(obj).name", js_string!("F")), + TestAction::assert_eq("Reflect.getPrototypeOf(obj).name", js_str!("F")), ]); } diff --git a/core/engine/src/builtins/regexp/mod.rs b/core/engine/src/builtins/regexp/mod.rs index f75552b512c..34be1ef9654 100644 --- a/core/engine/src/builtins/regexp/mod.rs +++ b/core/engine/src/builtins/regexp/mod.rs @@ -17,12 +17,13 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, CONSTRUCTOR}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16, CodePoint}, + string::{common::StaticJsStrings, CodePoint, JsStrVariant}, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsData, JsResult, JsString, }; use boa_gc::{Finalize, Trace}; +use boa_macros::{js_str, utf16}; use boa_parser::lexer::regex::RegExpFlags; use boa_profiler::Profiler; use regress::{Flags, Range, Regex}; @@ -238,12 +239,12 @@ impl BuiltInConstructor for RegExp { (p, f) } else if let Some(pattern) = pattern_is_regexp { // a. Let P be ? Get(pattern, "source"). - let p = pattern.get(js_string!("source"), context)?; + let p = pattern.get(js_str!("source"), context)?; // b. If flags is undefined, then let f = if flags.is_undefined() { // i. Let F be ? Get(pattern, "flags"). - pattern.get(js_string!("flags"), context)? + pattern.get(js_str!("flags"), context)? // c. Else, } else { // i. Let F be flags. @@ -636,54 +637,54 @@ impl RegExp { }; // 3. Let codeUnits be a new empty List. - let mut code_units = Vec::new(); + let mut code_units = String::new(); // 4. Let hasIndices be ToBoolean(? Get(R, "hasIndices")). // 5. If hasIndices is true, append the code unit 0x0064 (LATIN SMALL LETTER D) to codeUnits. - if object.get(utf16!("hasIndices"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("d")); + if object.get(js_str!("hasIndices"), context)?.to_boolean() { + code_units.push('d'); } // 6. Let global be ToBoolean(? Get(R, "global")). // 7. If global is true, append the code unit 0x0067 (LATIN SMALL LETTER G) to codeUnits. - if object.get(utf16!("global"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("g")); + if object.get(js_str!("global"), context)?.to_boolean() { + code_units.push('g'); } // 8. Let ignoreCase be ToBoolean(? Get(R, "ignoreCase")). // 9. If ignoreCase is true, append the code unit 0x0069 (LATIN SMALL LETTER I) to codeUnits. - if object.get(utf16!("ignoreCase"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("i")); + if object.get(js_str!("ignoreCase"), context)?.to_boolean() { + code_units.push('i'); } // 10. Let multiline be ToBoolean(? Get(R, "multiline")). // 11. If multiline is true, append the code unit 0x006D (LATIN SMALL LETTER M) to codeUnits. - if object.get(utf16!("multiline"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("m")); + if object.get(js_str!("multiline"), context)?.to_boolean() { + code_units.push('m'); } // 12. Let dotAll be ToBoolean(? Get(R, "dotAll")). // 13. If dotAll is true, append the code unit 0x0073 (LATIN SMALL LETTER S) to codeUnits. - if object.get(utf16!("dotAll"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("s")); + if object.get(js_str!("dotAll"), context)?.to_boolean() { + code_units.push('s'); } // 14. Let unicode be ToBoolean(? Get(R, "unicode")). // 15. If unicode is true, append the code unit 0x0075 (LATIN SMALL LETTER U) to codeUnits. - if object.get(utf16!("unicode"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("u")); + if object.get(js_str!("unicode"), context)?.to_boolean() { + code_units.push('u'); } // 16. Let unicodeSets be ToBoolean(? Get(R, "unicodeSets")). // 17. If unicodeSets is true, append the code unit 0x0076 (LATIN SMALL LETTER V) to codeUnits. - if object.get(utf16!("unicodeSets"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("v")); + if object.get(js_str!("unicodeSets"), context)?.to_boolean() { + code_units.push('v'); } // 18. Let sticky be ToBoolean(? Get(R, "sticky")). // 19. If sticky is true, append the code unit 0x0079 (LATIN SMALL LETTER Y) to codeUnits. - if object.get(utf16!("sticky"), context)?.to_boolean() { - code_units.extend_from_slice(utf16!("y")); + if object.get(js_str!("sticky"), context)?.to_boolean() { + code_units.push('y'); } // 20. Return the String value whose code units are the elements of the List codeUnits. @@ -724,7 +725,7 @@ impl RegExp { this, &JsValue::new(context.intrinsics().constructors().regexp().prototype()), ) { - Ok(JsValue::new(js_string!("(?:)"))) + Ok(JsValue::new(js_str!("(?:)"))) } else { Err(JsNativeError::typ() .with_message("RegExp.prototype.source method called on incompatible value") @@ -752,7 +753,7 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-escaperegexppattern fn escape_pattern(src: &JsString, _flags: &JsString) -> JsValue { if src.is_empty() { - js_string!("(?:)").into() + js_str!("(?:)").into() } else { let mut s = Vec::with_capacity(src.len()); let mut buf = [0; 2]; @@ -863,7 +864,7 @@ impl RegExp { // 2. Assert: Type(S) is String. // 3. Let exec be ? Get(R, "exec"). - let exec = this.get(utf16!("exec"), context)?; + let exec = this.get(js_str!("exec"), context)?; // 4. If IsCallable(exec) is true, then if let Some(exec) = exec.as_callable() { @@ -915,19 +916,21 @@ impl RegExp { let length = input.len() as u64; // 2. Let lastIndex be ℝ(? ToLength(? Get(R, "lastIndex"))). - let mut last_index = this.get(utf16!("lastIndex"), context)?.to_length(context)?; + let mut last_index = this + .get(js_str!("lastIndex"), context)? + .to_length(context)?; // 3. Let flags be R.[[OriginalFlags]]. let flags = &rx.original_flags; // 4. If flags contains "g", let global be true; else let global be false. - let global = flags.contains(&('g' as u16)); + let global = flags.contains(b'g'); // 5. If flags contains "y", let sticky be true; else let sticky be false. - let sticky = flags.contains(&('y' as u16)); + let sticky = flags.contains(b'y'); // 6. If flags contains "d", let hasIndices be true; else let hasIndices be false. - let has_indices = flags.contains(&('d' as u16)); + let has_indices = flags.contains(b'd'); // 7. If global is false and sticky is false, set lastIndex to 0. if !global && !sticky { @@ -938,7 +941,7 @@ impl RegExp { let matcher = &rx.matcher; // 9. If flags contains "u" or flags contains "v", let fullUnicode be true; else let fullUnicode be false. - let full_unicode = flags.contains(&('u' as u16)) || flags.contains(&('v' as u16)); + let full_unicode = flags.contains(b'u') || flags.contains(b'v'); // NOTE: The following steps are take care of by regress: // @@ -952,7 +955,7 @@ impl RegExp { // i. If global is true or sticky is true, then if global || sticky { // 1. Perform ? Set(R, "lastIndex", +0𝔽, true). - this.set(utf16!("lastIndex"), 0, true, context)?; + this.set(js_str!("lastIndex"), 0, true, context)?; } // ii. Return null. @@ -961,10 +964,20 @@ impl RegExp { // 13.b. Let inputIndex be the index into input of the character that was obtained from element lastIndex of S. // 13.c. Let r be matcher(input, inputIndex). - let r: Option = if full_unicode { - matcher.find_from_utf16(input, last_index as usize).next() - } else { - matcher.find_from_ucs2(input, last_index as usize).next() + let r: Option = match (full_unicode, input.as_str().variant()) { + (true | false, JsStrVariant::Latin1(_)) => { + // TODO: Currently regress does not support latin1 encoding. + let input = input.to_vec(); + + // NOTE: We can use the faster ucs2 variant since there will never be two byte unicode. + matcher.find_from_ucs2(&input, last_index as usize).next() + } + (true, JsStrVariant::Utf16(input)) => { + matcher.find_from_utf16(input, last_index as usize).next() + } + (false, JsStrVariant::Utf16(input)) => { + matcher.find_from_ucs2(input, last_index as usize).next() + } }; let Some(match_value) = r else { @@ -975,7 +988,7 @@ impl RegExp { // 13.a.i. If global is true or sticky is true, then if global || sticky { // 1. Perform ? Set(R, "lastIndex", +0𝔽, true). - this.set(utf16!("lastIndex"), 0, true, context)?; + this.set(js_str!("lastIndex"), 0, true, context)?; } // MOVE: ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode). @@ -994,7 +1007,7 @@ impl RegExp { // NOTE: regress currently doesn't support the sticky flag so we have to emulate it. if sticky && match_value.start() != last_index as usize { // 1. Perform ? Set(R, "lastIndex", +0𝔽, true). - this.set(utf16!("lastIndex"), 0, true, context)?; + this.set(js_str!("lastIndex"), 0, true, context)?; // 2. Return null. return Ok(None); @@ -1012,7 +1025,7 @@ impl RegExp { // 16. If global is true or sticky is true, then if global || sticky { // a. Perform ? Set(R, "lastIndex", 𝔽(e), true). - this.set(utf16!("lastIndex"), e, true, context)?; + this.set(js_str!("lastIndex"), e, true, context)?; } // 17. Let n be the number of elements in r's captures List. @@ -1026,11 +1039,11 @@ impl RegExp { let a = Array::array_create(n + 1, None, context)?; // 22. Perform ! CreateDataPropertyOrThrow(A, "index", 𝔽(lastIndex)). - a.create_data_property_or_throw(utf16!("index"), last_index, context) + a.create_data_property_or_throw(js_str!("index"), last_index, context) .expect("this CreateDataPropertyOrThrow call must not fail"); // 23. Perform ! CreateDataPropertyOrThrow(A, "input", S). - a.create_data_property_or_throw(utf16!("input"), input.clone(), context) + a.create_data_property_or_throw(js_str!("input"), input.clone(), context) .expect("this CreateDataPropertyOrThrow call must not fail"); // 24. Let match be the Match Record { [[StartIndex]]: lastIndex, [[EndIndex]]: e }. @@ -1051,7 +1064,7 @@ impl RegExp { .expect("this CreateDataPropertyOrThrow call must not fail"); // 28. Let matchedSubstr be GetMatchString(S, match). - let matched_substr = js_string!(&input[(last_index as usize)..(e)]); + let matched_substr = input.get_expect((last_index as usize)..(e)); // 29. Perform ! CreateDataPropertyOrThrow(A, "0", matchedSubstr). a.create_data_property_or_throw(0, matched_substr, context) @@ -1081,7 +1094,7 @@ impl RegExp { for (name, range) in named_groups { let name = js_string!(name); if let Some(range) = range { - let value = js_string!(&input[range.clone()]); + let value = input.get_expect(range.clone()); groups .create_data_property_or_throw(name.clone(), value, context) @@ -1126,11 +1139,11 @@ impl RegExp { // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups ) // 8. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). indices - .create_data_property_or_throw(utf16!("groups"), group_names, context) + .create_data_property_or_throw(js_str!("groups"), group_names, context) .expect("this CreateDataPropertyOrThrow call must not fail"); // 32. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). - a.create_data_property_or_throw(utf16!("groups"), groups, context) + a.create_data_property_or_throw(js_str!("groups"), groups, context) .expect("this CreateDataPropertyOrThrow call must not fail"); // 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do @@ -1141,9 +1154,9 @@ impl RegExp { // b. If captureI is undefined, let capturedValue be undefined. // c. Else if fullUnicode is true, then // d. Else, - let captured_value = capture - .clone() - .map_or_else(JsValue::undefined, |range| js_string!(&input[range]).into()); + let captured_value = capture.clone().map_or_else(JsValue::undefined, |range| { + js_string!(input.get_expect(range)).into() + }); // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue). a.create_data_property_or_throw(i, captured_value.clone(), context) @@ -1171,7 +1184,7 @@ impl RegExp { // a. Let indicesArray be MakeMatchIndicesIndexPairArray(S, indices, groupNames, hasGroups). // b. Perform ! CreateDataPropertyOrThrow(A, "indices", indicesArray). if has_indices { - a.create_data_property_or_throw(utf16!("indices"), indices, context) + a.create_data_property_or_throw(js_str!("indices"), indices, context) .expect("this CreateDataPropertyOrThrow call must not fail"); } @@ -1206,10 +1219,10 @@ impl RegExp { let arg_str = args.get_or_undefined(0).to_string(context)?; // 4. Let flags be ? ToString(? Get(rx, "flags")). - let flags = rx.get(utf16!("flags"), context)?.to_string(context)?; + let flags = rx.get(js_str!("flags"), context)?.to_string(context)?; // 5. If flags does not contain "g", then - if !flags.contains(&103) { + if !flags.contains(b'g') { // a. Return ? RegExpExec(rx, S). return (Self::abstract_exec(rx, arg_str, context)?) .map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into())); @@ -1218,10 +1231,10 @@ impl RegExp { // 6. Else, // a. If flags contains "u" or flags contains "v", let fullUnicode be true. Otherwise, let fullUnicode be false. - let full_unicode = flags.contains(&117) || flags.contains(&118); + let full_unicode = flags.contains(b'u') || flags.contains(b'v'); // b. Perform ? Set(rx, "lastIndex", +0𝔽, true). - rx.set(utf16!("lastIndex"), 0, true, context)?; + rx.set(js_str!("lastIndex"), 0, true, context)?; // c. Let A be ! ArrayCreate(0). let a = Array::array_create(0, None, context).expect("this ArrayCreate call must not fail"); @@ -1247,13 +1260,18 @@ impl RegExp { // 3. If matchStr is the empty String, then if match_str.is_empty() { // a. Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))). - let this_index = rx.get(utf16!("lastIndex"), context)?.to_length(context)?; + let this_index = rx.get(js_str!("lastIndex"), context)?.to_length(context)?; // b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). let next_index = advance_string_index(&arg_str, this_index, full_unicode); // c. Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true). - rx.set(utf16!("lastIndex"), JsValue::new(next_index), true, context)?; + rx.set( + js_str!("lastIndex"), + JsValue::new(next_index), + true, + context, + )?; } // 4. Set n to n + 1. @@ -1293,14 +1311,14 @@ impl RegExp { })?; // 3. Let pattern be ? ToString(? Get(R, "source")). - let pattern = regexp.get(utf16!("source"), context)?.to_string(context)?; + let pattern = regexp.get(js_str!("source"), context)?.to_string(context)?; // 4. Let flags be ? ToString(? Get(R, "flags")). - let flags = regexp.get(utf16!("flags"), context)?.to_string(context)?; + let flags = regexp.get(js_str!("flags"), context)?.to_string(context)?; // 5. Let result be the string-concatenation of "/", pattern, "/", and flags. // 6. Return result. - Ok(js_string!(utf16!("/"), &pattern, utf16!("/"), &flags).into()) + Ok(js_string!(js_str!("/"), &pattern, js_str!("/"), &flags).into()) } /// `RegExp.prototype[ @@matchAll ]( string )` @@ -1332,26 +1350,26 @@ impl RegExp { let c = regexp.species_constructor(StandardConstructors::regexp, context)?; // 5. Let flags be ? ToString(? Get(R, "flags")). - let flags = regexp.get(utf16!("flags"), context)?.to_string(context)?; + let flags = regexp.get(js_str!("flags"), context)?.to_string(context)?; // 6. Let matcher be ? Construct(C, « R, flags »). let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?; // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). let last_index = regexp - .get(utf16!("lastIndex"), context)? + .get(js_str!("lastIndex"), context)? .to_length(context)?; // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). - matcher.set(utf16!("lastIndex"), last_index, true, context)?; + matcher.set(js_str!("lastIndex"), last_index, true, context)?; // 9. If flags contains "g", let global be true. // 10. Else, let global be false. - let global = flags.contains(&('g' as u16)); + let global = flags.contains(b'g'); // 11. If flags contains "u", let fullUnicode be true. // 12. Else, let fullUnicode be false. - let unicode = flags.contains(&('u' as u16)); + let unicode = flags.contains(b'u'); // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode). Ok(RegExpStringIterator::create_regexp_string_iterator( @@ -1414,18 +1432,18 @@ impl RegExp { }; // 7. Let flags be ? ToString(? Get(rx, "flags")). - let flags = rx.get(utf16!("flags"), context)?.to_string(context)?; + let flags = rx.get(js_str!("flags"), context)?.to_string(context)?; // 8. If flags contains "g", let global be true. Otherwise, let global be false. - let global = flags.as_slice().contains(&u16::from(b'g')); + let global = flags.contains(b'g'); // 9. If global is true, then let full_unicode = if global { // a. If flags contains "u", let fullUnicode be true. Otherwise, let fullUnicode be false. - let full_unicode = flags.contains(&u16::from(b'u')); + let full_unicode = flags.contains(b'u'); // b. Perform ? Set(rx, "lastIndex", +0𝔽, true). - rx.set(utf16!("lastIndex"), 0, true, context)?; + rx.set(js_str!("lastIndex"), 0, true, context)?; full_unicode } else { @@ -1467,13 +1485,18 @@ impl RegExp { // 2. If matchStr is the empty String, then if match_str.is_empty() { // a. Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))). - let this_index = rx.get(utf16!("lastIndex"), context)?.to_length(context)?; + let this_index = rx.get(js_str!("lastIndex"), context)?.to_length(context)?; // b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). let next_index = advance_string_index(&s, this_index, full_unicode); // c. Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true). - rx.set(utf16!("lastIndex"), JsValue::new(next_index), true, context)?; + rx.set( + js_str!("lastIndex"), + JsValue::new(next_index), + true, + context, + )?; } } @@ -1502,7 +1525,7 @@ impl RegExp { // e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")). let position = result - .get(utf16!("index"), context)? + .get(js_str!("index"), context)? .to_integer_or_infinity(context)?; // f. Set position to the result of clamping position between 0 and lengthS. @@ -1534,7 +1557,7 @@ impl RegExp { } // j. Let namedCaptures be ? Get(result, "groups"). - let mut named_captures = result.get(utf16!("groups"), context)?; + let mut named_captures = result.get(js_str!("groups"), context)?; let replacement = match replace_value { // k. If functionalReplace is true, then @@ -1587,8 +1610,8 @@ impl RegExp { // In such cases, the corresponding substitution is ignored. // ii. Set accumulatedResult to the string-concatenation of accumulatedResult, the substring of S from nextSourcePosition to position, and replacement. - accumulated_result.extend_from_slice(&s[next_source_position..position]); - accumulated_result.extend_from_slice(&replacement); + accumulated_result.extend(s.get_expect(next_source_position..position).iter()); + accumulated_result.extend(replacement.iter()); // iii. Set nextSourcePosition to position + matchLength. next_source_position = position + match_length; @@ -1597,11 +1620,15 @@ impl RegExp { // 16. If nextSourcePosition ≥ lengthS, return accumulatedResult. if next_source_position >= length_s { - return Ok(js_string!(accumulated_result).into()); + return Ok(js_string!(&accumulated_result[..]).into()); } // 17. Return the string-concatenation of accumulatedResult and the substring of S from nextSourcePosition. - Ok(js_string!(&accumulated_result[..], &s[next_source_position..]).into()) + Ok(js_string!( + &JsString::from(&accumulated_result[..]), + s.get_expect(next_source_position..) + ) + .into()) } /// `RegExp.prototype[ @@search ]( string )` @@ -1630,31 +1657,31 @@ impl RegExp { let arg_str = args.get_or_undefined(0).to_string(context)?; // 4. Let previousLastIndex be ? Get(rx, "lastIndex"). - let previous_last_index = rx.get(utf16!("lastIndex"), context)?; + let previous_last_index = rx.get(js_str!("lastIndex"), context)?; // 5. If SameValue(previousLastIndex, +0𝔽) is false, then if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) { // a. Perform ? Set(rx, "lastIndex", +0𝔽, true). - rx.set(utf16!("lastIndex"), 0, true, context)?; + rx.set(js_str!("lastIndex"), 0, true, context)?; } // 6. Let result be ? RegExpExec(rx, S). let result = Self::abstract_exec(rx, arg_str, context)?; // 7. Let currentLastIndex be ? Get(rx, "lastIndex"). - let current_last_index = rx.get(utf16!("lastIndex"), context)?; + let current_last_index = rx.get(js_str!("lastIndex"), context)?; // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then if !JsValue::same_value(¤t_last_index, &previous_last_index) { // a. Perform ? Set(rx, "lastIndex", previousLastIndex, true). - rx.set(utf16!("lastIndex"), previous_last_index, true, context)?; + rx.set(js_str!("lastIndex"), previous_last_index, true, context)?; } // 9. If result is null, return -1𝔽. // 10. Return ? Get(result, "index"). result.map_or_else( || Ok(JsValue::new(-1)), - |result| result.get(utf16!("index"), context), + |result| result.get(js_str!("index"), context), ) } @@ -1687,18 +1714,18 @@ impl RegExp { let constructor = rx.species_constructor(StandardConstructors::regexp, context)?; // 5. Let flags be ? ToString(? Get(rx, "flags")). - let flags = rx.get(utf16!("flags"), context)?.to_string(context)?; + let flags = rx.get(js_str!("flags"), context)?.to_string(context)?; // 6. If flags contains "u", let unicodeMatching be true. // 7. Else, let unicodeMatching be false. - let unicode = flags.contains(&('u' as u16)); + let unicode = flags.contains(b'u'); // 8. If flags contains "y", let newFlags be flags. // 9. Else, let newFlags be the string-concatenation of flags and "y". - let new_flags = if flags.contains(&('y' as u16)) { + let new_flags = if flags.contains(b'y') { flags } else { - js_string!(&flags, utf16!("y")) + js_string!(&flags, js_str!("y")) }; // 10. Let splitter be ? Construct(C, « rx, newFlags »). @@ -1756,7 +1783,7 @@ impl RegExp { // 19. Repeat, while q < size, while q < size { // a. Perform ? Set(splitter, "lastIndex", 𝔽(q), true). - splitter.set(utf16!("lastIndex"), JsValue::new(q), true, context)?; + splitter.set(js_str!("lastIndex"), JsValue::new(q), true, context)?; // b. Let z be ? RegExpExec(splitter, S). let result = Self::abstract_exec(&splitter, arg_str.clone(), context)?; @@ -1766,7 +1793,7 @@ impl RegExp { if let Some(result) = result { // i. Let e be ℝ(? ToLength(? Get(splitter, "lastIndex"))). let mut e = splitter - .get(utf16!("lastIndex"), context)? + .get(js_str!("lastIndex"), context)? .to_length(context)?; // ii. Set e to min(e, size). @@ -1778,7 +1805,7 @@ impl RegExp { q = advance_string_index(&arg_str, q, unicode); } else { // 1. Let T be the substring of S from p to q. - let arg_str_substring = js_string!(&arg_str[p as usize..q as usize]); + let arg_str_substring = arg_str.get_expect(p as usize..q as usize); // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). a.create_data_property_or_throw(length_a, arg_str_substring, context) @@ -1829,7 +1856,7 @@ impl RegExp { } // 20. Let T be the substring of S from p to size. - let arg_str_substring = js_string!(&arg_str[p as usize..size as usize]); + let arg_str_substring = arg_str.get_expect(p as usize..size as usize); // 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T). a.create_data_property_or_throw(length_a, arg_str_substring, context) @@ -1890,7 +1917,7 @@ impl RegExp { .expect("already checked that the object was a RegExp") = regexp; } - this.set(utf16!("lastIndex"), 0, true, context)?; + this.set(js_str!("lastIndex"), 0, true, context)?; Ok(this.into()) } diff --git a/core/engine/src/builtins/regexp/regexp_string_iterator.rs b/core/engine/src/builtins/regexp/regexp_string_iterator.rs index 79772ab710a..2cb8620a8a5 100644 --- a/core/engine/src/builtins/regexp/regexp_string_iterator.rs +++ b/core/engine/src/builtins/regexp/regexp_string_iterator.rs @@ -18,11 +18,11 @@ use crate::{ object::JsObject, property::Attribute, realm::Realm, - string::utf16, symbol::JsSymbol, Context, JsData, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use regexp::{advance_string_index, RegExp}; @@ -160,7 +160,7 @@ impl RegExpStringIterator { // 1. Let thisIndex be ℝ(? ToLength(? Get(R, "lastIndex"))). let this_index = iterator .matcher - .get(utf16!("lastIndex"), context)? + .get(js_str!("lastIndex"), context)? .to_length(context)?; // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode). @@ -170,7 +170,7 @@ impl RegExpStringIterator { // 3. Perform ? Set(R, "lastIndex", 𝔽(nextIndex), true). iterator .matcher - .set(utf16!("lastIndex"), next_index, true, context)?; + .set(js_str!("lastIndex"), next_index, true, context)?; } // vi. Perform ? Yield(match). diff --git a/core/engine/src/builtins/regexp/tests.rs b/core/engine/src/builtins/regexp/tests.rs index ac711fd0ec9..dd0c45a2e05 100644 --- a/core/engine/src/builtins/regexp/tests.rs +++ b/core/engine/src/builtins/regexp/tests.rs @@ -2,6 +2,7 @@ use crate::{ js_string, native_function::NativeFunctionObject, run_test_actions, JsNativeErrorKind, JsObject, JsValue, TestAction, }; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -36,7 +37,7 @@ fn species() { // return-value TestAction::assert("Object.is(accessor.call(thisVal), thisVal)"), // symbol-species-name - TestAction::assert_eq("name.value", js_string!("get [Symbol.species]")), + TestAction::assert_eq("name.value", js_str!("get [Symbol.species]")), TestAction::assert("!name.enumerable"), TestAction::assert("!name.writable"), TestAction::assert("name.configurable"), @@ -65,7 +66,7 @@ fn flags() { TestAction::assert("!re_gi.dotAll"), TestAction::assert("!re_gi.unicode"), TestAction::assert("!re_gi.sticky"), - TestAction::assert_eq("re_gi.flags", js_string!("gi")), + TestAction::assert_eq("re_gi.flags", js_str!("gi")), // TestAction::assert("!re_sm.global"), TestAction::assert("!re_sm.ignoreCase"), @@ -73,7 +74,7 @@ fn flags() { TestAction::assert("re_sm.dotAll"), TestAction::assert("!re_sm.unicode"), TestAction::assert("!re_sm.sticky"), - TestAction::assert_eq("re_sm.flags", js_string!("ms")), + TestAction::assert_eq("re_sm.flags", js_str!("ms")), // TestAction::assert("!re_u.global"), TestAction::assert("!re_u.ignoreCase"), @@ -81,7 +82,7 @@ fn flags() { TestAction::assert("!re_u.dotAll"), TestAction::assert("re_u.unicode"), TestAction::assert("!re_u.sticky"), - TestAction::assert_eq("re_u.flags", js_string!("u")), + TestAction::assert_eq("re_u.flags", js_str!("u")), ]); } @@ -114,7 +115,7 @@ fn exec() { TestAction::assert_eq("result.index", 4), TestAction::assert_eq( "result.input", - js_string!("The Quick Brown Fox Jumps Over The Lazy Dog"), + js_str!("The Quick Brown Fox Jumps Over The Lazy Dog"), ), ]); } @@ -138,8 +139,8 @@ fn no_panic_on_parse_fail() { #[test] fn to_string() { run_test_actions([ - TestAction::assert_eq("(new RegExp('a+b+c')).toString()", js_string!("/a+b+c/")), - TestAction::assert_eq("(new RegExp('bar', 'g')).toString()", js_string!("/bar/g")), + TestAction::assert_eq("(new RegExp('a+b+c')).toString()", js_str!("/a+b+c/")), + TestAction::assert_eq("(new RegExp('bar', 'g')).toString()", js_str!("/bar/g")), TestAction::assert_eq(r"(new RegExp('\\n', 'g')).toString()", js_string!(r"/\n/g")), TestAction::assert_eq(r"/\n/g.toString()", js_string!(r"/\n/g")), TestAction::assert_eq(r"/,\;/.toString()", js_string!(r"/,\;/")), @@ -164,7 +165,7 @@ fn search() { TestAction::assert("!length.writable"), TestAction::assert("length.configurable"), // name - TestAction::assert_eq("name.value", js_string!("[Symbol.search]")), + TestAction::assert_eq("name.value", js_str!("[Symbol.search]")), TestAction::assert("!name.enumerable"), TestAction::assert("!name.writable"), TestAction::assert("name.configurable"), diff --git a/core/engine/src/builtins/set/mod.rs b/core/engine/src/builtins/set/mod.rs index 4c3d52d208e..a82f8c973fc 100644 --- a/core/engine/src/builtins/set/mod.rs +++ b/core/engine/src/builtins/set/mod.rs @@ -26,10 +26,11 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::js_str; use boa_profiler::Profiler; use num_traits::Zero; @@ -71,18 +72,18 @@ impl IntrinsicObject for Set { .method(Self::for_each, js_string!("forEach"), 1) .method(Self::has, js_string!("has"), 1) .property( - utf16!("keys"), + js_string!("keys"), values_function.clone(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .accessor( - utf16!("size"), + js_string!("size"), Some(size_getter), None, Attribute::CONFIGURABLE, ) .property( - utf16!("values"), + js_string!("values"), values_function.clone(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) @@ -138,7 +139,7 @@ impl BuiltInConstructor for Set { } // 5. Let adder be ? Get(set, "add"). - let adder = set.get(utf16!("add"), context)?; + let adder = set.get(js_str!("add"), context)?; // 6. If IsCallable(adder) is false, throw a TypeError exception. let adder = adder.as_callable().ok_or_else(|| { diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index 18b30bae143..a49f08a1866 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -17,12 +17,12 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyDescriptor}, realm::Realm, - string::{common::StaticJsStrings, utf16}, - string::{CodePoint, Utf16Trim}, + string::{common::StaticJsStrings, CodePoint}, symbol::JsSymbol, value::IntegerOrInfinity, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::{js_str, utf16}; use boa_profiler::Profiler; use icu_normalizer::{ComposingNormalizer, DecomposingNormalizer}; use std::cmp::{max, min}; @@ -32,6 +32,9 @@ use super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject}; mod string_iterator; pub(crate) use string_iterator::StringIterator; +#[cfg(feature = "annex-b")] +pub use crate::JsStr; + /// The set of normalizers required for the `String.prototype.normalize` function. #[derive(Debug)] pub(crate) struct StringNormalizers { @@ -70,6 +73,23 @@ pub(crate) const fn is_trimmable_whitespace(c: char) -> bool { ) } +/// Helper function to check if a `u8` latin1 character is trimmable. +pub(crate) const fn is_trimmable_whitespace_latin1(c: u8) -> bool { + // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does + // + // Rust uses \p{White_Space} by default, which also includes: + // `\u{0085}' (next line) + // And does not include: + // '\u{FEFF}' (zero width non-breaking space) + // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space + matches!( + c, + 0x09 | 0x0B | 0x0C | 0x20 | 0xA0 | + // Line terminators: https://tc39.es/ecma262/#sec-line-terminators + 0x0A | 0x0D + ) +} + /// JavaScript `String` implementation. #[derive(Debug, Clone, Copy)] pub(crate) struct String; @@ -382,7 +402,7 @@ impl String { let cooked = args.get_or_undefined(0).to_object(context)?; // 3. Let raw be ? ToObject(? Get(cooked, "raw")). - let raw = cooked.get(utf16!("raw"), context)?.to_object(context)?; + let raw = cooked.get(js_str!("raw"), context)?.to_object(context)?; // 4. Let literalSegments be ? LengthOfArrayLike(raw). let literal_segments = raw.length_of_array_like(context)?; @@ -407,13 +427,13 @@ impl String { let next_seg = raw.get(next_key, context)?.to_string(context)?; // c. Append the code unit elements of nextSeg to the end of stringElements. - string_elements.extend(next_seg.iter().copied()); + string_elements.extend(next_seg.iter()); // d. If nextIndex + 1 = literalSegments, then if next_index + 1 == literal_segments { // i. Return the String value whose code units are the elements in the List stringElements. // If stringElements has no elements, the empty String is returned. - return Ok(js_string!(string_elements).into()); + return Ok(js_string!(&string_elements[..]).into()); } // e. If nextIndex < numberOfSubstitutions, let next be substitutions[nextIndex]. @@ -429,7 +449,7 @@ impl String { let next_sub = next.to_string(context)?; // h. Append the code unit elements of nextSub to the end of stringElements. - string_elements.extend(next_sub.iter().copied()); + string_elements.extend(next_sub.iter()); // i. Set nextIndex to nextIndex + 1. next_index += 1; @@ -461,7 +481,7 @@ impl String { } // 3. Return result. - Ok(js_string!(result).into()) + Ok(js_string!(&result[..]).into()) } /// `String.prototype.toString ( )` @@ -511,7 +531,7 @@ impl String { // 6. Return the substring of S from position to position + 1. IntegerOrInfinity::Integer(i) if i >= 0 && i < string.len() as i64 => { let i = i as usize; - Ok(js_string!(&string[i..=i]).into()) + Ok(js_string!(string.get_expect(i..=i)).into()) } // 5. If position < 0 or position ≥ size, return the empty String. _ => Ok(js_string!().into()), @@ -553,7 +573,7 @@ impl String { }; // 8. Return the substring of S from k to k + 1. - Ok(js_string!(&s[k..=k]).into()) + Ok(js_string!(s.get_expect(k..=k)).into()) } /// `String.prototype.codePointAt( index )` @@ -629,9 +649,11 @@ impl String { match position { // 4. Let size be the length of S. - IntegerOrInfinity::Integer(i) if i >= 0 && i < string.len() as i64 => { + IntegerOrInfinity::Integer(i) if i >= 0 => { // 6. Return the Number value for the numeric value of the code unit at index position within the String S. - Ok(u32::from(string[i as usize]).into()) + Ok(string + .get(i as usize) + .map_or_else(JsValue::nan, JsValue::from)) } // 5. If position < 0 or position ≥ size, return NaN. _ => Ok(JsValue::nan()), @@ -708,14 +730,14 @@ impl String { return Ok(js_string!().into()); } let n = n as usize; - let mut result = Vec::with_capacity(n * len); + let mut result = Vec::with_capacity(n); - std::iter::repeat(&string[..]) + std::iter::repeat(string.as_str()) .take(n) - .for_each(|s| result.extend_from_slice(s)); + .for_each(|s| result.push(s)); // 6. Return the String value that is made from n copies of S appended together. - Ok(js_string!(result).into()) + Ok(JsString::concat_array(&result).into()) } // 5. If n is 0, return the empty String. IntegerOrInfinity::Integer(0) => Ok(js_string!().into()), @@ -790,7 +812,7 @@ impl String { Ok(js_string!().into()) } else { // 13. Return the substring of S from from to to. - Ok(js_string!(&string[from..to]).into()) + Ok(js_string!(string.get_expect(from..to)).into()) } } @@ -859,7 +881,7 @@ impl String { // 14. Return ! SameValueNonNumeric(substring, searchStr). // `SameValueNonNumeric` forwards to `==`, so directly check // equality to avoid converting to `JsValue` - Ok(JsValue::new(search_string == string[start..end])) + Ok(JsValue::new(search_string == string.get_expect(start..end))) } } @@ -922,7 +944,7 @@ impl String { // 14. Return ! SameValueNonNumeric(substring, searchStr). // `SameValueNonNumeric` forwards to `==`, so directly check // equality to avoid converting to `JsValue` - Ok(JsValue::new(search_str == string[start..end])) + Ok(JsValue::new(search_str == string.get_expect(start..end))) } else { // 12. If start < 0, return false. Ok(false.into()) @@ -973,7 +995,7 @@ impl String { // 10. Let index be ! StringIndexOf(S, searchStr, start). // 11. If index is not -1, return true. // 12. Return false. - Ok(string.index_of(&search_str, start).is_some().into()) + Ok(string.index_of(search_str.as_str(), start).is_some().into()) } /// `String.prototype.replace( regexp|substr, newSubstr|function )` @@ -1042,12 +1064,12 @@ impl String { // 8. Let position be ! StringIndexOf(string, searchString, 0). // 9. If position is -1, return string. - let Some(position) = string.index_of(&search_string, 0) else { + let Some(position) = string.index_of(search_string.as_str(), 0) else { return Ok(string.into()); }; // 10. Let preserved be the substring of string from 0 to position. - let preserved = &string[..position]; + let preserved = JsString::from(string.get_expect(..position)); let replacement = match replace_value { // 11. If functionalReplace is true, then @@ -1081,7 +1103,12 @@ impl String { }; // 13. Return the string-concatenation of preserved, replacement, and the substring of string from position + searchLength. - Ok(js_string!(preserved, &replacement, &string[position + search_length..]).into()) + Ok(js_string!( + &preserved, + &replacement, + &JsString::from(string.get_expect(position + search_length..)) + ) + .into()) } /// `22.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )` @@ -1117,13 +1144,13 @@ impl String { // b. If isRegExp is true, then if let Some(obj) = RegExp::is_reg_exp(search_value, context)? { // i. Let flags be ? Get(searchValue, "flags"). - let flags = obj.get(utf16!("flags"), context)?; + let flags = obj.get(js_str!("flags"), context)?; // ii. Perform ? RequireObjectCoercible(flags). flags.require_object_coercible()?; // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. - if !flags.to_string(context)?.contains(&u16::from(b'g')) { + if !flags.to_string(context)?.contains(b'g') { return Err(JsNativeError::typ() .with_message( "String.prototype.replaceAll called with a non-global RegExp argument", @@ -1167,7 +1194,7 @@ impl String { let mut match_positions = Vec::new(); // 10. Let position be ! StringIndexOf(string, searchString, 0). - let mut position = string.index_of(&search_string, 0); + let mut position = string.index_of(search_string.as_str(), 0); // 11. Repeat, while position is not -1, while let Some(p) = position { @@ -1175,7 +1202,7 @@ impl String { match_positions.push(p); // b. Set position to ! StringIndexOf(string, searchString, position + advanceBy). - position = string.index_of(&search_string, p + advance_by); + position = string.index_of(search_string.as_str(), p + advance_by); } // 12. Let endOfLastMatch be 0. @@ -1187,7 +1214,7 @@ impl String { // 14. For each element p of matchPositions, do for p in match_positions { // a. Let preserved be the substring of string from endOfLastMatch to p. - let preserved = &string[end_of_last_match..p]; + let preserved = string.get_expect(end_of_last_match..p); // c. Else, let replacement = match replace { @@ -1222,8 +1249,8 @@ impl String { }; // d. Set result to the string-concatenation of result, preserved, and replacement. - result.extend_from_slice(preserved); - result.extend_from_slice(&replacement); + result.extend(preserved.iter()); + result.extend(replacement.iter()); // e. Set endOfLastMatch to p + searchLength. end_of_last_match = p + search_length; @@ -1232,11 +1259,11 @@ impl String { // 15. If endOfLastMatch < the length of string, then if end_of_last_match < string.len() { // a. Set result to the string-concatenation of result and the substring of string from endOfLastMatch. - result.extend_from_slice(&string[end_of_last_match..]); + result.extend(string.get_expect(end_of_last_match..).iter()); } // 16. Return result. - Ok(js_string!(result).into()) + Ok(js_string!(&result[..]).into()) } /// `String.prototype.indexOf( searchValue[, fromIndex] )` @@ -1278,7 +1305,7 @@ impl String { // 8. Return 𝔽(! StringIndexOf(S, searchStr, start)). Ok(string - .index_of(&search_str, start) + .index_of(search_str.as_str(), start) .map_or(-1, |i| i as i64) .into()) } @@ -1368,10 +1395,10 @@ impl String { // 11. For each non-negative integer i starting with start such that i ≤ len - searchLen, in descending order, do for i in (0..=min(start, end)).rev() { // a. Let candidate be the substring of S from i to i + searchLen. - let candidate = &string[i..i + search_len]; + let candidate = string.get_expect(i..i + search_len); // b. If candidate is the same sequence of code units as searchStr, return 𝔽(i). - if candidate == &search_str { + if candidate == search_str { return Ok(i.into()); } } @@ -1431,6 +1458,9 @@ impl String { .expect("constructor must return a `Collator` object") .collator(); + let s = s.iter().collect::>(); + let that_value = that_value.iter().collect::>(); + collator.compare_utf16(&s, &that_value) as i8 } @@ -1544,15 +1574,15 @@ impl String { } }; - let truncated_string_filler = filler.repeat(repetitions as usize); - let truncated_string_filler = &truncated_string_filler[..fill_len as usize]; + let truncated_string_filler = filler.to_vec().repeat(repetitions as usize); + let truncated_string_filler = JsString::from(&truncated_string_filler[..fill_len as usize]); // 10. If placement is start, return the string-concatenation of truncatedStringFiller and S. if placement == Placement::Start { - Ok(js_string!(truncated_string_filler, &string).into()) + Ok(js_string!(&truncated_string_filler, &string).into()) } else { // 11. Else, return the string-concatenation of S and truncatedStringFiller. - Ok(js_string!(&string, truncated_string_filler).into()) + Ok(js_string!(&string, &truncated_string_filler).into()) } } @@ -1876,7 +1906,7 @@ impl String { let to = max(final_start, final_end); // 10. Return the substring of S from from to to. - Ok(js_string!(&string[from..to]).into()) + Ok(js_string!(string.get_expect(from..to)).into()) } /// `String.prototype.split ( separator, limit )` @@ -1946,9 +1976,10 @@ impl String { // b. Let codeUnits be a List consisting of the sequence of code units that are the elements of head. let head = this_str .get(..lim) - .unwrap_or(&this_str[..]) + .unwrap_or(this_str.as_str()) .iter() - .map(|code| js_string!(std::slice::from_ref(code)).into()); + .map(|code| js_string!(std::slice::from_ref(&code)).into()); + // c. Return ! CreateArrayFromList(codeUnits). return Ok(Array::create_array_from_list(head, context).into()); } @@ -1965,13 +1996,13 @@ impl String { let mut i = 0; // 13. Let j be ! StringIndexOf(S, R, 0). - let mut j = this_str.index_of(&separator_str, 0); + let mut j = this_str.index_of(separator_str.as_str(), 0); // 14. Repeat, while j is not -1 while let Some(index) = j { // a. Let T be the substring of S from i to j. // b. Append T as the last element of substrings. - substrings.push(js_string!(&this_str[i..index])); + substrings.push(this_str.get_expect(i..index).into()); // c. If the number of elements of substrings is lim, return ! CreateArrayFromList(substrings). if substrings.len() == lim { @@ -1985,12 +2016,12 @@ impl String { i = index + separator_length; // e. Set j to ! StringIndexOf(S, R, i). - j = this_str.index_of(&separator_str, i); + j = this_str.index_of(separator_str.as_str(), i); } // 15. Let T be the substring of S from i. // 16. Append T to substrings. - substrings.push(js_string!(&this_str[i..])); + substrings.push(JsString::from(this_str.get_expect(i..))); // 17. Return ! CreateArrayFromList(substrings). Ok( @@ -2045,13 +2076,13 @@ impl String { // b. If isRegExp is true, then if let Some(regexp) = RegExp::is_reg_exp(regexp, context)? { // i. Let flags be ? Get(regexp, "flags"). - let flags = regexp.get(utf16!("flags"), context)?; + let flags = regexp.get(js_str!("flags"), context)?; // ii. Perform ? RequireObjectCoercible(flags). flags.require_object_coercible()?; // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception. - if !flags.to_string(context)?.contains(&u16::from(b'g')) { + if !flags.to_string(context)?.contains(b'g') { return Err(JsNativeError::typ() .with_message( "String.prototype.matchAll called with a non-global RegExp argument", @@ -2071,7 +2102,7 @@ impl String { let s = o.to_string(context)?; // 4. Let rx be ? RegExpCreate(regexp, "g"). - let rx = RegExp::create(regexp, &JsValue::new(js_string!("g")), context)?; + let rx = RegExp::create(regexp, &JsValue::new(js_str!("g")), context)?; // 5. Return ? Invoke(rx, @@matchAll, « S »). rx.invoke(JsSymbol::match_all(), &[JsValue::new(s)], context) @@ -2115,10 +2146,10 @@ impl String { &JsValue::Undefined => Normalization::Nfc, // 4. Else, let f be ? ToString(form). f => match f.to_string(context)? { - ntype if &ntype == utf16!("NFC") => Normalization::Nfc, - ntype if &ntype == utf16!("NFD") => Normalization::Nfd, - ntype if &ntype == utf16!("NFKC") => Normalization::Nfkc, - ntype if &ntype == utf16!("NFKD") => Normalization::Nfkd, + ntype if &ntype == "NFC" => Normalization::Nfc, + ntype if &ntype == "NFD" => Normalization::Nfd, + ntype if &ntype == "NFKC" => Normalization::Nfkc, + ntype if &ntype == "NFKD" => Normalization::Nfkd, // 5. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError exception. _ => { return Err(JsNativeError::range() @@ -2147,6 +2178,8 @@ impl String { } }; + let s = s.iter().collect::>(); + let result = match normalization { Normalization::Nfc => normalizers.nfc.normalize_utf16(&s), Normalization::Nfd => normalizers.nfd.normalize_utf16(&s), @@ -2155,7 +2188,7 @@ impl String { }; // 7. Return ns. - Ok(js_string!(result).into()) + Ok(js_string!(&result[..]).into()) } /// `String.prototype.search( regexp )` @@ -2276,7 +2309,7 @@ impl String { // 11. Return the substring of S from intStart to intEnd. if let Some(substr) = s.get(int_start..int_end) { - Ok(js_string!(substr).into()) + Ok(substr.into()) } else { Ok(js_string!().into()) } @@ -2290,8 +2323,8 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-createhtml pub(crate) fn create_html( string: &JsValue, - tag: &[u16], - attribute_and_value: Option<(&[u16], &JsValue)>, + tag: JsStr<'_>, + attribute_and_value: Option<(JsStr<'_>, &JsValue)>, context: &mut Context, ) -> JsResult { // 1. Let str be ? RequireObjectCoercible(string). @@ -2301,7 +2334,7 @@ impl String { let s = str.to_string(context)?; // 3. Let p1 be the string-concatenation of "<" and tag. - let mut p1 = JsString::concat_array(&[utf16!("<"), tag]); + let mut p1 = js_string!(js_str!("<"), tag); // 4. If attribute is not the empty String, then if let Some((attribute, value)) = attribute_and_value { @@ -2312,7 +2345,7 @@ impl String { // of the code unit 0x0022 (QUOTATION MARK) in V has been replaced with the six // code unit sequence """. let mut escaped_v = Vec::with_capacity(v.len()); - for c in v.as_slice().iter().copied() { + for c in &v { if c == 0x0022 { escaped_v.extend(utf16!(""")); continue; @@ -2328,27 +2361,20 @@ impl String { // the code unit 0x0022 (QUOTATION MARK) // escapedV // the code unit 0x0022 (QUOTATION MARK) - p1 = JsString::concat_array(&[ - p1.as_slice(), - utf16!(" "), + p1 = js_string!( + &p1, + js_str!(" "), attribute, - utf16!("=\""), - escaped_v.as_slice(), - utf16!("\""), - ]); + js_str!("=\""), + &JsString::from(&escaped_v[..]), + js_str!("\"") + ); } // 5. Let p2 be the string-concatenation of p1 and ">". // 6. Let p3 be the string-concatenation of p2 and S. // 7. Let p4 be the string-concatenation of p3, "". - let p4 = JsString::concat_array(&[ - p1.as_slice(), - utf16!(">"), - s.as_slice(), - utf16!(""), - ]); + let p4 = js_string!(&p1, js_str!(">"), &s, js_str!("")); // 8. Return p4. Ok(p4.into()) @@ -2372,7 +2398,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "a", "name", name). - Self::create_html(s, utf16!("a"), Some((utf16!("name"), name)), context) + Self::create_html(s, js_str!("a"), Some((js_str!("name"), name)), context) } /// `String.prototype.big( )` @@ -2387,7 +2413,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "big", "", ""). - Self::create_html(s, utf16!("big"), None, context) + Self::create_html(s, js_str!("big"), None, context) } /// `String.prototype.blink( )` @@ -2402,7 +2428,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "blink", "", ""). - Self::create_html(s, utf16!("blink"), None, context) + Self::create_html(s, js_str!("blink"), None, context) } /// `String.prototype.bold( )` @@ -2417,7 +2443,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "b", "", ""). - Self::create_html(s, utf16!("b"), None, context) + Self::create_html(s, js_str!("b"), None, context) } /// `String.prototype.fixed( )` @@ -2432,7 +2458,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "big", "", ""). - Self::create_html(s, utf16!("tt"), None, context) + Self::create_html(s, js_str!("tt"), None, context) } /// `String.prototype.fontcolor( color )` @@ -2453,7 +2479,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "font", "color", color). - Self::create_html(s, utf16!("font"), Some((utf16!("color"), color)), context) + Self::create_html(s, js_str!("font"), Some((js_str!("color"), color)), context) } /// `String.prototype.fontsize( size )` @@ -2474,7 +2500,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "font", "size", size). - Self::create_html(s, utf16!("font"), Some((utf16!("size"), size)), context) + Self::create_html(s, js_str!("font"), Some((js_str!("size"), size)), context) } /// `String.prototype.italics( )` @@ -2493,7 +2519,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "i", "", ""). - Self::create_html(s, utf16!("i"), None, context) + Self::create_html(s, js_str!("i"), None, context) } /// `String.prototype.link( url )` @@ -2514,7 +2540,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "a", "href", url). - Self::create_html(s, utf16!("a"), Some((utf16!("href"), url)), context) + Self::create_html(s, js_str!("a"), Some((js_str!("href"), url)), context) } /// `String.prototype.small( )` @@ -2529,7 +2555,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "small", "", ""). - Self::create_html(s, utf16!("small"), None, context) + Self::create_html(s, js_str!("small"), None, context) } /// `String.prototype.strike( )` @@ -2548,7 +2574,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "strike", "", ""). - Self::create_html(s, utf16!("strike"), None, context) + Self::create_html(s, js_str!("strike"), None, context) } /// `String.prototype.sub( )` @@ -2563,7 +2589,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "sub", "", ""). - Self::create_html(s, utf16!("sub"), None, context) + Self::create_html(s, js_str!("sub"), None, context) } /// `String.prototype.sup( )` @@ -2578,7 +2604,7 @@ impl String { // 1. Let S be the this value. let s = this; // 2. Return ? CreateHTML(S, "sup", "", ""). - Self::create_html(s, utf16!("sup"), None, context) + Self::create_html(s, js_str!("sup"), None, context) } } @@ -2639,19 +2665,19 @@ pub(crate) fn get_substitution( // $& Some(CodePoint::Unicode('&')) => { // matched - result.extend_from_slice(matched); + result.extend(matched.iter()); } // $` Some(CodePoint::Unicode('`')) => { // The replacement is the substring of str from 0 to position. - result.extend_from_slice(&str[..position]); + result.extend(str.get_expect(..position).iter()); } // $' Some(CodePoint::Unicode('\'')) => { // If tailPos ≥ stringLength, the replacement is the empty String. // Otherwise the replacement is the substring of str from tailPos. if tail_pos < str_length { - result.extend_from_slice(&str[tail_pos..]); + result.extend(str.get_expect(tail_pos..).iter()); } } // $nn @@ -2698,7 +2724,7 @@ pub(crate) fn get_substitution( // a. Let refReplacement be capture. if let Some(capture) = captures.get(index - 1) { if let Some(s) = capture.as_string() { - result.extend_from_slice(s); + result.extend(s.iter()); } } @@ -2742,14 +2768,14 @@ pub(crate) fn get_substitution( // d. Else, } else { // i. Let groupName be the enclosed substring. - let group_name = js_string!(group_name); + let group_name = js_string!(&group_name[..]); // ii. Let capture be ? Get(namedCaptures, groupName). let capture = named_captures.get(group_name, context)?; // iii. If capture is undefined, replace the text through > with the empty String. // iv. Otherwise, replace the text through > with ? ToString(capture). if !capture.is_undefined() { - result.extend_from_slice(&capture.to_string(context)?); + result.extend(capture.to_string(context)?.iter()); } } } @@ -2768,5 +2794,5 @@ pub(crate) fn get_substitution( } // 11. Return result. - Ok(js_string!(result)) + Ok(js_string!(&result[..])) } diff --git a/core/engine/src/builtins/string/tests.rs b/core/engine/src/builtins/string/tests.rs index 5adc115c196..80d4e42f341 100644 --- a/core/engine/src/builtins/string/tests.rs +++ b/core/engine/src/builtins/string/tests.rs @@ -1,3 +1,4 @@ +use boa_macros::js_str; use indoc::indoc; use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; @@ -56,11 +57,11 @@ fn concat() { "#}), TestAction::assert_eq( "hello.concat(world, nice)", - js_string!("Hello, world! Have a nice day."), + js_str!("Hello, world! Have a nice day."), ), TestAction::assert_eq( "hello + world + nice", - js_string!("Hello, world! Have a nice day."), + js_str!("Hello, world! Have a nice day."), ), ]); } @@ -72,10 +73,7 @@ fn generic_concat() { Number.prototype.concat = String.prototype.concat; let number = new Number(100); "#}), - TestAction::assert_eq( - "number.concat(' - 50', ' = 50')", - js_string!("100 - 50 = 50"), - ), + TestAction::assert_eq("number.concat(' - 50', ' = 50')", js_str!("100 - 50 = 50")), ]); } @@ -100,8 +98,8 @@ fn repeat() { TestAction::assert_eq("empty.repeat(1)", js_string!()), TestAction::assert_eq("en.repeat(0)", js_string!()), TestAction::assert_eq("zh.repeat(0)", js_string!()), - TestAction::assert_eq("en.repeat(1)", js_string!("english")), - TestAction::assert_eq("zh.repeat(2)", js_string!("中文中文")), + TestAction::assert_eq("en.repeat(1)", js_str!("english")), + TestAction::assert_eq("zh.repeat(2)", js_str!("中文中文")), ]); } @@ -140,9 +138,9 @@ fn repeat_generic() { run_test_actions([ TestAction::run("Number.prototype.repeat = String.prototype.repeat;"), TestAction::assert_eq("(0).repeat(0)", js_string!()), - TestAction::assert_eq("(1).repeat(1)", js_string!("1")), - TestAction::assert_eq("(1).repeat(5)", js_string!("11111")), - TestAction::assert_eq("(12).repeat(3)", js_string!("121212")), + TestAction::assert_eq("(1).repeat(1)", js_str!("1")), + TestAction::assert_eq("(1).repeat(5)", js_str!("11111")), + TestAction::assert_eq("(12).repeat(3)", js_str!("121212")), ]); } @@ -152,7 +150,7 @@ fn replace() { indoc! {r#" "abc".replace("a", "2") "#}, - js_string!("2bc"), + js_str!("2bc"), )]); } @@ -162,7 +160,7 @@ fn replace_no_match() { indoc! {r#" "abc".replace(/d/, "$&$&") "#}, - js_string!("abc"), + js_str!("abc"), )]); } @@ -172,7 +170,7 @@ fn replace_with_capture_groups() { indoc! {r#" "John Smith".replace(/(\w+)\s(\w+)/, '$2, $1') "#}, - js_string!("Smith, John"), + js_str!("Smith, John"), )]); } @@ -183,7 +181,7 @@ fn replace_with_tenth_capture_group() { var re = /(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)(\d)/; "0123456789".replace(re, '$10') "#}, - js_string!("9"), + js_str!("9"), )]); } @@ -199,11 +197,11 @@ fn replace_substitutions() { var end = a.replace(re, " $' "); var no_sub = a.replace(re, " $_ "); "#}), - TestAction::assert_eq("a.replace(re, \" $$ \")", js_string!("one $ three")), - TestAction::assert_eq("a.replace(re, \"$&$&\")", js_string!("one two two three")), - TestAction::assert_eq("a.replace(re, \" $` \")", js_string!("one one three")), - TestAction::assert_eq("a.replace(re, \" $' \")", js_string!("one three three")), - TestAction::assert_eq("a.replace(re, \" $_ \")", js_string!("one $_ three")), + TestAction::assert_eq("a.replace(re, \" $$ \")", js_str!("one $ three")), + TestAction::assert_eq("a.replace(re, \"$&$&\")", js_str!("one two two three")), + TestAction::assert_eq("a.replace(re, \" $` \")", js_str!("one one three")), + TestAction::assert_eq("a.replace(re, \" $' \")", js_str!("one three three")), + TestAction::assert_eq("a.replace(re, \" $_ \")", js_str!("one $_ three")), ]); } @@ -222,11 +220,11 @@ fn replace_with_function() { "#}), TestAction::assert_eq( "\"ecmascript is cool\".replace(/c(o)(o)(l)/, replacer)", - js_string!("ecmascript is awesome!"), + js_str!("ecmascript is awesome!"), ), - TestAction::assert_eq("p1", js_string!("o")), - TestAction::assert_eq("p2", js_string!("o")), - TestAction::assert_eq("p3", js_string!("l")), + TestAction::assert_eq("p1", js_str!("o")), + TestAction::assert_eq("p2", js_str!("o")), + TestAction::assert_eq("p3", js_str!("l")), TestAction::assert_eq("length", 14), ]); } @@ -329,7 +327,7 @@ fn match_all_one() { ) "#}), TestAction::assert_eq("m1.value.index", 0), - TestAction::assert_eq("m1.value.input", js_string!("test1test2")), + TestAction::assert_eq("m1.value.input", js_str!("test1test2")), TestAction::assert_eq("m1.value.groups", JsValue::undefined()), TestAction::assert(indoc! {r#" arrayEquals( @@ -338,7 +336,7 @@ fn match_all_one() { ) "#}), TestAction::assert_eq("m2.value.index", 5), - TestAction::assert_eq("m2.value.input", js_string!("test1test2")), + TestAction::assert_eq("m2.value.input", js_str!("test1test2")), TestAction::assert_eq("m2.value.groups", JsValue::undefined()), TestAction::assert_eq("m3.value", JsValue::undefined()), ]); @@ -366,7 +364,7 @@ fn match_all_two() { ) "#}), TestAction::assert_eq("m1.value.index", 6), - TestAction::assert_eq("m1.value.input", js_string!("table football, foosball")), + TestAction::assert_eq("m1.value.input", js_str!("table football, foosball")), TestAction::assert_eq("m1.value.groups", JsValue::undefined()), TestAction::assert(indoc! {r#" arrayEquals( @@ -375,7 +373,7 @@ fn match_all_two() { ) "#}), TestAction::assert_eq("m2.value.index", 16), - TestAction::assert_eq("m2.value.input", js_string!("table football, foosball")), + TestAction::assert_eq("m2.value.input", js_str!("table football, foosball")), TestAction::assert_eq("m2.value.groups", JsValue::undefined()), TestAction::assert_eq("m3.value", JsValue::undefined()), ]); @@ -401,7 +399,7 @@ fn test_match() { TestAction::assert_eq("result1.index", 4), TestAction::assert_eq( "result1.input", - js_string!("The Quick Brown Fox Jumps Over The Lazy Dog"), + js_str!("The Quick Brown Fox Jumps Over The Lazy Dog"), ), TestAction::assert(indoc! {r#" arrayEquals( @@ -418,7 +416,7 @@ fn test_match() { TestAction::assert_eq("result3.index", 0), TestAction::assert_eq( "result3.input", - js_string!("The Quick Brown Fox Jumps Over The Lazy Dog"), + js_str!("The Quick Brown Fox Jumps Over The Lazy Dog"), ), TestAction::assert(indoc! {r#" arrayEquals( @@ -432,30 +430,30 @@ fn test_match() { #[test] fn trim() { run_test_actions([ - TestAction::assert_eq(r"'Hello'.trim()", js_string!("Hello")), - TestAction::assert_eq(r"' \nHello'.trim()", js_string!("Hello")), - TestAction::assert_eq(r"'Hello \n\r'.trim()", js_string!("Hello")), - TestAction::assert_eq(r"' Hello '.trim()", js_string!("Hello")), + TestAction::assert_eq(r"'Hello'.trim()", js_str!("Hello")), + TestAction::assert_eq(r"' \nHello'.trim()", js_str!("Hello")), + TestAction::assert_eq(r"'Hello \n\r'.trim()", js_str!("Hello")), + TestAction::assert_eq(r"' Hello '.trim()", js_str!("Hello")), ]); } #[test] fn trim_start() { run_test_actions([ - TestAction::assert_eq(r"'Hello'.trimStart()", js_string!("Hello")), - TestAction::assert_eq(r"' \nHello'.trimStart()", js_string!("Hello")), - TestAction::assert_eq(r"'Hello \n\r'.trimStart()", js_string!("Hello \n\r")), - TestAction::assert_eq(r"' Hello '.trimStart()", js_string!("Hello ")), + TestAction::assert_eq(r"'Hello'.trimStart()", js_str!("Hello")), + TestAction::assert_eq(r"' \nHello'.trimStart()", js_str!("Hello")), + TestAction::assert_eq(r"'Hello \n\r'.trimStart()", js_str!("Hello \n\r")), + TestAction::assert_eq(r"' Hello '.trimStart()", js_str!("Hello ")), ]); } #[test] fn trim_end() { run_test_actions([ - TestAction::assert_eq(r"'Hello'.trimEnd()", js_string!("Hello")), - TestAction::assert_eq(r"' \nHello'.trimEnd()", js_string!(" \nHello")), - TestAction::assert_eq(r"'Hello \n\r'.trimEnd()", js_string!("Hello")), - TestAction::assert_eq(r"' Hello '.trimEnd()", js_string!(" Hello")), + TestAction::assert_eq(r"'Hello'.trimEnd()", js_str!("Hello")), + TestAction::assert_eq(r"' \nHello'.trimEnd()", js_str!(" \nHello")), + TestAction::assert_eq(r"'Hello \n\r'.trimEnd()", js_str!("Hello")), + TestAction::assert_eq(r"' Hello '.trimEnd()", js_str!(" Hello")), ]); } @@ -578,7 +576,7 @@ fn split_with_symbol_split_method() { sep_a[Symbol.split] = function(s, limit) { return s + limit.toString(); }; 'hello'.split(sep_a, 10) "#}, - js_string!("hello10"), + js_str!("hello10"), ), TestAction::assert(indoc! {r#" let sep_b = {}; @@ -752,10 +750,10 @@ fn last_index_non_integer_position_argument() { fn char_at() { run_test_actions([ TestAction::assert_eq("'abc'.charAt(-1)", js_string!()), - TestAction::assert_eq("'abc'.charAt(1)", js_string!("b")), + TestAction::assert_eq("'abc'.charAt(1)", js_str!("b")), TestAction::assert_eq("'abc'.charAt(9)", js_string!()), - TestAction::assert_eq("'abc'.charAt()", js_string!("a")), - TestAction::assert_eq("'abc'.charAt(null)", js_string!("a")), + TestAction::assert_eq("'abc'.charAt()", js_str!("a")), + TestAction::assert_eq("'abc'.charAt(null)", js_str!("a")), TestAction::assert_eq(r"'\uDBFF'.charAt(0)", js_string!(&[0xDBFFu16])), ]); } @@ -794,10 +792,10 @@ fn code_point_at() { #[test] fn slice() { run_test_actions([ - TestAction::assert_eq("'abc'.slice()", js_string!("abc")), - TestAction::assert_eq("'abc'.slice(1)", js_string!("bc")), - TestAction::assert_eq("'abc'.slice(-1)", js_string!("c")), - TestAction::assert_eq("'abc'.slice(0, 9)", js_string!("abc")), + TestAction::assert_eq("'abc'.slice()", js_str!("abc")), + TestAction::assert_eq("'abc'.slice(1)", js_str!("bc")), + TestAction::assert_eq("'abc'.slice(-1)", js_str!("c")), + TestAction::assert_eq("'abc'.slice(0, 9)", js_str!("abc")), TestAction::assert_eq("'abc'.slice(9, 10)", js_string!()), ]); } @@ -844,8 +842,8 @@ fn unicode_iter() { fn string_get_property() { run_test_actions([ TestAction::assert_eq("'abc'[-1]", JsValue::undefined()), - TestAction::assert_eq("'abc'[1]", js_string!("b")), - TestAction::assert_eq("'abc'[2]", js_string!("c")), + TestAction::assert_eq("'abc'[1]", js_str!("b")), + TestAction::assert_eq("'abc'[2]", js_str!("c")), TestAction::assert_eq("'abc'[3]", JsValue::undefined()), TestAction::assert_eq("'abc'['foo']", JsValue::undefined()), TestAction::assert_eq("'😀'[0]", js_string!(&[0xD83D])), @@ -866,9 +864,9 @@ fn search() { fn from_code_point() { // Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint run_test_actions([ - TestAction::assert_eq("String.fromCodePoint(42)", js_string!("*")), - TestAction::assert_eq("String.fromCodePoint(65, 90)", js_string!("AZ")), - TestAction::assert_eq("String.fromCodePoint(0x404)", js_string!("Є")), + TestAction::assert_eq("String.fromCodePoint(42)", js_str!("*")), + TestAction::assert_eq("String.fromCodePoint(65, 90)", js_str!("AZ")), + TestAction::assert_eq("String.fromCodePoint(0x404)", js_str!("Є")), TestAction::assert_eq( "String.fromCodePoint(0x2f804)", js_string!(&[0xD87E, 0xDC04]), @@ -884,7 +882,7 @@ fn from_code_point() { ), TestAction::assert_eq( "String.fromCodePoint(9731, 9733, 9842, 0x4F60)", - js_string!("☃★♲你"), + js_str!("☃★♲你"), ), TestAction::assert_native_error( "String.fromCodePoint('_')", diff --git a/core/engine/src/builtins/symbol/mod.rs b/core/engine/src/builtins/symbol/mod.rs index 0efe1080c86..dd50afb1048 100644 --- a/core/engine/src/builtins/symbol/mod.rs +++ b/core/engine/src/builtins/symbol/mod.rs @@ -64,8 +64,8 @@ impl GlobalSymbolRegistry { } fn get_or_create_symbol(&self, key: &JsString) -> JsResult { - let slice = &**key; - if let Some(symbol) = self.keys.get(slice) { + let slice = key.iter().collect::>(); + if let Some(symbol) = self.keys.get(&slice[..]) { return Ok(symbol.clone()); } @@ -73,8 +73,10 @@ impl GlobalSymbolRegistry { JsNativeError::range() .with_message("reached the maximum number of symbols that can be created") })?; - self.keys.insert(slice.into(), symbol.clone()); - self.symbols.insert(symbol.clone(), slice.into()); + self.keys + .insert(slice.clone().into_boxed_slice(), symbol.clone()); + self.symbols + .insert(symbol.clone(), slice.into_boxed_slice()); Ok(symbol) } diff --git a/core/engine/src/builtins/symbol/tests.rs b/core/engine/src/builtins/symbol/tests.rs index 1ee48cd21f8..415cdd5eb13 100644 --- a/core/engine/src/builtins/symbol/tests.rs +++ b/core/engine/src/builtins/symbol/tests.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsValue, TestAction}; +use crate::{run_test_actions, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -12,7 +13,7 @@ fn call_symbol_and_check_return_type() { fn print_symbol_expect_description() { run_test_actions([TestAction::assert_eq( "String(Symbol('Hello'))", - js_string!("Symbol(Hello)"), + js_str!("Symbol(Hello)"), )]); } diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index cdfe449d669..e479ec9881d 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -18,10 +18,11 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{custom_trace, Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::calendar::{ @@ -75,7 +76,12 @@ impl IntrinsicObject for Calendar { Self::NAME, Attribute::CONFIGURABLE, ) - .accessor(utf16!("id"), Some(get_id), None, Attribute::CONFIGURABLE) + .accessor( + js_string!("id"), + Some(get_id), + None, + Attribute::CONFIGURABLE, + ) .static_method(Self::from, js_string!("from"), 1) .method(Self::date_from_fields, js_string!("dateFromFields"), 2) .method( @@ -244,7 +250,7 @@ impl Calendar { }; // 8. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option(&options, utf16!("overflow"), context)? + let overflow = get_option(&options, js_str!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); // NOTE: implement the below on the calenar itself @@ -327,7 +333,7 @@ impl Calendar { }; // 7. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option::(&options, utf16!("overflow"), context)? + let overflow = get_option::(&options, js_str!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); let result = calendar @@ -403,7 +409,7 @@ impl Calendar { }; // 8. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option(&options, utf16!("overflow"), context)? + let overflow = get_option(&options, js_str!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); let result = calendar @@ -439,7 +445,7 @@ impl Calendar { let options_obj = get_options_object(options)?; // 7. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option(&options_obj, utf16!("overflow"), context)? + let overflow = get_option(&options_obj, js_str!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); // 8. Let balanceResult be ? BalanceTimeDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], @@ -476,7 +482,7 @@ impl Calendar { // 8. If largestUnit is "auto", set largestUnit to "day". let largest_unit = super::options::get_temporal_unit( &options, - utf16!("largestUnit"), + js_str!("largestUnit"), TemporalUnitGroup::Date, None, context, @@ -1033,7 +1039,7 @@ pub(crate) fn get_temporal_calendar_slot_value_with_default( } // 2. Let calendarLike be ? Get(item, "calendar"). - let calendar_like = item.get(utf16!("calendar"), context)?; + let calendar_like = item.get(js_str!("calendar"), context)?; // 3. Return ? ToTemporalCalendarSlotValue(calendarLike, "iso8601"). to_temporal_calendar_slot_value(&calendar_like, context) diff --git a/core/engine/src/builtins/temporal/calendar/object.rs b/core/engine/src/builtins/temporal/calendar/object.rs index 51f0d2c3e3c..c50e63b2309 100644 --- a/core/engine/src/builtins/temporal/calendar/object.rs +++ b/core/engine/src/builtins/temporal/calendar/object.rs @@ -13,7 +13,7 @@ use crate::{ Context, JsObject, JsString, JsValue, }; -use boa_macros::utf16; +use boa_macros::js_str; use num_traits::ToPrimitive; use plain_date::PlainDate; use plain_date_time::PlainDateTime; @@ -41,7 +41,7 @@ impl CalendarProtocol for JsObject { context: &mut Context, ) -> TemporalResult> { let method = self - .get(utf16!("dateFromFields"), context) + .get(js_str!("dateFromFields"), context) .expect("method must exist on a object that implements the CalendarProtocol."); let fields = JsObject::from_temporal_fields(fields, context) @@ -51,7 +51,7 @@ impl CalendarProtocol for JsObject { overflow_obj .create_data_property_or_throw( - utf16!("overflow"), + js_str!("overflow"), JsString::from(overflow.to_string()), context, ) @@ -88,7 +88,7 @@ impl CalendarProtocol for JsObject { context: &mut Context, ) -> TemporalResult> { let method = self - .get(utf16!("yearMonthFromFields"), context) + .get(js_str!("yearMonthFromFields"), context) .expect("method must exist on a object that implements the CalendarProtocol."); let fields = JsObject::from_temporal_fields(fields, context) @@ -98,7 +98,7 @@ impl CalendarProtocol for JsObject { overflow_obj .create_data_property_or_throw( - utf16!("overflow"), + js_str!("overflow"), JsString::from(overflow.to_string()), context, ) @@ -137,7 +137,7 @@ impl CalendarProtocol for JsObject { context: &mut Context, ) -> TemporalResult> { let method = self - .get(utf16!("yearMonthFromFields"), context) + .get(js_str!("yearMonthFromFields"), context) .expect("method must exist on a object that implements the CalendarProtocol."); let fields = JsObject::from_temporal_fields(fields, context) @@ -147,7 +147,7 @@ impl CalendarProtocol for JsObject { overflow_obj .create_data_property_or_throw( - utf16!("overflow"), + js_str!("overflow"), JsString::from(overflow.to_string()), context, ) @@ -227,7 +227,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("year")), context) + .get(PropertyKey::from(js_str!("year")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -269,7 +269,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("month")), context) + .get(PropertyKey::from(js_str!("month")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -311,7 +311,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("monthCode")), context) + .get(PropertyKey::from(js_str!("monthCode")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -338,7 +338,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("day")), context) + .get(PropertyKey::from(js_str!("day")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -380,7 +380,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("dayOfWeek")), context) + .get(PropertyKey::from(js_str!("dayOfWeek")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -424,7 +424,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("dayOfYear")), context) + .get(PropertyKey::from(js_str!("dayOfYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -468,7 +468,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("weekOfYear")), context) + .get(PropertyKey::from(js_str!("weekOfYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -512,7 +512,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("yearOfWeek")), context) + .get(PropertyKey::from(js_str!("yearOfWeek")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -549,7 +549,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("daysInWeek")), context) + .get(PropertyKey::from(js_str!("daysInWeek")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -593,7 +593,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("daysInMonth")), context) + .get(PropertyKey::from(js_str!("daysInMonth")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() @@ -638,7 +638,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("daysInYear")), context) + .get(PropertyKey::from(js_str!("daysInYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -682,7 +682,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("monthsInYear")), context) + .get(PropertyKey::from(js_str!("monthsInYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -728,7 +728,7 @@ impl CalendarProtocol for JsObject { let date_like = date_like_to_object(date_like, context)?; let method = self - .get(PropertyKey::from(utf16!("inLeapYear")), context) + .get(PropertyKey::from(js_str!("inLeapYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method @@ -753,7 +753,7 @@ impl CalendarProtocol for JsObject { ); let method = self - .get(PropertyKey::from(utf16!("fields")), context) + .get(PropertyKey::from(js_str!("fields")), context) .expect("method must exist on an object that implements the CalendarProtocol."); let result = method @@ -799,7 +799,7 @@ impl CalendarProtocol for JsObject { .map_err(|e| TemporalError::general(e.to_string()))?; let method = self - .get(PropertyKey::from(utf16!("mergeFields")), context) + .get(PropertyKey::from(js_str!("mergeFields")), context) .expect("method must exist on an object that implements the CalendarProtocol."); let value = method @@ -824,7 +824,7 @@ impl CalendarProtocol for JsObject { fn identifier(&self, context: &mut Context) -> TemporalResult { let identifier = self .__get__( - &PropertyKey::from(utf16!("id")), + &PropertyKey::from(js_str!("id")), self.clone().into(), &mut context.into(), ) diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 4f0c3dc8227..8e242ae44ae 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -10,10 +10,11 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::Duration as InnerDuration, @@ -108,73 +109,73 @@ impl IntrinsicObject for Duration { Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .accessor( - utf16!("years"), + js_string!("years"), Some(get_years), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("months"), + js_string!("months"), Some(get_months), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("weeks"), + js_string!("weeks"), Some(get_weeks), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("days"), + js_string!("days"), Some(get_days), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("hours"), + js_string!("hours"), Some(get_hours), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("minutes"), + js_string!("minutes"), Some(get_minutes), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("seconds"), + js_string!("seconds"), Some(get_seconds), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("milliseconds"), + js_string!("milliseconds"), Some(get_milliseconds), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("microseconds"), + js_string!("microseconds"), Some(get_microseconds), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("nanoseconds"), + js_string!("nanoseconds"), Some(get_nanoseconds), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("sign"), + js_string!("sign"), Some(get_sign), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("blank"), + js_string!("blank"), Some(is_blank), None, Attribute::CONFIGURABLE, @@ -623,7 +624,7 @@ impl Duration { let new_round_to = JsObject::with_null_proto(); // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString). new_round_to.create_data_property_or_throw( - utf16!("smallestUnit"), + js_str!("smallestUnit"), param_string, context, )?; @@ -644,7 +645,7 @@ impl Duration { // 9. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »). let largest_unit = get_temporal_unit( &round_to, - utf16!("largestUnit"), + js_str!("largestUnit"), TemporalUnitGroup::DateTime, Some([TemporalUnit::Auto].into()), context, @@ -661,12 +662,12 @@ impl Duration { // 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). let rounding_mode = - get_option::(&round_to, utf16!("roundingMode"), context)?; + get_option::(&round_to, js_str!("roundingMode"), context)?; // 15. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined). let smallest_unit = get_temporal_unit( &round_to, - utf16!("smallestUnit"), + js_str!("smallestUnit"), TemporalUnitGroup::DateTime, None, context, @@ -725,7 +726,7 @@ impl Duration { let total_of = JsObject::with_null_proto(); // c. Perform ! CreateDataPropertyOrThrow(totalOf, "unit", paramString). total_of.create_data_property_or_throw( - utf16!("unit"), + js_str!("unit"), param_string.clone(), context, )?; @@ -748,7 +749,7 @@ impl Duration { // 10. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required). let _unit = get_temporal_unit( &total_of, - utf16!("unit"), + js_str!("unit"), TemporalUnitGroup::DateTime, None, context, @@ -905,70 +906,70 @@ pub(crate) fn to_temporal_partial_duration( // 3. NOTE: The following steps read properties and perform independent validation in alphabetical order. // 4. Let days be ? Get(temporalDurationLike, "days"). - let days = unknown_object.get(utf16!("days"), context)?; + let days = unknown_object.get(js_str!("days"), context)?; if !days.is_undefined() { // 5. If days is not undefined, set result.[[Days]] to ? ToIntegerIfIntegral(days). result.set_days(f64::from(to_integer_if_integral(&days, context)?)); } // 6. Let hours be ? Get(temporalDurationLike, "hours"). - let hours = unknown_object.get(utf16!("hours"), context)?; + let hours = unknown_object.get(js_str!("hours"), context)?; // 7. If hours is not undefined, set result.[[Hours]] to ? ToIntegerIfIntegral(hours). if !hours.is_undefined() { result.set_hours(f64::from(to_integer_if_integral(&hours, context)?)); } // 8. Let microseconds be ? Get(temporalDurationLike, "microseconds"). - let microseconds = unknown_object.get(utf16!("microseconds"), context)?; + let microseconds = unknown_object.get(js_str!("microseconds"), context)?; // 9. If microseconds is not undefined, set result.[[Microseconds]] to ? ToIntegerIfIntegral(microseconds). if !microseconds.is_undefined() { result.set_microseconds(f64::from(to_integer_if_integral(µseconds, context)?)); } // 10. Let milliseconds be ? Get(temporalDurationLike, "milliseconds"). - let milliseconds = unknown_object.get(utf16!("milliseconds"), context)?; + let milliseconds = unknown_object.get(js_str!("milliseconds"), context)?; // 11. If milliseconds is not undefined, set result.[[Milliseconds]] to ? ToIntegerIfIntegral(milliseconds). if !milliseconds.is_undefined() { result.set_milliseconds(f64::from(to_integer_if_integral(&milliseconds, context)?)); } // 12. Let minutes be ? Get(temporalDurationLike, "minutes"). - let minutes = unknown_object.get(utf16!("minutes"), context)?; + let minutes = unknown_object.get(js_str!("minutes"), context)?; // 13. If minutes is not undefined, set result.[[Minutes]] to ? ToIntegerIfIntegral(minutes). if !minutes.is_undefined() { result.set_minutes(f64::from(to_integer_if_integral(&minutes, context)?)); } // 14. Let months be ? Get(temporalDurationLike, "months"). - let months = unknown_object.get(utf16!("months"), context)?; + let months = unknown_object.get(js_str!("months"), context)?; // 15. If months is not undefined, set result.[[Months]] to ? ToIntegerIfIntegral(months). if !months.is_undefined() { result.set_months(f64::from(to_integer_if_integral(&months, context)?)); } // 16. Let nanoseconds be ? Get(temporalDurationLike, "nanoseconds"). - let nanoseconds = unknown_object.get(utf16!("nanoseconds"), context)?; + let nanoseconds = unknown_object.get(js_str!("nanoseconds"), context)?; // 17. If nanoseconds is not undefined, set result.[[Nanoseconds]] to ? ToIntegerIfIntegral(nanoseconds). if !nanoseconds.is_undefined() { result.set_nanoseconds(f64::from(to_integer_if_integral(&nanoseconds, context)?)); } // 18. Let seconds be ? Get(temporalDurationLike, "seconds"). - let seconds = unknown_object.get(utf16!("seconds"), context)?; + let seconds = unknown_object.get(js_str!("seconds"), context)?; // 19. If seconds is not undefined, set result.[[Seconds]] to ? ToIntegerIfIntegral(seconds). if !seconds.is_undefined() { result.set_seconds(f64::from(to_integer_if_integral(&seconds, context)?)); } // 20. Let weeks be ? Get(temporalDurationLike, "weeks"). - let weeks = unknown_object.get(utf16!("weeks"), context)?; + let weeks = unknown_object.get(js_str!("weeks"), context)?; // 21. If weeks is not undefined, set result.[[Weeks]] to ? ToIntegerIfIntegral(weeks). if !weeks.is_undefined() { result.set_weeks(f64::from(to_integer_if_integral(&weeks, context)?)); } // 22. Let years be ? Get(temporalDurationLike, "years"). - let years = unknown_object.get(utf16!("years"), context)?; + let years = unknown_object.get(js_str!("years"), context)?; // 23. If years is not undefined, set result.[[Years]] to ? ToIntegerIfIntegral(years). if !years.is_undefined() { result.set_years(f64::from(to_integer_if_integral(&years, context)?)); diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index c1259c637ad..48335b5db36 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -14,11 +14,12 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::Instant as InnerInstant, @@ -64,25 +65,25 @@ impl IntrinsicObject for Instant { Attribute::CONFIGURABLE, ) .accessor( - utf16!("epochSeconds"), + js_str!("epochSeconds"), Some(get_seconds), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("epochMilliseconds"), + js_str!("epochMilliseconds"), Some(get_millis), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("epochMicroseconds"), + js_str!("epochMicroseconds"), Some(get_micros), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("epochNanoseconds"), + js_str!("epochNanoseconds"), Some(get_nanos), None, Attribute::CONFIGURABLE, @@ -285,10 +286,10 @@ impl Instant { // Fetch the necessary options. let options = get_options_object(args.get_or_undefined(1))?; - let mode = get_option::(&options, utf16!("roundingMode"), context)?; - let increment = get_option::(&options, utf16!("roundingIncrement"), context)?; - let smallest_unit = get_option::(&options, utf16!("smallestUnit"), context)?; - let largest_unit = get_option::(&options, utf16!("largestUnit"), context)?; + let mode = get_option::(&options, js_str!("roundingMode"), context)?; + let increment = get_option::(&options, js_str!("roundingIncrement"), context)?; + let smallest_unit = get_option::(&options, js_str!("smallestUnit"), context)?; + let largest_unit = get_option::(&options, js_str!("largestUnit"), context)?; let result = instant .inner .until(&other, mode, increment, smallest_unit, largest_unit)?; @@ -313,10 +314,10 @@ impl Instant { // 3. Return ? DifferenceTemporalInstant(since, instant, other, options). let other = to_temporal_instant(args.get_or_undefined(0))?; let options = get_options_object(args.get_or_undefined(1))?; - let mode = get_option::(&options, utf16!("roundingMode"), context)?; - let increment = get_option::(&options, utf16!("roundingIncrement"), context)?; - let smallest_unit = get_option::(&options, utf16!("smallestUnit"), context)?; - let largest_unit = get_option::(&options, utf16!("largestUnit"), context)?; + let mode = get_option::(&options, js_str!("roundingMode"), context)?; + let increment = get_option::(&options, js_str!("roundingIncrement"), context)?; + let smallest_unit = get_option::(&options, js_str!("smallestUnit"), context)?; + let largest_unit = get_option::(&options, js_str!("largestUnit"), context)?; let result = instant .inner .since(&other, mode, increment, smallest_unit, largest_unit)?; @@ -353,7 +354,7 @@ impl Instant { let new_round_to = JsObject::with_null_proto(); // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString). new_round_to.create_data_property_or_throw( - utf16!("smallestUnit"), + js_str!("smallestUnit"), param_string, context, )?; @@ -370,16 +371,16 @@ impl Instant { // alphabetical order (ToTemporalRoundingIncrement reads "roundingIncrement" and ToTemporalRoundingMode reads "roundingMode"). // 7. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo). let rounding_increment = - get_option::(&round_to, utf16!("roundingIncrement"), context)?; + get_option::(&round_to, js_str!("roundingIncrement"), context)?; // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). let rounding_mode = - get_option::(&round_to, utf16!("roundingMode"), context)?; + get_option::(&round_to, js_str!("roundingMode"), context)?; // 9. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit"), time, required). let smallest_unit = get_temporal_unit( &round_to, - utf16!("smallestUnit"), + js_str!("smallestUnit"), TemporalUnitGroup::Time, None, context, diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index be27d833230..adb26887809 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -37,7 +37,7 @@ use crate::{ value::Type, Context, JsBigInt, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; -use boa_macros::utf16; +use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::{Date as TemporalDate, ZonedDateTime as TemporalZonedDateTime}, @@ -252,7 +252,7 @@ pub(crate) fn to_relative_temporal_object( options: &JsObject, context: &mut Context, ) -> RelativeTemporalObjectResult { - let relative_to = options.get(PropertyKey::from(utf16!("relativeTo")), context)?; + let relative_to = options.get(PropertyKey::from(js_str!("relativeTo")), context)?; let plain_date = match relative_to { JsValue::String(relative_to_str) => Some(relative_to_str.into()), JsValue::Object(relative_to_obj) => Some(relative_to_obj.into()), diff --git a/core/engine/src/builtins/temporal/options.rs b/core/engine/src/builtins/temporal/options.rs index bbdd360178f..a13fe2865de 100644 --- a/core/engine/src/builtins/temporal/options.rs +++ b/core/engine/src/builtins/temporal/options.rs @@ -10,7 +10,9 @@ use crate::{ builtins::options::{get_option, ParsableOptionType}, - js_string, Context, JsNativeError, JsObject, JsResult, + js_string, + string::JsStr, + Context, JsNativeError, JsObject, JsResult, }; use temporal_rs::options::{ ArithmeticOverflow, DurationOverflow, InstantDisambiguation, OffsetDisambiguation, @@ -58,7 +60,7 @@ pub(crate) fn get_temporal_rounding_increment( #[inline] pub(crate) fn get_temporal_unit( options: &JsObject, - key: &[u16], + key: JsStr<'_>, unit_group: TemporalUnitGroup, extra_values: Option>, context: &mut Context, diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index 43306a75550..aac77d7e019 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -13,10 +13,11 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::{ @@ -125,80 +126,85 @@ impl IntrinsicObject for PlainDate { Attribute::CONFIGURABLE, ) .accessor( - utf16!("calendarId"), + js_string!("calendarId"), Some(get_calendar_id), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("year"), + js_string!("year"), Some(get_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("month"), + js_string!("month"), Some(get_month), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("monthCode"), + js_string!("monthCode"), Some(get_month_code), None, Attribute::CONFIGURABLE, ) - .accessor(utf16!("day"), Some(get_day), None, Attribute::CONFIGURABLE) .accessor( - utf16!("dayOfWeek"), + js_string!("day"), + Some(get_day), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + js_string!("dayOfWeek"), Some(get_day_of_week), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("dayOfYear"), + js_string!("dayOfYear"), Some(get_day_of_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("weekOfYear"), + js_string!("weekOfYear"), Some(get_week_of_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("yearOfWeek"), + js_string!("yearOfWeek"), Some(get_year_of_week), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInWeek"), + js_string!("daysInWeek"), Some(get_days_in_week), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInMonth"), + js_string!("daysInMonth"), Some(get_days_in_month), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInYear"), + js_string!("daysInYear"), Some(get_days_in_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("monthsInYear"), + js_string!("monthsInYear"), Some(get_months_in_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("inLeapYear"), + js_string!("inLeapYear"), Some(get_in_leap_year), None, Attribute::CONFIGURABLE, @@ -643,7 +649,7 @@ pub(crate) fn to_temporal_date( // c. If item has an [[InitializedTemporalDateTime]] internal slot, then } else if let Some(date_time) = object.downcast_ref::() { // i. Perform ? ToTemporalOverflow(options). - let _o = get_option(&options_obj, utf16!("overflow"), context)? + let _o = get_option(&options_obj, js_str!("overflow"), context)? .unwrap_or(ArithmeticOverflow::Constrain); let date = InnerDate::from_datetime(date_time.inner()); diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index dceb7c452bb..462b1caf338 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -11,10 +11,11 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; #[cfg(test)] @@ -152,116 +153,116 @@ impl IntrinsicObject for PlainDateTime { Attribute::CONFIGURABLE, ) .accessor( - utf16!("calendarId"), + js_str!("calendarId"), Some(get_calendar_id), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("year"), + js_str!("year"), Some(get_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("month"), + js_str!("month"), Some(get_month), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("monthCode"), + js_str!("monthCode"), Some(get_month_code), None, Attribute::CONFIGURABLE, ) - .accessor(utf16!("day"), Some(get_day), None, Attribute::CONFIGURABLE) + .accessor(js_str!("day"), Some(get_day), None, Attribute::CONFIGURABLE) .accessor( - utf16!("hour"), + js_str!("hour"), Some(get_hour), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("minute"), + js_str!("minute"), Some(get_minute), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("second"), + js_str!("second"), Some(get_second), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("millisecond"), + js_str!("millisecond"), Some(get_millisecond), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("microsecond"), + js_str!("microsecond"), Some(get_microsecond), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("nanosecond"), + js_str!("nanosecond"), Some(get_nanosecond), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("dayOfWeek"), + js_str!("dayOfWeek"), Some(get_day_of_week), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("dayOfYear"), + js_str!("dayOfYear"), Some(get_day_of_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("weekOfYear"), + js_str!("weekOfYear"), Some(get_week_of_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("yearOfWeek"), + js_str!("yearOfWeek"), Some(get_year_of_week), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInWeek"), + js_str!("daysInWeek"), Some(get_days_in_week), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInMonth"), + js_str!("daysInMonth"), Some(get_days_in_month), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInYear"), + js_str!("daysInYear"), Some(get_days_in_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("monthsInYear"), + js_str!("monthsInYear"), Some(get_months_in_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("inLeapYear"), + js_str!("inLeapYear"), Some(get_in_leap_year), None, Attribute::CONFIGURABLE, diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index 7f5af857255..846b23cc63c 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -14,7 +14,7 @@ use crate::{ Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; -use boa_macros::utf16; +use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::Time, @@ -71,33 +71,38 @@ impl IntrinsicObject for PlainTime { Self::NAME, Attribute::CONFIGURABLE, ) - .accessor(utf16!("hour"), Some(get_hour), None, Attribute::default()) .accessor( - utf16!("minute"), + js_string!("hour"), + Some(get_hour), + None, + Attribute::default(), + ) + .accessor( + js_string!("minute"), Some(get_minute), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("second"), + js_string!("second"), Some(get_second), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("millisecond"), + js_string!("millisecond"), Some(get_millisecond), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("microsecond"), + js_string!("microsecond"), Some(get_microsecond), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("nanosecond"), + js_string!("nanosecond"), Some(get_nanosecond), None, Attribute::CONFIGURABLE, @@ -345,7 +350,7 @@ impl PlainTime { let new_round_to = JsObject::with_null_proto(); // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString). new_round_to.create_data_property_or_throw( - utf16!("smallestUnit"), + js_str!("smallestUnit"), param_string, context, )?; @@ -361,16 +366,16 @@ impl PlainTime { // 6. NOTE: The following steps read options and perform independent validation in alphabetical order (ToTemporalRoundingIncrement reads "roundingIncrement" and ToTemporalRoundingMode reads "roundingMode"). // 7. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo). let rounding_increment = - get_option::(&round_to, utf16!("roundingIncrement"), context)?; + get_option::(&round_to, js_str!("roundingIncrement"), context)?; // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). let rounding_mode = - get_option::(&round_to, utf16!("roundingMode"), context)?; + get_option::(&round_to, js_str!("roundingMode"), context)?; // 9. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required). let smallest_unit = get_temporal_unit( &round_to, - utf16!("smallestUnit"), + js_str!("smallestUnit"), TemporalUnitGroup::Time, None, context, @@ -404,29 +409,29 @@ impl PlainTime { let fields = JsObject::with_object_proto(context.intrinsics()); // 4. Perform ! CreateDataPropertyOrThrow(fields, "isoHour", 𝔽(temporalTime.[[ISOHour]])). - fields.create_data_property_or_throw(utf16!("isoHour"), time.inner.hour(), context)?; + fields.create_data_property_or_throw(js_str!("isoHour"), time.inner.hour(), context)?; // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoMicrosecond", 𝔽(temporalTime.[[ISOMicrosecond]])). fields.create_data_property_or_throw( - utf16!("isoMicrosecond"), + js_str!("isoMicrosecond"), time.inner.microsecond(), context, )?; // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMillisecond", 𝔽(temporalTime.[[ISOMillisecond]])). fields.create_data_property_or_throw( - utf16!("isoMillisecond"), + js_str!("isoMillisecond"), time.inner.millisecond(), context, )?; // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoMinute", 𝔽(temporalTime.[[ISOMinute]])). - fields.create_data_property_or_throw(utf16!("isoMinute"), time.inner.minute(), context)?; + fields.create_data_property_or_throw(js_str!("isoMinute"), time.inner.minute(), context)?; // 8. Perform ! CreateDataPropertyOrThrow(fields, "isoNanosecond", 𝔽(temporalTime.[[ISONanosecond]])). fields.create_data_property_or_throw( - utf16!("isoNanosecond"), + js_str!("isoNanosecond"), time.inner.nanosecond(), context, )?; // 9. Perform ! CreateDataPropertyOrThrow(fields, "isoSecond", 𝔽(temporalTime.[[ISOSecond]])). - fields.create_data_property_or_throw(utf16!("isoSecond"), time.inner.second(), context)?; + fields.create_data_property_or_throw(js_str!("isoSecond"), time.inner.second(), context)?; // 10. Return fields. Ok(fields.into()) diff --git a/core/engine/src/builtins/temporal/plain_year_month/mod.rs b/core/engine/src/builtins/temporal/plain_year_month/mod.rs index 4fdd69a000e..c651534b5d4 100644 --- a/core/engine/src/builtins/temporal/plain_year_month/mod.rs +++ b/core/engine/src/builtins/temporal/plain_year_month/mod.rs @@ -7,7 +7,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -98,49 +98,49 @@ impl IntrinsicObject for PlainYearMonth { Attribute::CONFIGURABLE, ) .accessor( - utf16!("calendarId"), + js_string!("calendarId"), Some(get_calendar_id), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("year"), + js_string!("year"), Some(get_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("month"), + js_string!("month"), Some(get_month), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("monthCode"), + js_string!("monthCode"), Some(get_month_code), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInMonth"), + js_string!("daysInMonth"), Some(get_days_in_month), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("daysInYear"), + js_string!("daysInYear"), Some(get_days_in_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("monthsInYear"), + js_string!("monthsInYear"), Some(get_months_in_year), None, Attribute::CONFIGURABLE, ) .accessor( - utf16!("inLeapYear"), + js_string!("inLeapYear"), Some(get_in_leap_year), None, Attribute::CONFIGURABLE, diff --git a/core/engine/src/builtins/temporal/tests.rs b/core/engine/src/builtins/temporal/tests.rs index 58dde9b608e..ee6095a6ae0 100644 --- a/core/engine/src/builtins/temporal/tests.rs +++ b/core/engine/src/builtins/temporal/tests.rs @@ -1,4 +1,6 @@ -use crate::{js_string, run_test_actions, JsValue, TestAction}; +use boa_macros::js_str; + +use crate::{run_test_actions, JsValue, TestAction}; // Temporal Object tests. @@ -8,9 +10,9 @@ fn temporal_object() { run_test_actions([ TestAction::assert_eq( "Object.prototype.toString.call(Temporal)", - js_string!("[object Temporal]"), + js_str!("[object Temporal]"), ), - TestAction::assert_eq("String(Temporal)", js_string!("[object Temporal]")), + TestAction::assert_eq("String(Temporal)", js_str!("[object Temporal]")), TestAction::assert_eq("Object.keys(Temporal).length === 0", true), ]); } @@ -22,7 +24,7 @@ fn now_object() { TestAction::assert_eq("Object.isExtensible(Temporal.Now)", true), TestAction::assert_eq( "Object.prototype.toString.call(Temporal.Now)", - js_string!("[object Temporal.Now]"), + js_str!("[object Temporal.Now]"), ), TestAction::assert_eq( "Object.getPrototypeOf(Temporal.Now) === Object.prototype", diff --git a/core/engine/src/builtins/temporal/time_zone/custom.rs b/core/engine/src/builtins/temporal/time_zone/custom.rs index 9734b3ab6c1..57aca912792 100644 --- a/core/engine/src/builtins/temporal/time_zone/custom.rs +++ b/core/engine/src/builtins/temporal/time_zone/custom.rs @@ -1,7 +1,8 @@ //! A custom `TimeZone` object. -use crate::{property::PropertyKey, string::utf16, Context, JsObject, JsValue}; +use crate::{property::PropertyKey, Context, JsObject, JsValue}; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use num_bigint::BigInt; use temporal_rs::{ components::{tz::TzProtocol, Instant}, @@ -18,7 +19,7 @@ impl TzProtocol for JsCustomTimeZone { fn get_offset_nanos_for(&self, context: &mut Context) -> TemporalResult { let method = self .tz - .get(utf16!("getOffsetNanosFor"), context) + .get(js_str!("getOffsetNanosFor"), context) .expect("Method must exist for the custom calendar to be valid."); let result = method @@ -45,7 +46,7 @@ impl TzProtocol for JsCustomTimeZone { let ident = self .tz .__get__( - &PropertyKey::from(utf16!("id")), + &PropertyKey::from(js_str!("id")), JsValue::undefined(), &mut context.into(), ) diff --git a/core/engine/src/builtins/temporal/time_zone/mod.rs b/core/engine/src/builtins/temporal/time_zone/mod.rs index c152364951e..826cf94bb52 100644 --- a/core/engine/src/builtins/temporal/time_zone/mod.rs +++ b/core/engine/src/builtins/temporal/time_zone/mod.rs @@ -11,7 +11,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, CONSTRUCTOR}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{custom_trace, Finalize, Trace}; @@ -95,7 +95,7 @@ impl IntrinsicObject for TimeZone { realm.intrinsics().constructors().time_zone().prototype(), Attribute::default(), ) - .accessor(utf16!("id"), Some(get_id), None, Attribute::default()) + .accessor(js_string!("id"), Some(get_id), None, Attribute::default()) .build(); } diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 12f97628124..36b25648c55 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -3,7 +3,7 @@ use std::{ sync::atomic::{self, Ordering}, }; -use boa_macros::utf16; +use boa_macros::{js_str, utf16}; use num_traits::Zero; use super::{ @@ -79,19 +79,19 @@ impl IntrinsicObject for BuiltinTypedArray { Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) .accessor( - utf16!("buffer"), + js_string!("buffer"), Some(get_buffer), None, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, ) .accessor( - utf16!("byteLength"), + js_string!("byteLength"), Some(get_byte_length), None, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, ) .accessor( - utf16!("byteOffset"), + js_string!("byteOffset"), Some(get_byte_offset), None, Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, @@ -1214,7 +1214,7 @@ impl BuiltinTypedArray { for k in 0..len { // a. If k > 0, set R to the string-concatenation of R and sep. if k > 0 { - r.extend_from_slice(&sep); + r.extend(sep.iter()); } // b. Let element be ! Get(O, ! ToString(𝔽(k))). @@ -1223,12 +1223,12 @@ impl BuiltinTypedArray { // c. If element is undefined, let next be the empty String; otherwise, let next be ! ToString(element). // d. Set R to the string-concatenation of R and next. if !element.is_undefined() { - r.extend_from_slice(&element.to_string(context)?); + r.extend(element.to_string(context)?.iter()); } } // 9. Return R. - Ok(js_string!(r).into()) + Ok(js_string!(&r[..]).into()) } /// `%TypedArray%.prototype.keys ( )` @@ -2493,7 +2493,7 @@ impl BuiltinTypedArray { if is_fixed_len || !next_element.is_undefined() { let s = next_element .invoke( - utf16!("toLocaleString"), + js_str!("toLocaleString"), &[ args.get_or_undefined(0).clone(), args.get_or_undefined(1).clone(), @@ -2502,11 +2502,11 @@ impl BuiltinTypedArray { )? .to_string(context)?; - r.extend_from_slice(&s); + r.extend(s.iter()); }; } - Ok(js_string!(r).into()) + Ok(js_string!(&r[..]).into()) } /// `%TypedArray%.prototype.values ( )` diff --git a/core/engine/src/builtins/typed_array/mod.rs b/core/engine/src/builtins/typed_array/mod.rs index a2b20fe51ea..15f63e0fe37 100644 --- a/core/engine/src/builtins/typed_array/mod.rs +++ b/core/engine/src/builtins/typed_array/mod.rs @@ -29,6 +29,7 @@ use crate::{ Context, JsArgs, JsResult, JsString, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; mod builtin; @@ -74,12 +75,12 @@ impl IntrinsicObject for T { Attribute::CONFIGURABLE, ) .property( - js_string!("BYTES_PER_ELEMENT"), + js_str!("BYTES_PER_ELEMENT"), std::mem::size_of::(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ) .static_property( - js_string!("BYTES_PER_ELEMENT"), + js_str!("BYTES_PER_ELEMENT"), std::mem::size_of::(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ) diff --git a/core/engine/src/builtins/typed_array/object.rs b/core/engine/src/builtins/typed_array/object.rs index bb32aa6e056..cdeb7491ed1 100644 --- a/core/engine/src/builtins/typed_array/object.rs +++ b/core/engine/src/builtins/typed_array/object.rs @@ -16,7 +16,7 @@ use crate::{ Context, JsNativeError, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; -use boa_macros::utf16; +use boa_macros::js_str; use super::{is_valid_integer_index, TypedArrayKind}; @@ -263,7 +263,7 @@ impl TypedArray { /// [spec]: https://tc39.es/ecma262/#sec-canonicalnumericindexstring fn canonical_numeric_index_string(argument: &JsString) -> Option { // 1. If argument is "-0", return -0𝔽. - if argument == utf16!("-0") { + if argument == &js_str!("-0") { return Some(-0.0); } diff --git a/core/engine/src/builtins/uri/mod.rs b/core/engine/src/builtins/uri/mod.rs index 82b711b3f26..3389a36f6db 100644 --- a/core/engine/src/builtins/uri/mod.rs +++ b/core/engine/src/builtins/uri/mod.rs @@ -305,11 +305,11 @@ where loop { // a. If k = strLen, return R. if k == str_len { - return Ok(js_string!(r)); + return Ok(js_string!(&r[..])); } // b. Let C be the code unit at index k within string. - let c = string[k]; + let c = string.get_expect(k); // c. If C is in unescapedSet, then if unescaped_set(c) { @@ -380,11 +380,11 @@ where loop { // a. If k = strLen, return R. if k == str_len { - return Ok(js_string!(r)); + return Ok(js_string!(&r[..])); } // b. Let C be the code unit at index k within string. - let c = string[k]; + let c = string.get_expect(k); // c. If C is not the code unit 0x0025 (PERCENT SIGN), then #[allow(clippy::if_not_else)] @@ -406,9 +406,10 @@ where // iii. If the code units at index (k + 1) and (k + 2) within string do not represent // hexadecimal digits, throw a URIError exception. // iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2). - let b = decode_hex_byte(string[k + 1], string[k + 2]).ok_or_else(|| { - JsNativeError::uri().with_message("invalid hexadecimal digit found") - })?; + let b = decode_hex_byte(string.get_expect(k + 1), string.get_expect(k + 2)) + .ok_or_else(|| { + JsNativeError::uri().with_message("invalid hexadecimal digit found") + })?; // v. Set k to k + 2. k += 2; @@ -428,7 +429,7 @@ where } else { // 3. Else, // a. Let S be the substring of string from start to k + 1. - Vec::from(&string[start..=k]) + string.get_expect(start..=k).to_vec() } } else { // viii. Else, @@ -456,7 +457,7 @@ where k += 1; // b. If the code unit at index k within string is not the code unit 0x0025 (PERCENT SIGN), throw a URIError exception. - if string[k] != 0x0025 { + if string.get_expect(k) != 0x0025 { return Err(JsNativeError::uri() .with_message("escape characters must be preceded with a % sign") .into()); @@ -464,9 +465,10 @@ where // c. If the code units at index (k + 1) and (k + 2) within string do not represent hexadecimal digits, throw a URIError exception. // d. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2). - let b = decode_hex_byte(string[k + 1], string[k + 2]).ok_or_else(|| { - JsNativeError::uri().with_message("invalid hexadecimal digit found") - })?; + let b = decode_hex_byte(string.get_expect(k + 1), string.get_expect(k + 2)) + .ok_or_else(|| { + JsNativeError::uri().with_message("invalid hexadecimal digit found") + })?; // e. Set k to k + 2. k += 2; diff --git a/core/engine/src/builtins/weak_map/mod.rs b/core/engine/src/builtins/weak_map/mod.rs index 6b2905b11fc..01cd63c0a98 100644 --- a/core/engine/src/builtins/weak_map/mod.rs +++ b/core/engine/src/builtins/weak_map/mod.rs @@ -17,11 +17,12 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, ErasedVTableObject, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; type NativeWeakMap = boa_gc::WeakMap; @@ -100,7 +101,7 @@ impl BuiltInConstructor for WeakMap { } // 5. Let adder be ? Get(map, "set"). - let adder = map.get(utf16!("set"), context)?; + let adder = map.get(js_str!("set"), context)?; // 6. If IsCallable(adder) is false, throw a TypeError exception. if !adder.is_callable() { diff --git a/core/engine/src/builtins/weak_set/mod.rs b/core/engine/src/builtins/weak_set/mod.rs index 61df3ba0d54..d90a8257725 100644 --- a/core/engine/src/builtins/weak_set/mod.rs +++ b/core/engine/src/builtins/weak_set/mod.rs @@ -14,11 +14,12 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, ErasedVTableObject, JsObject}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use boa_profiler::Profiler; type NativeWeakSet = boa_gc::WeakMap; @@ -96,7 +97,7 @@ impl BuiltInConstructor for WeakSet { } // 5. Let adder be ? Get(set, "add"). - let adder = weak_set.get(utf16!("add"), context)?; + let adder = weak_set.get(js_str!("add"), context)?; // 6. If IsCallable(adder) is false, throw a TypeError exception. let adder = adder diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index 16de09bcffd..b8b4fde7a04 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -18,7 +18,7 @@ use boa_ast::{ visitor::NodeRef, Declaration, Script, StatementListItem, }; -use boa_interner::Sym; +use boa_interner::{JStrRef, Sym}; #[cfg(feature = "annex-b")] use boa_ast::operations::annex_b_function_declarations_names; @@ -223,9 +223,11 @@ pub(crate) fn eval_declaration_instantiation_context( let private_identifiers = private_identifiers .into_iter() .map(|ident| { + // TODO: Replace JStrRef with JsStr this would eliminate the to_vec call. + let ident = ident.to_vec(); context .interner() - .get(ident.as_slice()) + .get(JStrRef::Utf16(&ident)) .expect("string should be in interner") }) .collect(); diff --git a/core/engine/src/bytecompiler/expression/mod.rs b/core/engine/src/bytecompiler/expression/mod.rs index 27a226874f1..fb229b41c60 100644 --- a/core/engine/src/bytecompiler/expression/mod.rs +++ b/core/engine/src/bytecompiler/expression/mod.rs @@ -4,7 +4,7 @@ mod object_literal; mod unary; mod update; -use super::{Access, Callable, NodeKind, Operand}; +use super::{Access, Callable, NodeKind, Operand, ToJsString}; use crate::{ bytecompiler::{ByteCompiler, Literal}, vm::{GeneratorResumeKind, Opcode}, @@ -22,9 +22,9 @@ use boa_ast::{ impl ByteCompiler<'_> { fn compile_literal(&mut self, lit: &AstLiteral, use_expr: bool) { match lit { - AstLiteral::String(v) => self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*v).into_common(false), - )), + AstLiteral::String(v) => { + self.emit_push_literal(Literal::String(v.to_js_string(self.interner()))); + } AstLiteral::Int(v) => self.emit_push_integer(*v), AstLiteral::Num(v) => self.emit_push_rational(*v), AstLiteral::BigInt(v) => { @@ -58,9 +58,9 @@ impl ByteCompiler<'_> { fn compile_template_literal(&mut self, template_literal: &TemplateLiteral, use_expr: bool) { for element in template_literal.elements() { match element { - TemplateElement::String(s) => self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*s).into_common(false), - )), + TemplateElement::String(s) => { + self.emit_push_literal(Literal::String(s.to_js_string(self.interner()))); + } TemplateElement::Expr(expr) => { self.compile_expr(expr, true); } @@ -268,14 +268,12 @@ impl ByteCompiler<'_> { for (cooked, raw) in template.cookeds().iter().zip(template.raws()) { if let Some(cooked) = cooked { self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*cooked).into_common(false), + cooked.to_js_string(self.interner()), )); } else { self.emit_opcode(Opcode::PushUndefined); } - self.emit_push_literal(Literal::String( - self.interner().resolve_expect(*raw).into_common(false), - )); + self.emit_push_literal(Literal::String(raw.to_js_string(self.interner()))); } self.emit( diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index 4b64f6774e6..fd30107566e 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -21,7 +21,7 @@ use crate::{ BindingOpcode, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, Handler, InlineCache, Opcode, VaryingOperandKind, }, - JsBigInt, JsString, + JsBigInt, JsStr, JsString, }; use boa_ast::{ declaration::{Binding, LexicalDeclaration, VarDeclaration}, @@ -55,7 +55,15 @@ pub(crate) trait ToJsString { impl ToJsString for Sym { fn to_js_string(&self, interner: &Interner) -> JsString { - js_string!(interner.resolve_expect(*self).utf16()) + // TODO: Identify latin1 encodeable strings during parsing to avoid this check. + let string = interner.resolve_expect(*self).utf16(); + for c in string { + if u8::try_from(*c).is_err() { + return js_string!(string); + } + } + let string = string.iter().map(|c| *c as u8).collect::>(); + js_string!(JsStr::latin1(&string)) } } @@ -392,9 +400,9 @@ impl<'ctx> ByteCompiler<'ctx> { return *index; } - let string = self.interner().resolve_expect(name.sym()).utf16(); let index = self.constants.len() as u32; - self.constants.push(Constant::String(js_string!(string))); + let string = name.to_js_string(self.interner()); + self.constants.push(Constant::String(string)); self.names_map.insert(name, index); index } @@ -744,7 +752,7 @@ impl<'ctx> ByteCompiler<'ctx> { } fn resolve_identifier_expect(&self, identifier: Identifier) -> JsString { - js_string!(self.interner().resolve_expect(identifier.sym()).utf16()) + identifier.to_js_string(self.interner()) } fn access_get(&mut self, access: Access<'_>, use_expr: bool) { diff --git a/core/engine/src/class.rs b/core/engine/src/class.rs index 4939e0cddd1..af0838c583e 100644 --- a/core/engine/src/class.rs +++ b/core/engine/src/class.rs @@ -10,7 +10,7 @@ //! # property::Attribute, //! # class::{Class, ClassBuilder}, //! # Context, JsResult, JsValue, -//! # JsArgs, Source, JsObject, js_string, +//! # JsArgs, Source, JsObject, js_str, js_string, //! # JsNativeError, JsData, //! # }; //! # use boa_gc::{Finalize, Trace}; @@ -56,7 +56,7 @@ //! let age = args.get_or_undefined(1).to_number(context)?; //! //! // Roughly equivalent to `this.age = Number(age)`. -//! instance.set(js_string!("age"), age, true, context)?; +//! instance.set(js_str!("age"), age, true, context)?; //! //! Ok(()) //! } @@ -70,9 +70,9 @@ //! if let Some(object) = this.as_object() { //! if let Some(animal) = object.downcast_ref::() { //! return Ok(match &*animal { -//! Self::Cat => js_string!("meow"), -//! Self::Dog => js_string!("woof"), -//! Self::Other => js_string!(r"¯\_(ツ)_/¯"), +//! Self::Cat => js_str!("meow"), +//! Self::Dog => js_str!("woof"), +//! Self::Other => js_str!(r"¯\_(ツ)_/¯"), //! }.into()); //! } //! } @@ -96,7 +96,7 @@ //! //! assert_eq!( //! result.as_string().unwrap(), -//! &js_string!("My pet is 3 years old. Right, buddy? - woof!") +//! &js_str!("My pet is 3 years old. Right, buddy? - woof!") //! ); //! } //! ``` diff --git a/core/engine/src/context/intrinsics.rs b/core/engine/src/context/intrinsics.rs index b1056f71bb1..a0d8519a7f7 100644 --- a/core/engine/src/context/intrinsics.rs +++ b/core/engine/src/context/intrinsics.rs @@ -1,6 +1,7 @@ //! Data structures that contain intrinsic objects and constructors. use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use crate::{ builtins::{iterable::IteratorPrototypes, uri::UriFunctions, Array, OrdinaryObject}, @@ -1394,7 +1395,7 @@ impl ObjectTemplates { let ordinary_object = ObjectTemplate::with_prototype(root_shape, constructors.object().prototype()); let mut array = ObjectTemplate::new(root_shape); - let length_property_key: PropertyKey = js_string!("length").into(); + let length_property_key: PropertyKey = js_str!("length").into(); array.property( length_property_key.clone(), Attribute::WRITABLE | Attribute::PERMANENT | Attribute::NON_ENUMERABLE, @@ -1419,7 +1420,7 @@ impl ObjectTemplates { let mut regexp = regexp_without_proto.clone(); regexp.set_prototype(constructors.regexp().prototype()); - let name_property_key: PropertyKey = js_string!("name").into(); + let name_property_key: PropertyKey = js_str!("name").into(); let mut function = ObjectTemplate::new(root_shape); function.property( length_property_key.clone(), @@ -1478,7 +1479,7 @@ impl ObjectTemplates { // [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, // [[Configurable]]: false }). unmapped_arguments.accessor( - js_string!("callee").into(), + js_str!("callee").into(), true, true, Attribute::NON_ENUMERABLE | Attribute::PERMANENT, @@ -1487,17 +1488,17 @@ impl ObjectTemplates { // 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { // [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }). mapped_arguments.property( - js_string!("callee").into(), + js_str!("callee").into(), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ); let mut iterator_result = ordinary_object.clone(); iterator_result.property( - js_string!("value").into(), + js_str!("value").into(), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::ENUMERABLE, ); iterator_result.property( - js_string!("done").into(), + js_str!("done").into(), Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::ENUMERABLE, ); @@ -1509,11 +1510,11 @@ impl ObjectTemplates { with_resolvers // 4. Perform ! CreateDataPropertyOrThrow(obj, "promise", promiseCapability.[[Promise]]). - .property(js_string!("promise").into(), Attribute::all()) + .property(js_str!("promise").into(), Attribute::all()) // 5. Perform ! CreateDataPropertyOrThrow(obj, "resolve", promiseCapability.[[Resolve]]). - .property(js_string!("resolve").into(), Attribute::all()) + .property(js_str!("resolve").into(), Attribute::all()) // 6. Perform ! CreateDataPropertyOrThrow(obj, "reject", promiseCapability.[[Reject]]). - .property(js_string!("reject").into(), Attribute::all()); + .property(js_str!("reject").into(), Attribute::all()); with_resolvers }; diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index 32df528b35d..99278a173c9 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -51,7 +51,7 @@ thread_local! { /// /// ```rust /// use boa_engine::{ -/// js_string, +/// js_str, /// object::ObjectInitializer, /// property::{Attribute, PropertyDescriptor}, /// Context, Source, @@ -73,10 +73,10 @@ thread_local! { /// /// // Create an object that can be used in eval calls. /// let arg = ObjectInitializer::new(&mut context) -/// .property(js_string!("x"), 12, Attribute::READONLY) +/// .property(js_str!("x"), 12, Attribute::READONLY) /// .build(); /// context -/// .register_global_property(js_string!("arg"), arg, Attribute::all()) +/// .register_global_property(js_str!("arg"), arg, Attribute::all()) /// .expect("property shouldn't exist"); /// /// let value = context.eval(Source::from_bytes("test(arg)")).unwrap(); @@ -211,7 +211,7 @@ impl Context { /// # Example /// ``` /// use boa_engine::{ - /// js_string, + /// js_str, /// object::ObjectInitializer, /// property::{Attribute, PropertyDescriptor}, /// Context, @@ -221,19 +221,19 @@ impl Context { /// /// context /// .register_global_property( - /// js_string!("myPrimitiveProperty"), + /// js_str!("myPrimitiveProperty"), /// 10, /// Attribute::all(), /// ) /// .expect("property shouldn't exist"); /// /// let object = ObjectInitializer::new(&mut context) - /// .property(js_string!("x"), 0, Attribute::all()) - /// .property(js_string!("y"), 1, Attribute::all()) + /// .property(js_str!("x"), 0, Attribute::all()) + /// .property(js_str!("y"), 1, Attribute::all()) /// .build(); /// context /// .register_global_property( - /// js_string!("myObjectProperty"), + /// js_str!("myObjectProperty"), /// object, /// Attribute::all(), /// ) diff --git a/core/engine/src/error.rs b/core/engine/src/error.rs index d56b20e68c4..4bf3637d623 100644 --- a/core/engine/src/error.rs +++ b/core/engine/src/error.rs @@ -8,10 +8,10 @@ use crate::{ object::JsObject, property::PropertyDescriptor, realm::Realm, - string::utf16, Context, JsString, JsValue, }; use boa_gc::{custom_trace, Finalize, Trace}; +use boa_macros::js_str; use thiserror::Error; /// The error type returned by all operations related @@ -30,11 +30,11 @@ use thiserror::Error; /// # Examples /// /// ```rust -/// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue, js_string}; -/// let cause = JsError::from_opaque(js_string!("error!").into()); +/// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue, js_str}; +/// let cause = JsError::from_opaque(js_str!("error!").into()); /// /// assert!(cause.as_opaque().is_some()); -/// assert_eq!(cause.as_opaque().unwrap(), &JsValue::from(js_string!("error!"))); +/// assert_eq!(cause.as_opaque().unwrap(), &JsValue::from(js_str!("error!"))); /// /// let native_error: JsError = JsNativeError::typ() /// .with_message("invalid type!") @@ -270,7 +270,7 @@ impl JsError { ErrorObject::Syntax => JsNativeErrorKind::Syntax, ErrorObject::Uri => JsNativeErrorKind::Uri, ErrorObject::Aggregate => { - let errors = obj.get(utf16!("errors"), context).map_err(|e| { + let errors = obj.get(js_str!("errors"), context).map_err(|e| { TryNativeError::InaccessibleProperty { property: "errors", source: e, @@ -878,7 +878,7 @@ impl JsNativeError { /// # Examples /// /// ```rust - /// # use boa_engine::{Context, JsError, JsNativeError, js_string}; + /// # use boa_engine::{Context, JsError, JsNativeError, js_str}; /// # use boa_engine::builtins::error::ErrorObject; /// let context = &mut Context::default(); /// @@ -887,8 +887,8 @@ impl JsNativeError { /// /// assert!(error_obj.is::()); /// assert_eq!( - /// error_obj.get(js_string!("message"), context).unwrap(), - /// js_string!("error!").into() + /// error_obj.get(js_str!("message"), context).unwrap(), + /// js_str!("error!").into() /// ) /// ``` /// @@ -941,14 +941,14 @@ impl JsNativeError { JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, tag); o.create_non_enumerable_data_property_or_throw( - js_string!("message"), + js_str!("message"), js_string!(&**message), context, ); if let Some(cause) = cause { o.create_non_enumerable_data_property_or_throw( - js_string!("cause"), + js_str!("cause"), cause.to_opaque(context), context, ); @@ -961,7 +961,7 @@ impl JsNativeError { .collect::>(); let errors = Array::create_array_from_list(errors, context); o.define_property_or_throw( - js_string!("errors"), + js_str!("errors"), PropertyDescriptor::builder() .configurable(true) .enumerable(false) diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index 713906e9bd3..5487cee8b6d 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -77,7 +77,6 @@ compile_error!("Boa requires a lock free `AtomicUsize` in order to work properly."); extern crate self as boa_engine; -extern crate static_assertions as sa; pub use boa_ast as ast; pub use boa_gc as gc; @@ -104,10 +103,11 @@ pub mod symbol; pub mod value; pub mod vm; +pub(crate) mod tagged; + mod host_defined; mod small_map; mod sys; -mod tagged; #[cfg(test)] mod tests; @@ -123,12 +123,12 @@ pub mod prelude { native_function::NativeFunction, object::{JsData, JsObject, NativeObject}, script::Script, - string::JsString, + string::{JsStr, JsString}, symbol::JsSymbol, value::JsValue, }; pub use boa_gc::{Finalize, Trace}; - pub use boa_macros::JsData; + pub use boa_macros::{js_str, JsData}; pub use boa_parser::Source; } @@ -138,6 +138,9 @@ use std::result::Result as StdResult; #[doc(inline)] pub use prelude::*; +#[doc(inline)] +pub use boa_parser::Source; + /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) pub type JsResult = StdResult; diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index edc1757db87..2062028e095 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -12,7 +12,7 @@ use boa_ast::{ }; use boa_gc::{Finalize, Gc, GcRefCell, Trace}; use boa_interner::Interner; -use boa_macros::utf16; +use boa_macros::js_str; use indexmap::IndexSet; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; @@ -506,7 +506,7 @@ impl SourceTextModule { // c. For each element n of starNames, do for n in requested_module.get_exported_names(export_star_set, interner) { // i. If SameValue(n, "default") is false, then - if &n != utf16!("default") { + if n != js_str!("default") { // 1. If exportedNames does not contain n, then // a. Append n to exportedNames. exported_names.insert(n); @@ -583,7 +583,7 @@ impl SourceTextModule { } // 7. If SameValue(exportName, "default") is true, then - if &export_name.clone() == utf16!("default") { + if export_name == &js_str!("default") { // a. Assert: A default export was not explicitly defined by this module. // b. Return null. // c. NOTE: A default export cannot be provided by an export * from "mod" declaration. diff --git a/core/engine/src/object/builtins/jsdate.rs b/core/engine/src/object/builtins/jsdate.rs index 6f6ad4b52ff..3dd6c341561 100644 --- a/core/engine/src/object/builtins/jsdate.rs +++ b/core/engine/src/object/builtins/jsdate.rs @@ -18,7 +18,7 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime}; /// /// ``` /// use boa_engine::{ -/// js_string, object::builtins::JsDate, Context, JsResult, JsValue, +/// js_str, object::builtins::JsDate, Context, JsResult, JsValue, /// }; /// /// fn main() -> JsResult<()> { @@ -31,7 +31,7 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime}; /// /// assert_eq!( /// date.to_date_string(context)?, -/// JsValue::from(js_string!("Mon Dec 04 1995")) +/// JsValue::from(js_str!("Mon Dec 04 1995")) /// ); /// /// Ok(()) diff --git a/core/engine/src/object/builtins/jsmap.rs b/core/engine/src/object/builtins/jsmap.rs index f462ceb4336..4ce123e6028 100644 --- a/core/engine/src/object/builtins/jsmap.rs +++ b/core/engine/src/object/builtins/jsmap.rs @@ -4,12 +4,12 @@ use crate::{ builtins::Map, error::JsNativeError, object::{JsFunction, JsMapIterator, JsObject, JsObjectType}, - string::utf16, value::TryFromJs, Context, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; +use boa_macros::js_str; use std::ops::Deref; /// `JsMap` provides a wrapper for Boa's implementation of the ECMAScript `Map` object. @@ -20,7 +20,7 @@ use std::ops::Deref; /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, -/// # Context, JsValue, JsResult, js_string +/// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// // Create default `Context` @@ -30,8 +30,8 @@ use std::ops::Deref; /// let map = JsMap::new(context); /// /// // Set key-value pairs for the `JsMap`. -/// map.set(js_string!("Key-1"), js_string!("Value-1"), context)?; -/// map.set(js_string!("Key-2"), 10, context)?; +/// map.set(js_str!("Key-1"), js_str!("Value-1"), context)?; +/// map.set(js_str!("Key-2"), 10, context)?; /// /// assert_eq!(map.get_size(context)?, 2.into()); /// # Ok(()) @@ -42,7 +42,7 @@ use std::ops::Deref; /// ``` /// # use boa_engine::{ /// # object::builtins::{JsArray, JsMap}, -/// # Context, JsValue, JsResult, js_string +/// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// // Create a default `Context` @@ -53,8 +53,8 @@ use std::ops::Deref; /// /// // Create a `[key, value]` pair of JsValues /// let vec_one: Vec = vec![ -/// js_string!("first-key").into(), -/// js_string!("first-value").into() +/// js_str!("first-key").into(), +/// js_str!("first-value").into() /// ]; /// /// // We create an push our `[key, value]` pair onto our array as a `JsArray` @@ -64,8 +64,8 @@ use std::ops::Deref; /// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context)?; /// /// assert_eq!( -/// js_iterable_map.get(js_string!("first-key"), context)?, -/// js_string!("first-value").into() +/// js_iterable_map.get(js_str!("first-key"), context)?, +/// js_str!("first-value").into() /// ); /// /// # Ok(()) @@ -102,7 +102,7 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::{JsArray, JsMap}, - /// # Context, JsResult, JsValue, js_string + /// # Context, JsResult, JsValue, js_str /// # }; /// # fn main() -> JsResult<()> { /// # // Create a default `Context` @@ -112,8 +112,8 @@ impl JsMap { /// /// // Create a `[key, value]` pair of JsValues and add it to the `JsArray` as a `JsArray` /// let vec_one: Vec = vec![ - /// js_string!("first-key").into(), - /// js_string!("first-value").into() + /// js_str!("first-key").into(), + /// js_str!("first-value").into() /// ]; /// js_array.push(JsArray::from_iter(vec_one, context), context)?; /// @@ -129,7 +129,7 @@ impl JsMap { // Let adder be Get(map, "set") per spec. This action should not fail with default map. let adder = map - .get(utf16!("set"), context) + .get(js_str!("set"), context) .expect("creating a map with the default prototype must not fail"); let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?; @@ -223,18 +223,18 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, - /// # Context, JsValue, JsResult, js_string + /// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// # let context = &mut Context::default(); /// let js_map = JsMap::new(context); /// - /// js_map.set(js_string!("foo"), js_string!("bar"), context)?; + /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; /// js_map.set(2, 4, context)?; /// /// assert_eq!( - /// js_map.get(js_string!("foo"), context)?, - /// js_string!("bar").into() + /// js_map.get(js_str!("foo"), context)?, + /// js_str!("bar").into() /// ); /// assert_eq!(js_map.get(2, context)?, 4.into()); /// # Ok(()) @@ -259,13 +259,13 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, - /// # Context, JsValue, JsResult, js_string + /// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// # let context = &mut Context::default(); /// let js_map = JsMap::new(context); /// - /// js_map.set(js_string!("foo"), js_string!("bar"), context)?; + /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; /// /// let map_size = js_map.get_size(context)?; /// @@ -285,19 +285,19 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, - /// # Context, JsValue, JsResult, js_string + /// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// # let context = &mut Context::default(); /// let js_map = JsMap::new(context); - /// js_map.set(js_string!("foo"), js_string!("bar"), context)?; - /// js_map.set(js_string!("hello"), js_string!("world"), context)?; + /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; + /// js_map.set(js_str!("hello"), js_str!("world"), context)?; /// - /// js_map.delete(js_string!("foo"), context)?; + /// js_map.delete(js_str!("foo"), context)?; /// /// assert_eq!(js_map.get_size(context)?, 1.into()); /// assert_eq!( - /// js_map.get(js_string!("foo"), context)?, + /// js_map.get(js_str!("foo"), context)?, /// JsValue::undefined() /// ); /// # Ok(()) @@ -317,16 +317,16 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, - /// # Context, JsValue, JsResult, js_string + /// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// # let context = &mut Context::default(); /// let js_map = JsMap::new(context); - /// js_map.set(js_string!("foo"), js_string!("bar"), context)?; + /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; /// - /// let retrieved_value = js_map.get(js_string!("foo"), context)?; + /// let retrieved_value = js_map.get(js_str!("foo"), context)?; /// - /// assert_eq!(retrieved_value, js_string!("bar").into()); + /// assert_eq!(retrieved_value, js_str!("bar").into()); /// # Ok(()) /// # } /// ``` @@ -344,13 +344,13 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, - /// # Context, JsValue, JsResult, js_string + /// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// # let context = &mut Context::default(); /// let js_map = JsMap::new(context); - /// js_map.set(js_string!("foo"), js_string!("bar"), context)?; - /// js_map.set(js_string!("hello"), js_string!("world"), context)?; + /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; + /// js_map.set(js_str!("hello"), js_str!("world"), context)?; /// /// js_map.clear(context)?; /// @@ -370,14 +370,14 @@ impl JsMap { /// ``` /// # use boa_engine::{ /// # object::builtins::JsMap, - /// # Context, JsValue, JsResult, js_string + /// # Context, JsValue, JsResult, js_str /// # }; /// # fn main() -> JsResult<()> { /// # let context = &mut Context::default(); /// let js_map = JsMap::new(context); - /// js_map.set(js_string!("foo"), js_string!("bar"), context)?; + /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; /// - /// let has_key = js_map.has(js_string!("foo"), context)?; + /// let has_key = js_map.has(js_str!("foo"), context)?; /// /// assert_eq!(has_key, true.into()); /// # Ok(()) diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index 2ee7327eec8..cf2358db79e 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -25,7 +25,7 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// ``` /// # use boa_engine::{ /// # builtins::promise::PromiseState, -/// # js_string, +/// # js_str, /// # object::{builtins::JsPromise, FunctionObjectBuilder}, /// # property::Attribute, /// # Context, JsArgs, JsError, JsValue, NativeFunction, @@ -35,14 +35,14 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// let context = &mut Context::default(); /// /// context.register_global_property( -/// js_string!("finally"), +/// js_str!("finally"), /// false, /// Attribute::all(), /// ); /// /// let promise = JsPromise::new( /// |resolvers, context| { -/// let result = js_string!("hello world!").into(); +/// let result = js_str!("hello world!").into(); /// resolvers.resolve.call( /// &JsValue::undefined(), /// &[result], @@ -75,7 +75,7 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// .finally( /// NativeFunction::from_fn_ptr(|_, _, context| { /// context.global_object().clone().set( -/// js_string!("finally"), +/// js_str!("finally"), /// JsValue::from(true), /// true, /// context, @@ -90,14 +90,14 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// /// assert_eq!( /// promise.state(), -/// PromiseState::Fulfilled(js_string!("hello world!").into()) +/// PromiseState::Fulfilled(js_str!("hello world!").into()) /// ); /// /// assert_eq!( /// context /// .global_object() /// .clone() -/// .get(js_string!("finally"), context)?, +/// .get(js_str!("finally"), context)?, /// JsValue::from(true) /// ); /// diff --git a/core/engine/src/object/builtins/jsproxy.rs b/core/engine/src/object/builtins/jsproxy.rs index 6574114c0c3..bb149852e35 100644 --- a/core/engine/src/object/builtins/jsproxy.rs +++ b/core/engine/src/object/builtins/jsproxy.rs @@ -1,16 +1,14 @@ //! A Rust API wrapper for the `Proxy` Builtin ECMAScript Object -use boa_gc::{Finalize, Trace}; - +use super::JsFunction; use crate::{ builtins::Proxy, + js_str, native_function::{NativeFunction, NativeFunctionPointer}, object::{FunctionObjectBuilder, JsObject, JsObjectType}, - string::utf16, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, }; - -use super::JsFunction; +use boa_gc::{Finalize, Trace}; /// `JsProxy` provides a wrapper for Boa's implementation of the ECMAScript `Proxy` object /// @@ -405,7 +403,7 @@ impl JsProxyBuilder { .length(3) .build(); handler - .create_data_property_or_throw(utf16!("apply"), f, context) + .create_data_property_or_throw(js_str!("apply"), f, context) .expect("new object should be writable"); } if let Some(construct) = self.construct { @@ -414,7 +412,7 @@ impl JsProxyBuilder { .length(3) .build(); handler - .create_data_property_or_throw(utf16!("construct"), f, context) + .create_data_property_or_throw(js_str!("construct"), f, context) .expect("new object should be writable"); } if let Some(define_property) = self.define_property { @@ -425,7 +423,7 @@ impl JsProxyBuilder { .length(3) .build(); handler - .create_data_property_or_throw(utf16!("defineProperty"), f, context) + .create_data_property_or_throw(js_str!("defineProperty"), f, context) .expect("new object should be writable"); } if let Some(delete_property) = self.delete_property { @@ -436,7 +434,7 @@ impl JsProxyBuilder { .length(2) .build(); handler - .create_data_property_or_throw(utf16!("deleteProperty"), f, context) + .create_data_property_or_throw(js_str!("deleteProperty"), f, context) .expect("new object should be writable"); } if let Some(get) = self.get { @@ -444,7 +442,7 @@ impl JsProxyBuilder { .length(3) .build(); handler - .create_data_property_or_throw(utf16!("get"), f, context) + .create_data_property_or_throw(js_str!("get"), f, context) .expect("new object should be writable"); } if let Some(get_own_property_descriptor) = self.get_own_property_descriptor { @@ -455,7 +453,7 @@ impl JsProxyBuilder { .length(2) .build(); handler - .create_data_property_or_throw(utf16!("getOwnPropertyDescriptor"), f, context) + .create_data_property_or_throw(js_str!("getOwnPropertyDescriptor"), f, context) .expect("new object should be writable"); } if let Some(get_prototype_of) = self.get_prototype_of { @@ -466,7 +464,7 @@ impl JsProxyBuilder { .length(1) .build(); handler - .create_data_property_or_throw(utf16!("getPrototypeOf"), f, context) + .create_data_property_or_throw(js_str!("getPrototypeOf"), f, context) .expect("new object should be writable"); } if let Some(has) = self.has { @@ -474,7 +472,7 @@ impl JsProxyBuilder { .length(2) .build(); handler - .create_data_property_or_throw(utf16!("has"), f, context) + .create_data_property_or_throw(js_str!("has"), f, context) .expect("new object should be writable"); } if let Some(is_extensible) = self.is_extensible { @@ -485,7 +483,7 @@ impl JsProxyBuilder { .length(1) .build(); handler - .create_data_property_or_throw(utf16!("isExtensible"), f, context) + .create_data_property_or_throw(js_str!("isExtensible"), f, context) .expect("new object should be writable"); } if let Some(own_keys) = self.own_keys { @@ -494,7 +492,7 @@ impl JsProxyBuilder { .length(1) .build(); handler - .create_data_property_or_throw(utf16!("ownKeys"), f, context) + .create_data_property_or_throw(js_str!("ownKeys"), f, context) .expect("new object should be writable"); } if let Some(prevent_extensions) = self.prevent_extensions { @@ -505,7 +503,7 @@ impl JsProxyBuilder { .length(1) .build(); handler - .create_data_property_or_throw(utf16!("preventExtensions"), f, context) + .create_data_property_or_throw(js_str!("preventExtensions"), f, context) .expect("new object should be writable"); } if let Some(set) = self.set { @@ -513,7 +511,7 @@ impl JsProxyBuilder { .length(4) .build(); handler - .create_data_property_or_throw(utf16!("set"), f, context) + .create_data_property_or_throw(js_str!("set"), f, context) .expect("new object should be writable"); } if let Some(set_prototype_of) = self.set_prototype_of { @@ -524,7 +522,7 @@ impl JsProxyBuilder { .length(2) .build(); handler - .create_data_property_or_throw(utf16!("setPrototypeOf"), f, context) + .create_data_property_or_throw(js_str!("setPrototypeOf"), f, context) .expect("new object should be writable"); } diff --git a/core/engine/src/object/internal_methods/string.rs b/core/engine/src/object/internal_methods/string.rs index e2c5809e7e2..ce26b9da301 100644 --- a/core/engine/src/object/internal_methods/string.rs +++ b/core/engine/src/object/internal_methods/string.rs @@ -1,5 +1,4 @@ use crate::{ - js_string, object::{JsData, JsObject}, property::{PropertyDescriptor, PropertyKey}, Context, JsResult, JsString, @@ -158,7 +157,7 @@ fn string_get_own_property(obj: &JsObject, key: &PropertyKey) -> Option Debug for JsObject { if self.is_callable() { let name_prop = obj .properties() - .get(&PropertyKey::String(JsString::from("name"))); + .get(&PropertyKey::String(js_string!("name"))); let name = match name_prop { None => JsString::default(), Some(prop) => prop diff --git a/core/engine/src/object/mod.rs b/core/engine/src/object/mod.rs index b992082301c..f2fc7ed64fe 100644 --- a/core/engine/src/object/mod.rs +++ b/core/engine/src/object/mod.rs @@ -2,6 +2,7 @@ //! //! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors. +use boa_macros::js_str; pub use jsobject::{RecursionLimiter, Ref, RefMut}; pub use operations::IntegrityLevel; pub use property_map::*; @@ -22,8 +23,8 @@ use crate::{ native_function::{NativeFunction, NativeFunctionObject}, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - string::{common::StaticJsStrings, utf16}, - Context, JsString, JsSymbol, JsValue, + string::common::StaticJsStrings, + Context, JsStr, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -52,10 +53,10 @@ pub use jsobject::*; pub(crate) trait JsObjectType: Into + Into {} /// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object. -pub const CONSTRUCTOR: &[u16] = utf16!("constructor"); +pub const CONSTRUCTOR: JsStr<'_> = js_str!("constructor"); /// Const `prototype`, usually set on constructors as a key to point to their respective prototype object. -pub const PROTOTYPE: &[u16] = utf16!("prototype"); +pub const PROTOTYPE: JsStr<'_> = js_str!("prototype"); /// Common field names. @@ -1017,7 +1018,7 @@ impl<'ctx> ConstructorBuilder<'ctx> { }; constructor.insert(StaticJsStrings::LENGTH, length); - constructor.insert(utf16!("name"), name); + constructor.insert(js_str!("name"), name); if let Some(proto) = self.custom_prototype.take() { constructor.set_prototype(proto); diff --git a/core/engine/src/optimizer/pass/constant_folding.rs b/core/engine/src/optimizer/pass/constant_folding.rs index 0c0aa18b73e..c1725ddfa38 100644 --- a/core/engine/src/optimizer/pass/constant_folding.rs +++ b/core/engine/src/optimizer/pass/constant_folding.rs @@ -1,5 +1,6 @@ use crate::{ - builtins::Number, optimizer::PassAction, value::Numeric, Context, JsBigInt, JsString, JsValue, + builtins::Number, bytecompiler::ToJsString, optimizer::PassAction, value::Numeric, Context, + JsBigInt, JsValue, }; use boa_ast::{ expression::{ @@ -12,12 +13,11 @@ use boa_ast::{ }, Expression, }; +use boa_interner::JStrRef; fn literal_to_js_value(literal: &Literal, context: &mut Context) -> JsValue { match literal { - Literal::String(v) => JsValue::new(JsString::from( - context.interner().resolve_expect(*v).utf16(), - )), + Literal::String(v) => JsValue::new(v.to_js_string(context.interner())), Literal::Num(v) => JsValue::new(*v), Literal::Int(v) => JsValue::new(*v), Literal::BigInt(v) => JsValue::new(JsBigInt::new(v.clone())), @@ -32,7 +32,11 @@ fn js_value_to_literal(value: JsValue, context: &mut Context) -> Literal { JsValue::Null => Literal::Null, JsValue::Undefined => Literal::Undefined, JsValue::Boolean(v) => Literal::Bool(v), - JsValue::String(v) => Literal::String(context.interner_mut().get_or_intern(v.as_ref())), + JsValue::String(v) => { + // TODO: Replace JStrRef with JsStr this would eliminate the to_vec call. + let v = v.to_vec(); + Literal::String(context.interner_mut().get_or_intern(JStrRef::Utf16(&v))) + } JsValue::Rational(v) => Literal::Num(v), JsValue::Integer(v) => Literal::Int(v), JsValue::BigInt(v) => Literal::BigInt(Box::new(v.as_inner().clone())), diff --git a/core/engine/src/property/mod.rs b/core/engine/src/property/mod.rs index c18da6c6cfc..bb095142f2a 100644 --- a/core/engine/src/property/mod.rs +++ b/core/engine/src/property/mod.rs @@ -18,7 +18,9 @@ mod attribute; mod nonmaxu32; -use crate::{js_string, object::shape::slot::SlotAttributes, JsString, JsSymbol, JsValue}; +use crate::{ + js_string, object::shape::slot::SlotAttributes, string::JsStr, JsString, JsSymbol, JsValue, +}; use boa_gc::{Finalize, Trace}; use std::{fmt, iter::FusedIterator}; @@ -664,23 +666,18 @@ where } } -impl From<&[u16]> for PropertyKey { +impl From> for PropertyKey { #[inline] - fn from(string: &[u16]) -> Self { - debug_assert!(parse_u32_index( - String::from_utf16(string) - .expect("should be ascii string") - .bytes() - ) - .is_none()); - Self::String(string.into()) + fn from(string: JsStr<'_>) -> Self { + return parse_u32_index(string.iter()) + .map_or_else(|| Self::String(string.into()), Self::Index); } } impl From for PropertyKey { #[inline] fn from(string: JsString) -> Self { - parse_u32_index(string.as_slice().iter().copied()).map_or(Self::String(string), Self::Index) + return parse_u32_index(string.as_str().iter()).map_or(Self::String(string), Self::Index); } } diff --git a/core/engine/src/string/common.rs b/core/engine/src/string/common.rs index 173ca15126a..ba7785d7f63 100644 --- a/core/engine/src/string/common.rs +++ b/core/engine/src/string/common.rs @@ -1,21 +1,21 @@ -use std::hash::BuildHasherDefault; +//! List of commonly used strings in Javascript code. -use crate::tagged::Tagged; +use crate::{tagged::Tagged, JsStr}; use super::JsString; -use boa_macros::utf16; +use boa_macros::js_str; use paste::paste; use rustc_hash::{FxHashMap, FxHasher}; +use std::hash::BuildHasherDefault; macro_rules! well_known_statics { ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { $( paste!{ #[doc = "Gets the static `JsString` for `\"" $string "\"`."] - #[allow(unused)] - pub(crate) const $name: JsString = JsString { + pub const $name: JsString = JsString { ptr: Tagged::from_tag( - Self::find_index(utf16!($string)), + Self::find_index($string), ), }; } @@ -26,13 +26,13 @@ macro_rules! well_known_statics { /// List of commonly used strings in Javascript code. /// /// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap. -#[derive(Debug)] -pub(crate) struct StaticJsStrings; +#[derive(Debug, Clone, Copy)] +pub struct StaticJsStrings; impl StaticJsStrings { // useful to search at compile time a certain string in the array - const fn find_index(candidate: &[u16]) -> usize { - const fn const_eq(lhs: &[u16], rhs: &[u16]) -> bool { + const fn find_index(candidate: &str) -> usize { + const fn const_eq(lhs: &[u8], rhs: &[u8]) -> bool { if lhs.len() != rhs.len() { return false; } @@ -46,20 +46,26 @@ impl StaticJsStrings { } true } + + let len = RAW_STATICS.len(); let mut i = 0; - while i < RAW_STATICS.len() { - let s = RAW_STATICS[i]; - if const_eq(s, candidate) { + while i < len { + let Some(s) = RAW_STATICS[i].as_latin1() else { + // All static strings are latin1 encoded + unreachable!() + }; + if const_eq(s, candidate.as_bytes()) { return i; } i += 1; } + panic!("couldn't find the required string on the common string array"); } /// Gets the `JsString` corresponding to `string`, or `None` if the string /// doesn't exist inside the static array. - pub(crate) fn get_string(string: &[u16]) -> Option { + pub(crate) fn get_string(string: &JsStr<'_>) -> Option { if string.len() > MAX_STATIC_LENGTH { return None; } @@ -73,7 +79,7 @@ impl StaticJsStrings { /// Gets the `&[u16]` slice corresponding to the provided index, or `None` if the index /// provided exceeds the size of the static array. - pub(crate) fn get(index: usize) -> Option<&'static [u16]> { + pub(crate) fn get(index: usize) -> Option> { RAW_STATICS.get(index).copied() } @@ -141,11 +147,11 @@ impl StaticJsStrings { (PLURAL_RULES, "PluralRules"), (SEGMENTER, "Segmenter"), (DATE_TIME_FORMAT, "DateTimeFormat"), - (NUMBER_FORMAT, "NumberFormat"), (JSON, "JSON"), (MAP, "Map"), (MATH, "Math"), (NUMBER, "Number"), + (NUMBER_FORMAT, "NumberFormat"), (IS_FINITE, "isFinite"), (IS_NAN, "isNaN"), (PARSE_INT, "parseInt"), @@ -192,10 +198,11 @@ impl StaticJsStrings { } } -static MAX_STATIC_LENGTH: usize = { +const MAX_STATIC_LENGTH: usize = { let mut max = 0; let mut i = 0; while i < RAW_STATICS.len() { + // TOOD: Because `get_index` is not const, we are accessing doc hidden stuff, that may change. let len = RAW_STATICS[i].len(); if len > max { max = len; @@ -207,7 +214,7 @@ static MAX_STATIC_LENGTH: usize = { thread_local! { /// Map from a string inside [`RAW_STATICS`] to its corresponding static index on `RAW_STATICS`. - static RAW_STATICS_CACHE: FxHashMap<&'static [u16], usize> = { + static RAW_STATICS_CACHE: FxHashMap, usize> = { let mut constants = FxHashMap::with_capacity_and_hasher( RAW_STATICS.len(), BuildHasherDefault::::default(), @@ -222,495 +229,722 @@ thread_local! { } /// Array of raw static strings that aren't reference counted. -const RAW_STATICS: &[&[u16]] = &[ - utf16!(""), +const RAW_STATICS: &[JsStr<'_>] = &[ + js_str!(""), // Well known symbols - utf16!("Symbol.asyncIterator"), - utf16!("[Symbol.asyncIterator]"), - utf16!("Symbol.hasInstance"), - utf16!("[Symbol.hasInstance]"), - utf16!("Symbol.isConcatSpreadable"), - utf16!("[Symbol.isConcatSpreadable]"), - utf16!("Symbol.iterator"), - utf16!("[Symbol.iterator]"), - utf16!("Symbol.match"), - utf16!("[Symbol.match]"), - utf16!("Symbol.matchAll"), - utf16!("[Symbol.matchAll]"), - utf16!("Symbol.replace"), - utf16!("[Symbol.replace]"), - utf16!("Symbol.search"), - utf16!("[Symbol.search]"), - utf16!("Symbol.species"), - utf16!("[Symbol.species]"), - utf16!("Symbol.split"), - utf16!("[Symbol.split]"), - utf16!("Symbol.toPrimitive"), - utf16!("[Symbol.toPrimitive]"), - utf16!("Symbol.toStringTag"), - utf16!("[Symbol.toStringTag]"), - utf16!("Symbol.unscopables"), - utf16!("[Symbol.unscopables]"), + js_str!("Symbol.asyncIterator"), + js_str!("[Symbol.asyncIterator]"), + js_str!("Symbol.hasInstance"), + js_str!("[Symbol.hasInstance]"), + js_str!("Symbol.isConcatSpreadable"), + js_str!("[Symbol.isConcatSpreadable]"), + js_str!("Symbol.iterator"), + js_str!("[Symbol.iterator]"), + js_str!("Symbol.match"), + js_str!("[Symbol.match]"), + js_str!("Symbol.matchAll"), + js_str!("[Symbol.matchAll]"), + js_str!("Symbol.replace"), + js_str!("[Symbol.replace]"), + js_str!("Symbol.search"), + js_str!("[Symbol.search]"), + js_str!("Symbol.species"), + js_str!("[Symbol.species]"), + js_str!("Symbol.split"), + js_str!("[Symbol.split]"), + js_str!("Symbol.toPrimitive"), + js_str!("[Symbol.toPrimitive]"), + js_str!("Symbol.toStringTag"), + js_str!("[Symbol.toStringTag]"), + js_str!("Symbol.unscopables"), + js_str!("[Symbol.unscopables]"), + js_str!("get [Symbol.species]"), + js_str!("get [Symbol.toStringTag]"), // Well known builtins - utf16!("Array"), - utf16!("ArrayBuffer"), - utf16!("SharedArrayBuffer"), - utf16!("AsyncFunction"), - utf16!("AsyncGenerator"), - utf16!("AsyncGeneratorFunction"), - utf16!("Atomics"), - utf16!("BigInt"), - utf16!("Boolean"), - utf16!("DataView"), - utf16!("Date"), - utf16!("Error"), - utf16!("AggregateError"), - utf16!("EvalError"), - utf16!("RangeError"), - utf16!("ReferenceError"), - utf16!("SyntaxError"), - utf16!("TypeError"), - utf16!("URIError"), - utf16!("escape"), - utf16!("unescape"), - utf16!("eval"), - utf16!("Function"), - utf16!("Generator"), - utf16!("GeneratorFunction"), - utf16!("Intl"), - utf16!("Collator"), - utf16!("ListFormat"), - utf16!("Locale"), - utf16!("PluralRules"), - utf16!("Segmenter"), - utf16!("DateTimeFormat"), - utf16!("NumberFormat"), - utf16!("JSON"), - utf16!("Map"), - utf16!("Math"), - utf16!("Number"), - utf16!("isFinite"), - utf16!("isNaN"), - utf16!("parseInt"), - utf16!("parseFloat"), - utf16!("Object"), - utf16!("Promise"), - utf16!("Proxy"), - utf16!("Reflect"), - utf16!("RegExp"), - utf16!("Set"), - utf16!("String"), - utf16!("Symbol"), - utf16!("TypedArray"), - utf16!("Int8Array"), - utf16!("Uint8Array"), - utf16!("Uint8ClampedArray"), - utf16!("Int16Array"), - utf16!("Uint16Array"), - utf16!("Int32Array"), - utf16!("Uint32Array"), - utf16!("BigInt64Array"), - utf16!("BigUint64Array"), - utf16!("Float32Array"), - utf16!("Float64Array"), - utf16!("encodeURI"), - utf16!("encodeURIComponent"), - utf16!("decodeURI"), - utf16!("decodeURIComponent"), - utf16!("WeakRef"), - utf16!("WeakMap"), - utf16!("WeakSet"), - utf16!("Temporal"), - utf16!("Temporal.Now"), - utf16!("Temporal.Instant"), - utf16!("Temporal.Duration"), - utf16!("Temporal.Calendar"), - utf16!("Temporal.PlainDate"), - utf16!("Temporal.PlainDateTime"), - utf16!("Temporal.PlainMonthDay"), - utf16!("Temporal.PlainYearMonth"), - utf16!("Temporal.PlainTime"), - utf16!("Temporal.TimeZone"), - utf16!("Temporal.ZonedDateTime"), + js_str!("Array"), + js_str!("ArrayBuffer"), + js_str!("SharedArrayBuffer"), + js_str!("AsyncFunction"), + js_str!("AsyncGenerator"), + js_str!("AsyncGeneratorFunction"), + js_str!("Atomics"), + js_str!("BigInt"), + js_str!("Boolean"), + js_str!("DataView"), + js_str!("Date"), + js_str!("Error"), + js_str!("AggregateError"), + js_str!("EvalError"), + js_str!("RangeError"), + js_str!("ReferenceError"), + js_str!("SyntaxError"), + js_str!("TypeError"), + js_str!("URIError"), + js_str!("escape"), + js_str!("unescape"), + js_str!("eval"), + js_str!("Function"), + js_str!("Generator"), + js_str!("GeneratorFunction"), + js_str!("Intl"), + js_str!("Collator"), + js_str!("ListFormat"), + js_str!("Locale"), + js_str!("PluralRules"), + js_str!("Segmenter"), + js_str!("DateTimeFormat"), + js_str!("JSON"), + js_str!("Map"), + js_str!("Math"), + js_str!("Number"), + js_str!("NumberFormat"), + js_str!("isFinite"), + js_str!("isNaN"), + js_str!("parseInt"), + js_str!("parseFloat"), + js_str!("Object"), + js_str!("Promise"), + js_str!("Proxy"), + js_str!("Reflect"), + js_str!("RegExp"), + js_str!("Set"), + js_str!("String"), + js_str!("Symbol"), + js_str!("TypedArray"), + js_str!("Int8Array"), + js_str!("Uint8Array"), + js_str!("Uint8ClampedArray"), + js_str!("Int16Array"), + js_str!("Uint16Array"), + js_str!("Int32Array"), + js_str!("Uint32Array"), + js_str!("BigInt64Array"), + js_str!("BigUint64Array"), + js_str!("Float32Array"), + js_str!("Float64Array"), + js_str!("encodeURI"), + js_str!("encodeURIComponent"), + js_str!("decodeURI"), + js_str!("decodeURIComponent"), + js_str!("WeakRef"), + js_str!("WeakMap"), + js_str!("WeakSet"), + js_str!("Temporal"), + js_str!("Temporal.Now"), + js_str!("Temporal.Instant"), + js_str!("Temporal.Duration"), + js_str!("Temporal.Calendar"), + js_str!("Temporal.PlainDate"), + js_str!("Temporal.PlainDateTime"), + js_str!("Temporal.PlainMonthDay"), + js_str!("Temporal.PlainYearMonth"), + js_str!("Temporal.PlainTime"), + js_str!("Temporal.TimeZone"), + js_str!("Temporal.ZonedDateTime"), // Misc - utf16!(","), - utf16!(":"), + js_str!(","), + js_str!(":"), // Generic use - utf16!("name"), - utf16!("length"), - utf16!("arguments"), - utf16!("prototype"), - utf16!("constructor"), - utf16!("return"), - utf16!("throw"), - utf16!("global"), - utf16!("globalThis"), + js_str!("name"), + js_str!("length"), + js_str!("arguments"), + js_str!("prototype"), + js_str!("constructor"), + js_str!("return"), + js_str!("throw"), + js_str!("global"), + js_str!("globalThis"), // typeof - utf16!("null"), - utf16!("undefined"), - utf16!("number"), - utf16!("string"), - utf16!("symbol"), - utf16!("bigint"), - utf16!("object"), - utf16!("function"), + js_str!("null"), + js_str!("undefined"), + js_str!("number"), + js_str!("string"), + js_str!("symbol"), + js_str!("bigint"), + js_str!("object"), + js_str!("function"), // Property descriptor - utf16!("value"), - utf16!("get"), - utf16!("set"), - utf16!("writable"), - utf16!("enumerable"), - utf16!("configurable"), + js_str!("value"), + js_str!("get"), + js_str!("set"), + js_str!("writable"), + js_str!("enumerable"), + js_str!("configurable"), // Object object - utf16!("Object"), - utf16!("assign"), - utf16!("create"), - utf16!("toString"), - utf16!("valueOf"), - utf16!("is"), - utf16!("seal"), - utf16!("isSealed"), - utf16!("freeze"), - utf16!("isFrozen"), - utf16!("isExtensible"), - utf16!("hasOwnProperty"), - utf16!("isPrototypeOf"), - utf16!("setPrototypeOf"), - utf16!("getPrototypeOf"), - utf16!("defineProperty"), - utf16!("defineProperties"), - utf16!("deleteProperty"), - utf16!("construct"), - utf16!("hasOwn"), - utf16!("ownKeys"), - utf16!("keys"), - utf16!("values"), - utf16!("entries"), - utf16!("fromEntries"), + js_str!("assign"), + js_str!("create"), + js_str!("toString"), + js_str!("valueOf"), + js_str!("is"), + js_str!("seal"), + js_str!("isSealed"), + js_str!("freeze"), + js_str!("isFrozen"), + js_str!("isExtensible"), + js_str!("hasOwnProperty"), + js_str!("isPrototypeOf"), + js_str!("setPrototypeOf"), + js_str!("getPrototypeOf"), + js_str!("defineProperty"), + js_str!("defineProperties"), + js_str!("deleteProperty"), + js_str!("construct"), + js_str!("hasOwn"), + js_str!("ownKeys"), + js_str!("keys"), + js_str!("values"), + js_str!("entries"), + js_str!("fromEntries"), + js_str!("propertyIsEnumerable"), + js_str!("preventExtensions"), + js_str!("getOwnPropertyDescriptor"), + js_str!("getOwnPropertyDescriptors"), + js_str!("getOwnPropertyNames"), + js_str!("getOwnPropertySymbols"), + js_str!("__defineGetter__"), + js_str!("__defineSetter__"), + js_str!("__lookupGetter__"), + js_str!("__lookupSetter__"), + js_str!("__proto__"), + js_str!("get __proto__"), + js_str!("set __proto__"), // Function object - utf16!("apply"), - utf16!("bind"), - utf16!("call"), + js_str!("apply"), + js_str!("bind"), + js_str!("call"), + js_str!("caller"), + // Arguments object + js_str!("callee"), // Array object - utf16!("at"), - utf16!("from"), - utf16!("isArray"), - utf16!("of"), - utf16!("copyWithin"), - utf16!("every"), - utf16!("fill"), - utf16!("filter"), - utf16!("find"), - utf16!("findIndex"), - utf16!("findLast"), - utf16!("findLastIndex"), - utf16!("flat"), - utf16!("flatMap"), - utf16!("forEach"), - utf16!("includes"), - utf16!("indexOf"), - utf16!("join"), - utf16!("map"), - utf16!("next"), - utf16!("reduce"), - utf16!("reduceRight"), - utf16!("reverse"), - utf16!("shift"), - utf16!("slice"), - utf16!("splice"), - utf16!("some"), - utf16!("sort"), - utf16!("unshift"), - utf16!("push"), - utf16!("pop"), + js_str!("at"), + js_str!("from"), + js_str!("isArray"), + js_str!("of"), + js_str!("copyWithin"), + js_str!("every"), + js_str!("fill"), + js_str!("filter"), + js_str!("find"), + js_str!("findIndex"), + js_str!("findLast"), + js_str!("findLastIndex"), + js_str!("flat"), + js_str!("flatMap"), + js_str!("forEach"), + js_str!("includes"), + js_str!("indexOf"), + js_str!("join"), + js_str!("map"), + js_str!("next"), + js_str!("reduce"), + js_str!("reduceRight"), + js_str!("reverse"), + js_str!("shift"), + js_str!("slice"), + js_str!("splice"), + js_str!("some"), + js_str!("sort"), + js_str!("unshift"), + js_str!("push"), + js_str!("pop"), + js_str!("groupBy"), + js_str!("toReversed"), + js_str!("toSorted"), + js_str!("toSpliced"), + js_str!("with"), // String object - utf16!("charAt"), - utf16!("charCodeAt"), - utf16!("codePointAt"), - utf16!("concat"), - utf16!("endsWith"), - utf16!("fromCharCode"), - utf16!("fromCodePoint"), - utf16!("lastIndexOf"), - utf16!("match"), - utf16!("matchAll"), - utf16!("normalize"), - utf16!("padEnd"), - utf16!("padStart"), - utf16!("raw"), - utf16!("repeat"), - utf16!("replace"), - utf16!("replaceAll"), - utf16!("search"), - utf16!("split"), - utf16!("startsWith"), - utf16!("substr"), - utf16!("substring"), - utf16!("toLocaleString"), - utf16!("toLowerCase"), - utf16!("toUpperCase"), - utf16!("trim"), - utf16!("trimEnd"), - utf16!("trimStart"), + js_str!("charAt"), + js_str!("charCodeAt"), + js_str!("codePointAt"), + js_str!("concat"), + js_str!("endsWith"), + js_str!("fromCharCode"), + js_str!("fromCodePoint"), + js_str!("lastIndexOf"), + js_str!("match"), + js_str!("matchAll"), + js_str!("normalize"), + js_str!("padEnd"), + js_str!("padStart"), + js_str!("raw"), + js_str!("repeat"), + js_str!("replace"), + js_str!("replaceAll"), + js_str!("search"), + js_str!("split"), + js_str!("startsWith"), + js_str!("substr"), + js_str!("substring"), + js_str!("toLocaleString"), + js_str!("toLowerCase"), + js_str!("toUpperCase"), + js_str!("trim"), + js_str!("trimEnd"), + js_str!("trimStart"), + js_str!("isWellFormed"), + js_str!("localeCompare"), + js_str!("toWellFormed"), + js_str!("toLocaleLowerCase"), + js_str!("toLocaleUpperCase"), + js_str!("trimLeft"), + js_str!("trimRight"), + js_str!("anchor"), + js_str!("big"), + js_str!("blink"), + js_str!("bold"), + js_str!("fixed"), + js_str!("fontcolor"), + js_str!("fontsize"), + js_str!("italics"), + js_str!("link"), + js_str!("small"), + js_str!("strike"), + js_str!("sub"), + js_str!("sup"), // Number object - utf16!("Infinity"), - utf16!("NaN"), - utf16!("EPSILON"), - utf16!("MAX_SAFE_INTEGER"), - utf16!("MIN_SAFE_INTEGER"), - utf16!("MAX_VALUE"), - utf16!("MIN_VALUE"), - utf16!("isSafeInteger"), - utf16!("isInteger"), - utf16!("toExponential"), - utf16!("toFixed"), - utf16!("toPrecision"), + js_str!("Infinity"), + js_str!("NaN"), + js_str!("EPSILON"), + js_str!("MAX_SAFE_INTEGER"), + js_str!("MIN_SAFE_INTEGER"), + js_str!("MAX_VALUE"), + js_str!("MIN_VALUE"), + js_str!("NEGATIVE_INFINITY"), + js_str!("POSITIVE_INFINITY"), + js_str!("isSafeInteger"), + js_str!("isInteger"), + js_str!("toExponential"), + js_str!("toFixed"), + js_str!("toPrecision"), // BigInt object - utf16!("asIntN"), - utf16!("asUintN"), + js_str!("asIntN"), + js_str!("asUintN"), // RegExp object - utf16!("exec"), - utf16!("test"), - utf16!("flags"), - utf16!("index"), - utf16!("lastIndex"), - utf16!("hasIndices"), - utf16!("ignoreCase"), - utf16!("multiline"), - utf16!("dotAll"), - utf16!("unicode"), - utf16!("sticky"), - utf16!("source"), - utf16!("get hasIndices"), - utf16!("get global"), - utf16!("get ignoreCase"), - utf16!("get multiline"), - utf16!("get dotAll"), - utf16!("get unicode"), - utf16!("get sticky"), - utf16!("get flags"), - utf16!("get source"), + js_str!("exec"), + js_str!("test"), + js_str!("compile"), + js_str!("flags"), + js_str!("index"), + js_str!("lastIndex"), + js_str!("hasIndices"), + js_str!("ignoreCase"), + js_str!("multiline"), + js_str!("dotAll"), + js_str!("unicode"), + js_str!("sticky"), + js_str!("source"), + js_str!("get hasIndices"), + js_str!("get global"), + js_str!("get ignoreCase"), + js_str!("get multiline"), + js_str!("get dotAll"), + js_str!("get unicode"), + js_str!("get sticky"), + js_str!("get flags"), + js_str!("get source"), // Symbol object - utf16!("for"), - utf16!("keyFor"), - utf16!("description"), - utf16!("asyncIterator"), - utf16!("hasInstance"), - utf16!("species"), - utf16!("unscopables"), - utf16!("iterator"), - utf16!("toStringTag"), - utf16!("toPrimitive"), - utf16!("get description"), + js_str!("for"), + js_str!("keyFor"), + js_str!("description"), + js_str!("asyncIterator"), + js_str!("hasInstance"), + js_str!("species"), + js_str!("unscopables"), + js_str!("iterator"), + js_str!("toStringTag"), + js_str!("toPrimitive"), + js_str!("isConcatSpreadable"), + js_str!("get description"), // Map object - utf16!("clear"), - utf16!("delete"), - utf16!("has"), - utf16!("size"), + js_str!("clear"), + js_str!("delete"), + js_str!("has"), + js_str!("size"), // Set object - utf16!("add"), + js_str!("add"), // Reflect object // Proxy object - utf16!("revocable"), + js_str!("revocable"), // Error objects - utf16!("message"), + js_str!("message"), // Date object - utf16!("toJSON"), - utf16!("getDate"), - utf16!("getDay"), - utf16!("getFullYear"), - utf16!("getHours"), - utf16!("getMilliseconds"), - utf16!("getMinutes"), - utf16!("getMonth"), - utf16!("getSeconds"), - utf16!("getTime"), - utf16!("getYear"), - utf16!("getUTCDate"), - utf16!("getUTCDay"), - utf16!("getUTCFullYear"), - utf16!("getUTCHours"), - utf16!("getUTCMinutes"), - utf16!("getUTCMonth"), - utf16!("getUTCSeconds"), - utf16!("setDate"), - utf16!("setFullYear"), - utf16!("setHours"), - utf16!("setMilliseconds"), - utf16!("setMinutes"), - utf16!("setMonth"), - utf16!("setSeconds"), - utf16!("setYear"), - utf16!("setTime"), - utf16!("setUTCDate"), - utf16!("setUTCFullYear"), - utf16!("setUTCHours"), - utf16!("setUTCMinutes"), - utf16!("setUTCMonth"), - utf16!("setUTCSeconds"), - utf16!("toDateString"), - utf16!("toGMTString"), - utf16!("toISOString"), - utf16!("toTimeString"), - utf16!("toUTCString"), - utf16!("now"), - utf16!("UTC"), + js_str!("toJSON"), + js_str!("getDate"), + js_str!("getDay"), + js_str!("getFullYear"), + js_str!("getHours"), + js_str!("getMilliseconds"), + js_str!("getMinutes"), + js_str!("getMonth"), + js_str!("getSeconds"), + js_str!("getTime"), + js_str!("getYear"), + js_str!("getUTCDate"), + js_str!("getUTCDay"), + js_str!("getUTCFullYear"), + js_str!("getUTCHours"), + js_str!("getUTCMinutes"), + js_str!("getUTCMonth"), + js_str!("getUTCSeconds"), + js_str!("setDate"), + js_str!("setFullYear"), + js_str!("setHours"), + js_str!("setMilliseconds"), + js_str!("setMinutes"), + js_str!("setMonth"), + js_str!("setSeconds"), + js_str!("setYear"), + js_str!("setTime"), + js_str!("setUTCDate"), + js_str!("setUTCFullYear"), + js_str!("setUTCHours"), + js_str!("setUTCMinutes"), + js_str!("setUTCMonth"), + js_str!("setUTCSeconds"), + js_str!("toDateString"), + js_str!("toGMTString"), + js_str!("toISOString"), + js_str!("toTimeString"), + js_str!("toUTCString"), + js_str!("now"), + js_str!("UTC"), + js_str!("getTimezoneOffset"), + js_str!("getUTCMilliseconds"), + js_str!("setUTCMilliseconds"), + js_str!("toLocaleDateString"), + js_str!("toLocaleTimeString"), // JSON object - utf16!("parse"), - utf16!("stringify"), + js_str!("parse"), + js_str!("stringify"), + // Promise object + js_str!("promise"), + js_str!("resolve"), + js_str!("reject"), + js_str!("all"), + js_str!("allSettled"), + js_str!("any"), + js_str!("race"), + js_str!("then"), + js_str!("catch"), + js_str!("finally"), + js_str!("withResolvers"), // Iterator object - utf16!("Array Iterator"), - utf16!("Set Iterator"), - utf16!("String Iterator"), - utf16!("Map Iterator"), - utf16!("For In Iterator"), + js_str!("Array Iterator"), + js_str!("Set Iterator"), + js_str!("String Iterator"), + js_str!("Map Iterator"), + js_str!("For In Iterator"), + js_str!("RegExp String Iterator"), + // Iterator result object + js_str!("done"), // Math object - utf16!("LN10"), - utf16!("LN2"), - utf16!("LOG10E"), - utf16!("LOG2E"), - utf16!("PI"), - utf16!("SQRT1_2"), - utf16!("SQRT2"), - utf16!("abs"), - utf16!("acos"), - utf16!("acosh"), - utf16!("asin"), - utf16!("asinh"), - utf16!("atan"), - utf16!("atanh"), - utf16!("atan2"), - utf16!("cbrt"), - utf16!("ceil"), - utf16!("clz32"), - utf16!("cos"), - utf16!("cosh"), - utf16!("exp"), - utf16!("expm1"), - utf16!("floor"), - utf16!("fround"), - utf16!("hypot"), - utf16!("imul"), - utf16!("log"), - utf16!("log1p"), - utf16!("log10"), - utf16!("log2"), - utf16!("max"), - utf16!("min"), - utf16!("pow"), - utf16!("random"), - utf16!("round"), - utf16!("sign"), - utf16!("sin"), - utf16!("sinh"), - utf16!("sqrt"), - utf16!("tan"), - utf16!("tanh"), - utf16!("trunc"), + js_str!("LN10"), + js_str!("LN2"), + js_str!("LOG10E"), + js_str!("LOG2E"), + js_str!("PI"), + js_str!("SQRT1_2"), + js_str!("SQRT2"), + js_str!("abs"), + js_str!("acos"), + js_str!("acosh"), + js_str!("asin"), + js_str!("asinh"), + js_str!("atan"), + js_str!("atanh"), + js_str!("atan2"), + js_str!("cbrt"), + js_str!("ceil"), + js_str!("clz32"), + js_str!("cos"), + js_str!("cosh"), + js_str!("exp"), + js_str!("expm1"), + js_str!("floor"), + js_str!("fround"), + js_str!("hypot"), + js_str!("imul"), + js_str!("log"), + js_str!("log1p"), + js_str!("log10"), + js_str!("log2"), + js_str!("max"), + js_str!("min"), + js_str!("pow"), + js_str!("random"), + js_str!("round"), + js_str!("sign"), + js_str!("sin"), + js_str!("sinh"), + js_str!("sqrt"), + js_str!("tan"), + js_str!("tanh"), + js_str!("trunc"), // TypedArray object - utf16!("buffer"), - utf16!("byteLength"), - utf16!("byteOffset"), - utf16!("isView"), - utf16!("subarray"), - utf16!("get byteLength"), - utf16!("get buffer"), - utf16!("get byteOffset"), - utf16!("get size"), - utf16!("get length"), + js_str!("BYTES_PER_ELEMENT"), + js_str!("buffer"), + js_str!("byteLength"), + js_str!("byteOffset"), + js_str!("isView"), + js_str!("subarray"), + js_str!("get byteLength"), + js_str!("get buffer"), + js_str!("get byteOffset"), + js_str!("get size"), + js_str!("get length"), // DataView object - utf16!("getBigInt64"), - utf16!("getBigUint64"), - utf16!("getFloat32"), - utf16!("getFloat64"), - utf16!("getInt8"), - utf16!("getInt16"), - utf16!("getInt32"), - utf16!("getUint8"), - utf16!("getUint16"), - utf16!("getUint32"), - utf16!("setBigInt64"), - utf16!("setBigUint64"), - utf16!("setFloat32"), - utf16!("setFloat64"), - utf16!("setInt8"), - utf16!("setInt16"), - utf16!("setInt32"), - utf16!("setUint8"), - utf16!("setUint16"), - utf16!("setUint32"), + js_str!("getBigInt64"), + js_str!("getBigUint64"), + js_str!("getFloat32"), + js_str!("getFloat64"), + js_str!("getInt8"), + js_str!("getInt16"), + js_str!("getInt32"), + js_str!("getUint8"), + js_str!("getUint16"), + js_str!("getUint32"), + js_str!("setBigInt64"), + js_str!("setBigUint64"), + js_str!("setFloat32"), + js_str!("setFloat64"), + js_str!("setInt8"), + js_str!("setInt16"), + js_str!("setInt32"), + js_str!("setUint8"), + js_str!("setUint16"), + js_str!("setUint32"), + // WeakRef object + js_str!("deref"), + // Atomic object + js_str!("and"), + js_str!("compareExchange"), + js_str!("exchange"), + js_str!("isLockFree"), + js_str!("load"), + js_str!("or"), + js_str!("store"), + js_str!("wait"), + js_str!("notify"), + js_str!("xor"), + // Intl object + js_str!("getCanonicalLocales"), + js_str!("get compare"), + js_str!("supportedLocalesOf"), + js_str!("Intl.Collator"), + js_str!("compare"), + js_str!("resolvedOptions"), + js_str!("Intl.ListFormat"), + js_str!("format"), + js_str!("formatToParts"), + js_str!("get baseName"), + js_str!("get calendar"), + js_str!("get caseFirst"), + js_str!("get collation"), + js_str!("get hourCycle"), + js_str!("get numeric"), + js_str!("get numberingSystem"), + js_str!("get language"), + js_str!("get script"), + js_str!("get region"), + js_str!("Intl.Locale"), + js_str!("maximize"), + js_str!("minimize"), + js_str!("baseName"), + js_str!("calendar"), + js_str!("caseFirst"), + js_str!("collation"), + js_str!("hourCycle"), + js_str!("numeric"), + js_str!("numberingSystem"), + js_str!("language"), + js_str!("script"), + js_str!("region"), + js_str!("Intl.Segmenter"), + js_str!("segment"), + js_str!("containing"), + js_str!("Segmenter String Iterator"), + js_str!("Intl.PluralRules"), + js_str!("select"), + // Temporal object + js_str!("get Id"), + js_str!("getOffsetNanosecondsFor"), + js_str!("getOffsetStringFor"), + js_str!("getPlainDateTimeFor"), + js_str!("getInstantFor"), + js_str!("getPossibleInstantFor"), + js_str!("getNextTransition"), + js_str!("getPreviousTransition"), + js_str!("id"), + js_str!("Now"), + js_str!("Calendar"), + js_str!("Duration"), + js_str!("Instant"), + js_str!("PlainDate"), + js_str!("PlainDateTime"), + js_str!("PlainMonthDay"), + js_str!("PlainTime"), + js_str!("PlainYearMonth"), + js_str!("TimeZone"), + js_str!("ZonedDateTime"), + js_str!("timeZoneId"), + js_str!("instant"), + js_str!("plainDateTime"), + js_str!("plainDateTimeISO"), + js_str!("zonedDateTime"), + js_str!("zonedDateTimeISO"), + js_str!("plainDate"), + js_str!("plainDateISO"), + js_str!("get epochSeconds"), + js_str!("get epochMilliseconds"), + js_str!("get epochMicroseconds"), + js_str!("get epochNanoseconds"), + js_str!("epochSeconds"), + js_str!("epochMilliseconds"), + js_str!("epochMicroseconds"), + js_str!("epochNanoseconds"), + js_str!("subtract"), + js_str!("until"), + js_str!("since"), + js_str!("equals"), + js_str!("toZonedDateTime"), + js_str!("toZonedDateTimeISO"), + js_str!("get Years"), + js_str!("get Months"), + js_str!("get Weeks"), + js_str!("get Days"), + js_str!("get Hours"), + js_str!("get Minutes"), + js_str!("get Seconds"), + js_str!("get Milliseconds"), + js_str!("get Microseconds"), + js_str!("get Nanoseconds"), + js_str!("get Sign"), + js_str!("get blank"), + js_str!("years"), + js_str!("months"), + js_str!("weeks"), + js_str!("days"), + js_str!("hours"), + js_str!("minutes"), + js_str!("seconds"), + js_str!("milliseconds"), + js_str!("microseconds"), + js_str!("nanoseconds"), + js_str!("blank"), + js_str!("negated"), + js_str!("total"), + js_str!("get calendarId"), + js_str!("get year"), + js_str!("get month"), + js_str!("get monthCode"), + js_str!("get day"), + js_str!("get dayOfWeek"), + js_str!("get dayOfYear"), + js_str!("get weekOfYear"), + js_str!("get yearOfWeek"), + js_str!("get daysInWeek"), + js_str!("get daysInMonth"), + js_str!("get daysInYear"), + js_str!("get monthsInYear"), + js_str!("get inLeapYear"), + js_str!("calendarId"), + js_str!("year"), + js_str!("month"), + js_str!("monthCode"), + js_str!("day"), + js_str!("dayOfWeek"), + js_str!("dayOfYear"), + js_str!("weekOfYear"), + js_str!("yearOfWeek"), + js_str!("daysInWeek"), + js_str!("daysInMonth"), + js_str!("daysInYear"), + js_str!("monthsInYear"), + js_str!("inLeapYear"), + js_str!("toPlainYearMonth"), + js_str!("toPlainMonthDay"), + js_str!("getISOFields"), + js_str!("getCalendar"), + js_str!("withCalendar"), + js_str!("dateFromFields"), + js_str!("yearMonthFromFields"), + js_str!("monthDayFromFields"), + js_str!("dateAdd"), + js_str!("dateUntil"), + js_str!("era"), + js_str!("eraYear"), + js_str!("fields"), + js_str!("mergeFields"), // Console object - utf16!("console"), - utf16!("assert"), - utf16!("debug"), - utf16!("error"), - utf16!("info"), - utf16!("trace"), - utf16!("warn"), - utf16!("exception"), - utf16!("count"), - utf16!("countReset"), - utf16!("group"), - utf16!("groupCollapsed"), - utf16!("groupEnd"), - utf16!("time"), - utf16!("timeLog"), - utf16!("timeEnd"), - utf16!("dir"), - utf16!("dirxml"), + js_str!("console"), + js_str!("assert"), + js_str!("debug"), + js_str!("error"), + js_str!("info"), + js_str!("trace"), + js_str!("warn"), + js_str!("exception"), + js_str!("count"), + js_str!("countReset"), + js_str!("group"), + js_str!("groupCollapsed"), + js_str!("groupEnd"), + js_str!("time"), + js_str!("timeLog"), + js_str!("timeEnd"), + js_str!("dir"), + js_str!("dirxml"), // Minified name - utf16!("a"), - utf16!("b"), - utf16!("c"), - utf16!("d"), - utf16!("e"), - utf16!("f"), - utf16!("g"), - utf16!("h"), - utf16!("i"), - utf16!("j"), - utf16!("k"), - utf16!("l"), - utf16!("m"), - utf16!("n"), - utf16!("o"), - utf16!("p"), - utf16!("q"), - utf16!("r"), - utf16!("s"), - utf16!("t"), - utf16!("u"), - utf16!("v"), - utf16!("w"), - utf16!("x"), - utf16!("y"), - utf16!("z"), - utf16!("A"), - utf16!("B"), - utf16!("C"), - utf16!("D"), - utf16!("E"), - utf16!("F"), - utf16!("G"), - utf16!("H"), - utf16!("I"), - utf16!("J"), - utf16!("K"), - utf16!("L"), - utf16!("M"), - utf16!("N"), - utf16!("O"), - utf16!("P"), - utf16!("Q"), - utf16!("R"), - utf16!("S"), - utf16!("T"), - utf16!("U"), - utf16!("V"), - utf16!("W"), - utf16!("X"), - utf16!("Y"), - utf16!("Z"), - utf16!("_"), - utf16!("$"), + js_str!("a"), + js_str!("c"), + js_str!("d"), + js_str!("e"), + js_str!("f"), + js_str!("g"), + js_str!("h"), + js_str!("i"), + js_str!("j"), + js_str!("k"), + js_str!("l"), + js_str!("m"), + js_str!("n"), + js_str!("o"), + js_str!("p"), + js_str!("q"), + js_str!("r"), + js_str!("s"), + js_str!("t"), + js_str!("u"), + js_str!("v"), + js_str!("w"), + js_str!("x"), + js_str!("y"), + js_str!("z"), + js_str!("A"), + js_str!("C"), + js_str!("D"), + js_str!("E"), + js_str!("F"), + js_str!("G"), + js_str!("H"), + js_str!("I"), + js_str!("J"), + js_str!("K"), + js_str!("L"), + js_str!("M"), + js_str!("N"), + js_str!("O"), + js_str!("P"), + js_str!("Q"), + js_str!("R"), + js_str!("S"), + js_str!("T"), + js_str!("U"), + js_str!("V"), + js_str!("W"), + js_str!("X"), + js_str!("Y"), + js_str!("Z"), + js_str!("_"), + js_str!("$"), ]; diff --git a/core/engine/src/string/iter.rs b/core/engine/src/string/iter.rs new file mode 100644 index 00000000000..03dedcb6b8d --- /dev/null +++ b/core/engine/src/string/iter.rs @@ -0,0 +1,95 @@ +use std::iter::FusedIterator; + +use crate::JsStr; + +use super::JsStrVariant; + +#[derive(Debug, Clone)] +enum IterInner<'a> { + U8(std::iter::Copied>), + U16(std::iter::Copied>), +} + +/// Iterator over a [`JsStr`]. +#[derive(Debug, Clone)] +pub struct Iter<'a> { + inner: IterInner<'a>, +} + +impl<'a> Iter<'a> { + pub(crate) fn new(s: JsStr<'a>) -> Self { + let inner = match s.variant() { + JsStrVariant::Latin1(s) => IterInner::U8(s.iter().copied()), + JsStrVariant::Utf16(s) => IterInner::U16(s.iter().copied()), + }; + Iter { inner } + } +} + +impl Iterator for Iter<'_> { + type Item = u16; + + fn next(&mut self) -> Option { + match &mut self.inner { + IterInner::U8(iter) => iter.map(u16::from).next(), + IterInner::U16(iter) => iter.next(), + } + } +} + +impl FusedIterator for Iter<'_> {} + +impl ExactSizeIterator for Iter<'_> { + fn len(&self) -> usize { + match &self.inner { + IterInner::U8(v) => v.len(), + IterInner::U16(v) => v.len(), + } + } +} + +#[derive(Debug, Clone)] +enum WindowsInner<'a> { + U8(std::slice::Windows<'a, u8>), + U16(std::slice::Windows<'a, u16>), +} + +/// An iterator over overlapping subslices of length size. +/// +/// This struct is created by the `windows` method. +#[derive(Debug, Clone)] +pub struct Windows<'a> { + inner: WindowsInner<'a>, +} + +impl<'a> Windows<'a> { + pub(crate) fn new(string: JsStr<'a>, size: usize) -> Self { + let inner = match string.variant() { + JsStrVariant::Latin1(v) => WindowsInner::U8(v.windows(size)), + JsStrVariant::Utf16(v) => WindowsInner::U16(v.windows(size)), + }; + Self { inner } + } +} + +impl<'a> Iterator for Windows<'a> { + type Item = JsStr<'a>; + + fn next(&mut self) -> Option { + match &mut self.inner { + WindowsInner::U8(iter) => iter.next().map(JsStr::latin1), + WindowsInner::U16(iter) => iter.next().map(JsStr::utf16), + } + } +} + +impl FusedIterator for Windows<'_> {} + +impl ExactSizeIterator for Windows<'_> { + fn len(&self) -> usize { + match &self.inner { + WindowsInner::U8(v) => v.len(), + WindowsInner::U16(v) => v.len(), + } + } +} diff --git a/core/engine/src/string/mod.rs b/core/engine/src/string/mod.rs index ccbbebed2db..16f21727310 100644 --- a/core/engine/src/string/mod.rs +++ b/core/engine/src/string/mod.rs @@ -1,10 +1,10 @@ -//! A UTF-16–encoded, reference counted, immutable string. +//! A Latin1 or UTF-16 encoded, reference counted, immutable string. //! //! This module contains the [`JsString`] type, the [`js_string`][crate::js_string] macro and the -//! [`utf16`] macro. +//! [`js_str`][crate::js_str] macro. //! //! The [`js_string`][crate::js_string] macro is used when you need to create a new [`JsString`], -//! and the [`utf16`] macro is used for const conversions of string literals to UTF-16. +//! and the [`js_str`][crate::js_str] macro is used for const conversions of string literals to [`JsStr`]. // Required per unsafe code standards to ensure every unsafe usage is properly documented. // - `unsafe_op_in_unsafe_fn` will be warn-by-default in edition 2024: @@ -21,32 +21,32 @@ // the same names from the unstable functions of the `std::ptr` module. #![allow(unstable_name_collisions)] -pub(crate) mod common; +pub mod common; +mod iter; +mod str; +use self::{common::StaticJsStrings, iter::Windows, str::JsSliceIndex}; +#[doc(inline)] +pub use crate::string::{ + iter::Iter, + str::{JsStr, JsStrVariant}, +}; use crate::{ builtins::string::is_trimmable_whitespace, tagged::{Tagged, UnwrappedTagged}, - JsBigInt, }; use boa_gc::{Finalize, Trace}; -pub use boa_macros::utf16; - use std::{ alloc::{alloc, dealloc, Layout}, - borrow::Borrow, cell::Cell, convert::Infallible, hash::{Hash, Hasher}, iter::Peekable, - ops::{Deref, Index}, process::abort, ptr::{self, addr_of, addr_of_mut, NonNull}, - slice::SliceIndex, str::FromStr, }; -use self::common::StaticJsStrings; - fn alloc_overflow() -> ! { panic!("detected overflow during string allocation") } @@ -59,7 +59,6 @@ fn alloc_overflow() -> ! { /// /// ``` /// use boa_engine::js_string; -/// use boa_engine::string::utf16; /// /// let empty_str = js_string!(); /// assert!(empty_str.is_empty()); @@ -71,9 +70,8 @@ fn alloc_overflow() -> ! { /// /// ``` /// # use boa_engine::js_string; -/// # use boa_engine::string::utf16; /// let hw = js_string!("Hello, world!"); -/// assert_eq!(&hw, utf16!("Hello, world!")); +/// assert_eq!(&hw, "Hello, world!"); /// ``` /// /// Any `&[u16]` slice is a valid `JsString`, including unpaired surrogates: @@ -87,30 +85,30 @@ fn alloc_overflow() -> ! { /// the concatenation of every slice: /// /// ``` -/// # use boa_engine::js_string; -/// # use boa_engine::string::utf16; -/// const NAME: &[u16] = utf16!("human! "); +/// # use boa_engine::{js_string, js_str, JsStr}; +/// const NAME: JsStr<'_> = js_str!("human! "); /// let greeting = js_string!("Hello, "); -/// let msg = js_string!(&greeting, &NAME, utf16!("Nice to meet you!")); +/// let msg = js_string!(&greeting, NAME, js_str!("Nice to meet you!")); /// -/// assert_eq!(&msg, utf16!("Hello, human! Nice to meet you!")); +/// assert_eq!(&msg, "Hello, human! Nice to meet you!"); /// ``` #[macro_export] +#[allow(clippy::module_name_repetitions)] macro_rules! js_string { () => { - $crate::JsString::default() + $crate::string::JsString::default() }; ($s:literal) => { - $crate::JsString::from($crate::string::utf16!($s)) + $crate::string::JsString::from($crate::js_str!($s)) }; ($s:expr) => { - $crate::JsString::from($s) + $crate::string::JsString::from($s) }; ( $x:expr, $y:expr ) => { - $crate::JsString::concat($x, $y) + $crate::string::JsString::concat($crate::string::JsStr::from($x), $crate::string::JsStr::from($y)) }; ( $( $s:expr ),+ ) => { - $crate::JsString::concat_array(&[ $( $s ),+ ]) + $crate::string::JsString::concat_array(&[ $( $crate::string::JsStr::from($s) ),+ ]) }; } @@ -177,8 +175,10 @@ impl CodePoint { /// The raw representation of a [`JsString`] in the heap. #[repr(C)] struct RawJsString { - /// The UTF-16 length. - len: usize, + /// Contains the flags and Latin1/UTF-16 length. + /// + /// The latin1 flag is stored in the bottom bit. + flags_and_len: usize, /// The number of references to the string. /// @@ -189,63 +189,142 @@ struct RawJsString { data: [u16; 0], } +impl RawJsString { + const LATIN1_BITFLAG: usize = 1 << 0; + const BITFLAG_COUNT: usize = 1; + + const fn is_latin1(&self) -> bool { + (self.flags_and_len & Self::LATIN1_BITFLAG) != 0 + } + + const fn len(&self) -> usize { + self.flags_and_len >> Self::BITFLAG_COUNT + } + + const fn encode_flags_and_len(len: usize, latin1: bool) -> usize { + (len << Self::BITFLAG_COUNT) | (latin1 as usize) + } +} + const DATA_OFFSET: usize = std::mem::size_of::(); -/// A UTF-16–encoded, reference counted, immutable string. +/// A Latin1 or UTF-16–encoded, reference counted, immutable string. /// /// This is pretty similar to a [Rc][std::rc::Rc]\<[\[u16\]][slice]\>, but without the /// length metadata associated with the `Rc` fat pointer. Instead, the length of every string is /// stored on the heap, along with its reference counter and its data. /// +/// The string can be latin1 (stored as a byte for space efficiency) or U16 encoding. +/// /// We define some commonly used string constants in an interner. For these strings, we don't allocate /// memory on the heap to reduce the overhead of memory allocation and reference counting. -/// -/// # Deref -/// -/// [`JsString`] implements [Deref], inheriting all of -/// \[u16\]'s methods. #[derive(Trace, Finalize)] // Safety: `JsString` does not contain any objects which needs to be traced, so this is safe. #[boa_gc(unsafe_empty_trace)] +#[allow(clippy::module_name_repetitions)] pub struct JsString { ptr: Tagged, } // JsString should always be pointer sized. -sa::assert_eq_size!(JsString, *const ()); +static_assertions::assert_eq_size!(JsString, *const ()); + +impl<'a> From<&'a JsString> for JsStr<'a> { + fn from(value: &'a JsString) -> Self { + value.as_str() + } +} + +impl<'a> IntoIterator for &'a JsString { + type IntoIter = Iter<'a>; + + type Item = u16; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} impl JsString { + /// Create an iterator over the [`JsString`]. + #[inline] + #[must_use] + pub fn iter(&self) -> Iter<'_> { + Iter::new(self.as_str()) + } + + /// Create an iterator over overlapping subslices of length size. + #[inline] + #[must_use] + pub fn windows(&self, size: usize) -> Windows<'_> { + Windows::new(self.as_str(), size) + } + /// Obtains the underlying [`&[u16]`][slice] slice of a [`JsString`] #[must_use] - pub fn as_slice(&self) -> &[u16] { - self + pub fn as_str(&self) -> JsStr<'_> { + match self.ptr.unwrap() { + UnwrappedTagged::Ptr(h) => { + // SAFETY: + // - The `RawJsString` type has all the necessary information to reconstruct a valid + // slice (length and starting pointer). + // + // - We aligned `h.data` on allocation, and the block is of size `h.len`, so this + // should only generate valid reads. + // + // - The lifetime of `&Self::Target` is shorter than the lifetime of `self`, as seen + // by its signature, so this doesn't outlive `self`. + unsafe { + let h = h.as_ptr(); + + if (*h).is_latin1() { + JsStr::latin1(std::slice::from_raw_parts( + addr_of!((*h).data).cast(), + (*h).len(), + )) + } else { + JsStr::utf16(std::slice::from_raw_parts( + addr_of!((*h).data).cast(), + (*h).len(), + )) + } + } + } + UnwrappedTagged::Tag(index) => { + // SAFETY: all static strings are valid indices on `STATIC_JS_STRINGS`, so `get` should always + // return `Some`. + unsafe { StaticJsStrings::get(index).unwrap_unchecked() } + } + } } /// Creates a new [`JsString`] from the concatenation of `x` and `y`. #[must_use] - pub fn concat(x: &[u16], y: &[u16]) -> Self { + pub fn concat(x: JsStr<'_>, y: JsStr<'_>) -> Self { Self::concat_array(&[x, y]) } /// Creates a new [`JsString`] from the concatenation of every element of /// `strings`. #[must_use] - pub fn concat_array(strings: &[&[u16]]) -> Self { + pub fn concat_array(strings: &[JsStr<'_>]) -> Self { + let mut latin1_encoding = true; let mut full_count = 0usize; - for &string in strings { + for string in strings { let Some(sum) = full_count.checked_add(string.len()) else { alloc_overflow() }; + if !string.is_latin1() { + latin1_encoding = false; + } full_count = sum; } - let ptr = Self::allocate_inner(full_count); + let ptr = Self::allocate_inner(full_count, latin1_encoding); let string = { // SAFETY: `allocate_inner` guarantees that `ptr` is a valid pointer. - let mut data = unsafe { addr_of_mut!((*ptr.as_ptr()).data).cast() }; - for string in strings { - let count = string.len(); + let mut data = unsafe { addr_of_mut!((*ptr.as_ptr()).data).cast::() }; + for &string in strings { // SAFETY: // The sum of all `count` for each `string` equals `full_count`, and since we're // iteratively writing each of them to `data`, `copy_non_overlapping` always stays @@ -257,8 +336,30 @@ impl JsString { // `allocate_inner` must return a valid pointer to newly allocated memory, meaning // `ptr` and all `string`s should never overlap. unsafe { - ptr::copy_nonoverlapping(string.as_ptr(), data, count); - data = data.add(count); + // NOTE: The aligment is checked when we allocate the array. + #[allow(clippy::cast_ptr_alignment)] + match (latin1_encoding, string.variant()) { + (true, JsStrVariant::Latin1(s)) => { + let count = s.len(); + ptr::copy_nonoverlapping(s.as_ptr(), data.cast::(), count); + data = data.cast::().add(count).cast::(); + } + (false, JsStrVariant::Latin1(s)) => { + let count = s.len(); + for (i, byte) in s.iter().enumerate() { + *data.cast::().add(i) = u16::from(*byte); + } + data = data.cast::().add(count).cast::(); + } + (false, JsStrVariant::Utf16(s)) => { + let count = s.len(); + ptr::copy_nonoverlapping(s.as_ptr(), data.cast::(), count); + data = data.cast::().add(count).cast::(); + } + (true, JsStrVariant::Utf16(_)) => { + unreachable!("Already checked that it's latin1 encoding") + } + } } } Self { @@ -267,7 +368,7 @@ impl JsString { } }; - StaticJsStrings::get_string(&string[..]).unwrap_or(string) + StaticJsStrings::get_string(&string.as_str()).unwrap_or(string) } /// Decodes a [`JsString`] into a [`String`], replacing invalid data with its escaped representation @@ -278,9 +379,15 @@ impl JsString { } /// Decodes a [`JsString`] into a [`String`], returning + /// + /// # Errors + /// /// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data. pub fn to_std_string(&self) -> Result { - String::from_utf16(self) + match self.as_str().variant() { + JsStrVariant::Latin1(v) => Ok(v.iter().copied().map(char::from).collect()), + JsStrVariant::Utf16(v) => String::from_utf16(v), + } } /// Decodes a [`JsString`] into an iterator of [`Result`], returning surrogates as @@ -350,12 +457,12 @@ impl JsString { } } - js_string!(text) + js_string!(&text[..]) } /// Gets an iterator of all the Unicode codepoints of a [`JsString`]. pub fn code_points(&self) -> impl Iterator + Clone + '_ { - char::decode_utf16(self.iter().copied()).map(|res| match res { + char::decode_utf16(self.iter()).map(|res| match res { Ok(c) => CodePoint::Unicode(c), Err(e) => CodePoint::UnpairedSurrogate(e.unpaired_surrogate()), }) @@ -370,7 +477,8 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-stringindexof - pub(crate) fn index_of(&self, search_value: &[u16], from_index: usize) -> Option { + #[must_use] + pub fn index_of(&self, search_value: JsStr<'_>, from_index: usize) -> Option { // 1. Assert: Type(string) is String. // 2. Assert: Type(searchValue) is String. // 3. Assert: fromIndex is a non-negative integer. @@ -410,7 +518,12 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-codepointat - pub(crate) fn code_point_at(&self, position: usize) -> CodePoint { + /// + /// # Panics + /// + /// If `position` is smaller than size of string. + #[must_use] + pub fn code_point_at(&self, position: usize) -> CodePoint { // 1. Let size be the length of string. let size = self.len(); @@ -418,28 +531,35 @@ impl JsString { // position >= 0 ensured by position: usize assert!(position < size); - // 3. Let first be the code unit at index position within string. - // 4. Let cp be the code point whose numeric value is that of first. - // 5. If first is not a leading surrogate or trailing surrogate, then - // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }. - // 6. If first is a trailing surrogate or position + 1 = size, then - // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }. - // 7. Let second be the code unit at index position + 1 within string. - // 8. If second is not a trailing surrogate, then - // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }. - // 9. Set cp to ! UTF16SurrogatePairToCodePoint(first, second). - - // We can skip the checks and instead use the `char::decode_utf16` function to take care of that for us. - let code_point = self - .get(position..=position + 1) - .unwrap_or(&self[position..=position]); - - match char::decode_utf16(code_point.iter().copied()) - .next() - .expect("code_point always has a value") - { - Ok(c) => CodePoint::Unicode(c), - Err(e) => CodePoint::UnpairedSurrogate(e.unpaired_surrogate()), + match self.as_str().variant() { + JsStrVariant::Latin1(v) => { + let code_point = v.get(position).expect("Already checked the size"); + CodePoint::Unicode(*code_point as char) + } + // 3. Let first be the code unit at index position within string. + // 4. Let cp be the code point whose numeric value is that of first. + // 5. If first is not a leading surrogate or trailing surrogate, then + // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }. + // 6. If first is a trailing surrogate or position + 1 = size, then + // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }. + // 7. Let second be the code unit at index position + 1 within string. + // 8. If second is not a trailing surrogate, then + // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }. + // 9. Set cp to ! UTF16SurrogatePairToCodePoint(first, second). + JsStrVariant::Utf16(v) => { + // We can skip the checks and instead use the `char::decode_utf16` function to take care of that for us. + let code_point = v + .get(position..=position + 1) + .unwrap_or(&v[position..=position]); + + match char::decode_utf16(code_point.iter().copied()) + .next() + .expect("code_point always has a value") + { + Ok(c) => CodePoint::Unicode(c), + Err(e) => CodePoint::UnpairedSurrogate(e.unpaired_surrogate()), + } + } } } @@ -449,7 +569,7 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-stringtonumber - pub(crate) fn to_number(&self) -> f64 { + pub fn to_number(&self) -> f64 { // 1. Let text be ! StringToCodePoints(str). // 2. Let literal be ParseText(text, StringNumericLiteral). let Ok(string) = self.to_std_string() else { @@ -504,29 +624,13 @@ impl JsString { fast_float::parse(string).unwrap_or(f64::NAN) } - /// Abstract operation `StringToBigInt ( str )` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint - pub(crate) fn to_big_int(&self) -> Option { - // 1. Let text be ! StringToCodePoints(str). - // 2. Let literal be ParseText(text, StringIntegerLiteral). - // 3. If literal is a List of errors, return undefined. - // 4. Let mv be the MV of literal. - // 5. Assert: mv is an integer. - // 6. Return ℤ(mv). - JsBigInt::from_string(self.to_std_string().ok().as_ref()?) - } - /// Allocates a new [`RawJsString`] with an internal capacity of `str_len` chars. /// /// # Panics /// /// Panics if `try_allocate_inner` returns `Err`. - fn allocate_inner(str_len: usize) -> NonNull { - match Self::try_allocate_inner(str_len) { + fn allocate_inner(str_len: usize, latin1: bool) -> NonNull { + match Self::try_allocate_inner(str_len, latin1) { Ok(v) => v, Err(None) => alloc_overflow(), Err(Some(layout)) => std::alloc::handle_alloc_error(layout), @@ -541,11 +645,18 @@ impl JsString { /// /// Returns `Err(None)` on integer overflows `usize::MAX`. /// Returns `Err(Some(Layout))` on allocation error. - fn try_allocate_inner(str_len: usize) -> Result, Option> { - let (layout, offset) = Layout::array::(str_len) - .and_then(|arr| Layout::new::().extend(arr)) - .map(|(layout, offset)| (layout.pad_to_align(), offset)) - .map_err(|_| None)?; + fn try_allocate_inner( + str_len: usize, + latin1: bool, + ) -> Result, Option> { + let (layout, offset) = if latin1 { + Layout::array::(str_len) + } else { + Layout::array::(str_len) + } + .and_then(|arr| Layout::new::().extend(arr)) + .map(|(layout, offset)| (layout.pad_to_align(), offset)) + .map_err(|_| None)?; debug_assert_eq!(offset, DATA_OFFSET); @@ -566,7 +677,7 @@ impl JsString { unsafe { // Write the first part, the `RawJsString`. inner.as_ptr().write(RawJsString { - len: str_len, + flags_and_len: RawJsString::encode_flags_and_len(str_len, latin1), refcount: Cell::new(1), data: [0; 0], }); @@ -593,12 +704,13 @@ impl JsString { } /// Creates a new [`JsString`] from `data`, without checking if the string is in the interner. - fn from_slice_skip_interning(string: &[u16]) -> Self { + fn from_slice_skip_interning(string: JsStr<'_>) -> Self { let count = string.len(); - let ptr = Self::allocate_inner(count); + let ptr = Self::allocate_inner(count, string.is_latin1()); // SAFETY: `allocate_inner` guarantees that `ptr` is a valid pointer. - let data = unsafe { addr_of_mut!((*ptr.as_ptr()).data) }; + let data = unsafe { addr_of_mut!((*ptr.as_ptr()).data).cast::() }; + // SAFETY: // - We read `count = data.len()` elements from `data`, which is within the bounds of the slice. // - `allocate_inner` must allocate at least `count` elements, which allows us to safely @@ -608,24 +720,125 @@ impl JsString { // - `allocate_inner` must return a valid pointer to newly allocated memory, meaning `ptr` // and `data` should never overlap. unsafe { - ptr::copy_nonoverlapping(string.as_ptr(), data.cast(), count); + // NOTE: The aligment is checked when we allocate the array. + #[allow(clippy::cast_ptr_alignment)] + match string.variant() { + JsStrVariant::Latin1(s) => { + ptr::copy_nonoverlapping(s.as_ptr(), data.cast::(), count); + } + JsStrVariant::Utf16(s) => { + ptr::copy_nonoverlapping(s.as_ptr(), data.cast::(), count); + } + } } Self { // Safety: `allocate_inner` guarantees `ptr` is a valid heap pointer. ptr: Tagged::from_non_null(ptr), } } -} -impl AsRef<[u16]> for JsString { - fn as_ref(&self) -> &[u16] { - self + /// Creates a new [`JsString`] from `data`. + fn from_slice(string: JsStr<'_>) -> Self { + if let Some(s) = StaticJsStrings::get_string(&string) { + return s; + } + Self::from_slice_skip_interning(string) + } + + /// Get the length of the [`JsString`]. + #[inline] + #[must_use] + pub fn len(&self) -> usize { + match self.ptr.unwrap() { + UnwrappedTagged::Ptr(h) => { + // SAFETY: + // - The `RawJsString` type has all the necessary information to reconstruct a valid + // slice (length and starting pointer). + // + // - We aligned `h.data` on allocation, and the block is of size `h.len`, so this + // should only generate valid reads. + // + // - The lifetime of `&Self::Target` is shorter than the lifetime of `self`, as seen + // by its signature, so this doesn't outlive `self`. + unsafe { + let h = h.as_ptr(); + (*h).len() + } + } + UnwrappedTagged::Tag(index) => { + // SAFETY: all static strings are valid indices on `STATIC_JS_STRINGS`, so `get` should always + // return `Some`. + unsafe { StaticJsStrings::get(index).unwrap_unchecked().len() } + } + } + } + + /// Return true if the [`JsString`] is emtpy. + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Convert the [`JsString`] into a [`Vec`]. + #[must_use] + pub fn to_vec(&self) -> Vec { + self.as_str().to_vec() + } + + /// Check if the [`JsString`] contains a byte. + #[must_use] + pub fn contains(&self, element: u8) -> bool { + match self.as_str().variant() { + JsStrVariant::Latin1(v) => v.contains(&element), + JsStrVariant::Utf16(v) => v.contains(&u16::from(element)), + } } -} -impl Borrow<[u16]> for JsString { - fn borrow(&self) -> &[u16] { - self + /// Trim whitespace from the start and end of the [`JsString`]. + #[must_use] + pub fn trim(&self) -> JsStr<'_> { + self.as_str().trim() + } + + /// Trim whitespace from the start of the [`JsString`]. + #[must_use] + pub fn trim_start(&self) -> JsStr<'_> { + self.as_str().trim_start() + } + + /// Trim whitespace from the end of the [`JsString`]. + #[must_use] + pub fn trim_end(&self) -> JsStr<'_> { + self.as_str().trim_end() + } + + /// Check if the [`JsString`] is static. + #[must_use] + pub fn is_static(&self) -> bool { + self.ptr.is_tagged() + } + + /// Get the element a the given index, [`None`] otherwise. + #[must_use] + pub fn get<'a, I>(&'a self, index: I) -> Option + where + I: JsSliceIndex<'a>, + { + I::get(self.as_str(), index) + } + + /// Get the element a the given index. + /// + /// # Panics + /// + /// If the index is out of bounds. + #[must_use] + pub fn get_expect<'a, I>(&'a self, index: I) -> I::Value + where + I: JsSliceIndex<'a>, + { + self.get(index).expect("Index out of bounds") } } @@ -668,12 +881,21 @@ impl Drop for JsString { // All the checks for the validity of the layout have already been made on `alloc_inner`, // so we can skip the unwrap. let layout = unsafe { - Layout::for_value(inner) - .extend(Layout::array::(inner.len).unwrap_unchecked()) - .unwrap_unchecked() - .0 - .pad_to_align() + if inner.is_latin1() { + Layout::for_value(inner) + .extend(Layout::array::(inner.len()).unwrap_unchecked()) + .unwrap_unchecked() + .0 + .pad_to_align() + } else { + Layout::for_value(inner) + .extend(Layout::array::(inner.len()).unwrap_unchecked()) + .unwrap_unchecked() + .0 + .pad_to_align() + } }; + // Safety: // If refcount is 0 and we call drop, that means this is the last `JsString` which // points to this memory allocation, so deallocating it is safe. @@ -684,72 +906,64 @@ impl Drop for JsString { } } -impl std::fmt::Debug for JsString { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::char::decode_utf16(self.as_slice().to_owned()) - .map(|r| { - r.map_or_else( - |err| format!("<0x{:04x}>", err.unpaired_surrogate()), - String::from, - ) - }) - .collect::() - .fmt(f) +impl ToStringEscaped for JsString { + #[inline] + fn to_string_escaped(&self) -> String { + match self.as_str().variant() { + JsStrVariant::Latin1(v) => v.iter().copied().map(char::from).collect(), + JsStrVariant::Utf16(v) => v.to_string_escaped(), + } } } -impl Deref for JsString { - type Target = [u16]; - - fn deref(&self) -> &Self::Target { - match self.ptr.unwrap() { - UnwrappedTagged::Ptr(h) => { - // SAFETY: - // - The `RawJsString` type has all the necessary information to reconstruct a valid - // slice (length and starting pointer). - // - // - We aligned `h.data` on allocation, and the block is of size `h.len`, so this - // should only generate valid reads. - // - // - The lifetime of `&Self::Target` is shorter than the lifetime of `self`, as seen - // by its signature, so this doesn't outlive `self`. - unsafe { - let h = h.as_ptr(); - std::slice::from_raw_parts(addr_of!((*h).data).cast(), (*h).len) - } - } - UnwrappedTagged::Tag(index) => { - // SAFETY: all static strings are valid indices on `STATIC_JS_STRINGS`, so `get` should always - // return `Some`. - unsafe { StaticJsStrings::get(index).unwrap_unchecked() } - } - } +impl std::fmt::Debug for JsString { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.to_std_string_escaped().fmt(f) } } impl Eq for JsString {} impl From<&[u16]> for JsString { + #[inline] fn from(s: &[u16]) -> Self { - StaticJsStrings::get_string(s).unwrap_or_else(|| Self::from_slice_skip_interning(s)) - } -} - -impl From> for JsString { - fn from(vec: Vec) -> Self { - Self::from(&vec[..]) + JsString::from_slice(JsStr::utf16(s)) } } impl From<&str> for JsString { #[inline] fn from(s: &str) -> Self { + // TODO: Check for latin1 encoding + if s.is_ascii() { + let js_str = JsStr::latin1(s.as_bytes()); + return StaticJsStrings::get_string(&js_str) + .unwrap_or_else(|| JsString::from_slice_skip_interning(js_str)); + } let s = s.encode_utf16().collect::>(); + JsString::from_slice_skip_interning(JsStr::utf16(&s[..])) + } +} - Self::from(&s[..]) +impl From> for JsString { + fn from(value: JsStr<'_>) -> Self { + StaticJsStrings::get_string(&value) + .unwrap_or_else(|| JsString::from_slice_skip_interning(value)) } } +impl From<&[JsString]> for JsString { + fn from(value: &[JsString]) -> Self { + Self::concat_array( + &value + .iter() + .map(Self::as_str) + .map(Into::into) + .collect::>()[..], + ) + } +} impl From for JsString { #[inline] fn from(s: String) -> Self { @@ -765,34 +979,39 @@ impl From<&[u16; N]> for JsString { impl Hash for JsString { fn hash(&self, state: &mut H) { - self[..].hash(state); + self.as_str().hash(state); } } -impl> Index for JsString { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - Index::index(&**self, index) +impl PartialOrd for JsStr<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } impl Ord for JsString { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self[..].cmp(other) + self.as_str().cmp(&other.as_str()) } } impl PartialEq for JsString { fn eq(&self, other: &Self) -> bool { - self[..] == other[..] + self.as_str() == other.as_str() } } impl PartialEq for [u16] { fn eq(&self, other: &JsString) -> bool { - self == &**other + if self.len() != other.len() { + return false; + } + for (x, y) in self.iter().copied().zip(other.iter()) { + if x != y { + return false; + } + } + true } } @@ -804,7 +1023,7 @@ impl PartialEq for [u16; N] { impl PartialEq<[u16]> for JsString { fn eq(&self, other: &[u16]) -> bool { - &**self == other + other == self } } @@ -838,6 +1057,18 @@ impl PartialEq for str { } } +impl PartialEq> for JsString { + fn eq(&self, other: &JsStr<'_>) -> bool { + self.as_str() == *other + } +} + +impl PartialEq for JsStr<'_> { + fn eq(&self, other: &JsString) -> bool { + other == self + } +} + impl PartialOrd for JsString { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -852,45 +1083,6 @@ impl FromStr for JsString { } } -/// Utility trait that adds trimming functionality to every `UTF-16` string. -pub(crate) trait Utf16Trim { - /// Trims both leading and trailing space from `self`. - fn trim(&self) -> &Self { - self.trim_start().trim_end() - } - - /// Trims all leading space from `self`. - fn trim_start(&self) -> &Self; - - /// Trims all trailing space from `self`. - fn trim_end(&self) -> &Self; -} - -impl Utf16Trim for [u16] { - fn trim_start(&self) -> &Self { - if let Some(left) = self - .iter() - .copied() - .position(|r| !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)) - { - &self[left..] - } else { - &[] - } - } - fn trim_end(&self) -> &Self { - if let Some(right) = self - .iter() - .copied() - .rposition(|r| !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)) - { - &self[..=right] - } else { - &[] - } - } -} - /// Utility trait that adds a `UTF-16` escaped representation to every [`[u16]`][slice]. pub(crate) trait ToStringEscaped { /// Decodes `self` as an `UTF-16` encoded string, escaping any unpaired surrogates by its @@ -912,10 +1104,13 @@ impl ToStringEscaped for [u16] { #[allow(clippy::redundant_clone)] #[cfg(test)] mod tests { - use crate::tagged::UnwrappedTagged; + use std::hash::{BuildHasher, BuildHasherDefault, Hash}; + + use crate::{string::common::StaticJsStrings, tagged::UnwrappedTagged, JsStr}; - use super::utf16; use super::JsString; + use boa_macros::{js_str, utf16}; + use rustc_hash::FxHasher; impl JsString { /// Gets the number of `JsString`s which point to this allocation. @@ -931,10 +1126,14 @@ mod tests { } } + fn hash_value(value: &T) -> u64 { + BuildHasherDefault::::default().hash_one(value) + } + #[test] fn empty() { let s = js_string!(); - assert_eq!(*s, "".encode_utf16().collect::>()); + assert_eq!(&s, utf16!("")); } #[test] @@ -1005,29 +1204,24 @@ mod tests { #[test] fn as_str() { - const HELLO: &str = "Hello"; + const HELLO: &[u16] = utf16!("Hello"); let x = js_string!(HELLO); - assert_eq!(*x, HELLO.encode_utf16().collect::>()); + assert_eq!(&x, HELLO); } #[test] fn hash() { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - - const HELLOWORLD: &[u16] = utf16!("Hello World!"); + const HELLOWORLD: JsStr<'_> = js_str!("Hello World!"); let x = js_string!(HELLOWORLD); - assert_eq!(&*x, HELLOWORLD); + assert_eq!(x.as_str(), HELLOWORLD); - let mut hasher = DefaultHasher::new(); - HELLOWORLD.hash(&mut hasher); - let s_hash = hasher.finish(); + assert!(HELLOWORLD.is_latin1()); + assert!(x.as_str().is_latin1()); - let mut hasher = DefaultHasher::new(); - x.hash(&mut hasher); - let x_hash = hasher.finish(); + let s_hash = hash_value(&HELLOWORLD); + let x_hash = hash_value(&x); assert_eq!(s_hash, x_hash); } @@ -1040,7 +1234,7 @@ mod tests { let x = js_string!("hello"); let z = js_string!("world"); - let xy = js_string!(&x, Y); + let xy = js_string!(&x, &JsString::from(Y)); assert_eq!(&xy, utf16!("hello, ")); assert_eq!(xy.refcount(), Some(1)); @@ -1048,8 +1242,45 @@ mod tests { assert_eq!(&xyz, utf16!("hello, world")); assert_eq!(xyz.refcount(), Some(1)); - let xyzw = js_string!(&xyz, W); + let xyzw = js_string!(&xyz, &JsString::from(W)); assert_eq!(&xyzw, utf16!("hello, world!")); assert_eq!(xyzw.refcount(), Some(1)); } + + #[test] + fn trim_start_non_ascii_to_ascii() { + let s = "\u{2029}abc"; + let x = js_string!(s); + + let y = js_string!(x.trim_start()); + + assert_eq!(&y, s.trim_start()); + } + + #[test] + fn conversion_to_known_static_js_string() { + const JS_STR_U8: &JsStr<'_> = &js_str!("length"); + const JS_STR_U16: &JsStr<'_> = &JsStr::utf16(utf16!("length")); + + assert!(JS_STR_U8.is_latin1()); + assert!(!JS_STR_U16.is_latin1()); + + assert_eq!(JS_STR_U8, JS_STR_U8); + assert_eq!(JS_STR_U16, JS_STR_U16); + + assert_eq!(JS_STR_U8, JS_STR_U16); + assert_eq!(JS_STR_U16, JS_STR_U8); + + assert_eq!(hash_value(JS_STR_U8), hash_value(JS_STR_U16)); + + let string = StaticJsStrings::get_string(JS_STR_U8); + + assert!(string.is_some()); + assert!(string.unwrap().as_str().is_latin1()); + + let string = StaticJsStrings::get_string(JS_STR_U16); + + assert!(string.is_some()); + assert!(string.unwrap().as_str().is_latin1()); + } } diff --git a/core/engine/src/string/str.rs b/core/engine/src/string/str.rs new file mode 100644 index 00000000000..6ce3c4cabd4 --- /dev/null +++ b/core/engine/src/string/str.rs @@ -0,0 +1,358 @@ +use crate::{ + builtins::string::{is_trimmable_whitespace, is_trimmable_whitespace_latin1}, + string::Iter, +}; +use std::{ + hash::{Hash, Hasher}, + slice::SliceIndex, +}; + +use super::iter::Windows; + +// Modified port of +#[inline] +pub(crate) const fn trim_latin1_start(mut bytes: &[u8]) -> &[u8] { + // Note: A pattern matching based approach (instead of indexing) allows + // making the function const. + while let [first, rest @ ..] = bytes { + if is_trimmable_whitespace_latin1(*first) { + bytes = rest; + } else { + break; + } + } + bytes +} + +// Modified port of +#[inline] +pub(crate) const fn trim_latin1_end(mut bytes: &[u8]) -> &[u8] { + // Note: A pattern matching based approach (instead of indexing) allows + // making the function const. + while let [rest @ .., last] = bytes { + if is_trimmable_whitespace_latin1(*last) { + bytes = rest; + } else { + break; + } + } + bytes +} + +/// Inner representation of a [`JsStr`]. +#[derive(Debug, Clone, Copy)] +pub enum JsStrVariant<'a> { + /// Latin1 string representation. + Latin1(&'a [u8]), + + /// U16 string representation. + Utf16(&'a [u16]), +} + +/// This is equivalent to Rust's `&str`. +#[derive(Debug, Clone, Copy)] +pub struct JsStr<'a> { + inner: JsStrVariant<'a>, +} + +impl<'a> JsStr<'a> { + /// This represents an empty string. + pub const EMPTY: Self = Self::latin1("".as_bytes()); + + /// Creates a [`JsStr`] from codepoints that can fit in a `u8`. + #[inline] + #[must_use] + pub const fn latin1(value: &'a [u8]) -> Self { + Self { + inner: JsStrVariant::Latin1(value), + } + } + + /// Creates a [`JsStr`] from utf16 encoded string. + #[inline] + #[must_use] + pub const fn utf16(value: &'a [u16]) -> Self { + Self { + inner: JsStrVariant::Utf16(value), + } + } + + /// Get the length of the [`JsStr`]. + #[inline] + #[must_use] + pub const fn len(&self) -> usize { + match self.inner { + JsStrVariant::Latin1(v) => v.len(), + JsStrVariant::Utf16(v) => v.len(), + } + } + + /// Return the inner [`JsStrVariant`] varient of the [`JsStr`]. + #[inline] + #[must_use] + pub fn variant(self) -> JsStrVariant<'a> { + self.inner + } + + /// Check if the [`JsStr`] is latin1 encoded. + #[inline] + #[must_use] + pub fn is_latin1(&self) -> bool { + matches!(self.inner, JsStrVariant::Latin1(_)) + } + + /// Returns [`u8`] slice if the [`JsStr`] is latin1 encoded, otherwise [`None`]. + #[inline] + #[must_use] + pub const fn as_latin1(&self) -> Option<&[u8]> { + if let JsStrVariant::Latin1(slice) = self.inner { + return Some(slice); + } + + None + } + + /// Iterate over the codepoints of the string. + #[inline] + #[must_use] + pub fn iter(self) -> Iter<'a> { + Iter::new(self) + } + + /// Iterate over the codepoints of the string. + #[inline] + #[must_use] + pub fn windows(self, size: usize) -> Windows<'a> { + Windows::new(self, size) + } + + /// Check if the [`JsStr`] is empty. + #[inline] + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Trims both leading and trailing space. + #[inline] + #[must_use] + pub fn trim(self) -> JsStr<'a> { + self.trim_start().trim_end() + } + + /// Trims all leading space. + #[inline] + #[must_use] + pub fn trim_start(self) -> Self { + match self.variant() { + JsStrVariant::Latin1(s) => Self::latin1(trim_latin1_start(s)), + JsStrVariant::Utf16(s) => { + let value = if let Some(left) = s.iter().copied().position(|r| { + !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace) + }) { + &s[left..] + } else { + return Self::EMPTY; + }; + + JsStr { + inner: JsStrVariant::Utf16(value), + } + } + } + } + + /// Trims all trailing space. + #[inline] + #[must_use] + pub fn trim_end(self) -> Self { + match self.variant() { + JsStrVariant::Latin1(s) => Self::latin1(trim_latin1_end(s)), + JsStrVariant::Utf16(s) => { + let value = if let Some(right) = s.iter().copied().rposition(|r| { + !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace) + }) { + &s[..=right] + } else { + return Self::EMPTY; + }; + + JsStr { + inner: JsStrVariant::Utf16(value), + } + } + } + } + + /// Returns an element or subslice depending on the type of index, otherwise [`None`]. + #[inline] + #[must_use] + pub fn get(self, index: I) -> Option + where + I: JsSliceIndex<'a>, + { + I::get(self, index) + } + + /// Convert the [`JsStr`] into a [`Vec`]. + pub fn to_vec(&self) -> Vec { + match self.variant() { + JsStrVariant::Latin1(v) => v.iter().copied().map(u16::from).collect(), + JsStrVariant::Utf16(v) => v.to_vec(), + } + } + + /// Returns true if needle is a prefix of the [`JsStr`]. + #[must_use] + pub fn starts_with(&self, needle: JsStr<'_>) -> bool { + let n = needle.len(); + self.len() >= n && needle == self.get(..n).expect("already checked size") + } + /// Returns `true` if `needle` is a suffix of the [`JsStr`]. + #[must_use] + pub fn ends_with(&self, needle: JsStr<'_>) -> bool { + let (m, n) = (self.len(), needle.len()); + m >= n && needle == self.get(m - n..).expect("already checked size") + } +} + +impl Hash for JsStr<'_> { + fn hash(&self, state: &mut H) { + // NOTE: The hash function has been inlined to ensure that a hash of latin1 and U16 + // encoded strings remains the same if they have the same characters + match self.variant() { + JsStrVariant::Latin1(s) => { + state.write_usize(s.len()); + for elem in s { + state.write_u16(u16::from(*elem)); + } + } + JsStrVariant::Utf16(s) => { + state.write_usize(s.len()); + for elem in s { + state.write_u16(*elem); + } + } + } + } +} + +impl Ord for JsStr<'_> { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match (self.variant(), other.variant()) { + (JsStrVariant::Latin1(x), JsStrVariant::Latin1(y)) => x.cmp(y), + (JsStrVariant::Utf16(x), JsStrVariant::Utf16(y)) => x.cmp(y), + _ => self.iter().cmp(other.iter()), + } + } +} + +impl Eq for JsStr<'_> {} + +impl PartialEq for JsStr<'_> { + fn eq(&self, other: &Self) -> bool { + match (self.variant(), other.variant()) { + (JsStrVariant::Latin1(lhs), JsStrVariant::Latin1(rhs)) => return lhs == rhs, + (JsStrVariant::Utf16(lhs), JsStrVariant::Utf16(rhs)) => return lhs == rhs, + _ => {} + } + if self.len() != other.len() { + return false; + } + for (x, y) in self.iter().zip(other.iter()) { + if x != y { + return false; + } + } + true + } +} + +impl<'a> PartialEq> for [u16] { + fn eq(&self, other: &JsStr<'a>) -> bool { + if self.len() != other.len() { + return false; + } + for (x, y) in self.iter().copied().zip(other.iter()) { + if x != y { + return false; + } + } + true + } +} + +pub trait JsSliceIndex<'a>: SliceIndex<[u8]> + SliceIndex<[u16]> { + type Value; + + fn get(_: JsStr<'a>, index: Self) -> Option; +} + +impl<'a> JsSliceIndex<'a> for usize { + type Value = u16; + + #[inline] + fn get(value: JsStr<'a>, index: Self) -> Option { + match value.variant() { + JsStrVariant::Latin1(v) => v.get(index).copied().map(u16::from), + JsStrVariant::Utf16(v) => v.get(index).copied(), + } + } +} + +impl<'a> JsSliceIndex<'a> for std::ops::Range { + type Value = JsStr<'a>; + + #[inline] + fn get(value: JsStr<'a>, index: Self) -> Option { + match value.variant() { + JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1), + JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), + } + } +} + +impl<'a> JsSliceIndex<'a> for std::ops::RangeInclusive { + type Value = JsStr<'a>; + + #[inline] + fn get(value: JsStr<'a>, index: Self) -> Option { + match value.variant() { + JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1), + JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), + } + } +} + +impl<'a> JsSliceIndex<'a> for std::ops::RangeFrom { + type Value = JsStr<'a>; + + #[inline] + fn get(value: JsStr<'a>, index: Self) -> Option { + match value.variant() { + JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1), + JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), + } + } +} + +impl<'a> JsSliceIndex<'a> for std::ops::RangeTo { + type Value = JsStr<'a>; + + #[inline] + fn get(value: JsStr<'a>, index: Self) -> Option { + match value.variant() { + JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1), + JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16), + } + } +} + +impl<'a> JsSliceIndex<'a> for std::ops::RangeFull { + type Value = JsStr<'a>; + + #[inline] + fn get(value: JsStr<'a>, _index: Self) -> Option { + Some(value) + } +} diff --git a/core/engine/src/symbol.rs b/core/engine/src/symbol.rs index 06c6590ec05..11b04dd9e69 100644 --- a/core/engine/src/symbol.rs +++ b/core/engine/src/symbol.rs @@ -23,12 +23,12 @@ use crate::{ js_string, - string::{common::StaticJsStrings, utf16}, + string::{common::StaticJsStrings, JsString}, tagged::{Tagged, UnwrappedTagged}, - JsData, JsString, }; use boa_gc::{Finalize, Trace}; +use boa_macros::{js_str, JsData}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::{ @@ -135,6 +135,7 @@ struct Inner { // Safety: JsSymbol does not contain any objects which needs to be traced, // so this is safe. #[boa_gc(unsafe_empty_trace)] +#[allow(clippy::module_name_repetitions)] pub struct JsSymbol { repr: Tagged, } @@ -167,7 +168,7 @@ impl JsSymbol { let hash = get_id()?; let arc = Arc::new(Inner { hash, - description: description.map(|s| Box::from(&*s)), + description: description.map(|s| s.iter().collect::>().into_boxed_slice()), }); Some(Self { @@ -209,7 +210,7 @@ impl JsSymbol { return wk.fn_name(); } self.description() - .map(|s| js_string!(utf16!("["), &*s, utf16!("]"))) + .map(|s| js_string!(js_str!("["), &s, js_str!("]"))) .unwrap_or_default() } @@ -243,7 +244,7 @@ impl JsSymbol { pub fn descriptive_string(&self) -> JsString { self.description().as_ref().map_or_else( || js_string!("Symbol()"), - |desc| js_string!(utf16!("Symbol("), desc, utf16!(")")), + |desc| js_string!(js_str!("Symbol("), desc, js_str!(")")), ) } diff --git a/core/engine/src/tests/control_flow/loops.rs b/core/engine/src/tests/control_flow/loops.rs index 52cebb62430..d5c0254e89a 100644 --- a/core/engine/src/tests/control_flow/loops.rs +++ b/core/engine/src/tests/control_flow/loops.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -87,7 +88,7 @@ fn for_loop() { b } "#}, - js_string!("hello"), + js_str!("hello"), ), TestAction::assert_eq( indoc! {r#" @@ -192,7 +193,7 @@ fn try_break_finally_edge_cases() { a + b "#; - run_test_actions([TestAction::assert_eq(scenario, js_string!("foobar"))]); + run_test_actions([TestAction::assert_eq(scenario, js_str!("foobar"))]); } #[test] @@ -228,7 +229,7 @@ fn try_break_labels() { } "#; - run_test_actions([TestAction::assert_eq(scenario, js_string!("finally! :)"))]); + run_test_actions([TestAction::assert_eq(scenario, js_str!("finally! :)"))]); } #[test] @@ -268,10 +269,10 @@ fn break_nested_labels_loops_and_try() { run_test_actions([ TestAction::run(scenario), - TestAction::assert_eq("nestedLabels(true)", js_string!("foobar broke-foo")), + TestAction::assert_eq("nestedLabels(true)", js_str!("foobar broke-foo")), TestAction::assert_eq( "nestedLabels(false)", - js_string!("foobar broke-bar broke-spacer broke-foo"), + js_str!("foobar broke-bar broke-spacer broke-foo"), ), ]); } @@ -376,7 +377,7 @@ fn break_environment_gauntlet() { } "#; - run_test_actions([TestAction::assert_eq(scenario, js_string!("5601try_block"))]); + run_test_actions([TestAction::assert_eq(scenario, js_str!("5601try_block"))]); } #[test] @@ -707,7 +708,7 @@ fn for_loop_break_label() { } str "#}, - js_string!("01"), + js_str!("01"), )]); } @@ -822,7 +823,7 @@ fn for_in_break_label() { } str "#}, - js_string!("0"), + js_str!("0"), )]); } @@ -843,6 +844,6 @@ fn for_in_continue_label() { } str "#}, - js_string!("00"), + js_str!("00"), )]); } diff --git a/core/engine/src/tests/control_flow/mod.rs b/core/engine/src/tests/control_flow/mod.rs index 270c917f249..a326cae8932 100644 --- a/core/engine/src/tests/control_flow/mod.rs +++ b/core/engine/src/tests/control_flow/mod.rs @@ -1,7 +1,8 @@ +use boa_macros::js_str; use indoc::indoc; mod loops; -use crate::{js_string, run_test_actions, JsNativeErrorKind, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, TestAction}; #[test] fn test_invalid_break() { @@ -381,7 +382,7 @@ fn string_switch() { a; "#}, - js_string!("world"), + js_str!("world"), )]); } @@ -418,13 +419,13 @@ fn bigger_switch_example() { return b; } "#}), - TestAction::assert_eq("f(0)", js_string!("Mon")), - TestAction::assert_eq("f(1)", js_string!("Tue")), - TestAction::assert_eq("f(2)", js_string!("Wed")), - TestAction::assert_eq("f(3)", js_string!("Thurs")), - TestAction::assert_eq("f(4)", js_string!("Fri")), - TestAction::assert_eq("f(5)", js_string!("Sat")), - TestAction::assert_eq("f(6)", js_string!("Sun")), + TestAction::assert_eq("f(0)", js_str!("Mon")), + TestAction::assert_eq("f(1)", js_str!("Tue")), + TestAction::assert_eq("f(2)", js_str!("Wed")), + TestAction::assert_eq("f(3)", js_str!("Thurs")), + TestAction::assert_eq("f(4)", js_str!("Fri")), + TestAction::assert_eq("f(5)", js_str!("Sat")), + TestAction::assert_eq("f(6)", js_str!("Sun")), ]); } @@ -440,7 +441,7 @@ fn break_labelled_if_statement() { } result "#}, - js_string!("foo"), + js_str!("foo"), )]); } @@ -458,6 +459,6 @@ fn break_labelled_try_statement() { } result "#}, - js_string!("foo"), + js_str!("foo"), )]); } diff --git a/core/engine/src/tests/env.rs b/core/engine/src/tests/env.rs index 372641cbf48..c15bb16e896 100644 --- a/core/engine/src/tests/env.rs +++ b/core/engine/src/tests/env.rs @@ -1,6 +1,7 @@ +use boa_macros::js_str; use indoc::indoc; -use crate::{js_string, run_test_actions, JsNativeErrorKind, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, TestAction}; #[test] // https://github.com/boa-dev/boa/issues/2317 @@ -15,7 +16,7 @@ fn fun_block_eval_2317() { return y + x; })("arg"); "#}, - js_string!("arginner"), + js_str!("arginner"), ), TestAction::assert_eq( indoc! {r#" @@ -26,7 +27,7 @@ fn fun_block_eval_2317() { return y + x; })(); "#}, - js_string!("defaultinner"), + js_str!("defaultinner"), ), ]); } diff --git a/core/engine/src/tests/function.rs b/core/engine/src/tests/function.rs index 4a359fff7d1..6c70c5ec4d7 100644 --- a/core/engine/src/tests/function.rs +++ b/core/engine/src/tests/function.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -24,7 +25,7 @@ fn property_accessor_member_expression_dot_notation_on_function() { function asd () {}; asd.name; "#}, - js_string!("asd"), + js_str!("asd"), )]); } @@ -35,7 +36,7 @@ fn property_accessor_member_expression_bracket_notation_on_function() { function asd () {}; asd['name']; "#}, - js_string!("asd"), + js_str!("asd"), )]); } @@ -62,7 +63,7 @@ fn early_return() { } outer_fnct() "#}, - js_string!("outer"), + js_str!("outer"), ), ]); } @@ -78,8 +79,8 @@ fn should_set_this_value() { var bar = new Foo(); "#}), - TestAction::assert_eq("bar.a", js_string!("a")), - TestAction::assert_eq("bar.b", js_string!("b")), + TestAction::assert_eq("bar.a", js_str!("a")), + TestAction::assert_eq("bar.b", js_str!("b")), ]); } diff --git a/core/engine/src/tests/mod.rs b/core/engine/src/tests/mod.rs index 4c1e0d658f6..705375cd778 100644 --- a/core/engine/src/tests/mod.rs +++ b/core/engine/src/tests/mod.rs @@ -1,3 +1,4 @@ +use boa_macros::js_str; use indoc::indoc; mod control_flow; @@ -8,7 +9,7 @@ mod operators; mod promise; mod spread; -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; #[test] fn length_correct_value_on_string_literal() { @@ -355,7 +356,7 @@ fn multiline_str_concat() { 'world'; a "#}, - js_string!("hello world"), + js_str!("hello world"), )]); } @@ -479,7 +480,7 @@ fn template_literal() { let a = 10; `result: ${a} and ${a+10}`; "#}, - js_string!("result: 10 and 20"), + js_str!("result: 10 and 20"), )]); } diff --git a/core/engine/src/tests/operators.rs b/core/engine/src/tests/operators.rs index 553d8135e60..a414687ecbe 100644 --- a/core/engine/src/tests/operators.rs +++ b/core/engine/src/tests/operators.rs @@ -1,11 +1,12 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] fn property_accessor_member_expression_dot_notation_on_string_literal() { run_test_actions([TestAction::assert_eq( "typeof 'asd'.matchAll", - js_string!("function"), + js_str!("function"), )]); } @@ -13,7 +14,7 @@ fn property_accessor_member_expression_dot_notation_on_string_literal() { fn property_accessor_member_expression_bracket_notation_on_string_literal() { run_test_actions([TestAction::assert_eq( "typeof 'asd'['matchAll']", - js_string!("function"), + js_str!("function"), )]); } @@ -197,15 +198,15 @@ fn unary_operations_on_this() { #[test] fn typeofs() { run_test_actions([ - TestAction::assert_eq("typeof String()", js_string!("string")), - TestAction::assert_eq("typeof 5", js_string!("number")), - TestAction::assert_eq("typeof 0.5", js_string!("number")), - TestAction::assert_eq("typeof undefined", js_string!("undefined")), - TestAction::assert_eq("typeof true", js_string!("boolean")), - TestAction::assert_eq("typeof null", js_string!("object")), - TestAction::assert_eq("typeof {}", js_string!("object")), - TestAction::assert_eq("typeof Symbol()", js_string!("symbol")), - TestAction::assert_eq("typeof function(){}", js_string!("function")), + TestAction::assert_eq("typeof String()", js_str!("string")), + TestAction::assert_eq("typeof 5", js_str!("number")), + TestAction::assert_eq("typeof 0.5", js_str!("number")), + TestAction::assert_eq("typeof undefined", js_str!("undefined")), + TestAction::assert_eq("typeof true", js_str!("boolean")), + TestAction::assert_eq("typeof null", js_str!("object")), + TestAction::assert_eq("typeof {}", js_str!("object")), + TestAction::assert_eq("typeof Symbol()", js_str!("symbol")), + TestAction::assert_eq("typeof function(){}", js_str!("function")), ]); } @@ -248,7 +249,7 @@ fn unary_void() { const b = void test() + ''; a + b "#}, - js_string!("42undefined"), + js_str!("42undefined"), ), ]); } @@ -494,10 +495,7 @@ fn logical_assignment() { #[test] fn conditional_op() { - run_test_actions([TestAction::assert_eq( - "1 === 2 ? 'a' : 'b'", - js_string!("b"), - )]); + run_test_actions([TestAction::assert_eq("1 === 2 ? 'a' : 'b'", js_str!("b"))]); } #[test] diff --git a/core/engine/src/tests/spread.rs b/core/engine/src/tests/spread.rs index ff0d9ddecd6..b272df56fb2 100644 --- a/core/engine/src/tests/spread.rs +++ b/core/engine/src/tests/spread.rs @@ -1,4 +1,5 @@ -use crate::{js_string, run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction}; +use boa_macros::js_str; use indoc::indoc; #[test] @@ -26,7 +27,7 @@ fn spread_with_arguments() { var result = foo(...a); "#}), TestAction::assert_eq("result[0]", 1), - TestAction::assert_eq("result[1]", js_string!("test")), + TestAction::assert_eq("result[1]", js_str!("test")), TestAction::assert_eq("result[2]", 3), TestAction::assert_eq("result[3]", 4), ]); @@ -131,7 +132,7 @@ fn spread_with_new() { } f('message').m; "#}, - js_string!("message"), + js_str!("message"), )]); } @@ -147,6 +148,6 @@ fn spread_with_call() { } g('message'); "#}, - js_string!("message"), + js_str!("message"), )]); } diff --git a/core/engine/src/value/conversions/mod.rs b/core/engine/src/value/conversions/mod.rs index a23f048d080..b247db10f93 100644 --- a/core/engine/src/value/conversions/mod.rs +++ b/core/engine/src/value/conversions/mod.rs @@ -1,6 +1,6 @@ //! Conversions from JavaScript values into Rust values, and the other way around. -use crate::js_string; +use crate::{js_string, string::JsStr}; use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; @@ -10,6 +10,14 @@ pub(super) mod try_from_js; pub(super) mod convert; +impl From> for JsValue { + fn from(value: JsStr<'_>) -> Self { + let _timer = Profiler::global().start_event("From>", "value"); + + Self::String(value.into()) + } +} + impl From for JsValue { fn from(value: JsString) -> Self { let _timer = Profiler::global().start_event("From", "value"); diff --git a/core/engine/src/value/conversions/serde_json.rs b/core/engine/src/value/conversions/serde_json.rs index 7b79e780c26..104d928e7c9 100644 --- a/core/engine/src/value/conversions/serde_json.rs +++ b/core/engine/src/value/conversions/serde_json.rs @@ -176,12 +176,13 @@ impl JsValue { #[cfg(test)] mod tests { + use boa_macros::js_str; use indoc::indoc; use serde_json::json; use crate::object::JsArray; - use crate::{js_string, run_test_actions, TestAction}; - use crate::{string::utf16, JsValue}; + use crate::JsValue; + use crate::{run_test_actions, TestAction}; #[test] fn json_conversions() { @@ -210,23 +211,23 @@ mod tests { let value = JsValue::from_json(&json, ctx).unwrap(); let obj = value.as_object().unwrap(); assert_eq!( - obj.get(utf16!("name"), ctx).unwrap(), - js_string!("John Doe").into() + obj.get(js_str!("name"), ctx).unwrap(), + js_str!("John Doe").into() ); - assert_eq!(obj.get(utf16!("age"), ctx).unwrap(), 43_i32.into()); - assert_eq!(obj.get(utf16!("minor"), ctx).unwrap(), false.into()); - assert_eq!(obj.get(utf16!("adult"), ctx).unwrap(), true.into()); + assert_eq!(obj.get(js_str!("age"), ctx).unwrap(), 43_i32.into()); + assert_eq!(obj.get(js_str!("minor"), ctx).unwrap(), false.into()); + assert_eq!(obj.get(js_str!("adult"), ctx).unwrap(), true.into()); { - let extra = obj.get(utf16!("extra"), ctx).unwrap(); + let extra = obj.get(js_str!("extra"), ctx).unwrap(); let extra = extra.as_object().unwrap(); - assert!(extra.get(utf16!("address"), ctx).unwrap().is_null()); + assert!(extra.get(js_str!("address"), ctx).unwrap().is_null()); } { - let phones = obj.get(utf16!("phones"), ctx).unwrap(); + let phones = obj.get(js_str!("phones"), ctx).unwrap(); let phones = phones.as_object().unwrap(); let arr = JsArray::from_object(phones.clone()).unwrap(); - assert_eq!(arr.at(0, ctx).unwrap(), js_string!("+44 1234567").into()); + assert_eq!(arr.at(0, ctx).unwrap(), js_str!("+44 1234567").into()); assert_eq!(arr.at(1, ctx).unwrap(), JsValue::from(-45_i32)); assert!(arr.at(2, ctx).unwrap().is_object()); assert_eq!(arr.at(3, ctx).unwrap(), true.into()); diff --git a/core/engine/src/value/display.rs b/core/engine/src/value/display.rs index 97e478b3b7c..cdf463ea1ae 100644 --- a/core/engine/src/value/display.rs +++ b/core/engine/src/value/display.rs @@ -1,16 +1,14 @@ -use std::borrow::Cow; - +use super::{fmt, Display, HashSet, JsValue}; use crate::{ builtins::{ error::ErrorObject, map::ordered_map::OrderedMap, promise::PromiseState, set::ordered_set::OrderedSet, Array, Promise, }, + js_str, property::PropertyDescriptor, - string::utf16, JsError, JsString, }; - -use super::{fmt, Display, HashSet, JsValue}; +use std::borrow::Cow; /// This object is used for displaying a `Value`. #[derive(Debug, Clone, Copy)] @@ -120,7 +118,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children } else if v_bor.is::() { let len = v_bor .properties() - .get(&utf16!("length").into()) + .get(&js_str!("length").into()) .expect("array object must have 'length' property") // FIXME: handle accessor descriptors .expect_value() @@ -196,7 +194,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children } else if v_bor.is::() { drop(v_bor); let name: Cow<'static, str> = v - .get_property(&utf16!("name").into()) + .get_property(&js_str!("name").into()) .as_ref() .and_then(PropertyDescriptor::value) .map_or_else( @@ -211,7 +209,7 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children }, ); let message = v - .get_property(&utf16!("message").into()) + .get_property(&js_str!("message").into()) .as_ref() .and_then(PropertyDescriptor::value) .map(|v| { diff --git a/core/engine/src/value/equality.rs b/core/engine/src/value/equality.rs index 4f3c2846eab..d14a95d1af2 100644 --- a/core/engine/src/value/equality.rs +++ b/core/engine/src/value/equality.rs @@ -65,14 +65,14 @@ impl JsValue { // a. Let n be ! StringToBigInt(y). // b. If n is NaN, return false. // c. Return the result of the comparison x == n. - (Self::BigInt(ref a), Self::String(ref b)) => { - b.to_big_int().as_ref().map_or(false, |b| a == b) - } + (Self::BigInt(ref a), Self::String(ref b)) => JsBigInt::from_js_string(b) + .as_ref() + .map_or(false, |b| a == b), // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. - (Self::String(ref a), Self::BigInt(ref b)) => { - a.to_big_int().as_ref().map_or(false, |a| a == b) - } + (Self::String(ref a), Self::BigInt(ref b)) => JsBigInt::from_js_string(a) + .as_ref() + .map_or(false, |a| a == b), // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. (Self::Boolean(x), _) => return other.equals(&Self::new(i32::from(*x)), context), diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index 6678e294d63..bae1f87add6 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -8,6 +8,7 @@ use std::{ ops::Sub, }; +use boa_macros::js_str; use num_bigint::BigInt; use num_integer::Integer; use num_traits::{ToPrimitive, Zero}; @@ -393,9 +394,9 @@ impl JsValue { // 1. Assert: preferredType is number. // 2. Let hint be "number". let hint = match preferred_type { - PreferredType::Default => js_string!("default"), - PreferredType::String => js_string!("string"), - PreferredType::Number => js_string!("number"), + PreferredType::Default => js_str!("default"), + PreferredType::String => js_str!("string"), + PreferredType::Number => js_str!("number"), } .into(); @@ -440,7 +441,7 @@ impl JsValue { Self::Undefined => Err(JsNativeError::typ() .with_message("cannot convert undefined to a BigInt") .into()), - Self::String(ref string) => string.to_big_int().map_or_else( + Self::String(ref string) => JsBigInt::from_js_string(string).map_or_else( || { Err(JsNativeError::syntax() .with_message(format!( @@ -495,8 +496,8 @@ impl JsValue { /// This function is equivalent to `String(value)` in JavaScript. pub fn to_string(&self, context: &mut Context) -> JsResult { match self { - Self::Null => Ok("null".into()), - Self::Undefined => Ok("undefined".into()), + Self::Null => Ok(js_string!("null")), + Self::Undefined => Ok(js_string!("undefined")), Self::Boolean(boolean) => Ok(boolean.to_string().into()), Self::Rational(rational) => Ok(Number::to_js_string(*rational)), Self::Integer(integer) => Ok(integer.to_string().into()), @@ -518,7 +519,6 @@ impl JsValue { /// /// See: pub fn to_object(&self, context: &mut Context) -> JsResult { - // TODO: add fast paths with object template match self { Self::Undefined | Self::Null => Err(JsNativeError::typ() .with_message("cannot convert 'null' or 'undefined' to object") @@ -991,21 +991,22 @@ impl JsValue { #[must_use] pub fn js_type_of(&self) -> JsString { match *self { - Self::Rational(_) | Self::Integer(_) => js_string!("number"), - Self::String(_) => js_string!("string"), - Self::Boolean(_) => js_string!("boolean"), - Self::Symbol(_) => js_string!("symbol"), - Self::Null => js_string!("object"), - Self::Undefined => js_string!("undefined"), - Self::BigInt(_) => js_string!("bigint"), + Self::Rational(_) | Self::Integer(_) => js_str!("number"), + Self::String(_) => js_str!("string"), + Self::Boolean(_) => js_str!("boolean"), + Self::Symbol(_) => js_str!("symbol"), + Self::Null => js_str!("object"), + Self::Undefined => js_str!("undefined"), + Self::BigInt(_) => js_str!("bigint"), Self::Object(ref object) => { if object.is_callable() { - js_string!("function") + js_str!("function") } else { - js_string!("object") + js_str!("object") } } } + .into() } /// Abstract operation `IsArray ( argument )` diff --git a/core/engine/src/value/operations.rs b/core/engine/src/value/operations.rs index e7a403296a6..c26a0b00bab 100644 --- a/core/engine/src/value/operations.rs +++ b/core/engine/src/value/operations.rs @@ -537,11 +537,9 @@ impl JsValue { match (px, py) { (Self::String(ref x), Self::String(ref y)) => (x < y).into(), - (Self::BigInt(ref x), Self::String(ref y)) => y - .to_big_int() + (Self::BigInt(ref x), Self::String(ref y)) => JsBigInt::from_js_string(y) .map_or(AbstractRelation::Undefined, |y| (*x < y).into()), - (Self::String(ref x), Self::BigInt(ref y)) => x - .to_big_int() + (Self::String(ref x), Self::BigInt(ref y)) => JsBigInt::from_js_string(x) .map_or(AbstractRelation::Undefined, |x| (x < *y).into()), (px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) { (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y), diff --git a/core/engine/src/value/tests.rs b/core/engine/src/value/tests.rs index 74c2c7ed5e5..4d8fffc169f 100644 --- a/core/engine/src/value/tests.rs +++ b/core/engine/src/value/tests.rs @@ -1,4 +1,3 @@ -use boa_macros::utf16; use indoc::indoc; use super::*; @@ -28,9 +27,9 @@ fn get_set_field() { run_test_actions([TestAction::assert_context(|ctx| { let obj = &JsObject::with_object_proto(ctx.intrinsics()); // Create string and convert it to a Value - let s = JsValue::new(js_string!("bar")); - obj.set(js_string!("foo"), s, false, ctx).unwrap(); - obj.get(js_string!("foo"), ctx).unwrap() == JsValue::new(js_string!("bar")) + let s = JsValue::new(js_str!("bar")); + obj.set(js_str!("foo"), s, false, ctx).unwrap(); + obj.get(js_str!("foo"), ctx).unwrap() == JsValue::new(js_str!("bar")) })]); } @@ -241,7 +240,7 @@ fn add_number_and_number() { fn add_number_and_string() { run_test_actions([TestAction::assert_eq( "1 + \" + 2 = 3\"", - js_string!("1 + 2 = 3"), + js_str!("1 + 2 = 3"), )]); } @@ -249,7 +248,7 @@ fn add_number_and_string() { fn add_string_and_string() { run_test_actions([TestAction::assert_eq( "\"Hello\" + \", world\"", - js_string!("Hello, world"), + js_str!("Hello, world"), )]); } @@ -262,7 +261,7 @@ fn add_number_object_and_number() { fn add_number_object_and_string_object() { run_test_actions([TestAction::assert_eq( "new Number(10) + new String(\"0\")", - js_string!("100"), + js_str!("100"), )]); } @@ -445,7 +444,7 @@ fn to_integer_or_infinity() { IntegerOrInfinity::Integer(11) ); assert_eq!( - JsValue::new(js_string!("12")) + JsValue::new(js_str!("12")) .to_integer_or_infinity(ctx) .unwrap(), IntegerOrInfinity::Integer(12) @@ -465,8 +464,8 @@ fn test_accessors() { let a = { get b() { return "c" }, set b(value) { arr = arr.concat([value]) }} ; a.b = "a"; "#}), - TestAction::assert_eq("a.b", js_string!("c")), - TestAction::assert_eq("arr[0]", js_string!("a")), + TestAction::assert_eq("a.b", js_str!("c")), + TestAction::assert_eq("arr[0]", js_str!("a")), ]); } @@ -683,16 +682,13 @@ fn to_int32() { #[test] fn to_string() { run_test_actions([TestAction::inspect_context(|ctx| { - assert_eq!(&JsValue::null().to_string(ctx).unwrap(), utf16!("null")); + assert_eq!(&JsValue::null().to_string(ctx).unwrap(), "null"); + assert_eq!(&JsValue::undefined().to_string(ctx).unwrap(), "undefined"); + assert_eq!(&JsValue::new(55).to_string(ctx).unwrap(), "55"); + assert_eq!(&JsValue::new(55.0).to_string(ctx).unwrap(), "55"); assert_eq!( - &JsValue::undefined().to_string(ctx).unwrap(), - utf16!("undefined") - ); - assert_eq!(&JsValue::new(55).to_string(ctx).unwrap(), utf16!("55")); - assert_eq!(&JsValue::new(55.0).to_string(ctx).unwrap(), utf16!("55")); - assert_eq!( - &JsValue::new(js_string!("hello")).to_string(ctx).unwrap(), - utf16!("hello") + JsValue::new(js_str!("hello")).to_string(ctx).unwrap(), + js_str!("hello") ); })]); } @@ -704,7 +700,7 @@ fn to_bigint() { assert!(JsValue::undefined().to_bigint(ctx).is_err()); assert!(JsValue::new(55).to_bigint(ctx).is_err()); assert!(JsValue::new(10.0).to_bigint(ctx).is_err()); - assert!(JsValue::new(js_string!("100")).to_bigint(ctx).is_ok()); + assert!(JsValue::new(js_str!("100")).to_bigint(ctx).is_ok()); })]); } @@ -737,7 +733,7 @@ mod cyclic_conversions { let a = [b, b]; JSON.stringify(a) "#}, - js_string!("[[],[]]"), + js_str!("[[],[]]"), )]); } diff --git a/core/engine/src/vm/flowgraph/mod.rs b/core/engine/src/vm/flowgraph/mod.rs index 273053d15b4..0c931d5afed 100644 --- a/core/engine/src/vm/flowgraph/mod.rs +++ b/core/engine/src/vm/flowgraph/mod.rs @@ -1,13 +1,13 @@ //! This module is responsible for generating the vm instruction flowgraph. use crate::vm::CodeBlock; -use boa_macros::utf16; mod color; mod edge; mod graph; mod node; +use boa_macros::js_str; pub use color::*; pub use edge::*; pub use graph::*; @@ -20,7 +20,7 @@ impl CodeBlock { #[allow(clippy::match_same_arms)] pub fn to_graph(&self, graph: &mut SubGraph) { // Have to remove any invalid graph chars like `<` or `>`. - let name = if self.name() == utf16!("
") { + let name = if self.name() == &js_str!("
") { "__main__".to_string() } else { self.name().to_std_string_escaped() diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index e9fc03f79c5..580bf18071f 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -1,4 +1,5 @@ use boa_gc::{Gc, GcRefCell}; +use boa_macros::js_str; use crate::{ builtins::{ @@ -82,7 +83,7 @@ impl Operation for Await { captures.clone(), ), ) - .name("") + .name(js_str!("")) .length(1) .build(); @@ -122,7 +123,7 @@ impl Operation for Await { captures, ), ) - .name("") + .name(js_str!("")) .length(1) .build(); diff --git a/core/engine/src/vm/opcode/concat/mod.rs b/core/engine/src/vm/opcode/concat/mod.rs index d6d33a215c7..2bcc3c55059 100644 --- a/core/engine/src/vm/opcode/concat/mod.rs +++ b/core/engine/src/vm/opcode/concat/mod.rs @@ -20,8 +20,9 @@ impl ConcatToString { let s = JsString::concat_array( &strings .iter() - .map(JsString::as_slice) - .collect::>(), + .map(JsString::as_str) + .map(Into::into) + .collect::>(), ); context.vm.push(s); Ok(CompletionType::Normal) diff --git a/core/engine/src/vm/opcode/define/class/getter.rs b/core/engine/src/vm/opcode/define/class/getter.rs index 7eda9a7f14b..174576778c9 100644 --- a/core/engine/src/vm/opcode/define/class/getter.rs +++ b/core/engine/src/vm/opcode/define/class/getter.rs @@ -1,9 +1,11 @@ +use boa_macros::js_str; + use crate::{ builtins::function::{set_function_name, OrdinaryFunction}, object::internal_methods::InternalMethodContext, property::PropertyDescriptor, vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsString, + Context, JsResult, }; /// `DefineClassStaticGetterByName` implements the Opcode Operation for `Opcode::DefineClassStaticGetterByName` @@ -28,7 +30,7 @@ impl DefineClassStaticGetterByName { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("get")), context); + set_function_name(function_object, &key, Some(js_str!("get")), context); function_object .downcast_mut::() .expect("method must be function object") @@ -96,7 +98,7 @@ impl DefineClassGetterByName { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("get")), context); + set_function_name(function_object, &key, Some(js_str!("get")), context); function_object .downcast_mut::() .expect("method must be function object") @@ -166,7 +168,7 @@ impl Operation for DefineClassStaticGetterByValue { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("get")), context); + set_function_name(function_object, &key, Some(js_str!("get")), context); function_object .downcast_mut::() .expect("method must be function object") @@ -216,7 +218,7 @@ impl Operation for DefineClassGetterByValue { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("get")), context); + set_function_name(function_object, &key, Some(js_str!("get")), context); function_object .downcast_mut::() .expect("method must be function object") diff --git a/core/engine/src/vm/opcode/define/class/setter.rs b/core/engine/src/vm/opcode/define/class/setter.rs index 1a8cdbfe4d2..8d2272b2f43 100644 --- a/core/engine/src/vm/opcode/define/class/setter.rs +++ b/core/engine/src/vm/opcode/define/class/setter.rs @@ -1,9 +1,11 @@ +use boa_macros::js_str; + use crate::{ builtins::function::{set_function_name, OrdinaryFunction}, object::internal_methods::InternalMethodContext, property::PropertyDescriptor, vm::{opcode::Operation, CompletionType}, - Context, JsResult, JsString, + Context, JsResult, }; /// `DefineClassStaticSetterByName` implements the Opcode Operation for `Opcode::DefineClassStaticSetterByName` @@ -28,7 +30,7 @@ impl DefineClassStaticSetterByName { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("set")), context); + set_function_name(function_object, &key, Some(js_str!("set")), context); function_object .downcast_mut::() .expect("method must be function object") @@ -97,7 +99,7 @@ impl DefineClassSetterByName { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("set")), context); + set_function_name(function_object, &key, Some(js_str!("set")), context); function_object .downcast_mut::() .expect("method must be function object") @@ -169,7 +171,7 @@ impl Operation for DefineClassStaticSetterByValue { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("set")), context); + set_function_name(function_object, &key, Some(js_str!("set")), context); function_object .downcast_mut::() .expect("method must be function object") @@ -220,7 +222,7 @@ impl Operation for DefineClassSetterByValue { let function_object = function .as_object() .expect("method must be function object"); - set_function_name(function_object, &key, Some(JsString::from("set")), context); + set_function_name(function_object, &key, Some(js_str!("set")), context); function_object .downcast_mut::() .expect("method must be function object") diff --git a/core/engine/src/vm/opcode/generator/mod.rs b/core/engine/src/vm/opcode/generator/mod.rs index 53197a3bdc6..fb6f98fa90e 100644 --- a/core/engine/src/vm/opcode/generator/mod.rs +++ b/core/engine/src/vm/opcode/generator/mod.rs @@ -8,8 +8,8 @@ use crate::{ generator::{GeneratorContext, GeneratorState}, }, error::JsNativeError, + js_str, object::PROTOTYPE, - string::utf16, vm::{ call_frame::GeneratorResumeKind, opcode::{Operation, ReThrow}, @@ -244,7 +244,7 @@ impl Operation for GeneratorDelegateNext { GeneratorResumeKind::Throw => { let throw = iterator_record .iterator() - .get_method(utf16!("throw"), context)?; + .get_method(js_str!("throw"), context)?; if let Some(throw) = throw { let result = throw.call( &iterator_record.iterator().clone().into(), @@ -265,7 +265,7 @@ impl Operation for GeneratorDelegateNext { GeneratorResumeKind::Return => { let r#return = iterator_record .iterator() - .get_method(utf16!("return"), context)?; + .get_method(js_str!("return"), context)?; if let Some(r#return) = r#return { let result = r#return.call( &iterator_record.iterator().clone().into(), diff --git a/core/engine/src/vm/opcode/iteration/for_in.rs b/core/engine/src/vm/opcode/iteration/for_in.rs index 0baa43e8dd6..ee6998f9a6c 100644 --- a/core/engine/src/vm/opcode/iteration/for_in.rs +++ b/core/engine/src/vm/opcode/iteration/for_in.rs @@ -1,6 +1,6 @@ use crate::{ builtins::{iterable::IteratorRecord, object::for_in_iterator::ForInIterator}, - js_string, + js_str, vm::{opcode::Operation, CompletionType}, Context, JsResult, JsValue, }; @@ -23,7 +23,7 @@ impl Operation for CreateForInIterator { let object = object.to_object(context)?; let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context); let next_method = iterator - .get(js_string!("next"), context) + .get(js_str!("next"), context) .expect("ForInIterator must have a `next` method"); context diff --git a/core/engine/src/vm/opcode/iteration/iterator.rs b/core/engine/src/vm/opcode/iteration/iterator.rs index cf19d7be3b1..c43afd192d3 100644 --- a/core/engine/src/vm/opcode/iteration/iterator.rs +++ b/core/engine/src/vm/opcode/iteration/iterator.rs @@ -1,8 +1,6 @@ -use std::matches; - use crate::{ builtins::{iterable::create_iter_result_object, Array}, - js_string, + js_str, vm::{opcode::Operation, CompletionType, GeneratorResumeKind}, Context, JsResult, }; @@ -245,10 +243,7 @@ impl Operation for IteratorReturn { return Ok(CompletionType::Normal); } - let Some(ret) = record - .iterator() - .get_method(js_string!("return"), context)? - else { + let Some(ret) = record.iterator().get_method(js_str!("return"), context)? else { context.vm.push(false); return Ok(CompletionType::Normal); }; diff --git a/core/engine/src/vm/opcode/push/class/private.rs b/core/engine/src/vm/opcode/push/class/private.rs index a33ff5a47f0..032c3300006 100644 --- a/core/engine/src/vm/opcode/push/class/private.rs +++ b/core/engine/src/vm/opcode/push/class/private.rs @@ -1,9 +1,8 @@ use crate::{ builtins::function::OrdinaryFunction, - js_string, + js_str, js_string, object::{internal_methods::InternalMethodContext, PrivateElement}, property::PropertyDescriptor, - string::utf16, vm::{opcode::Operation, CompletionType}, Context, JsResult, }; @@ -22,7 +21,7 @@ impl PushClassPrivateMethod { let method = context.vm.pop(); let method_object = method.as_callable().expect("method must be callable"); - let name_string = js_string!(utf16!("#"), &name); + let name_string = js_string!(js_str!("#"), &name); let desc = PropertyDescriptor::builder() .value(name_string) .writable(false) @@ -31,7 +30,7 @@ impl PushClassPrivateMethod { .build(); method_object .__define_own_property__( - &utf16!("name").into(), + &js_str!("name").into(), desc, &mut InternalMethodContext::new(context), ) diff --git a/core/engine/src/vm/opcode/set/private.rs b/core/engine/src/vm/opcode/set/private.rs index 98e2b771122..dd0c8631f16 100644 --- a/core/engine/src/vm/opcode/set/private.rs +++ b/core/engine/src/vm/opcode/set/private.rs @@ -1,8 +1,7 @@ use crate::{ - js_string, + js_str, js_string, object::PrivateElement, property::PropertyDescriptor, - string::utf16, vm::{opcode::Operation, CompletionType}, Context, JsResult, }; @@ -114,7 +113,7 @@ impl SetPrivateMethod { let value = context.vm.pop(); let value = value.as_callable().expect("method must be callable"); - let name_string = js_string!(utf16!("#"), &name); + let name_string = js_string!(js_str!("#"), &name); let desc = PropertyDescriptor::builder() .value(name_string) .writable(false) diff --git a/core/engine/src/vm/opcode/set/property.rs b/core/engine/src/vm/opcode/set/property.rs index c1233b395ce..07256681061 100644 --- a/core/engine/src/vm/opcode/set/property.rs +++ b/core/engine/src/vm/opcode/set/property.rs @@ -1,9 +1,11 @@ +use boa_macros::js_str; + use crate::{ builtins::function::set_function_name, object::{internal_methods::InternalMethodContext, shape::slot::SlotAttributes}, property::{PropertyDescriptor, PropertyKey}, vm::{opcode::Operation, CompletionType}, - Context, JsNativeError, JsResult, JsString, JsValue, + Context, JsNativeError, JsResult, JsValue, }; /// `SetPropertyByName` implements the Opcode Operation for `Opcode::SetPropertyByName` @@ -384,8 +386,8 @@ impl Operation for SetFunctionName { }; let prefix = match prefix { - 1 => Some(JsString::from("get")), - 2 => Some(JsString::from("set")), + 1 => Some(js_str!("get")), + 2 => Some(js_str!("set")), _ => None, }; diff --git a/core/engine/src/vm/opcode/templates/mod.rs b/core/engine/src/vm/opcode/templates/mod.rs index c33e831d194..affb3a723ea 100644 --- a/core/engine/src/vm/opcode/templates/mod.rs +++ b/core/engine/src/vm/opcode/templates/mod.rs @@ -1,11 +1,11 @@ use crate::{ builtins::array::Array, + js_str, object::IntegrityLevel, property::PropertyDescriptor, vm::{opcode::Operation, CompletionType}, Context, JsResult, }; -use boa_macros::utf16; /// `TemplateLookup` implements the Opcode Operation for `Opcode::TemplateLookup` /// @@ -79,7 +79,7 @@ impl TemplateCreate { .expect("should never fail per spec"); template .define_property_or_throw( - utf16!("raw"), + js_str!("raw"), PropertyDescriptor::builder() .value(raw_obj) .writable(false) diff --git a/core/engine/src/vm/tests.rs b/core/engine/src/vm/tests.rs index 89252006825..e59a6597bf4 100644 --- a/core/engine/src/vm/tests.rs +++ b/core/engine/src/vm/tests.rs @@ -2,6 +2,7 @@ use crate::{ js_string, property::Attribute, run_test_actions, Context, JsNativeErrorKind, JsValue, TestAction, }; +use boa_macros::js_str; use boa_parser::Source; use indoc::indoc; @@ -12,7 +13,7 @@ fn typeof_string() { const a = "hello"; typeof a; "#}, - js_string!("string"), + js_str!("string"), )]); } @@ -23,7 +24,7 @@ fn typeof_number() { let a = 1234; typeof a; "#}, - js_string!("number"), + js_str!("number"), )]); } @@ -54,7 +55,7 @@ fn try_catch_finally_from_init() { } finally { } "#}, - js_string!("h"), + js_str!("h"), )]); } @@ -89,7 +90,7 @@ fn use_last_expr_try_block() { "Bye!" } "#}, - js_string!("Hello!"), + js_str!("Hello!"), )]); } @@ -106,7 +107,7 @@ fn use_last_expr_catch_block() { "Hello!"; } "#}, - js_string!("Hello!"), + js_str!("Hello!"), )]); } @@ -137,7 +138,7 @@ fn finally_block_binding_env() { } buf "#}, - js_string!("Hey hey people"), + js_str!("Hey hey people"), )]); } @@ -154,7 +155,7 @@ fn run_super_method_in_object() { Object.setPrototypeOf(obj, proto); obj.v(); "#}, - js_string!("super"), + js_str!("super"), )]); } @@ -178,7 +179,7 @@ fn get_reference_by_super() { obj.method(); fromA + fromB "#}, - js_string!("ab"), + js_str!("ab"), )]); } @@ -238,7 +239,7 @@ fn order_of_execution_in_assigment_with_comma_expressions() { (f(1), a)[(f(2), 0)][(f(3), 0)] = (f(4), 123); result "#}, - js_string!("1234"), + js_str!("1234"), )]); } @@ -325,7 +326,7 @@ fn arguments_object_constructor_valid_index() { new F(); typeof args "#}, - js_string!("object"), + js_str!("object"), )]); } @@ -387,8 +388,8 @@ fn super_construction_with_paramater_expression() { } } "#}), - TestAction::assert_eq("new Student().name", js_string!("unknown")), - TestAction::assert_eq("new Student('Jack').name", js_string!("Jack")), + TestAction::assert_eq("new Student().name", js_str!("unknown")), + TestAction::assert_eq("new Student('Jack').name", js_str!("Jack")), ]); } diff --git a/core/macros/src/lib.rs b/core/macros/src/lib.rs index 600b566bdb3..027e6c0478a 100644 --- a/core/macros/src/lib.rs +++ b/core/macros/src/lib.rs @@ -181,6 +181,33 @@ pub fn utf16(input: TokenStream) -> TokenStream { .into() } +/// Convert a utf8 string literal to a `JsString` +#[proc_macro] +pub fn js_str(input: TokenStream) -> TokenStream { + let literal = parse_macro_input!(input as LitStr); + let utf8 = literal.value(); + + let mut is_latin1 = true; + let utf16 = utf8 + .encode_utf16() + .inspect(|x| { + if *x > u8::MAX.into() { + is_latin1 = false; + } + }) + .collect::>(); + if is_latin1 { + quote! { + ::boa_engine::string::JsStr::latin1(#utf8.as_bytes()) + } + } else { + quote! { + ::boa_engine::string::JsStr::utf16([#(#utf16),*].as_slice()) + } + } + .into() +} + decl_derive! { [Trace, attributes(boa_gc, unsafe_ignore_trace)] => /// Derive the `Trace` trait. diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index df890496874..afb3e2e3bfe 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -15,15 +15,13 @@ mod tests; use boa_engine::{ - js_string, + js_str, js_string, native_function::NativeFunction, object::{JsObject, ObjectInitializer}, - string::utf16, value::{JsValue, Numeric}, - Context, JsArgs, JsData, JsResult, JsString, + Context, JsArgs, JsData, JsResult, JsStr, JsString, }; use boa_gc::{Finalize, Trace}; -// use boa_profiler::Profiler; use rustc_hash::FxHashMap; use std::{cell::RefCell, rc::Rc, time::SystemTime}; @@ -132,7 +130,7 @@ pub struct Console { impl Console { /// Name of the built-in `console` property. - pub const NAME: &'static str = "console"; + pub const NAME: JsStr<'static> = js_str!("console"); /// Initializes the `console` built-in object. #[allow(clippy::too_many_lines)] @@ -280,8 +278,8 @@ impl Console { } else if !args[0].is_string() { args.insert(0, JsValue::new(message)); } else { - let value: Vec = args[0].display().to_string().encode_utf16().collect(); - let concat = js_string!(&message, utf16!(": "), &value); + let value = JsString::from(args[0].display().to_string()); + let concat = js_string!(message.as_str(), js_str!(": "), &value); args[0] = JsValue::new(concat); } diff --git a/docs/boa_object.md b/docs/boa_object.md index e5e5a92f67d..b3fa7098a39 100644 --- a/docs/boa_object.md +++ b/docs/boa_object.md @@ -313,3 +313,34 @@ function x() { } x(); // RuntimeLimit: Maximum recursion limit 100 exceeded ``` + +## Module `$boa.string` + +This module contains helpful functions for getting information about a strings. + +### Function `$boa.string.storage(str)` + +Returns the string's inner storage type, if it's a well known string that is stored in the `STATIC_STRINGS` array in boa, +then `"static"` is returned, `"heap"` otherwise. + +```JavaScript +$boa.string.storage("push") // "static" +$boa.string.storage("specialFunction") // "heap" +``` + +### Function `$boa.string.encoding(str)` + +Returns the string's inner encoding of the string. + +```JavaScript +$boa.string.encoding("Greeting") // "latin1" +$boa.string.encoding("挨拶") // "utf16" +``` + +### Function `$boa.string.summary(str)` + +Returns an object with a short summary of the of the given string. + +```JavaScript +$boa.string.summary("Greeting") // { storage: "heap", encoding: "latin1" } +``` diff --git a/examples/src/bin/classes.rs b/examples/src/bin/classes.rs index 943a27410c7..44da13ce8b2 100644 --- a/examples/src/bin/classes.rs +++ b/examples/src/bin/classes.rs @@ -2,12 +2,11 @@ use boa_engine::{ class::{Class, ClassBuilder}, error::JsNativeError, - js_string, + js_str, js_string, native_function::NativeFunction, property::Attribute, Context, JsArgs, JsData, JsResult, JsString, JsValue, Source, }; - use boa_gc::{Finalize, Trace}; use boa_runtime::Console; @@ -121,13 +120,13 @@ impl Class for Person { // We add an `"inheritedProperty"` property to the prototype of `Person` with // a value of `10` and default attribute flags `READONLY`, `NON_ENUMERABLE` and `PERMANENT`. - class.property(js_string!("inheritedProperty"), 10, Attribute::default()); + class.property(js_str!("inheritedProperty"), 10, Attribute::default()); // Finally, we add a `"staticProperty"` property to `Person` with a value // of `"Im a static property"` and attribute flags `WRITABLE`, `ENUMERABLE` and `PERMANENT`. class.static_property( - js_string!("staticProperty"), - js_string!("Im a static property"), + js_str!("staticProperty"), + js_str!("Im a static property"), Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::PERMANENT, ); @@ -140,7 +139,7 @@ fn add_runtime(context: &mut Context) { // We first add the `console` object, to be able to call `console.log()`. let console = Console::init(context); context - .register_global_property(js_string!(Console::NAME), console, Attribute::all()) + .register_global_property(Console::NAME, console, Attribute::all()) .expect("the console builtin shouldn't exist"); // Then we need to register the global class `Person` inside `context`. diff --git a/examples/src/bin/closures.rs b/examples/src/bin/closures.rs index 97408827639..efc414061e1 100644 --- a/examples/src/bin/closures.rs +++ b/examples/src/bin/closures.rs @@ -4,11 +4,10 @@ use std::cell::{Cell, RefCell}; use boa_engine::{ - js_string, + js_str, js_string, native_function::NativeFunction, object::{builtins::JsArray, FunctionObjectBuilder, JsObject}, property::{Attribute, PropertyDescriptor}, - string::utf16, Context, JsError, JsNativeError, JsString, JsValue, Source, }; use boa_gc::{Finalize, GcRefCell, Trace}; @@ -24,7 +23,7 @@ fn main() -> Result<(), JsError> { // We register a global closure function that has the name 'closure' with length 0. context .register_global_callable( - js_string!("closure"), + JsString::from("closure"), 0, NativeFunction::from_copy_closure(move |_, _, _| { println!("Called `closure`"); @@ -53,9 +52,9 @@ fn main() -> Result<(), JsError> { // We create a new `JsObject` with some data let object = JsObject::with_object_proto(context.intrinsics()); object.define_property_or_throw( - js_string!("name"), + js_str!("name"), PropertyDescriptor::builder() - .value(js_string!("Boa dev")) + .value(js_str!("Boa dev")) .writable(false) .enumerable(false) .configurable(false), @@ -78,18 +77,18 @@ fn main() -> Result<(), JsError> { let BigStruct { greeting, object } = &mut *captures; println!("Called `createMessage`"); // We obtain the `name` property of `captures.object` - let name = object.get(js_string!("name"), context)?; + let name = object.get(js_str!("name"), context)?; // We create a new message from our captured variable. let message = js_string!( - utf16!("message from `"), + js_str!("message from `"), &name.to_string(context)?, - utf16!("`: "), - greeting + js_str!("`: "), + &*greeting ); // We can also mutate the moved data inside the closure. - captures.greeting = js_string!(greeting, utf16!(" Hello!")); + captures.greeting = js_string!(&*greeting, js_str!(" Hello!")); println!("{}", message.to_std_string_escaped()); println!(); @@ -102,7 +101,7 @@ fn main() -> Result<(), JsError> { ), ) // And here we assign `createMessage` to the `name` property of the closure. - .name("createMessage") + .name(js_str!("createMessage")) // By default all `FunctionBuilder`s set the `length` property to `0` and // the `constructable` property to `false`. .build(); @@ -112,7 +111,7 @@ fn main() -> Result<(), JsError> { .register_global_property( // We set the key to access the function the same as its name for // consistency, but it may be different if needed. - js_string!("createMessage"), + js_str!("createMessage"), // We pass `js_function` as a property value. js_function, // We assign to the "createMessage" property the desired attributes. @@ -145,7 +144,7 @@ fn main() -> Result<(), JsError> { // We register a global closure that is not `Copy`. context .register_global_callable( - js_string!("enumerate"), + js_str!("enumerate").into(), 0, // Note that it is required to use `unsafe` code, since the compiler cannot verify that the // types captured by the closure are not traceable. diff --git a/examples/src/bin/futures.rs b/examples/src/bin/futures.rs index d677a761af2..3a5a3487f9b 100644 --- a/examples/src/bin/futures.rs +++ b/examples/src/bin/futures.rs @@ -8,7 +8,7 @@ use std::{ use boa_engine::{ context::ContextBuilder, job::{FutureJob, JobQueue, NativeJob}, - js_string, + js_str, native_function::NativeFunction, property::Attribute, Context, JsArgs, JsResult, JsValue, Source, @@ -137,13 +137,13 @@ fn add_runtime(context: &mut Context) { // First add the `console` object, to be able to call `console.log()`. let console = Console::init(context); context - .register_global_property(js_string!(Console::NAME), console, Attribute::all()) + .register_global_property(Console::NAME, console, Attribute::all()) .expect("the console builtin shouldn't exist"); // Then, bind the defined async function to the ECMAScript function "delay". context .register_global_builtin_callable( - js_string!("delay"), + js_str!("delay").into(), 1, NativeFunction::from_async_fn(delay), ) diff --git a/examples/src/bin/host_defined.rs b/examples/src/bin/host_defined.rs index c6b12d3661d..475e21cfab4 100644 --- a/examples/src/bin/host_defined.rs +++ b/examples/src/bin/host_defined.rs @@ -1,7 +1,7 @@ // This example goes into the details on how to store user defined structs/state that is shared. use boa_engine::{ - js_string, native_function::NativeFunction, Context, JsArgs, JsData, JsError, JsNativeError, + native_function::NativeFunction, Context, JsArgs, JsData, JsError, JsNativeError, JsString, JsValue, Source, }; use boa_gc::{Finalize, Trace}; @@ -90,7 +90,7 @@ fn main() -> Result<(), JsError> { // // The funtion lives in the context's realm and has access to the host-defined field. context.register_global_builtin_callable( - js_string!("setRealmValue"), + JsString::from("setRealmValue"), 1, NativeFunction::from_fn_ptr(|_, args, context| { let value: usize = args.get_or_undefined(0).try_js_into(context)?; @@ -112,7 +112,7 @@ fn main() -> Result<(), JsError> { )?; context.register_global_builtin_callable( - js_string!("getRealmValue"), + JsString::from("getRealmValue"), 0, NativeFunction::from_fn_ptr(|_, _, context| { let mut host_defined = context.realm().host_defined_mut(); diff --git a/examples/src/bin/jsarray.rs b/examples/src/bin/jsarray.rs index c870831bb07..3f09b0b7574 100644 --- a/examples/src/bin/jsarray.rs +++ b/examples/src/bin/jsarray.rs @@ -1,10 +1,9 @@ // This example shows how to manipulate a Javascript array using Rust code. use boa_engine::{ - js_string, + js_str, native_function::NativeFunction, object::{builtins::JsArray, FunctionObjectBuilder}, - string::utf16, Context, JsResult, JsValue, }; @@ -17,16 +16,13 @@ fn main() -> JsResult<()> { assert!(array.is_empty(context)?); - array.push(js_string!("Hello, world"), context)?; // [ "Hello, world" ] + array.push(js_str!("Hello, world"), context)?; // [ "Hello, world" ] array.push(true, context)?; // [ "Hello, world", true ] assert!(!array.is_empty(context)?); assert_eq!(array.pop(context)?, JsValue::new(true)); // [ "Hello, world" ] - assert_eq!( - array.pop(context)?, - JsValue::new(js_string!("Hello, world")) - ); // [ ] + assert_eq!(array.pop(context)?, JsValue::new(js_str!("Hello, world"))); // [ ] assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ] array.push(1, context)?; // [ 1 ] @@ -58,12 +54,12 @@ fn main() -> JsResult<()> { // Join the array with an optional separator (default ","). let joined_array = array.join(None, context)?; - assert_eq!(&joined_array, utf16!("14,13,12,11,10")); + assert_eq!(&joined_array, "14,13,12,11,10"); array.fill(false, Some(1), Some(4), context)?; let joined_array = array.join(Some("::".into()), context)?; - assert_eq!(&joined_array, utf16!("14::false::false::false::10")); + assert_eq!(&joined_array, "14::false::false::false::10"); let filter_callback = FunctionObjectBuilder::new( context.realm(), @@ -97,7 +93,7 @@ fn main() -> JsResult<()> { .concat(&[another_array.into()], context)? // [ 100, 196, 1, 2, 3, 4, 5 ] .slice(Some(1), Some(5), context)?; // [ 196, 1, 2, 3 ] - assert_eq!(&chained_array.join(None, context)?, utf16!("196,1,2,3")); + assert_eq!(&chained_array.join(None, context)?, "196,1,2,3"); let reduce_callback = FunctionObjectBuilder::new( context.realm(), @@ -117,7 +113,7 @@ fn main() -> JsResult<()> { context .global_object() - .set(js_string!("myArray"), array, true, context)?; + .set(js_str!("myArray"), array, true, context)?; Ok(()) } diff --git a/examples/src/bin/jsarraybuffer.rs b/examples/src/bin/jsarraybuffer.rs index 5b321a50069..c5e6e44a4b7 100644 --- a/examples/src/bin/jsarraybuffer.rs +++ b/examples/src/bin/jsarraybuffer.rs @@ -1,7 +1,7 @@ // This example shows how to manipulate a Javascript array using Rust code. use boa_engine::{ - js_string, + js_str, object::builtins::{JsArrayBuffer, JsDataView, JsUint32Array, JsUint8Array}, property::Attribute, Context, JsResult, JsValue, @@ -54,7 +54,7 @@ fn main() -> JsResult<()> { // We can also register it as a global property context .register_global_property( - js_string!("myArrayBuffer"), + js_str!("myArrayBuffer"), array_buffer, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ) diff --git a/examples/src/bin/jsmap.rs b/examples/src/bin/jsmap.rs index 3ac1b06c8aa..39257497866 100644 --- a/examples/src/bin/jsmap.rs +++ b/examples/src/bin/jsmap.rs @@ -1,6 +1,6 @@ use boa_engine::{ js_string, - object::{builtins::JsArray, builtins::JsMap}, + object::builtins::{JsArray, JsMap}, Context, JsResult, JsValue, }; diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index 8b6faabfd87..72009def83b 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -1,13 +1,13 @@ // This example shows how to manipulate a Javascript array using Rust code. use boa_engine::{ - js_string, + js_str, native_function::NativeFunction, object::{ builtins::{JsArray, JsArrayBuffer, JsUint8Array}, FunctionObjectBuilder, }, - property::{Attribute, PropertyKey}, + property::Attribute, Context, JsNativeError, JsResult, JsValue, }; use boa_gc::{Gc, GcRefCell}; @@ -168,7 +168,7 @@ fn main() -> JsResult<()> { .buffer(context)? .as_object() .unwrap() - .get(PropertyKey::String(js_string!("byteLength")), context) + .get(js_str!("byteLength"), context) .unwrap(), JsValue::new(8) ); @@ -195,23 +195,23 @@ fn main() -> JsResult<()> { // toLocaleString // let array = JsUint32Array::from_iter(vec![500, 8123, 12], context)?; - // let locales: Option = Some(js_string!("de-DE").into()); + // let locales: Option = Some(js_str!("de-DE").into()); // let options = Some(context.eval(Source::from_bytes( // r##"let options = { style: "currency", currency: "EUR" }; options;"##, // ))?); // assert_eq!( // array.to_locale_string(locales, options, context)?, - // js_string!("500,00 €,8.123,00 €,12,00 €").into() + // js_str!("500,00 €,8.123,00 €,12,00 €").into() // ); // toStringTag let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; let tag = array.to_string_tag(context)?.to_string(context)?; - assert_eq!(tag, js_string!("Uint8Array")); + assert_eq!(tag, js_str!("Uint8Array")); context .register_global_property( - js_string!("myUint8Array"), + js_str!("myUint8Array"), array, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ) diff --git a/examples/src/bin/module_fetch.rs b/examples/src/bin/module_fetch.rs index 9e1adb57cab..0f2de3b8462 100644 --- a/examples/src/bin/module_fetch.rs +++ b/examples/src/bin/module_fetch.rs @@ -7,9 +7,8 @@ use std::{ use boa_engine::{ builtins::promise::PromiseState, job::{FutureJob, JobQueue, NativeJob}, - js_string, + js_str, module::ModuleLoader, - string::utf16, Context, JsNativeError, JsResult, JsString, JsValue, Module, }; use boa_parser::Source; @@ -149,9 +148,7 @@ fn main() -> JsResult<()> { } } - let default = module - .namespace(context) - .get(js_string!("default"), context)?; + let default = module.namespace(context).get(js_str!("default"), context)?; // `default` should contain the result of our calculations. let default = default @@ -163,14 +160,14 @@ fn main() -> JsResult<()> { .get(0, context)? .as_string() .ok_or_else(|| JsNativeError::typ().with_message("array element was not a string"))?, - utf16!("aGVsbG8=") + &js_str!("aGVsbG8=") ); assert_eq!( default .get(1, context)? .as_string() .ok_or_else(|| JsNativeError::typ().with_message("array element was not a string"))?, - utf16!("d29ybGQ=") + &js_str!("d29ybGQ=") ); Ok(()) diff --git a/examples/src/bin/modulehandler.rs b/examples/src/bin/modulehandler.rs index d77198934a3..87660f524de 100644 --- a/examples/src/bin/modulehandler.rs +++ b/examples/src/bin/modulehandler.rs @@ -2,7 +2,7 @@ // the require/module.exports pattern use boa_engine::{ - js_string, native_function::NativeFunction, prelude::JsObject, property::Attribute, Context, + js_str, native_function::NativeFunction, prelude::JsObject, property::Attribute, Context, JsArgs, JsNativeError, JsResult, JsValue, Source, }; use boa_runtime::Console; @@ -13,7 +13,7 @@ fn add_runtime(context: &mut Context) { // We first add the `console` object, to be able to call `console.log()`. let console = Console::init(context); context - .register_global_property(js_string!(Console::NAME), console, Attribute::all()) + .register_global_property(Console::NAME, console, Attribute::all()) .expect("the console builtin shouldn't exist"); } @@ -28,23 +28,19 @@ fn main() -> Result<(), Box> { add_runtime(&mut ctx); // Adding custom implementation that mimics 'require' - ctx.register_global_callable( - js_string!("require"), - 0, - NativeFunction::from_fn_ptr(require), - )?; + ctx.register_global_callable("require".into(), 0, NativeFunction::from_fn_ptr(require))?; // Adding custom object that mimics 'module.exports' let moduleobj = JsObject::default(); moduleobj.set( - js_string!("exports"), - JsValue::from(js_string!(" ")), + js_str!("exports"), + JsValue::from(js_str!(" ")), false, &mut ctx, )?; ctx.register_global_property( - js_string!("module"), + js_str!("module"), JsValue::from(moduleobj), Attribute::default(), )?; @@ -72,9 +68,9 @@ fn require(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult Result<(), Box> { // We can access the full namespace of the module with all its exports. let namespace = module.namespace(context); - let result = namespace.get(js_string!("result"), context)?; + let result = namespace.get(js_str!("result"), context)?; println!("result = {}", result.display()); - assert_eq!( - namespace.get(js_string!("result"), context)?, - JsValue::from(5) - ); + assert_eq!(namespace.get(js_str!("result"), context)?, JsValue::from(5)); let mix = namespace - .get(js_string!("mix"), context)? + .get(js_str!("mix"), context)? .as_callable() .cloned() .ok_or_else(|| JsNativeError::typ().with_message("mix export wasn't a function!"))?; diff --git a/examples/src/bin/synthetic.rs b/examples/src/bin/synthetic.rs index ffab2c6ba47..80511e9e046 100644 --- a/examples/src/bin/synthetic.rs +++ b/examples/src/bin/synthetic.rs @@ -9,7 +9,8 @@ use boa_engine::builtins::promise::PromiseState; use boa_engine::module::{SimpleModuleLoader, SyntheticModuleInitializer}; use boa_engine::object::FunctionObjectBuilder; use boa_engine::{ - js_string, Context, JsArgs, JsError, JsNativeError, JsValue, Module, NativeFunction, Source, + js_str, js_string, Context, JsArgs, JsError, JsNativeError, JsValue, Module, NativeFunction, + Source, }; fn main() -> Result<(), Box> { @@ -77,17 +78,14 @@ fn main() -> Result<(), Box> { // We can access the full namespace of the module with all its exports. let namespace = module.namespace(context); - let result = namespace.get(js_string!("result"), context)?; + let result = namespace.get(js_str!("result"), context)?; println!("result = {}", result.display()); - assert_eq!( - namespace.get(js_string!("result"), context)?, - JsValue::from(5) - ); + assert_eq!(namespace.get(js_str!("result"), context)?, JsValue::from(5)); let mix = namespace - .get(js_string!("mix"), context)? + .get(js_str!("mix"), context)? .as_callable() .cloned() .ok_or_else(|| JsNativeError::typ().with_message("mix export wasn't a function!"))?; @@ -111,7 +109,7 @@ fn create_operations_module(context: &mut Context) -> Module { }), ) .length(2) - .name(js_string!("sum")) + .name("sum") .build(); let sub = FunctionObjectBuilder::new( context.realm(), @@ -120,7 +118,7 @@ fn create_operations_module(context: &mut Context) -> Module { }), ) .length(2) - .name(js_string!("sub")) + .name("sub") .build(); let mult = FunctionObjectBuilder::new( context.realm(), @@ -129,7 +127,7 @@ fn create_operations_module(context: &mut Context) -> Module { }), ) .length(2) - .name(js_string!("mult")) + .name("mult") .build(); let div = FunctionObjectBuilder::new( context.realm(), @@ -138,7 +136,7 @@ fn create_operations_module(context: &mut Context) -> Module { }), ) .length(2) - .name(js_string!("div")) + .name("div") .build(); let sqrt = FunctionObjectBuilder::new( context.realm(), @@ -148,7 +146,7 @@ fn create_operations_module(context: &mut Context) -> Module { }), ) .length(1) - .name(js_string!("sqrt")) + .name("sqrt") .build(); Module::synthetic( diff --git a/tests/tester/src/exec/js262.rs b/tests/tester/src/exec/js262.rs index 62cedb9be52..6e44571f14c 100644 --- a/tests/tester/src/exec/js262.rs +++ b/tests/tester/src/exec/js262.rs @@ -256,7 +256,7 @@ fn agent_obj(handles: WorkerHandles, context: &mut Context) -> JsObject { return Ok(JsValue::null()); }; - Ok(js_string!(msg).into()) + Ok(js_string!(&msg[..]).into()) }) }; diff --git a/tests/tester/src/exec/mod.rs b/tests/tester/src/exec/mod.rs index a8c7f177e35..e5ce0553cfd 100644 --- a/tests/tester/src/exec/mod.rs +++ b/tests/tester/src/exec/mod.rs @@ -8,7 +8,7 @@ use crate::{ }; use boa_engine::{ builtins::promise::PromiseState, - js_string, + js_str, js_string, module::{Module, SimpleModuleLoader}, native_function::NativeFunction, object::FunctionObjectBuilder, @@ -535,11 +535,7 @@ impl Test { if console { let console = boa_runtime::Console::init(&mut context); context - .register_global_property( - js_string!(boa_runtime::Console::NAME), - console, - Attribute::all(), - ) + .register_global_property(boa_runtime::Console::NAME, console, Attribute::all()) .expect("the console builtin shouldn't exist"); } @@ -601,10 +597,10 @@ fn is_error_type(error: &JsError, target_type: ErrorType, context: &mut Context) .as_opaque() .expect("try_native cannot fail if e is not opaque") .as_object() - .and_then(|o| o.get(js_string!("constructor"), context).ok()) + .and_then(|o| o.get(js_str!("constructor"), context).ok()) .as_ref() .and_then(JsValue::as_object) - .and_then(|o| o.get(js_string!("name"), context).ok()) + .and_then(|o| o.get(js_str!("name"), context).ok()) .as_ref() .and_then(JsValue::as_string) .is_some_and(|s| s == target_type.as_str()); @@ -647,7 +643,7 @@ fn register_print_fn(context: &mut Context, async_result: AsyncResult) { context .register_global_property( - js_string!("print"), + js_str!("print"), js_function, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) From 5af3f7c07915d707b0dcfab10971d06b1c4fc86d Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Thu, 2 May 2024 22:12:31 +0200 Subject: [PATCH 016/212] Fix Rust 1.78.0 Clippy lints (#3838) --- core/ast/src/declaration/import.rs | 2 +- core/ast/src/expression/literal/mod.rs | 2 +- core/ast/src/expression/operator/unary/op.rs | 2 +- core/ast/src/operations.rs | 9 +++--- core/ast/src/pattern.rs | 32 +++++++++---------- core/ast/src/visitor.rs | 4 +-- core/engine/src/bigint.rs | 2 +- core/engine/src/builtins/atomics/futex.rs | 4 +-- core/engine/src/builtins/builder.rs | 18 +++++------ .../builtins/intl/number_format/options.rs | 20 ++++++------ core/engine/src/builtins/options.rs | 2 +- core/engine/src/builtins/promise/mod.rs | 10 +++--- .../src/builtins/temporal/calendar/mod.rs | 5 ++- .../src/builtins/typed_array/builtin.rs | 10 +++--- .../engine/src/builtins/typed_array/object.rs | 6 ++-- core/engine/src/bytecompiler/mod.rs | 2 +- core/engine/src/module/mod.rs | 2 +- core/engine/src/object/builtins/jsarray.rs | 4 +-- .../src/object/builtins/jsarraybuffer.rs | 4 +-- core/engine/src/object/builtins/jsdataview.rs | 4 +-- core/engine/src/object/builtins/jsdate.rs | 7 +--- core/engine/src/object/builtins/jsfunction.rs | 9 ++---- .../engine/src/object/builtins/jsgenerator.rs | 8 ++--- core/engine/src/object/builtins/jsmap.rs | 4 +-- .../src/object/builtins/jsmap_iterator.rs | 9 ++---- core/engine/src/object/builtins/jspromise.rs | 4 +-- core/engine/src/object/builtins/jsproxy.rs | 4 +-- core/engine/src/object/builtins/jsregexp.rs | 4 +-- core/engine/src/object/builtins/jsset.rs | 4 +-- .../src/object/builtins/jsset_iterator.rs | 9 ++---- .../object/builtins/jssharedarraybuffer.rs | 9 ++---- .../src/object/builtins/jstypedarray.rs | 4 +-- core/engine/src/object/jsobject.rs | 12 ++++--- core/engine/src/object/mod.rs | 6 ++-- core/engine/src/object/property_map.rs | 6 ++-- core/engine/src/script.rs | 2 +- core/engine/src/small_map/mod.rs | 26 +++++++-------- core/engine/src/value/mod.rs | 2 +- core/engine/src/vm/call_frame/mod.rs | 2 +- core/engine/src/vm/code_block.rs | 2 +- core/engine/src/vm/opcode/await/mod.rs | 2 +- core/gc/src/cell.rs | 4 +-- core/gc/src/lib.rs | 6 ++-- core/parser/src/error/mod.rs | 1 + core/parser/src/lexer/cursor.rs | 2 +- core/parser/src/lexer/regex.rs | 24 +++++++------- core/parser/src/lexer/string.rs | 1 + core/parser/src/lexer/token.rs | 6 ++-- core/parser/src/parser/mod.rs | 2 +- .../declaration/hoistable/class_decl/mod.rs | 14 +++----- core/parser/src/parser/statement/mod.rs | 12 ++++--- core/runtime/src/console/mod.rs | 10 +++--- tests/tester/src/results.rs | 2 +- 53 files changed, 156 insertions(+), 207 deletions(-) diff --git a/core/ast/src/declaration/import.rs b/core/ast/src/declaration/import.rs index 3c8ef43c021..103f79f637a 100644 --- a/core/ast/src/declaration/import.rs +++ b/core/ast/src/declaration/import.rs @@ -25,7 +25,7 @@ use super::ModuleSpecifier; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq, Eq)] pub enum ImportKind { - /// Default (`import defaultName from "module-name"`) or unnamed (`import "module-name"). + /// Default (`import defaultName from "module-name"`) or unnamed (`import "module-name"`). DefaultOrUnnamed, /// Namespaced import (`import * as name from "module-name"`). Namespaced { diff --git a/core/ast/src/expression/literal/mod.rs b/core/ast/src/expression/literal/mod.rs index 9694af4eca5..78414d7e5d5 100644 --- a/core/ast/src/expression/literal/mod.rs +++ b/core/ast/src/expression/literal/mod.rs @@ -74,7 +74,7 @@ pub enum Literal { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals Int(i32), - /// BigInt provides a way to represent whole numbers larger than the largest number ECMAScript + /// `BigInt` provides a way to represent whole numbers larger than the largest number ECMAScript /// can reliably represent with the `Number` primitive. /// /// More information: diff --git a/core/ast/src/expression/operator/unary/op.rs b/core/ast/src/expression/operator/unary/op.rs index 12abb5c0e67..78b0659f12a 100644 --- a/core/ast/src/expression/operator/unary/op.rs +++ b/core/ast/src/expression/operator/unary/op.rs @@ -67,7 +67,7 @@ pub enum UnaryOp { /// Syntax: `~x` /// /// NOT `a` yields the inverted value (or one's complement) of `a`. - /// Bitwise NOTing any number x yields -(x + 1). For example, ~-5 yields 4. + /// Bitwise `NOT`ing any number x yields -(x + 1). For example, ~-5 yields 4. /// /// More information: /// - [ECMAScript reference][spec] diff --git a/core/ast/src/operations.rs b/core/ast/src/operations.rs index 09d7634d06b..d3bb506746f 100644 --- a/core/ast/src/operations.rs +++ b/core/ast/src/operations.rs @@ -57,7 +57,7 @@ pub enum ContainsSymbol { This, /// A method definition. MethodDefinition, - /// The BindingIdentifier "eval" or "arguments". + /// The `BindingIdentifier` "eval" or "arguments". EvalOrArguments, } @@ -1328,9 +1328,10 @@ where fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow { match node { - crate::ModuleItem::ImportDeclaration(_) - | crate::ModuleItem::ExportDeclaration(_) => ControlFlow::Continue(()), - crate::ModuleItem::StatementListItem(node) => self.visit_statement_list_item(node), + ModuleItem::ImportDeclaration(_) | ModuleItem::ExportDeclaration(_) => { + ControlFlow::Continue(()) + } + ModuleItem::StatementListItem(node) => self.visit_statement_list_item(node), } } } diff --git a/core/ast/src/pattern.rs b/core/ast/src/pattern.rs index 8a6b19da46e..c37c5ef9cf7 100644 --- a/core/ast/src/pattern.rs +++ b/core/ast/src/pattern.rs @@ -276,7 +276,7 @@ impl VisitWith for ArrayPattern { #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq)] pub enum ObjectPatternElement { - /// SingleName represents one of the following properties: + /// `SingleName` represents one of the following properties: /// /// - `SingleName` with an identifier and an optional default initializer. /// - `BindingProperty` with an property name and a `SingleNameBinding` as the `BindingElement`. @@ -296,7 +296,7 @@ pub enum ObjectPatternElement { default_init: Option, }, - /// RestProperty represents a `BindingRestProperty` with an identifier. + /// `RestProperty` represents a `BindingRestProperty` with an identifier. /// /// It also includes a list of the property keys that should be excluded from the rest, /// because they where already assigned. @@ -312,10 +312,10 @@ pub enum ObjectPatternElement { excluded_keys: Vec, }, - /// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement. + /// `AssignmentGetField` represents an `AssignmentProperty` with an expression field member expression `AssignmentElement`. /// - /// Note: According to the spec this is not part of an ObjectBindingPattern. - /// This is only used when a object literal is used to cover an AssignmentPattern. + /// Note: According to the spec this is not part of an `ObjectBindingPattern`. + /// This is only used when a object literal is used to cover an `AssignmentPattern`. /// /// More information: /// - [ECMAScript reference][spec] @@ -330,10 +330,10 @@ pub enum ObjectPatternElement { default_init: Option, }, - /// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget. + /// `AssignmentRestProperty` represents a rest property with a `DestructuringAssignmentTarget`. /// - /// Note: According to the spec this is not part of an ObjectBindingPattern. - /// This is only used when a object literal is used to cover an AssignmentPattern. + /// Note: According to the spec this is not part of an `ObjectBindingPattern`. + /// This is only used when a object literal is used to cover an `AssignmentPattern`. /// /// More information: /// - [ECMAScript reference][spec] @@ -587,7 +587,7 @@ pub enum ArrayPatternElement { /// [spec1]: https://tc39.es/ecma262/#prod-Elision Elision, - /// SingleName represents a `SingleName` with an identifier and an optional default initializer. + /// `SingleName` represents a `SingleName` with an identifier and an optional default initializer. /// /// More information: /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1] @@ -600,9 +600,9 @@ pub enum ArrayPatternElement { default_init: Option, }, - /// PropertyAccess represents a binding with a property accessor. + /// `PropertyAccess` represents a binding with a property accessor. /// - /// Note: According to the spec this is not part of an ArrayBindingPattern. + /// Note: According to the spec this is not part of an `ArrayBindingPattern`. /// This is only used when a array literal is used as the left-hand-side of an assignment expression. /// /// More information: @@ -618,7 +618,7 @@ pub enum ArrayPatternElement { /// Pattern represents a `Pattern` in an `Element` of an array pattern. /// - /// The pattern and the optional default initializer are both stored in the DeclarationPattern. + /// The pattern and the optional default initializer are both stored in the `DeclarationPattern`. /// /// More information: /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1] @@ -631,7 +631,7 @@ pub enum ArrayPatternElement { default_init: Option, }, - /// SingleNameRest represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern. + /// `SingleNameRest` represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern. /// /// More information: /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] @@ -642,9 +642,9 @@ pub enum ArrayPatternElement { ident: Identifier, }, - /// PropertyAccess represents a rest (spread operator) with a property accessor. + /// `PropertyAccess` represents a rest (spread operator) with a property accessor. /// - /// Note: According to the spec this is not part of an ArrayBindingPattern. + /// Note: According to the spec this is not part of an `ArrayBindingPattern`. /// This is only used when a array literal is used as the left-hand-side of an assignment expression. /// /// More information: @@ -656,7 +656,7 @@ pub enum ArrayPatternElement { access: PropertyAccess, }, - /// PatternRest represents a `Pattern` in a `RestElement` of an array pattern. + /// `PatternRest` represents a `Pattern` in a `RestElement` of an array pattern. /// /// More information: /// - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1] diff --git a/core/ast/src/visitor.rs b/core/ast/src/visitor.rs index d1cd1153959..ded44fe7b80 100644 --- a/core/ast/src/visitor.rs +++ b/core/ast/src/visitor.rs @@ -645,13 +645,13 @@ impl VisitWith for Sym { where V: Visitor<'a>, { - core::ops::ControlFlow::Continue(()) + ControlFlow::Continue(()) } fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow where V: VisitorMut<'a>, { - core::ops::ControlFlow::Continue(()) + ControlFlow::Continue(()) } } diff --git a/core/engine/src/bigint.rs b/core/engine/src/bigint.rs index 87584fa2b9c..ed44e8083a2 100644 --- a/core/engine/src/bigint.rs +++ b/core/engine/src/bigint.rs @@ -451,7 +451,7 @@ pub struct TryFromF64Error; impl Display for TryFromF64Error { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Could not convert f64 value to a BigInt type") } } diff --git a/core/engine/src/builtins/atomics/futex.rs b/core/engine/src/builtins/atomics/futex.rs index b265a6aa68e..f8be6d6b2ca 100644 --- a/core/engine/src/builtins/atomics/futex.rs +++ b/core/engine/src/builtins/atomics/futex.rs @@ -242,8 +242,8 @@ impl FutexWaiters { let addr = unsafe { (*node).addr }; let mut wl = match self.waiters.entry(addr) { - crate::small_map::Entry::Occupied(wl) => wl, - crate::small_map::Entry::Vacant(_) => return, + Entry::Occupied(wl) => wl, + Entry::Vacant(_) => return, }; // SAFETY: `node` must be inside the wait list associated with `node.addr`. diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index d02627a9911..c87b9ab7290 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -210,11 +210,10 @@ impl BuiltInConstructorWithPrototype<'_> { .length(length) .build(); - debug_assert!(self + debug_assert!(!self .object_property_table .map - .get(&binding.binding) - .is_none()); + .contains_key(&binding.binding)); self.object_property_table.insert( binding.binding, SlotAttributes::WRITABLE | SlotAttributes::CONFIGURABLE, @@ -231,7 +230,7 @@ impl BuiltInConstructorWithPrototype<'_> { { let key = key.into(); - debug_assert!(self.object_property_table.map.get(&key).is_none()); + debug_assert!(!self.object_property_table.map.contains_key(&key)); self.object_property_table .insert(key, SlotAttributes::from_bits_truncate(attribute.bits())); self.object_storage.push(value.into()); @@ -256,7 +255,7 @@ impl BuiltInConstructorWithPrototype<'_> { let key = key.into(); - debug_assert!(self.object_property_table.map.get(&key).is_none()); + debug_assert!(!self.object_property_table.map.contains_key(&key)); self.object_property_table.insert(key, attributes); self.object_storage.extend([ get.map(JsValue::new).unwrap_or_default(), @@ -289,11 +288,10 @@ impl BuiltInConstructorWithPrototype<'_> { .length(length) .build(); - debug_assert!(self + debug_assert!(!self .prototype_property_table .map - .get(&binding.binding) - .is_none()); + .contains_key(&binding.binding)); self.prototype_property_table.insert( binding.binding, SlotAttributes::WRITABLE | SlotAttributes::CONFIGURABLE, @@ -310,7 +308,7 @@ impl BuiltInConstructorWithPrototype<'_> { { let key = key.into(); - debug_assert!(self.prototype_property_table.map.get(&key).is_none()); + debug_assert!(!self.prototype_property_table.map.contains_key(&key)); self.prototype_property_table .insert(key, SlotAttributes::from_bits_truncate(attribute.bits())); self.prototype_storage.push(value.into()); @@ -335,7 +333,7 @@ impl BuiltInConstructorWithPrototype<'_> { let key = key.into(); - debug_assert!(self.prototype_property_table.map.get(&key).is_none()); + debug_assert!(!self.prototype_property_table.map.contains_key(&key)); self.prototype_property_table.insert(key, attributes); self.prototype_storage.extend([ get.map(JsValue::new).unwrap_or_default(), diff --git a/core/engine/src/builtins/intl/number_format/options.rs b/core/engine/src/builtins/intl/number_format/options.rs index b5e85ce4d48..ccf33a328f7 100644 --- a/core/engine/src/builtins/intl/number_format/options.rs +++ b/core/engine/src/builtins/intl/number_format/options.rs @@ -37,7 +37,7 @@ impl Style { pub(crate) struct ParseStyleError; impl fmt::Display for ParseStyleError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid style option") } } @@ -82,7 +82,7 @@ impl CurrencyDisplay { pub(crate) struct ParseCurrencyDisplayError; impl fmt::Display for ParseCurrencyDisplayError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid currencyDisplay option") } } @@ -123,7 +123,7 @@ impl CurrencySign { pub(crate) struct ParseCurrencySignError; impl fmt::Display for ParseCurrencySignError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid currencySign option") } } @@ -164,7 +164,7 @@ impl UnitDisplay { pub(crate) struct ParseUnitDisplayError; impl fmt::Display for ParseUnitDisplayError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid unitDisplay option") } } @@ -205,7 +205,7 @@ impl Currency { pub(crate) struct ParseCurrencyError; impl fmt::Display for ParseCurrencyError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid currency") } } @@ -267,7 +267,7 @@ impl Unit { pub(crate) struct ParseUnitError; impl fmt::Display for ParseUnitError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid unit") } } @@ -995,7 +995,7 @@ impl CompactDisplay { pub(crate) struct ParseCompactDisplayError; impl fmt::Display for ParseCompactDisplayError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid compactDisplay option") } } @@ -1038,7 +1038,7 @@ impl NotationKind { pub(crate) struct ParseNotationKindError; impl fmt::Display for ParseNotationKindError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid notation option") } } @@ -1100,7 +1100,7 @@ impl RoundingPriority { pub(crate) struct ParseRoundingPriorityError; impl fmt::Display for ParseRoundingPriorityError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid rounding priority") } } @@ -1140,7 +1140,7 @@ impl TrailingZeroDisplay { pub(crate) struct ParseTrailingZeroDisplayError; impl fmt::Display for ParseTrailingZeroDisplayError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid trailing zero display option") } } diff --git a/core/engine/src/builtins/options.rs b/core/engine/src/builtins/options.rs index 6b6777a0cf5..368566e0686 100644 --- a/core/engine/src/builtins/options.rs +++ b/core/engine/src/builtins/options.rs @@ -160,7 +160,7 @@ impl RoundingMode { pub(crate) struct ParseRoundingModeError; impl fmt::Display for ParseRoundingModeError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("provided string was not a valid rounding mode") } } diff --git a/core/engine/src/builtins/promise/mod.rs b/core/engine/src/builtins/promise/mod.rs index 8d1b45beb67..b3fc4d103fa 100644 --- a/core/engine/src/builtins/promise/mod.rs +++ b/core/engine/src/builtins/promise/mod.rs @@ -664,10 +664,8 @@ impl Promise { // iii. If remainingElementsCount.[[Value]] is 0, then if remaining_elements_count.get() == 0 { // 1. Let valuesArray be CreateArrayFromList(values). - let values_array = crate::builtins::Array::create_array_from_list( - values.borrow().iter().cloned(), - context, - ); + let values_array = + Array::create_array_from_list(values.borrow().iter().cloned(), context); // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »). result_capability.functions.resolve.call( @@ -733,7 +731,7 @@ impl Promise { // 10. If remainingElementsCount.[[Value]] is 0, then if captures.remaining_elements_count.get() == 0 { // a. Let valuesArray be CreateArrayFromList(values). - let values_array = crate::builtins::Array::create_array_from_list( + let values_array = Array::create_array_from_list( captures.values.borrow().as_slice().iter().cloned(), context, ); @@ -892,7 +890,7 @@ impl Promise { // iii. If remainingElementsCount.[[Value]] is 0, then if remaining_elements_count.get() == 0 { // 1. Let valuesArray be CreateArrayFromList(values). - let values_array = crate::builtins::Array::create_array_from_list( + let values_array = Array::create_array_from_list( values.borrow().as_slice().iter().cloned(), context, ); diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index e479ec9881d..2da4335a92b 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -915,11 +915,10 @@ impl Calendar { let additional_fields = args.get_or_undefined(1).to_object(context)?; // 3. Let fieldsCopy be ? SnapshotOwnProperties(? ToObject(fields), null, « », « undefined »). - let fields_copy = temporal::fields::object_to_temporal_fields(&fields, context)?; + let fields_copy = fields::object_to_temporal_fields(&fields, context)?; // 4. Let additionalFieldsCopy be ? SnapshotOwnProperties(? ToObject(additionalFields), null, « », « undefined »). - let additional_copy = - temporal::fields::object_to_temporal_fields(&additional_fields, context)?; + let additional_copy = fields::object_to_temporal_fields(&additional_fields, context)?; // Custom Calendars override the `fields` method. if let CalendarSlot::Protocol(proto) = &calendar.slot { diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 36b25648c55..9119edd361c 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -1,6 +1,6 @@ use std::{ cmp::{self, min}, - sync::atomic::{self, Ordering}, + sync::atomic::Ordering, }; use boa_macros::{js_str, utf16}; @@ -1896,7 +1896,7 @@ impl BuiltinTypedArray { let value = unsafe { src_buffer .subslice(src_byte_index..) - .get_value(src_type, atomic::Ordering::Relaxed) + .get_value(src_type, Ordering::Relaxed) }; let value = JsValue::from(value); @@ -1910,7 +1910,7 @@ impl BuiltinTypedArray { unsafe { target_buffer .subslice_mut(target_byte_index..) - .set_value(value, atomic::Ordering::Relaxed); + .set_value(value, Ordering::Relaxed); } // iii. Set srcByteIndex to srcByteIndex + srcElementSize. @@ -2913,7 +2913,7 @@ impl BuiltinTypedArray { let value = unsafe { src_data .subslice(src_byte_index..) - .get_value(src_type, atomic::Ordering::Relaxed) + .get_value(src_type, Ordering::Relaxed) }; let value = JsValue::from(value); @@ -2928,7 +2928,7 @@ impl BuiltinTypedArray { // bytes available, which makes `target_byte_index` always in-bounds. unsafe { data.subslice_mut(target_byte_index..) - .set_value(value, atomic::Ordering::Relaxed); + .set_value(value, Ordering::Relaxed); } // iii. Set srcByteIndex to srcByteIndex + srcElementSize. diff --git a/core/engine/src/builtins/typed_array/object.rs b/core/engine/src/builtins/typed_array/object.rs index cdeb7491ed1..dc59a60427a 100644 --- a/core/engine/src/builtins/typed_array/object.rs +++ b/core/engine/src/builtins/typed_array/object.rs @@ -1,6 +1,6 @@ //! This module implements the `TypedArray` exotic object. -use std::sync::atomic::{self, Ordering}; +use std::sync::atomic::Ordering; use crate::{ builtins::{array_buffer::BufferObject, Number}, @@ -601,7 +601,7 @@ fn typed_array_get_element(obj: &JsObject, index: f64) -> Option { let value = unsafe { buffer .subslice(byte_index..) - .get_value(elem_type, atomic::Ordering::Relaxed) + .get_value(elem_type, Ordering::Relaxed) }; Some(value.into()) @@ -657,7 +657,7 @@ pub(crate) fn typed_array_set_element( unsafe { buffer .subslice_mut(byte_index..) - .set_value(value, atomic::Ordering::Relaxed); + .set_value(value, Ordering::Relaxed); } // 4. Return unused. diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index fd30107566e..c1bbe380f99 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -268,7 +268,7 @@ pub struct ByteCompiler<'ctx> { pub(crate) register_count: u32, - /// \[\[ThisMode\]\] + /// `[[ThisMode]]` pub(crate) this_mode: ThisMode, /// Parameters passed to this function. diff --git a/core/engine/src/module/mod.rs b/core/engine/src/module/mod.rs index 5190930f542..289e41614d8 100644 --- a/core/engine/src/module/mod.rs +++ b/core/engine/src/module/mod.rs @@ -159,7 +159,7 @@ impl Module { context: &mut Context, ) -> JsResult { let _timer = Profiler::global().start_event("Module parsing", "Main"); - let path = src.path().map(std::path::Path::to_path_buf); + let path = src.path().map(Path::to_path_buf); let mut parser = Parser::new(src); parser.set_identifier(context.next_parser_identifier()); let module = parser.parse_module(context.interner_mut())?; diff --git a/core/engine/src/object/builtins/jsarray.rs b/core/engine/src/object/builtins/jsarray.rs index ae8d2b8acab..7b7aff8d0e7 100644 --- a/core/engine/src/object/builtins/jsarray.rs +++ b/core/engine/src/object/builtins/jsarray.rs @@ -2,7 +2,7 @@ use crate::{ builtins::Array, error::JsNativeError, - object::{JsFunction, JsObject, JsObjectType}, + object::{JsFunction, JsObject}, value::{IntoOrUndefined, TryFromJs}, Context, JsResult, JsString, JsValue, }; @@ -437,8 +437,6 @@ impl Deref for JsArray { } } -impl JsObjectType for JsArray {} - impl TryFromJs for JsArray { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsarraybuffer.rs b/core/engine/src/object/builtins/jsarraybuffer.rs index 259a4c93e67..684e69f3307 100644 --- a/core/engine/src/object/builtins/jsarraybuffer.rs +++ b/core/engine/src/object/builtins/jsarraybuffer.rs @@ -3,7 +3,7 @@ use crate::{ builtins::array_buffer::ArrayBuffer, context::intrinsics::StandardConstructors, error::JsNativeError, - object::{internal_methods::get_prototype_from_constructor, JsObject, JsObjectType, Object}, + object::{internal_methods::get_prototype_from_constructor, JsObject, Object}, value::TryFromJs, Context, JsResult, JsValue, }; @@ -300,8 +300,6 @@ impl Deref for JsArrayBuffer { } } -impl JsObjectType for JsArrayBuffer {} - impl TryFromJs for JsArrayBuffer { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsdataview.rs b/core/engine/src/object/builtins/jsdataview.rs index c1194dc013e..fdcf05b375b 100644 --- a/core/engine/src/object/builtins/jsdataview.rs +++ b/core/engine/src/object/builtins/jsdataview.rs @@ -1,7 +1,7 @@ //! A Rust API wrapper for Boa's `DataView` Builtin ECMAScript Object use crate::{ builtins::{array_buffer::BufferObject, DataView}, - object::{JsArrayBuffer, JsObject, JsObjectType}, + object::{JsArrayBuffer, JsObject}, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, }; @@ -527,8 +527,6 @@ impl Deref for JsDataView { } } -impl JsObjectType for JsDataView {} - impl TryFromJs for JsDataView { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsdate.rs b/core/engine/src/object/builtins/jsdate.rs index 3dd6c341561..c647ac5704e 100644 --- a/core/engine/src/object/builtins/jsdate.rs +++ b/core/engine/src/object/builtins/jsdate.rs @@ -1,10 +1,7 @@ //! A Rust API wrapper for Boa's `Date` ECMAScript Builtin Object. use crate::{ - builtins::Date, - object::{JsObject, JsObjectType}, - value::TryFromJs, - Context, JsNativeError, JsResult, JsValue, + builtins::Date, object::JsObject, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; use std::ops::Deref; @@ -587,8 +584,6 @@ impl Deref for JsDate { } } -impl JsObjectType for JsDate {} - impl TryFromJs for JsDate { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsfunction.rs b/core/engine/src/object/builtins/jsfunction.rs index 406fe958cb6..61c23f1f4cd 100644 --- a/core/engine/src/object/builtins/jsfunction.rs +++ b/core/engine/src/object/builtins/jsfunction.rs @@ -1,10 +1,7 @@ //! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object use crate::{ - builtins::function::ConstructorKind, - native_function::NativeFunctionObject, - object::{JsObject, JsObjectType}, - value::TryFromJs, - Context, JsNativeError, JsResult, JsValue, NativeFunction, + builtins::function::ConstructorKind, native_function::NativeFunctionObject, object::JsObject, + value::TryFromJs, Context, JsNativeError, JsResult, JsValue, NativeFunction, }; use boa_gc::{Finalize, Trace}; use std::ops::Deref; @@ -74,8 +71,6 @@ impl Deref for JsFunction { } } -impl JsObjectType for JsFunction {} - impl TryFromJs for JsFunction { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsgenerator.rs b/core/engine/src/object/builtins/jsgenerator.rs index 0dd03083c52..33a6ba6bb5c 100644 --- a/core/engine/src/object/builtins/jsgenerator.rs +++ b/core/engine/src/object/builtins/jsgenerator.rs @@ -1,9 +1,7 @@ //! A Rust API wrapper for Boa's `Generator` Builtin ECMAScript Object use crate::{ - builtins::generator::Generator, - object::{JsObject, JsObjectType}, - value::TryFromJs, - Context, JsNativeError, JsResult, JsValue, + builtins::generator::Generator, object::JsObject, value::TryFromJs, Context, JsNativeError, + JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -83,8 +81,6 @@ impl Deref for JsGenerator { } } -impl JsObjectType for JsGenerator {} - impl TryFromJs for JsGenerator { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsmap.rs b/core/engine/src/object/builtins/jsmap.rs index 4ce123e6028..d34868c56e5 100644 --- a/core/engine/src/object/builtins/jsmap.rs +++ b/core/engine/src/object/builtins/jsmap.rs @@ -3,7 +3,7 @@ use crate::{ builtins::map::{add_entries_from_iterable, ordered_map::OrderedMap}, builtins::Map, error::JsNativeError, - object::{JsFunction, JsMapIterator, JsObject, JsObjectType}, + object::{JsFunction, JsMapIterator, JsObject}, value::TryFromJs, Context, JsResult, JsValue, }; @@ -438,8 +438,6 @@ impl Deref for JsMap { } } -impl JsObjectType for JsMap {} - impl TryFromJs for JsMap { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsmap_iterator.rs b/core/engine/src/object/builtins/jsmap_iterator.rs index 9b07148b3f3..d6fa7a82faf 100644 --- a/core/engine/src/object/builtins/jsmap_iterator.rs +++ b/core/engine/src/object/builtins/jsmap_iterator.rs @@ -1,10 +1,7 @@ //! A Rust API wrapper for Boa's `MapIterator` Builtin ECMAScript Object use crate::{ - builtins::map::MapIterator, - error::JsNativeError, - object::{JsObject, JsObjectType}, - value::TryFromJs, - Context, JsResult, JsValue, + builtins::map::MapIterator, error::JsNativeError, object::JsObject, value::TryFromJs, Context, + JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; @@ -58,8 +55,6 @@ impl Deref for JsMapIterator { } } -impl JsObjectType for JsMapIterator {} - impl TryFromJs for JsMapIterator { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index cf2358db79e..7f610f670d4 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -9,7 +9,7 @@ use crate::{ Promise, }, job::NativeJob, - object::{JsObject, JsObjectType}, + object::JsObject, value::TryFromJs, Context, JsArgs, JsError, JsNativeError, JsResult, JsValue, NativeFunction, }; @@ -1088,8 +1088,6 @@ impl std::ops::Deref for JsPromise { } } -impl JsObjectType for JsPromise {} - impl TryFromJs for JsPromise { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsproxy.rs b/core/engine/src/object/builtins/jsproxy.rs index bb149852e35..d96e9e7beb1 100644 --- a/core/engine/src/object/builtins/jsproxy.rs +++ b/core/engine/src/object/builtins/jsproxy.rs @@ -4,7 +4,7 @@ use crate::{ builtins::Proxy, js_str, native_function::{NativeFunction, NativeFunctionPointer}, - object::{FunctionObjectBuilder, JsObject, JsObjectType}, + object::{FunctionObjectBuilder, JsObject}, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, }; @@ -70,8 +70,6 @@ impl std::ops::Deref for JsProxy { } } -impl JsObjectType for JsProxy {} - impl TryFromJs for JsProxy { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsregexp.rs b/core/engine/src/object/builtins/jsregexp.rs index 35b00809224..fef40e6e0cc 100644 --- a/core/engine/src/object/builtins/jsregexp.rs +++ b/core/engine/src/object/builtins/jsregexp.rs @@ -1,7 +1,7 @@ //! A Rust API wrapper for Boa's `RegExp` Builtin ECMAScript Object use crate::{ builtins::RegExp, - object::{JsArray, JsObject, JsObjectType}, + object::{JsArray, JsObject}, value::TryFromJs, Context, JsNativeError, JsResult, JsValue, }; @@ -270,8 +270,6 @@ impl Deref for JsRegExp { } } -impl JsObjectType for JsRegExp {} - impl TryFromJs for JsRegExp { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsset.rs b/core/engine/src/object/builtins/jsset.rs index 6fcf8f64155..d886c15c8b7 100644 --- a/core/engine/src/object/builtins/jsset.rs +++ b/core/engine/src/object/builtins/jsset.rs @@ -6,7 +6,7 @@ use boa_gc::{Finalize, Trace}; use crate::{ builtins::{set::ordered_set::OrderedSet, Set}, error::JsNativeError, - object::{JsFunction, JsObject, JsObjectType, JsSetIterator}, + object::{JsFunction, JsObject, JsSetIterator}, value::TryFromJs, Context, JsResult, JsValue, }; @@ -185,8 +185,6 @@ impl Deref for JsSet { } } -impl JsObjectType for JsSet {} - impl TryFromJs for JsSet { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jsset_iterator.rs b/core/engine/src/object/builtins/jsset_iterator.rs index c11753242c7..124b987f334 100644 --- a/core/engine/src/object/builtins/jsset_iterator.rs +++ b/core/engine/src/object/builtins/jsset_iterator.rs @@ -4,11 +4,8 @@ use std::ops::Deref; use boa_gc::{Finalize, Trace}; use crate::{ - builtins::set::SetIterator, - error::JsNativeError, - object::{JsObject, JsObjectType}, - value::TryFromJs, - Context, JsResult, JsValue, + builtins::set::SetIterator, error::JsNativeError, object::JsObject, value::TryFromJs, Context, + JsResult, JsValue, }; /// `JsSetIterator` provides a wrapper for Boa's implementation of the ECMAScript `SetIterator` object @@ -58,8 +55,6 @@ impl Deref for JsSetIterator { } } -impl JsObjectType for JsSetIterator {} - impl TryFromJs for JsSetIterator { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jssharedarraybuffer.rs b/core/engine/src/object/builtins/jssharedarraybuffer.rs index e051dc160ca..d5dc69f8b5a 100644 --- a/core/engine/src/object/builtins/jssharedarraybuffer.rs +++ b/core/engine/src/object/builtins/jssharedarraybuffer.rs @@ -1,10 +1,7 @@ //! A Rust API wrapper for Boa's `SharedArrayBuffer` Builtin ECMAScript Object use crate::{ - builtins::array_buffer::SharedArrayBuffer, - error::JsNativeError, - object::{JsObject, JsObjectType}, - value::TryFromJs, - Context, JsResult, JsValue, + builtins::array_buffer::SharedArrayBuffer, error::JsNativeError, object::JsObject, + value::TryFromJs, Context, JsResult, JsValue, }; use boa_gc::{Finalize, Trace}; use std::{ops::Deref, sync::atomic::Ordering}; @@ -118,8 +115,6 @@ impl Deref for JsSharedArrayBuffer { } } -impl JsObjectType for JsSharedArrayBuffer {} - impl TryFromJs for JsSharedArrayBuffer { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/builtins/jstypedarray.rs b/core/engine/src/object/builtins/jstypedarray.rs index 567d5c8ee22..d043b47363b 100644 --- a/core/engine/src/object/builtins/jstypedarray.rs +++ b/core/engine/src/object/builtins/jstypedarray.rs @@ -3,7 +3,7 @@ use crate::{ builtins::typed_array::BuiltinTypedArray, builtins::{typed_array::TypedArray, BuiltInConstructor}, error::JsNativeError, - object::{JsArrayBuffer, JsFunction, JsObject, JsObjectType}, + object::{JsArrayBuffer, JsFunction, JsObject}, value::{IntoOrUndefined, TryFromJs}, Context, JsResult, JsString, JsValue, }; @@ -910,8 +910,6 @@ impl Deref for JsTypedArray { } } -impl JsObjectType for JsTypedArray {} - impl TryFromJs for JsTypedArray { fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult { match value { diff --git a/core/engine/src/object/jsobject.rs b/core/engine/src/object/jsobject.rs index 9e41228cb15..c7d1718f329 100644 --- a/core/engine/src/object/jsobject.rs +++ b/core/engine/src/object/jsobject.rs @@ -534,6 +534,8 @@ Cannot both specify accessors and a value or writable attribute", Ok(()) } + // Allow lint, false positive. + #[allow(clippy::assigning_clones)] pub(crate) fn get_property(&self, key: &PropertyKey) -> Option { let mut obj = Some(self.clone()); @@ -842,7 +844,7 @@ impl Error for BorrowMutError {} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] enum RecursionValueState { - /// This value is "live": there's an active RecursionLimiter that hasn't been dropped. + /// This value is "live": there's an active `RecursionLimiter` that hasn't been dropped. Live, /// This value has been seen before, but the recursion limiter has been dropped. /// For example: @@ -863,11 +865,11 @@ enum RecursionValueState { pub struct RecursionLimiter { /// If this was the first `JsObject` in the tree. top_level: bool, - /// The ptr being kept in the HashSet, so we can delete it when we drop. + /// The ptr being kept in the `HashSet`, so we can delete it when we drop. ptr: usize, - /// If this JsObject has been visited before in the graph, but not in the current branch. + /// If this `JsObject` has been visited before in the graph, but not in the current branch. pub visited: bool, - /// If this JsObject has been visited in the current branch of the graph. + /// If this `JsObject` has been visited in the current branch of the graph. pub live: bool, } @@ -923,7 +925,7 @@ impl RecursionLimiter { } impl Debug for JsObject { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let limiter = RecursionLimiter::new(self.as_ref()); // Typically, using `!limiter.live` would be good enough here. diff --git a/core/engine/src/object/mod.rs b/core/engine/src/object/mod.rs index f2fc7ed64fe..ffffaa5579f 100644 --- a/core/engine/src/object/mod.rs +++ b/core/engine/src/object/mod.rs @@ -50,8 +50,6 @@ pub(crate) use builtins::*; pub use datatypes::JsData; pub use jsobject::*; -pub(crate) trait JsObjectType: Into + Into {} - /// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object. pub const CONSTRUCTOR: JsStr<'_> = js_str!("constructor"); @@ -317,10 +315,10 @@ impl Object { } = value { if existing_getter.is_none() { - *existing_getter = getter.clone(); + existing_getter.clone_from(getter); } if existing_setter.is_none() { - *existing_setter = setter.clone(); + existing_setter.clone_from(setter); } return; } diff --git a/core/engine/src/object/property_map.rs b/core/engine/src/object/property_map.rs index a480653df23..b6504b1794f 100644 --- a/core/engine/src/object/property_map.rs +++ b/core/engine/src/object/property_map.rs @@ -790,7 +790,7 @@ pub enum IndexProperties<'a> { /// An iterator over dense, Vec backed indexed property entries of an `Object`. DenseElement(std::iter::Enumerate>), - /// An iterator over sparse, HashMap backed indexed property entries of an `Object`. + /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`. Sparse(hash_map::Iter<'a, u32, PropertyDescriptor>), } @@ -851,7 +851,7 @@ pub enum IndexPropertyKeys<'a> { /// An iterator over dense, Vec backed indexed property entries of an `Object`. Dense(std::ops::Range), - /// An iterator over sparse, HashMap backed indexed property entries of an `Object`. + /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`. Sparse(hash_map::Keys<'a, u32, PropertyDescriptor>), } @@ -900,7 +900,7 @@ pub enum IndexPropertyValues<'a> { /// An iterator over dense, Vec backed indexed property entries of an `Object`. DenseElement(std::slice::Iter<'a, JsValue>), - /// An iterator over sparse, HashMap backed indexed property entries of an `Object`. + /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`. Sparse(hash_map::Values<'a, u32, PropertyDescriptor>), } diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index 3351ae4492d..457b731704b 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -84,7 +84,7 @@ impl Script { context: &mut Context, ) -> JsResult { let _timer = Profiler::global().start_event("Script parsing", "Main"); - let path = src.path().map(std::path::Path::to_path_buf); + let path = src.path().map(Path::to_path_buf); let mut parser = Parser::new(src); parser.set_identifier(context.next_parser_identifier()); if context.is_strict() { diff --git a/core/engine/src/small_map/mod.rs b/core/engine/src/small_map/mod.rs index 627b71eee33..0e2c1337690 100644 --- a/core/engine/src/small_map/mod.rs +++ b/core/engine/src/small_map/mod.rs @@ -158,10 +158,10 @@ impl SmallMap { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. - pub fn get(&self, key: &Q) -> Option<&V> + pub fn get(&self, key: &Q) -> Option<&V> where K: Borrow + Ord + Eq, - Q: Ord + Eq, + Q: ?Sized + Ord + Eq, { match &self.inner { Inner::Inline(v) => v.iter().find(|(k, _)| k.borrow() == key).map(|(_, v)| v), @@ -174,10 +174,10 @@ impl SmallMap { /// The supplied key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. #[allow(clippy::map_identity)] - pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where K: Borrow + Ord + Eq, - Q: Ord + Eq, + Q: ?Sized + Ord + Eq, { match &self.inner { Inner::Inline(v) => v @@ -192,10 +192,10 @@ impl SmallMap { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. - pub fn contains_key(&self, key: &Q) -> bool + pub fn contains_key(&self, key: &Q) -> bool where K: Borrow + Ord + Eq, - Q: Ord + Eq, + Q: ?Sized + Ord + Eq, { self.get(key).is_some() } @@ -204,10 +204,10 @@ impl SmallMap { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where K: Borrow + Ord + Eq, - Q: Ord + Eq, + Q: ?Sized + Ord + Eq, { match &mut self.inner { Inner::Inline(v) => v @@ -246,10 +246,10 @@ impl SmallMap { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. - pub fn remove(&mut self, key: &Q) -> Option + pub fn remove(&mut self, key: &Q) -> Option where K: Borrow + Ord + Eq, - Q: Ord + Eq, + Q: ?Sized + Ord + Eq, { self.remove_entry(key).map(|(_, v)| v) } @@ -259,10 +259,10 @@ impl SmallMap { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. - pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> where K: Borrow + Ord, - Q: Ord, + Q: ?Sized + Ord, { match &mut self.inner { Inner::Inline(v) => v @@ -581,7 +581,7 @@ impl Eq for SmallMap fmt::Debug for SmallMap { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.iter()).finish() } } diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index bae1f87add6..57d1af5a2b9 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -1051,7 +1051,7 @@ pub enum PreferredType { pub enum Numeric { /// Double precision floating point number. Number(f64), - /// BigInt an integer of arbitrary size. + /// `BigInt` an integer of arbitrary size. BigInt(JsBigInt), } diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 1913a77f37c..a48f73b543a 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -59,7 +59,7 @@ pub struct CallFrame { /// How many iterations a loop has done. pub(crate) loop_iteration_count: u64, - /// \[\[ScriptOrModule\]\] + /// `[[ScriptOrModule]]` pub(crate) active_runnable: Option, /// \[\[Environment\]\] diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index 2a486da098b..a1c26992192 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -139,7 +139,7 @@ pub struct CodeBlock { pub(crate) register_count: u32, - /// \[\[ThisMode\]\] + /// `[[ThisMode]]` pub(crate) this_mode: ThisMode, /// Parameters passed to this function. diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index 580bf18071f..b78908ab966 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -163,7 +163,7 @@ impl Operation for CreatePromiseCapability { return Ok(CompletionType::Normal); } - let promise_capability = crate::builtins::promise::PromiseCapability::new( + let promise_capability = PromiseCapability::new( &context.intrinsics().constructors().promise().constructor(), context, ) diff --git a/core/gc/src/cell.rs b/core/gc/src/cell.rs index 471a204e60d..fefef87d449 100644 --- a/core/gc/src/cell.rs +++ b/core/gc/src/cell.rs @@ -199,7 +199,7 @@ impl GcRefCell { pub struct BorrowError; impl Display for BorrowError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt("GcCell already mutably borrowed", f) } } @@ -209,7 +209,7 @@ impl Display for BorrowError { pub struct BorrowMutError; impl Display for BorrowMutError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt("GcCell already borrowed", f) } } diff --git a/core/gc/src/lib.rs b/core/gc/src/lib.rs index 5ed1e44f6d5..4484249188d 100644 --- a/core/gc/src/lib.rs +++ b/core/gc/src/lib.rs @@ -533,7 +533,7 @@ impl Collector { fn dump(gc: &mut BoaGc) { // Weak maps have to be dropped first, since the process dereferences GcBoxes. // This can be done without initializing a dropguard since no GcBox's are being dropped. - for node in std::mem::take(&mut gc.weak_maps) { + for node in mem::take(&mut gc.weak_maps) { // SAFETY: // The `Allocator` must always ensure its start node is a valid, non-null pointer that // was allocated by `Box::from_raw(Box::new(..))`. @@ -543,7 +543,7 @@ impl Collector { // Not initializing a dropguard since this should only be invoked when BOA_GC is being dropped. let _guard = DropGuard::new(); - for node in std::mem::take(&mut gc.strongs) { + for node in mem::take(&mut gc.strongs) { // SAFETY: // The `Allocator` must always ensure its start node is a valid, non-null pointer that // was allocated by `Box::from_raw(Box::new(..))`. @@ -555,7 +555,7 @@ impl Collector { } } - for node in std::mem::take(&mut gc.weaks) { + for node in mem::take(&mut gc.weaks) { // SAFETY: // The `Allocator` must always ensure its start node is a valid, non-null pointer that // was allocated by `Box::from_raw(Box::new(..))`. diff --git a/core/parser/src/error/mod.rs b/core/parser/src/error/mod.rs index 2e7183e7f97..727c247bbeb 100644 --- a/core/parser/src/error/mod.rs +++ b/core/parser/src/error/mod.rs @@ -16,6 +16,7 @@ pub(crate) trait ErrorContext { fn set_context(self, context: &'static str) -> Self; /// Gets the context of the error, if any. + #[allow(dead_code)] fn context(&self) -> Option<&'static str>; } diff --git a/core/parser/src/lexer/cursor.rs b/core/parser/src/lexer/cursor.rs index e3f8eae0b48..ae352621da1 100644 --- a/core/parser/src/lexer/cursor.rs +++ b/core/parser/src/lexer/cursor.rs @@ -141,7 +141,7 @@ impl Cursor { } else if let Some(c) = self.next_char()? { buf.push(c); } else { - return Err(io::Error::new( + return Err(Error::new( ErrorKind::UnexpectedEof, format!("Unexpected end of file when looking for character {stop}"), )); diff --git a/core/parser/src/lexer/regex.rs b/core/parser/src/lexer/regex.rs index 999ac476bb0..22ee82125a2 100644 --- a/core/parser/src/lexer/regex.rs +++ b/core/parser/src/lexer/regex.rs @@ -7,6 +7,7 @@ use boa_ast::Position; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; use regress::{Flags, Regex}; +use std::fmt::{Display, Write}; use std::str::{self, FromStr}; /// Regex literal lexing. @@ -217,34 +218,33 @@ fn parse_regex_flags(s: &str, start: Position, interner: &mut Interner) -> Resul } } -impl ToString for RegExpFlags { - fn to_string(&self) -> String { - let mut s = String::new(); +impl Display for RegExpFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.contains(Self::HAS_INDICES) { - s.push('d'); + f.write_char('d')?; } if self.contains(Self::GLOBAL) { - s.push('g'); + f.write_char('g')?; } if self.contains(Self::IGNORE_CASE) { - s.push('i'); + f.write_char('i')?; } if self.contains(Self::MULTILINE) { - s.push('m'); + f.write_char('m')?; } if self.contains(Self::DOT_ALL) { - s.push('s'); + f.write_char('s')?; } if self.contains(Self::UNICODE) { - s.push('u'); + f.write_char('u')?; } if self.contains(Self::STICKY) { - s.push('y'); + f.write_char('y')?; } if self.contains(Self::UNICODE_SETS) { - s.push('v'); + f.write_char('v')?; } - s + Ok(()) } } diff --git a/core/parser/src/lexer/string.rs b/core/parser/src/lexer/string.rs index a49d5ab5221..40861585f8d 100644 --- a/core/parser/src/lexer/string.rs +++ b/core/parser/src/lexer/string.rs @@ -48,6 +48,7 @@ pub(crate) trait UTF16CodeUnitsBuffer { fn push_code_point(&mut self, code_point: u32); /// Decodes the buffer into a String and replace the invalid data with the replacement character (U+FFFD). + #[allow(dead_code)] fn to_string_lossy(&self) -> String; } diff --git a/core/parser/src/lexer/token.rs b/core/parser/src/lexer/token.rs index ff51dce5e97..bdb11fe8b9d 100644 --- a/core/parser/src/lexer/token.rs +++ b/core/parser/src/lexer/token.rs @@ -59,13 +59,13 @@ impl Token { #[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, PartialEq, Debug)] pub enum Numeric { - /// A floating point number + /// A floating point number. Rational(f64), - /// An integer + /// An integer. Integer(i32), - /// A BigInt + /// A `BigInt`. BigInt(Box), } diff --git a/core/parser/src/parser/mod.rs b/core/parser/src/parser/mod.rs index f0b85938be9..1f2bd364070 100644 --- a/core/parser/src/parser/mod.rs +++ b/core/parser/src/parser/mod.rs @@ -321,7 +321,7 @@ where type Output = StatementList; fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { - let body = self::statement::StatementList::new( + let body = statement::StatementList::new( false, false, false, diff --git a/core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs b/core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs index d65dc3a678c..afb9a42e93b 100644 --- a/core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs +++ b/core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs @@ -728,9 +728,7 @@ where cursor.set_strict(strict); statement_list }; - function::ClassElement::StaticBlock(ast::function::FunctionBody::new( - statement_list, - )) + function::ClassElement::StaticBlock(function::FunctionBody::new(statement_list)) } TokenKind::Punctuator(Punctuator::Mul) => { let token = cursor.peek(1, interner).or_abrupt()?; @@ -895,7 +893,9 @@ where } } } - TokenKind::IdentifierName((Sym::GET, ContainsEscapeSequence(true))) if is_keyword => { + TokenKind::IdentifierName((Sym::GET | Sym::SET, ContainsEscapeSequence(true))) + if is_keyword => + { return Err(Error::general( "keyword must not contain escaped characters", token.span().start(), @@ -1026,12 +1026,6 @@ where } } } - TokenKind::IdentifierName((Sym::SET, ContainsEscapeSequence(true))) if is_keyword => { - return Err(Error::general( - "keyword must not contain escaped characters", - token.span().start(), - )) - } TokenKind::IdentifierName((Sym::SET, ContainsEscapeSequence(false))) if is_keyword => { cursor.advance(interner); let token = cursor.peek(0, interner).or_abrupt()?; diff --git a/core/parser/src/parser/statement/mod.rs b/core/parser/src/parser/statement/mod.rs index cd4f3e4ca06..9a48b1f07ff 100644 --- a/core/parser/src/parser/statement/mod.rs +++ b/core/parser/src/parser/statement/mod.rs @@ -534,10 +534,14 @@ where TokenKind::Punctuator(Punctuator::OpenBracket) | TokenKind::StringLiteral(_) | TokenKind::NumericLiteral(_) => true, - TokenKind::IdentifierName(_) if next_token_is_colon => true, - TokenKind::Keyword(_) if next_token_is_colon => true, - TokenKind::BooleanLiteral(_) if next_token_is_colon => true, - TokenKind::NullLiteral(_) if next_token_is_colon => true, + TokenKind::IdentifierName(_) + | TokenKind::Keyword(_) + | TokenKind::BooleanLiteral(_) + | TokenKind::NullLiteral(_) + if next_token_is_colon => + { + true + } _ => false, }; diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index afb3e2e3bfe..607eee6b50c 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -23,7 +23,7 @@ use boa_engine::{ }; use boa_gc::{Finalize, Trace}; use rustc_hash::FxHashMap; -use std::{cell::RefCell, rc::Rc, time::SystemTime}; +use std::{cell::RefCell, collections::hash_map::Entry, rc::Rc, time::SystemTime}; /// This represents the different types of log messages. #[derive(Debug)] @@ -527,7 +527,10 @@ impl Console { None => "default".into(), }; - if console.timer_map.get(&label).is_some() { + if let Entry::Vacant(e) = console.timer_map.entry(label.clone()) { + let time = Self::system_time_in_ms(); + e.insert(time); + } else { logger( LogMessage::Warn(format!( "Timer '{}' already exist", @@ -535,9 +538,6 @@ impl Console { )), console, ); - } else { - let time = Self::system_time_in_ms(); - console.timer_map.insert(label, time); } Ok(JsValue::undefined()) diff --git a/tests/tester/src/results.rs b/tests/tester/src/results.rs index 2825d08cd23..c169bb0c238 100644 --- a/tests/tester/src/results.rs +++ b/tests/tester/src/results.rs @@ -89,7 +89,7 @@ pub(crate) fn write_json( ) -> Result<()> { let mut branch = env::var("GITHUB_REF").unwrap_or_default(); if branch.starts_with("refs/pull") { - branch = "pull".to_owned(); + "pull".clone_into(&mut branch); } let output_dir = if branch.is_empty() { From 12adec5eedafdd1cbf7fa805590f3635943a9a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?19=E5=B9=B4=E6=A2=A6=E9=86=92?= <3949379+getong@users.noreply.github.com> Date: Sat, 4 May 2024 06:01:49 +0800 Subject: [PATCH 017/212] replace js_str with js_string (#3836) --- examples/src/bin/classes.rs | 8 ++++---- examples/src/bin/closures.rs | 20 ++++++++++---------- examples/src/bin/futures.rs | 4 ++-- examples/src/bin/jsarray.rs | 11 +++++++---- examples/src/bin/jsarraybuffer.rs | 4 ++-- examples/src/bin/jstypedarray.rs | 12 ++++++------ examples/src/bin/module_fetch.rs | 10 ++++++---- examples/src/bin/modulehandler.rs | 12 ++++++------ examples/src/bin/modules.rs | 11 +++++++---- examples/src/bin/synthetic.rs | 12 +++++++----- 10 files changed, 57 insertions(+), 47 deletions(-) diff --git a/examples/src/bin/classes.rs b/examples/src/bin/classes.rs index 44da13ce8b2..9d6f833c122 100644 --- a/examples/src/bin/classes.rs +++ b/examples/src/bin/classes.rs @@ -2,7 +2,7 @@ use boa_engine::{ class::{Class, ClassBuilder}, error::JsNativeError, - js_str, js_string, + js_string, native_function::NativeFunction, property::Attribute, Context, JsArgs, JsData, JsResult, JsString, JsValue, Source, @@ -120,13 +120,13 @@ impl Class for Person { // We add an `"inheritedProperty"` property to the prototype of `Person` with // a value of `10` and default attribute flags `READONLY`, `NON_ENUMERABLE` and `PERMANENT`. - class.property(js_str!("inheritedProperty"), 10, Attribute::default()); + class.property(js_string!("inheritedProperty"), 10, Attribute::default()); // Finally, we add a `"staticProperty"` property to `Person` with a value // of `"Im a static property"` and attribute flags `WRITABLE`, `ENUMERABLE` and `PERMANENT`. class.static_property( - js_str!("staticProperty"), - js_str!("Im a static property"), + js_string!("staticProperty"), + js_string!("Im a static property"), Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::PERMANENT, ); diff --git a/examples/src/bin/closures.rs b/examples/src/bin/closures.rs index efc414061e1..4ccd9fc1980 100644 --- a/examples/src/bin/closures.rs +++ b/examples/src/bin/closures.rs @@ -4,7 +4,7 @@ use std::cell::{Cell, RefCell}; use boa_engine::{ - js_str, js_string, + js_string, native_function::NativeFunction, object::{builtins::JsArray, FunctionObjectBuilder, JsObject}, property::{Attribute, PropertyDescriptor}, @@ -52,9 +52,9 @@ fn main() -> Result<(), JsError> { // We create a new `JsObject` with some data let object = JsObject::with_object_proto(context.intrinsics()); object.define_property_or_throw( - js_str!("name"), + js_string!("name"), PropertyDescriptor::builder() - .value(js_str!("Boa dev")) + .value(js_string!("Boa dev")) .writable(false) .enumerable(false) .configurable(false), @@ -77,18 +77,18 @@ fn main() -> Result<(), JsError> { let BigStruct { greeting, object } = &mut *captures; println!("Called `createMessage`"); // We obtain the `name` property of `captures.object` - let name = object.get(js_str!("name"), context)?; + let name = object.get(js_string!("name"), context)?; // We create a new message from our captured variable. let message = js_string!( - js_str!("message from `"), + &js_string!("message from `"), &name.to_string(context)?, - js_str!("`: "), + &js_string!("`: "), &*greeting ); // We can also mutate the moved data inside the closure. - captures.greeting = js_string!(&*greeting, js_str!(" Hello!")); + captures.greeting = js_string!(&*greeting, &js_string!(" Hello!")); println!("{}", message.to_std_string_escaped()); println!(); @@ -101,7 +101,7 @@ fn main() -> Result<(), JsError> { ), ) // And here we assign `createMessage` to the `name` property of the closure. - .name(js_str!("createMessage")) + .name(js_string!("createMessage")) // By default all `FunctionBuilder`s set the `length` property to `0` and // the `constructable` property to `false`. .build(); @@ -111,7 +111,7 @@ fn main() -> Result<(), JsError> { .register_global_property( // We set the key to access the function the same as its name for // consistency, but it may be different if needed. - js_str!("createMessage"), + js_string!("createMessage"), // We pass `js_function` as a property value. js_function, // We assign to the "createMessage" property the desired attributes. @@ -144,7 +144,7 @@ fn main() -> Result<(), JsError> { // We register a global closure that is not `Copy`. context .register_global_callable( - js_str!("enumerate").into(), + js_string!("enumerate"), 0, // Note that it is required to use `unsafe` code, since the compiler cannot verify that the // types captured by the closure are not traceable. diff --git a/examples/src/bin/futures.rs b/examples/src/bin/futures.rs index 3a5a3487f9b..c90217a99fc 100644 --- a/examples/src/bin/futures.rs +++ b/examples/src/bin/futures.rs @@ -8,7 +8,7 @@ use std::{ use boa_engine::{ context::ContextBuilder, job::{FutureJob, JobQueue, NativeJob}, - js_str, + js_string, native_function::NativeFunction, property::Attribute, Context, JsArgs, JsResult, JsValue, Source, @@ -143,7 +143,7 @@ fn add_runtime(context: &mut Context) { // Then, bind the defined async function to the ECMAScript function "delay". context .register_global_builtin_callable( - js_str!("delay").into(), + js_string!("delay"), 1, NativeFunction::from_async_fn(delay), ) diff --git a/examples/src/bin/jsarray.rs b/examples/src/bin/jsarray.rs index 3f09b0b7574..bf65c3815f6 100644 --- a/examples/src/bin/jsarray.rs +++ b/examples/src/bin/jsarray.rs @@ -1,7 +1,7 @@ // This example shows how to manipulate a Javascript array using Rust code. use boa_engine::{ - js_str, + js_string, native_function::NativeFunction, object::{builtins::JsArray, FunctionObjectBuilder}, Context, JsResult, JsValue, @@ -16,13 +16,16 @@ fn main() -> JsResult<()> { assert!(array.is_empty(context)?); - array.push(js_str!("Hello, world"), context)?; // [ "Hello, world" ] + array.push(js_string!("Hello, world"), context)?; // [ "Hello, world" ] array.push(true, context)?; // [ "Hello, world", true ] assert!(!array.is_empty(context)?); assert_eq!(array.pop(context)?, JsValue::new(true)); // [ "Hello, world" ] - assert_eq!(array.pop(context)?, JsValue::new(js_str!("Hello, world"))); // [ ] + assert_eq!( + array.pop(context)?, + JsValue::new(js_string!("Hello, world")) + ); // [ ] assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ] array.push(1, context)?; // [ 1 ] @@ -113,7 +116,7 @@ fn main() -> JsResult<()> { context .global_object() - .set(js_str!("myArray"), array, true, context)?; + .set(js_string!("myArray"), array, true, context)?; Ok(()) } diff --git a/examples/src/bin/jsarraybuffer.rs b/examples/src/bin/jsarraybuffer.rs index c5e6e44a4b7..5b321a50069 100644 --- a/examples/src/bin/jsarraybuffer.rs +++ b/examples/src/bin/jsarraybuffer.rs @@ -1,7 +1,7 @@ // This example shows how to manipulate a Javascript array using Rust code. use boa_engine::{ - js_str, + js_string, object::builtins::{JsArrayBuffer, JsDataView, JsUint32Array, JsUint8Array}, property::Attribute, Context, JsResult, JsValue, @@ -54,7 +54,7 @@ fn main() -> JsResult<()> { // We can also register it as a global property context .register_global_property( - js_str!("myArrayBuffer"), + js_string!("myArrayBuffer"), array_buffer, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ) diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index 72009def83b..7ffec384740 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -1,7 +1,7 @@ // This example shows how to manipulate a Javascript array using Rust code. use boa_engine::{ - js_str, + js_string, native_function::NativeFunction, object::{ builtins::{JsArray, JsArrayBuffer, JsUint8Array}, @@ -168,7 +168,7 @@ fn main() -> JsResult<()> { .buffer(context)? .as_object() .unwrap() - .get(js_str!("byteLength"), context) + .get(js_string!("byteLength"), context) .unwrap(), JsValue::new(8) ); @@ -195,23 +195,23 @@ fn main() -> JsResult<()> { // toLocaleString // let array = JsUint32Array::from_iter(vec![500, 8123, 12], context)?; - // let locales: Option = Some(js_str!("de-DE").into()); + // let locales: Option = Some(js_string!("de-DE").into()); // let options = Some(context.eval(Source::from_bytes( // r##"let options = { style: "currency", currency: "EUR" }; options;"##, // ))?); // assert_eq!( // array.to_locale_string(locales, options, context)?, - // js_str!("500,00 €,8.123,00 €,12,00 €").into() + // js_string!("500,00 €,8.123,00 €,12,00 €").into() // ); // toStringTag let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; let tag = array.to_string_tag(context)?.to_string(context)?; - assert_eq!(tag, js_str!("Uint8Array")); + assert_eq!(tag, js_string!("Uint8Array")); context .register_global_property( - js_str!("myUint8Array"), + js_string!("myUint8Array"), array, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, ) diff --git a/examples/src/bin/module_fetch.rs b/examples/src/bin/module_fetch.rs index 0f2de3b8462..991d58fd0ed 100644 --- a/examples/src/bin/module_fetch.rs +++ b/examples/src/bin/module_fetch.rs @@ -7,7 +7,7 @@ use std::{ use boa_engine::{ builtins::promise::PromiseState, job::{FutureJob, JobQueue, NativeJob}, - js_str, + js_string, module::ModuleLoader, Context, JsNativeError, JsResult, JsString, JsValue, Module, }; @@ -148,7 +148,9 @@ fn main() -> JsResult<()> { } } - let default = module.namespace(context).get(js_str!("default"), context)?; + let default = module + .namespace(context) + .get(js_string!("default"), context)?; // `default` should contain the result of our calculations. let default = default @@ -160,14 +162,14 @@ fn main() -> JsResult<()> { .get(0, context)? .as_string() .ok_or_else(|| JsNativeError::typ().with_message("array element was not a string"))?, - &js_str!("aGVsbG8=") + &js_string!("aGVsbG8=") ); assert_eq!( default .get(1, context)? .as_string() .ok_or_else(|| JsNativeError::typ().with_message("array element was not a string"))?, - &js_str!("d29ybGQ=") + &js_string!("d29ybGQ=") ); Ok(()) diff --git a/examples/src/bin/modulehandler.rs b/examples/src/bin/modulehandler.rs index 87660f524de..c36aaa2600d 100644 --- a/examples/src/bin/modulehandler.rs +++ b/examples/src/bin/modulehandler.rs @@ -2,7 +2,7 @@ // the require/module.exports pattern use boa_engine::{ - js_str, native_function::NativeFunction, prelude::JsObject, property::Attribute, Context, + js_string, native_function::NativeFunction, prelude::JsObject, property::Attribute, Context, JsArgs, JsNativeError, JsResult, JsValue, Source, }; use boa_runtime::Console; @@ -33,14 +33,14 @@ fn main() -> Result<(), Box> { // Adding custom object that mimics 'module.exports' let moduleobj = JsObject::default(); moduleobj.set( - js_str!("exports"), - JsValue::from(js_str!(" ")), + js_string!("exports"), + JsValue::from(js_string!(" ")), false, &mut ctx, )?; ctx.register_global_property( - js_str!("module"), + js_string!("module"), JsValue::from(moduleobj), Attribute::default(), )?; @@ -68,9 +68,9 @@ fn require(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult Result<(), Box> { // We can access the full namespace of the module with all its exports. let namespace = module.namespace(context); - let result = namespace.get(js_str!("result"), context)?; + let result = namespace.get(js_string!("result"), context)?; println!("result = {}", result.display()); - assert_eq!(namespace.get(js_str!("result"), context)?, JsValue::from(5)); + assert_eq!( + namespace.get(js_string!("result"), context)?, + JsValue::from(5) + ); let mix = namespace - .get(js_str!("mix"), context)? + .get(js_string!("mix"), context)? .as_callable() .cloned() .ok_or_else(|| JsNativeError::typ().with_message("mix export wasn't a function!"))?; diff --git a/examples/src/bin/synthetic.rs b/examples/src/bin/synthetic.rs index 80511e9e046..e38562cde4b 100644 --- a/examples/src/bin/synthetic.rs +++ b/examples/src/bin/synthetic.rs @@ -9,8 +9,7 @@ use boa_engine::builtins::promise::PromiseState; use boa_engine::module::{SimpleModuleLoader, SyntheticModuleInitializer}; use boa_engine::object::FunctionObjectBuilder; use boa_engine::{ - js_str, js_string, Context, JsArgs, JsError, JsNativeError, JsValue, Module, NativeFunction, - Source, + js_string, Context, JsArgs, JsError, JsNativeError, JsValue, Module, NativeFunction, Source, }; fn main() -> Result<(), Box> { @@ -78,14 +77,17 @@ fn main() -> Result<(), Box> { // We can access the full namespace of the module with all its exports. let namespace = module.namespace(context); - let result = namespace.get(js_str!("result"), context)?; + let result = namespace.get(js_string!("result"), context)?; println!("result = {}", result.display()); - assert_eq!(namespace.get(js_str!("result"), context)?, JsValue::from(5)); + assert_eq!( + namespace.get(js_string!("result"), context)?, + JsValue::from(5) + ); let mix = namespace - .get(js_str!("mix"), context)? + .get(js_string!("mix"), context)? .as_callable() .cloned() .ok_or_else(|| JsNativeError::typ().with_message("mix export wasn't a function!"))?; From 09c08c543525857869d571d3c699716723fe03d6 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Fri, 10 May 2024 23:36:31 +0100 Subject: [PATCH 018/212] Switch from actions-rs/toolchain to dtolnay/rust-toolchain (#3845) --- .github/workflows/pull_request.yml | 4 +--- .github/workflows/release.yml | 14 ++++--------- .github/workflows/rust.yml | 32 ++++++++---------------------- .github/workflows/test262.yml | 4 +--- .github/workflows/webassembly.yml | 4 +--- 5 files changed, 15 insertions(+), 43 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 19ce8b6bd96..1f7b720445f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -15,11 +15,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - name: Cache cargo uses: actions/cache@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 425e7d32b6a..3385334e062 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,11 +15,9 @@ jobs: uses: actions/checkout@v4 - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - profile: minimal - override: true - name: Install cargo-workspaces uses: actions-rs/install@v0.1 @@ -49,12 +47,10 @@ jobs: uses: actions/checkout@v4 - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - target: wasm32-unknown-unknown - profile: minimal - override: true + targets: wasm32-unknown-unknown - name: Install wasm-pack uses: jetli/wasm-pack-action@v0.4.0 @@ -105,11 +101,9 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - profile: minimal - override: true - name: Build run: cargo build --target ${{ matrix.target }} --verbose --release --locked --bin boa - name: Upload binaries to release diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b0faffcde8a..9526392b25d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,11 +20,9 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - uses: Swatinem/rust-cache@v2 with: key: tarpaulin @@ -51,11 +49,9 @@ jobs: - windows-latest steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - uses: Swatinem/rust-cache@v2 - name: Build tests run: cargo test --no-run --profile ci @@ -79,11 +75,9 @@ jobs: - name: Get rust_version id: rust_version run: echo "rust_version=$(grep '^rust-version' Cargo.toml | cut -d' ' -f3 | tr -d '"')" >> $GITHUB_OUTPUT - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ steps.rust_version.outputs.rust_version }} - override: true - profile: minimal - name: Check compilation run: cargo check --all-features --all-targets @@ -93,11 +87,9 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal components: rustfmt - name: Format (rustfmt) run: cargo fmt --all --check @@ -108,11 +100,9 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal components: clippy - uses: Swatinem/rust-cache@v2 with: @@ -140,11 +130,9 @@ jobs: RUSTDOCFLAGS: -D warnings steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - uses: Swatinem/rust-cache@v2 with: key: docs @@ -157,11 +145,9 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - uses: Swatinem/rust-cache@v2 with: key: build-fuzz @@ -179,11 +165,9 @@ jobs: timeout-minutes: 60 steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - uses: Swatinem/rust-cache@v2 with: key: build-run-examples diff --git a/.github/workflows/test262.yml b/.github/workflows/test262.yml index d51beb91e08..be17998cc14 100644 --- a/.github/workflows/test262.yml +++ b/.github/workflows/test262.yml @@ -18,11 +18,9 @@ jobs: submodules: true path: boa - name: Install the Rust toolchain - uses: actions-rs/toolchain@v1.0.7 + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - name: Cache cargo uses: actions/cache@v4 with: diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index 08b5df72b49..cb41ab05b04 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -30,11 +30,9 @@ jobs: RUSTFLAGS: -D warnings steps: - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1.0.7 + - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - override: true - profile: minimal - uses: Swatinem/rust-cache@v2 - name: Install wasm-pack uses: baptiste0928/cargo-install@v3.1.0 From ce57ff1ed48f14ed3db675701983a2fb7792d975 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 01:19:15 +0200 Subject: [PATCH 019/212] Bump num-traits from 0.2.18 to 0.2.19 (#3840) Bumps [num-traits](https://github.com/rust-num/num-traits) from 0.2.18 to 0.2.19. - [Changelog](https://github.com/rust-num/num-traits/blob/master/RELEASES.md) - [Commits](https://github.com/rust-num/num-traits/compare/num-traits-0.2.18...num-traits-0.2.19) --- updated-dependencies: - dependency-name: num-traits dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96a62deae14..0db2252a7f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2537,9 +2537,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] diff --git a/Cargo.toml b/Cargo.toml index 155025aa701..336237f8511 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ indexmap = { version = "2.2.6", default-features = false } indoc = "2.0.5" jemallocator = "0.5.4" num-bigint = "0.4.4" -num-traits = "0.2.18" +num-traits = "0.2.19" once_cell = { version = "1.19.0", default-features = false } phf = { version = "0.11.2", default-features = false } pollster = "0.3.0" From c32e3cb57ab6a99d74ffbcd42a67fd7de488dcf3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 03:34:16 +0200 Subject: [PATCH 020/212] Bump serde from 1.0.199 to 1.0.201 (#3846) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.199 to 1.0.201. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.199...v1.0.201) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0db2252a7f1..24bb9a5f77d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3346,9 +3346,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -3374,9 +3374,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 336237f8511..75650ecd0b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ regex = "1.10.4" regress = { version="0.9.1", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } serde_json = "1.0.116" -serde = "1.0.199" +serde = "1.0.201" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" From 2fc85d0f2242a79e32a5a6f409cf157c1631b08c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 03:34:34 +0200 Subject: [PATCH 021/212] Bump trybuild from 1.0.91 to 1.0.95 (#3847) Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.91 to 1.0.95. - [Release notes](https://github.com/dtolnay/trybuild/releases) - [Commits](https://github.com/dtolnay/trybuild/compare/1.0.91...1.0.95) --- updated-dependencies: - dependency-name: trybuild dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 5 ++--- tests/macros/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24bb9a5f77d..38894767745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4063,12 +4063,11 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.91" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad7eb6319ebadebca3dacf1f85a93bc54b73dd81b9036795f73de7ddfe27d5a" +checksum = "4ddb747392ea12569d501a5bbca08852e4c8cd88b92566074b2243b8846f09e6" dependencies = [ "glob", - "once_cell", "serde", "serde_derive", "serde_json", diff --git a/tests/macros/Cargo.toml b/tests/macros/Cargo.toml index c03b38d8164..030308047d4 100644 --- a/tests/macros/Cargo.toml +++ b/tests/macros/Cargo.toml @@ -11,7 +11,7 @@ repository.workspace = true rust-version.workspace = true [dev-dependencies] -trybuild = "1.0.91" +trybuild = "1.0.95" boa_engine.workspace = true [lints] From 371a7c8b43ab45d04e4c84ad94e2a528bdcab1c8 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Sat, 11 May 2024 03:05:41 +0100 Subject: [PATCH 022/212] Replace archived github actions from actions-rs (#3848) * Replace archived github actions from actions-rs * fix syntax error --- .github/workflows/pull_request.yml | 8 ++- .github/workflows/release.yml | 9 +-- .github/workflows/rust.yml | 90 ++++++++++++++++++---------- .github/workflows/security_audit.yml | 5 +- .github/workflows/test262.yml | 6 +- .github/workflows/webassembly.yml | 8 ++- 6 files changed, 84 insertions(+), 42 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1f7b720445f..f90d5dd520f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -12,12 +12,16 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 120 steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: dtolnay/rust-toolchain@stable + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - name: Cache cargo uses: actions/cache@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3385334e062..b9982f43d81 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,9 +20,7 @@ jobs: toolchain: stable - name: Install cargo-workspaces - uses: actions-rs/install@v0.1 - with: - crate: cargo-workspaces + run: cargo install cargo-workspaces - name: Release env: @@ -99,11 +97,14 @@ jobs: binary_name: boa.exe runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - name: Build run: cargo build --target ${{ matrix.target }} --verbose --release --locked --bin boa - name: Upload binaries to release diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9526392b25d..189da64b52e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,10 +19,14 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - uses: Swatinem/rust-cache@v2 with: key: tarpaulin @@ -30,11 +34,10 @@ jobs: uses: baptiste0928/cargo-install@v3.1.0 with: crate: cargo-tarpaulin + - name: Run tarpaulin - uses: actions-rs/cargo@v1 - with: - command: tarpaulin - args: --workspace --features annex-b,intl_bundled,experimental --ignore-tests --engine llvm --out xml + run: cargo tarpaulin --workspace --features annex-b,intl_bundled,experimental --ignore-tests --engine llvm --out xml + - name: Upload to codecov.io uses: codecov/codecov-action@v4 @@ -48,10 +51,14 @@ jobs: - macos-latest - windows-latest steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - uses: Swatinem/rust-cache@v2 - name: Build tests run: cargo test --no-run --profile ci @@ -70,14 +77,19 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 + # Get the rust_version from the Cargo.toml - name: Get rust_version id: rust_version run: echo "rust_version=$(grep '^rust-version' Cargo.toml | cut -d' ' -f3 | tr -d '"')" >> $GITHUB_OUTPUT - - uses: dtolnay/rust-toolchain@stable + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ steps.rust_version.outputs.rust_version }} + - name: Check compilation run: cargo check --all-features --all-targets @@ -86,11 +98,15 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable components: rustfmt + - name: Format (rustfmt) run: cargo fmt --all --check @@ -99,18 +115,22 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable components: clippy + - uses: Swatinem/rust-cache@v2 with: key: clippy + - name: Install cargo-workspaces - uses: actions-rs/install@v0.1 - with: - crate: cargo-workspaces + run: cargo install cargo-workspaces + - name: Clippy (All features) run: cargo workspaces exec cargo clippy --all-features --all-targets - name: Clippy (No features) @@ -129,10 +149,14 @@ jobs: env: RUSTDOCFLAGS: -D warnings steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - uses: Swatinem/rust-cache@v2 with: key: docs @@ -144,18 +168,21 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - uses: Swatinem/rust-cache@v2 with: key: build-fuzz + - name: Install cargo-fuzz - uses: actions-rs/install@v0.1 - with: - crate: cargo-fuzz - version: latest + run: cargo install cargo-fuzz + - name: Build fuzz run: cd tests/fuzz && cargo fuzz build -s none --dev @@ -164,17 +191,20 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - uses: Swatinem/rust-cache@v2 with: key: build-run-examples + - name: Install cargo-workspaces - uses: actions-rs/install@v0.1 - with: - crate: cargo-workspaces + run: cargo install cargo-workspaces - name: Build (All features) run: cargo workspaces exec cargo build --all-features --all-targets --profile ci diff --git a/.github/workflows/security_audit.yml b/.github/workflows/security_audit.yml index 8fde86a9fa8..fd4fb530ff7 100644 --- a/.github/workflows/security_audit.yml +++ b/.github/workflows/security_audit.yml @@ -7,7 +7,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 - - uses: actions-rs/audit-check@v1.2.0 + - name: Checkout repository + uses: actions/checkout@v4 + - uses: rustsec/audit-check@v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test262.yml b/.github/workflows/test262.yml index be17998cc14..8d1c55d0c97 100644 --- a/.github/workflows/test262.yml +++ b/.github/workflows/test262.yml @@ -12,15 +12,17 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - name: Checkout the repository + - name: Checkout repository uses: actions/checkout@v4 with: submodules: true path: boa - - name: Install the Rust toolchain + + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - name: Cache cargo uses: actions/cache@v4 with: diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index cb41ab05b04..2ac89d3abf6 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -29,10 +29,14 @@ jobs: WASM_PACK_PATH: ~/.cargo/bin/wasm-pack RUSTFLAGS: -D warnings steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable with: toolchain: stable + - uses: Swatinem/rust-cache@v2 - name: Install wasm-pack uses: baptiste0928/cargo-install@v3.1.0 From ced222fdbabacc695f8f081c5b009afc9be6b8d0 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Sun, 12 May 2024 21:48:06 +0200 Subject: [PATCH 023/212] Separate `JsString` into its own crate (#3837) --- ABOUT.md | 2 + Cargo.lock | 16 +- Cargo.toml | 1 + cli/ABOUT.md | 2 + core/ast/ABOUT.md | 2 + core/engine/ABOUT.md | 2 + core/engine/Cargo.toml | 5 +- core/engine/src/builtins/array/mod.rs | 2 +- core/engine/src/builtins/array_buffer/mod.rs | 2 +- .../src/builtins/array_buffer/shared.rs | 2 +- .../engine/src/builtins/async_function/mod.rs | 2 +- .../src/builtins/async_generator/mod.rs | 2 +- .../builtins/async_generator_function/mod.rs | 2 +- core/engine/src/builtins/atomics/mod.rs | 2 +- core/engine/src/builtins/bigint/mod.rs | 2 +- core/engine/src/builtins/boolean/mod.rs | 2 +- core/engine/src/builtins/builder.rs | 2 +- core/engine/src/builtins/dataview/mod.rs | 2 +- core/engine/src/builtins/date/mod.rs | 2 +- core/engine/src/builtins/error/aggregate.rs | 2 +- core/engine/src/builtins/error/eval.rs | 2 +- core/engine/src/builtins/error/mod.rs | 2 +- core/engine/src/builtins/error/range.rs | 2 +- core/engine/src/builtins/error/reference.rs | 2 +- core/engine/src/builtins/error/syntax.rs | 2 +- core/engine/src/builtins/error/type.rs | 2 +- core/engine/src/builtins/error/uri.rs | 2 +- core/engine/src/builtins/escape/mod.rs | 4 +- core/engine/src/builtins/eval/mod.rs | 2 +- core/engine/src/builtins/function/mod.rs | 2 +- core/engine/src/builtins/generator/mod.rs | 2 +- .../src/builtins/generator_function/mod.rs | 2 +- core/engine/src/builtins/intl/collator/mod.rs | 2 +- .../src/builtins/intl/date_time_format.rs | 2 +- .../src/builtins/intl/list_format/mod.rs | 2 +- core/engine/src/builtins/intl/locale/mod.rs | 2 +- core/engine/src/builtins/intl/mod.rs | 2 +- .../src/builtins/intl/number_format/mod.rs | 2 +- .../src/builtins/intl/plural_rules/mod.rs | 2 +- .../engine/src/builtins/intl/segmenter/mod.rs | 2 +- core/engine/src/builtins/json/mod.rs | 2 +- core/engine/src/builtins/map/mod.rs | 2 +- core/engine/src/builtins/math/mod.rs | 4 +- core/engine/src/builtins/number/globals.rs | 2 +- core/engine/src/builtins/number/mod.rs | 2 +- core/engine/src/builtins/object/mod.rs | 2 +- core/engine/src/builtins/promise/mod.rs | 2 +- core/engine/src/builtins/proxy/mod.rs | 2 +- core/engine/src/builtins/reflect/mod.rs | 2 +- core/engine/src/builtins/regexp/mod.rs | 2 +- core/engine/src/builtins/set/mod.rs | 2 +- core/engine/src/builtins/string/mod.rs | 19 +- core/engine/src/builtins/symbol/mod.rs | 2 +- .../src/builtins/temporal/calendar/mod.rs | 2 +- .../src/builtins/temporal/duration/mod.rs | 2 +- .../src/builtins/temporal/instant/mod.rs | 2 +- core/engine/src/builtins/temporal/mod.rs | 2 +- core/engine/src/builtins/temporal/now.rs | 2 +- .../src/builtins/temporal/plain_date/mod.rs | 2 +- .../builtins/temporal/plain_date_time/mod.rs | 2 +- .../builtins/temporal/plain_month_day/mod.rs | 2 +- .../src/builtins/temporal/plain_time/mod.rs | 2 +- .../builtins/temporal/plain_year_month/mod.rs | 2 +- .../src/builtins/temporal/time_zone/mod.rs | 2 +- .../builtins/temporal/zoned_date_time/mod.rs | 2 +- .../src/builtins/typed_array/builtin.rs | 2 +- core/engine/src/builtins/typed_array/mod.rs | 2 +- core/engine/src/builtins/uri/mod.rs | 2 +- core/engine/src/builtins/weak/weak_ref.rs | 2 +- core/engine/src/builtins/weak_map/mod.rs | 2 +- core/engine/src/builtins/weak_set/mod.rs | 2 +- core/engine/src/object/mod.rs | 2 +- core/engine/src/object/operations.rs | 2 +- core/engine/src/string.rs | 211 ++++ core/engine/src/string/common.rs | 950 ----------------- core/engine/src/symbol.rs | 2 +- core/engine/src/tagged.rs | 1 + core/engine/src/vm/opcode/push/array.rs | 2 +- core/gc/ABOUT.md | 2 + core/gc/Cargo.toml | 3 + core/gc/src/trace.rs | 12 + core/icu_provider/ABOUT.md | 2 + core/interner/ABOUT.md | 2 + core/interop/ABOUT.md | 2 + core/macros/ABOUT.md | 2 + core/parser/ABOUT.md | 2 + core/profiler/ABOUT.md | 2 + core/runtime/ABOUT.md | 2 + core/string/ABOUT.md | 34 + core/string/Cargo.toml | 28 + core/string/src/common.rs | 951 ++++++++++++++++++ .../{engine/src/string => string/src}/iter.rs | 4 + .../src/string/mod.rs => string/src/lib.rs} | 369 ++----- core/{engine/src/string => string/src}/str.rs | 17 +- core/string/src/tagged.rs | 109 ++ core/string/src/tests.rs | 166 +++ 96 files changed, 1750 insertions(+), 1308 deletions(-) create mode 100644 core/engine/src/string.rs delete mode 100644 core/engine/src/string/common.rs create mode 100644 core/string/ABOUT.md create mode 100644 core/string/Cargo.toml create mode 100644 core/string/src/common.rs rename core/{engine/src/string => string/src}/iter.rs (97%) rename core/{engine/src/string/mod.rs => string/src/lib.rs} (82%) rename core/{engine/src/string => string/src}/str.rs (96%) create mode 100644 core/string/src/tagged.rs create mode 100644 core/string/src/tests.rs diff --git a/ABOUT.md b/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/ABOUT.md +++ b/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/Cargo.lock b/Cargo.lock index 38894767745..2a012fc6025 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,6 +395,7 @@ dependencies = [ "boa_macros", "boa_parser", "boa_profiler", + "boa_string", "bytemuck", "cfg-if", "criterion", @@ -427,7 +428,6 @@ dependencies = [ "num-traits", "num_enum", "once_cell", - "paste", "pollster", "portable-atomic", "rand", @@ -437,7 +437,6 @@ dependencies = [ "serde", "serde_json", "sptr", - "static_assertions", "sys-locale", "tap", "temporal_rs", @@ -475,6 +474,7 @@ version = "0.18.0" dependencies = [ "boa_macros", "boa_profiler", + "boa_string", "hashbrown 0.14.5", "icu_locid", "thin-vec", @@ -571,6 +571,18 @@ dependencies = [ "textwrap", ] +[[package]] +name = "boa_string" +version = "0.18.0" +dependencies = [ + "boa_macros", + "fast-float", + "paste", + "rustc-hash", + "sptr", + "static_assertions", +] + [[package]] name = "boa_tester" version = "0.18.0" diff --git a/Cargo.toml b/Cargo.toml index 75650ecd0b9..ac1c3bf75d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ boa_macros = { version = "~0.18.0", path = "core/macros" } boa_parser = { version = "~0.18.0", path = "core/parser" } boa_profiler = { version = "~0.18.0", path = "core/profiler" } boa_runtime = { version = "~0.18.0", path = "core/runtime" } +boa_string = { version = "~0.18.0", path = "core/string" } # Shared deps arbitrary = "1" diff --git a/cli/ABOUT.md b/cli/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/cli/ABOUT.md +++ b/cli/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/ast/ABOUT.md b/core/ast/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/ast/ABOUT.md +++ b/core/ast/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/engine/ABOUT.md b/core/engine/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/engine/ABOUT.md +++ b/core/engine/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 40883df1bd9..73502507c17 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -69,11 +69,12 @@ js = ["dep:web-time"] [dependencies] boa_interner.workspace = true -boa_gc = { workspace = true, features = ["thin-vec"] } +boa_gc = { workspace = true, features = ["thin-vec", "boa_string"] } boa_profiler.workspace = true boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true +boa_string.workspace = true serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true rand = "0.8.5" @@ -89,7 +90,6 @@ fast-float.workspace = true once_cell = { workspace = true, features = ["std"] } tap = "1.0.1" sptr = "0.3.2" -static_assertions.workspace = true thiserror = "1.0.59" dashmap = "5.5.3" num_enum = "0.7.2" @@ -97,7 +97,6 @@ pollster.workspace = true thin-vec.workspace = true itertools = { version = "0.12.1", default-features = false } icu_normalizer = { workspace = true, features = ["compiled_data"] } -paste = "1.0" portable-atomic = "1.6.0" bytemuck = { version = "1.15.0", features = ["derive"] } arrayvec = "0.7.4" diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index aa962470408..31472c605fc 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -32,7 +32,7 @@ use crate::{ }, property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::{IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, JsString, diff --git a/core/engine/src/builtins/array_buffer/mod.rs b/core/engine/src/builtins/array_buffer/mod.rs index 1145c26f826..d5ea40ab554 100644 --- a/core/engine/src/builtins/array_buffer/mod.rs +++ b/core/engine/src/builtins/array_buffer/mod.rs @@ -29,7 +29,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, Object}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsData, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/array_buffer/shared.rs b/core/engine/src/builtins/array_buffer/shared.rs index 790a15eda0a..10c1092ee76 100644 --- a/core/engine/src/builtins/array_buffer/shared.rs +++ b/core/engine/src/builtins/array_buffer/shared.rs @@ -18,7 +18,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/async_function/mod.rs b/core/engine/src/builtins/async_function/mod.rs index 865510b4ea0..f8db6134e9a 100644 --- a/core/engine/src/builtins/async_function/mod.rs +++ b/core/engine/src/builtins/async_function/mod.rs @@ -12,7 +12,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/async_generator/mod.rs b/core/engine/src/builtins/async_generator/mod.rs index 976dab93cfe..6fa423ddbdc 100644 --- a/core/engine/src/builtins/async_generator/mod.rs +++ b/core/engine/src/builtins/async_generator/mod.rs @@ -19,7 +19,7 @@ use crate::{ object::{FunctionObjectBuilder, JsObject, CONSTRUCTOR}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, vm::{CompletionRecord, GeneratorResumeKind}, diff --git a/core/engine/src/builtins/async_generator_function/mod.rs b/core/engine/src/builtins/async_generator_function/mod.rs index 975bb842cd3..c0ca21b09ec 100644 --- a/core/engine/src/builtins/async_generator_function/mod.rs +++ b/core/engine/src/builtins/async_generator_function/mod.rs @@ -11,7 +11,7 @@ use crate::{ object::{JsObject, PROTOTYPE}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsResult, JsString, diff --git a/core/engine/src/builtins/atomics/mod.rs b/core/engine/src/builtins/atomics/mod.rs index db74e6fbca8..2d4ff957aee 100644 --- a/core/engine/src/builtins/atomics/mod.rs +++ b/core/engine/src/builtins/atomics/mod.rs @@ -16,7 +16,7 @@ use std::sync::atomic::Ordering; use crate::{ builtins::BuiltInObject, context::intrinsics::Intrinsics, js_string, object::JsObject, - property::Attribute, realm::Realm, string::common::StaticJsStrings, symbol::JsSymbol, + property::Attribute, realm::Realm, string::StaticJsStrings, symbol::JsSymbol, sys::time::Duration, value::IntegerOrInfinity, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/bigint/mod.rs b/core/engine/src/builtins/bigint/mod.rs index 3dc75356b18..947183afa54 100644 --- a/core/engine/src/builtins/bigint/mod.rs +++ b/core/engine/src/builtins/bigint/mod.rs @@ -20,7 +20,7 @@ use crate::{ object::JsObject, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::{IntegerOrInfinity, PreferredType}, Context, JsArgs, JsBigInt, JsResult, JsString, JsValue, diff --git a/core/engine/src/builtins/boolean/mod.rs b/core/engine/src/builtins/boolean/mod.rs index 3ae156b44b2..b33525a0238 100644 --- a/core/engine/src/builtins/boolean/mod.rs +++ b/core/engine/src/builtins/boolean/mod.rs @@ -19,7 +19,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsResult, JsString, JsValue, }; use boa_profiler::Profiler; diff --git a/core/engine/src/builtins/builder.rs b/core/engine/src/builtins/builder.rs index c87b9ab7290..610a910db33 100644 --- a/core/engine/src/builtins/builder.rs +++ b/core/engine/src/builtins/builder.rs @@ -9,7 +9,7 @@ use crate::{ }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, JsObject, JsString, JsValue, NativeFunction, }; diff --git a/core/engine/src/builtins/dataview/mod.rs b/core/engine/src/builtins/dataview/mod.rs index 7274cc1fd94..dd8dfef3091 100644 --- a/core/engine/src/builtins/dataview/mod.rs +++ b/core/engine/src/builtins/dataview/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsData, JsResult, JsString, diff --git a/core/engine/src/builtins/date/mod.rs b/core/engine/src/builtins/date/mod.rs index 49be23c5f88..17304c6e5ee 100644 --- a/core/engine/src/builtins/date/mod.rs +++ b/core/engine/src/builtins/date/mod.rs @@ -27,7 +27,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::{JsValue, PreferredType}, Context, JsArgs, JsData, JsError, JsResult, JsString, diff --git a/core/engine/src/builtins/error/aggregate.rs b/core/engine/src/builtins/error/aggregate.rs index bc4b9c32928..6a8042df31f 100644 --- a/core/engine/src/builtins/error/aggregate.rs +++ b/core/engine/src/builtins/error/aggregate.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyDescriptorBuilder}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/error/eval.rs b/core/engine/src/builtins/error/eval.rs index 266fd820530..88f6d21f0d1 100644 --- a/core/engine/src/builtins/error/eval.rs +++ b/core/engine/src/builtins/error/eval.rs @@ -18,7 +18,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/error/mod.rs b/core/engine/src/builtins/error/mod.rs index ed005d0e380..39911f71156 100644 --- a/core/engine/src/builtins/error/mod.rs +++ b/core/engine/src/builtins/error/mod.rs @@ -18,7 +18,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsResult, JsString, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/error/range.rs b/core/engine/src/builtins/error/range.rs index cc192396c33..15cf63d64f4 100644 --- a/core/engine/src/builtins/error/range.rs +++ b/core/engine/src/builtins/error/range.rs @@ -16,7 +16,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/error/reference.rs b/core/engine/src/builtins/error/reference.rs index eb8a1158623..f56f6b403a7 100644 --- a/core/engine/src/builtins/error/reference.rs +++ b/core/engine/src/builtins/error/reference.rs @@ -16,7 +16,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/error/syntax.rs b/core/engine/src/builtins/error/syntax.rs index 9074856694d..70333aaa10f 100644 --- a/core/engine/src/builtins/error/syntax.rs +++ b/core/engine/src/builtins/error/syntax.rs @@ -18,7 +18,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/error/type.rs b/core/engine/src/builtins/error/type.rs index 3b3223d0fb0..364afba24dc 100644 --- a/core/engine/src/builtins/error/type.rs +++ b/core/engine/src/builtins/error/type.rs @@ -24,7 +24,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, NativeFunction, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/error/uri.rs b/core/engine/src/builtins/error/uri.rs index fcf7b1d208b..36e6f275bc8 100644 --- a/core/engine/src/builtins/error/uri.rs +++ b/core/engine/src/builtins/error/uri.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsString, JsValue, }; use boa_macros::js_str; diff --git a/core/engine/src/builtins/escape/mod.rs b/core/engine/src/builtins/escape/mod.rs index e3fcf1cb2b0..dc11cd0ae31 100644 --- a/core/engine/src/builtins/escape/mod.rs +++ b/core/engine/src/builtins/escape/mod.rs @@ -11,8 +11,8 @@ //! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object use crate::{ - context::intrinsics::Intrinsics, js_string, realm::Realm, string::common::StaticJsStrings, - Context, JsArgs, JsObject, JsResult, JsString, JsValue, + context::intrinsics::Intrinsics, js_string, realm::Realm, string::StaticJsStrings, Context, + JsArgs, JsObject, JsResult, JsString, JsValue, }; use super::{BuiltInBuilder, BuiltInObject, IntrinsicObject}; diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 2026208ab7e..8982c716c07 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -20,7 +20,7 @@ use crate::{ js_string, object::JsObject, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, vm::{CallFrame, CallFrameFlags, Opcode}, Context, JsArgs, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 653a402ec77..1bf76b5e82c 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -30,7 +30,7 @@ use crate::{ }, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::IntegerOrInfinity, vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock}, diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index 3a9d56aa5b6..da8e38d181d 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{JsObject, CONSTRUCTOR}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, vm::{CallFrame, CallFrameFlags, CompletionRecord, GeneratorResumeKind}, diff --git a/core/engine/src/builtins/generator_function/mod.rs b/core/engine/src/builtins/generator_function/mod.rs index 300600d1ec9..c4687f1b051 100644 --- a/core/engine/src/builtins/generator_function/mod.rs +++ b/core/engine/src/builtins/generator_function/mod.rs @@ -16,7 +16,7 @@ use crate::{ object::PROTOTYPE, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsResult, JsString, diff --git a/core/engine/src/builtins/intl/collator/mod.rs b/core/engine/src/builtins/intl/collator/mod.rs index a99aec57a68..edbb9314362 100644 --- a/core/engine/src/builtins/intl/collator/mod.rs +++ b/core/engine/src/builtins/intl/collator/mod.rs @@ -29,7 +29,7 @@ use crate::{ }, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsData, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/intl/date_time_format.rs b/core/engine/src/builtins/intl/date_time_format.rs index 0dff2fe4dd8..f6dca66bfc7 100644 --- a/core/engine/src/builtins/intl/date_time_format.rs +++ b/core/engine/src/builtins/intl/date_time_format.rs @@ -17,7 +17,7 @@ use crate::{ js_string, object::{internal_methods::get_prototype_from_constructor, JsObject}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsData, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/intl/list_format/mod.rs b/core/engine/src/builtins/intl/list_format/mod.rs index c76889ffaed..6bc069570ef 100644 --- a/core/engine/src/builtins/intl/list_format/mod.rs +++ b/core/engine/src/builtins/intl/list_format/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsData, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/intl/locale/mod.rs b/core/engine/src/builtins/intl/locale/mod.rs index a70ab770f63..9c1afe0bd2a 100644 --- a/core/engine/src/builtins/intl/locale/mod.rs +++ b/core/engine/src/builtins/intl/locale/mod.rs @@ -1,4 +1,4 @@ -use crate::{builtins::options::get_option, realm::Realm, string::common::StaticJsStrings}; +use crate::{builtins::options::get_option, realm::Realm, string::StaticJsStrings}; use boa_macros::js_str; use boa_profiler::Profiler; use icu_collator::CaseFirst; diff --git a/core/engine/src/builtins/intl/mod.rs b/core/engine/src/builtins/intl/mod.rs index 149babe56b9..5f033a75682 100644 --- a/core/engine/src/builtins/intl/mod.rs +++ b/core/engine/src/builtins/intl/mod.rs @@ -20,7 +20,7 @@ use crate::{ object::JsObject, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsData, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index 3b63fca375d..a94d8e56ccd 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -32,7 +32,7 @@ use crate::{ }, property::{Attribute, PropertyDescriptor}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, value::PreferredType, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, NativeFunction, diff --git a/core/engine/src/builtins/intl/plural_rules/mod.rs b/core/engine/src/builtins/intl/plural_rules/mod.rs index d0dad83b308..917e85c1917 100644 --- a/core/engine/src/builtins/intl/plural_rules/mod.rs +++ b/core/engine/src/builtins/intl/plural_rules/mod.rs @@ -21,7 +21,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, ObjectInitializer}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsStr, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/intl/segmenter/mod.rs b/core/engine/src/builtins/intl/segmenter/mod.rs index 517e359b688..7353acfef5f 100644 --- a/core/engine/src/builtins/intl/segmenter/mod.rs +++ b/core/engine/src/builtins/intl/segmenter/mod.rs @@ -18,7 +18,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectInitializer}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsResult, JsStr, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index af6a4513baa..52e8debde26 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -27,7 +27,7 @@ use crate::{ object::{internal_methods::InternalMethodContext, JsObject}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::{common::StaticJsStrings, CodePoint}, + string::{CodePoint, StaticJsStrings}, symbol::JsSymbol, value::IntegerOrInfinity, vm::{CallFrame, CallFrameFlags}, diff --git a/core/engine/src/builtins/map/mod.rs b/core/engine/src/builtins/map/mod.rs index c5c4edee468..ef8eaad1e58 100644 --- a/core/engine/src/builtins/map/mod.rs +++ b/core/engine/src/builtins/map/mod.rs @@ -18,7 +18,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/math/mod.rs b/core/engine/src/builtins/math/mod.rs index 13d5748c071..424aed1e2dc 100644 --- a/core/engine/src/builtins/math/mod.rs +++ b/core/engine/src/builtins/math/mod.rs @@ -13,8 +13,8 @@ use crate::{ builtins::BuiltInObject, context::intrinsics::Intrinsics, js_string, object::JsObject, - property::Attribute, realm::Realm, string::common::StaticJsStrings, symbol::JsSymbol, Context, - JsArgs, JsResult, JsString, JsValue, + property::Attribute, realm::Realm, string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, + JsResult, JsString, JsValue, }; use boa_profiler::Profiler; diff --git a/core/engine/src/builtins/number/globals.rs b/core/engine/src/builtins/number/globals.rs index 1900fa850c0..9564d4de924 100644 --- a/core/engine/src/builtins/number/globals.rs +++ b/core/engine/src/builtins/number/globals.rs @@ -3,7 +3,7 @@ use crate::{ context::intrinsics::Intrinsics, object::JsObject, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsResult, JsStr, JsString, JsValue, }; diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index 025c25acf6e..73345912782 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -21,7 +21,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, value::{AbstractRelation, IntegerOrInfinity, JsValue}, Context, JsArgs, JsResult, JsString, }; diff --git a/core/engine/src/builtins/object/mod.rs b/core/engine/src/builtins/object/mod.rs index a0fe6351913..92d2abe8352 100644 --- a/core/engine/src/builtins/object/mod.rs +++ b/core/engine/src/builtins/object/mod.rs @@ -28,7 +28,7 @@ use crate::{ }, property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsData, JsResult, JsString, diff --git a/core/engine/src/builtins/promise/mod.rs b/core/engine/src/builtins/promise/mod.rs index b3fc4d103fa..0578ad98eb4 100644 --- a/core/engine/src/builtins/promise/mod.rs +++ b/core/engine/src/builtins/promise/mod.rs @@ -17,7 +17,7 @@ use crate::{ }, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsError, JsResult, JsString, diff --git a/core/engine/src/builtins/proxy/mod.rs b/core/engine/src/builtins/proxy/mod.rs index ea0d0329107..ff6327c63c5 100644 --- a/core/engine/src/builtins/proxy/mod.rs +++ b/core/engine/src/builtins/proxy/mod.rs @@ -26,7 +26,7 @@ use crate::{ }, property::{PropertyDescriptor, PropertyKey}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, value::Type, Context, JsArgs, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/reflect/mod.rs b/core/engine/src/builtins/reflect/mod.rs index f4d405adf76..841a24d4b3d 100644 --- a/core/engine/src/builtins/reflect/mod.rs +++ b/core/engine/src/builtins/reflect/mod.rs @@ -19,7 +19,7 @@ use crate::{ object::{internal_methods::InternalMethodContext, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/regexp/mod.rs b/core/engine/src/builtins/regexp/mod.rs index 34be1ef9654..beb782af1ac 100644 --- a/core/engine/src/builtins/regexp/mod.rs +++ b/core/engine/src/builtins/regexp/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, CONSTRUCTOR}, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, CodePoint, JsStrVariant}, + string::{CodePoint, JsStrVariant, StaticJsStrings}, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsData, JsResult, JsString, diff --git a/core/engine/src/builtins/set/mod.rs b/core/engine/src/builtins/set/mod.rs index a82f8c973fc..2927a9e8466 100644 --- a/core/engine/src/builtins/set/mod.rs +++ b/core/engine/src/builtins/set/mod.rs @@ -26,7 +26,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyNameKind}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index a49f08a1866..f0a3d8ce34b 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::{Attribute, PropertyDescriptor}, realm::Realm, - string::{common::StaticJsStrings, CodePoint}, + string::{CodePoint, StaticJsStrings}, symbol::JsSymbol, value::IntegerOrInfinity, Context, JsArgs, JsResult, JsString, JsValue, @@ -73,23 +73,6 @@ pub(crate) const fn is_trimmable_whitespace(c: char) -> bool { ) } -/// Helper function to check if a `u8` latin1 character is trimmable. -pub(crate) const fn is_trimmable_whitespace_latin1(c: u8) -> bool { - // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does - // - // Rust uses \p{White_Space} by default, which also includes: - // `\u{0085}' (next line) - // And does not include: - // '\u{FEFF}' (zero width non-breaking space) - // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space - matches!( - c, - 0x09 | 0x0B | 0x0C | 0x20 | 0xA0 | - // Line terminators: https://tc39.es/ecma262/#sec-line-terminators - 0x0A | 0x0D - ) -} - /// JavaScript `String` implementation. #[derive(Debug, Clone, Copy)] pub(crate) struct String; diff --git a/core/engine/src/builtins/symbol/mod.rs b/core/engine/src/builtins/symbol/mod.rs index dd50afb1048..ca18aee702a 100644 --- a/core/engine/src/builtins/symbol/mod.rs +++ b/core/engine/src/builtins/symbol/mod.rs @@ -28,7 +28,7 @@ use crate::{ object::JsObject, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsResult, JsString, diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index 2da4335a92b..489e8f0163d 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -18,7 +18,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{custom_trace, Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 8e242ae44ae..f22ef7599c1 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -10,7 +10,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index 48335b5db36..d4c927e4ca4 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -14,7 +14,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index adb26887809..d8aa749aaaf 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -33,7 +33,7 @@ use crate::{ js_string, property::{Attribute, PropertyKey}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, value::Type, Context, JsBigInt, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/temporal/now.rs b/core/engine/src/builtins/temporal/now.rs index 4e13a3424d8..05782441442 100644 --- a/core/engine/src/builtins/temporal/now.rs +++ b/core/engine/src/builtins/temporal/now.rs @@ -9,7 +9,7 @@ use crate::{ js_string, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, sys::time::SystemTime, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index aac77d7e019..558d8121a67 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -13,7 +13,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 462b1caf338..55e4d815621 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -11,7 +11,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/plain_month_day/mod.rs b/core/engine/src/builtins/temporal/plain_month_day/mod.rs index 3cdc17652bf..62e483ab2bd 100644 --- a/core/engine/src/builtins/temporal/plain_month_day/mod.rs +++ b/core/engine/src/builtins/temporal/plain_month_day/mod.rs @@ -6,7 +6,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index 846b23cc63c..adba3b53ed6 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -10,7 +10,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/plain_year_month/mod.rs b/core/engine/src/builtins/temporal/plain_year_month/mod.rs index c651534b5d4..4b06c7ae46c 100644 --- a/core/engine/src/builtins/temporal/plain_year_month/mod.rs +++ b/core/engine/src/builtins/temporal/plain_year_month/mod.rs @@ -7,7 +7,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/time_zone/mod.rs b/core/engine/src/builtins/temporal/time_zone/mod.rs index 826cf94bb52..a1e35e93e6a 100644 --- a/core/engine/src/builtins/temporal/time_zone/mod.rs +++ b/core/engine/src/builtins/temporal/time_zone/mod.rs @@ -11,7 +11,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, CONSTRUCTOR}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{custom_trace, Finalize, Trace}; diff --git a/core/engine/src/builtins/temporal/zoned_date_time/mod.rs b/core/engine/src/builtins/temporal/zoned_date_time/mod.rs index d95f7de02f3..3c0e8691d50 100644 --- a/core/engine/src/builtins/temporal/zoned_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/zoned_date_time/mod.rs @@ -4,7 +4,7 @@ use crate::{ context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{custom_trace, Finalize, Trace}; diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 9119edd361c..faa4961e6e1 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -24,7 +24,7 @@ use crate::{ object::internal_methods::get_prototype_from_constructor, property::{Attribute, PropertyNameKind}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, value::IntegerOrInfinity, Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/builtins/typed_array/mod.rs b/core/engine/src/builtins/typed_array/mod.rs index 15f63e0fe37..8bbdf820a6d 100644 --- a/core/engine/src/builtins/typed_array/mod.rs +++ b/core/engine/src/builtins/typed_array/mod.rs @@ -23,7 +23,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, value::{JsValue, Numeric}, Context, JsArgs, JsResult, JsString, diff --git a/core/engine/src/builtins/uri/mod.rs b/core/engine/src/builtins/uri/mod.rs index 3389a36f6db..0e7fb3d4764 100644 --- a/core/engine/src/builtins/uri/mod.rs +++ b/core/engine/src/builtins/uri/mod.rs @@ -25,7 +25,7 @@ use crate::{ js_string, object::{JsFunction, JsObject}, realm::Realm, - string::{common::StaticJsStrings, CodePoint}, + string::{CodePoint, StaticJsStrings}, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/weak/weak_ref.rs b/core/engine/src/builtins/weak/weak_ref.rs index 8442e6ac1bc..a19ec15a7cc 100644 --- a/core/engine/src/builtins/weak/weak_ref.rs +++ b/core/engine/src/builtins/weak/weak_ref.rs @@ -8,7 +8,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, ErasedVTableObject, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/weak_map/mod.rs b/core/engine/src/builtins/weak_map/mod.rs index 01cd63c0a98..57e2bd588e9 100644 --- a/core/engine/src/builtins/weak_map/mod.rs +++ b/core/engine/src/builtins/weak_map/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, ErasedVTableObject, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/builtins/weak_set/mod.rs b/core/engine/src/builtins/weak_set/mod.rs index d90a8257725..493051b873a 100644 --- a/core/engine/src/builtins/weak_set/mod.rs +++ b/core/engine/src/builtins/weak_set/mod.rs @@ -14,7 +14,7 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, ErasedVTableObject, JsObject}, property::Attribute, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, symbol::JsSymbol, Context, JsArgs, JsNativeError, JsResult, JsString, JsValue, }; diff --git a/core/engine/src/object/mod.rs b/core/engine/src/object/mod.rs index ffffaa5579f..b6696e6e665 100644 --- a/core/engine/src/object/mod.rs +++ b/core/engine/src/object/mod.rs @@ -23,7 +23,7 @@ use crate::{ native_function::{NativeFunction, NativeFunctionObject}, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, Context, JsStr, JsString, JsSymbol, JsValue, }; diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index 6cee0140e11..0301d6c10b6 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -9,7 +9,7 @@ use crate::{ object::{JsObject, PrivateElement, PrivateName, CONSTRUCTOR, PROTOTYPE}, property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind}, realm::Realm, - string::common::StaticJsStrings, + string::StaticJsStrings, value::Type, Context, JsResult, JsSymbol, JsValue, }; diff --git a/core/engine/src/string.rs b/core/engine/src/string.rs new file mode 100644 index 00000000000..cfddb9328ce --- /dev/null +++ b/core/engine/src/string.rs @@ -0,0 +1,211 @@ +//! This module contains the [`js_string`][crate::js_string] macro and the +//! [`js_str`][crate::js_str] macro. +//! +//! The [`js_string`][crate::js_string] macro is used when you need to create a new [`JsString`], +//! and the [`js_str`][crate::js_str] macro is used for const conversions of string literals to [`JsStr`]. + +#[doc(inline)] +pub use boa_string::*; + +/// Utility macro to create a [`JsString`]. +/// +/// # Examples +/// +/// You can call the macro without arguments to create an empty `JsString`: +/// +/// ``` +/// use boa_engine::js_string; +/// +/// let empty_str = js_string!(); +/// assert!(empty_str.is_empty()); +/// ``` +/// +/// +/// You can create a `JsString` from a string literal, which completely skips the runtime +/// conversion from [`&str`] to [&\[u16\]][slice]: +/// +/// ``` +/// # use boa_engine::js_string; +/// let hw = js_string!("Hello, world!"); +/// assert_eq!(&hw, "Hello, world!"); +/// ``` +/// +/// Any `&[u16]` slice is a valid `JsString`, including unpaired surrogates: +/// +/// ``` +/// # use boa_engine::js_string; +/// let array = js_string!(&[0xD8AFu16, 0x00A0, 0xD8FF, 0x00F0]); +/// ``` +/// +/// You can also pass it any number of `&[u16]` as arguments to create a new `JsString` with +/// the concatenation of every slice: +/// +/// ``` +/// # use boa_engine::{js_string, js_str, JsStr}; +/// const NAME: JsStr<'_> = js_str!("human! "); +/// let greeting = js_string!("Hello, "); +/// let msg = js_string!(&greeting, NAME, js_str!("Nice to meet you!")); +/// +/// assert_eq!(&msg, "Hello, human! Nice to meet you!"); +/// ``` +#[macro_export] +#[allow(clippy::module_name_repetitions)] +macro_rules! js_string { + () => { + $crate::string::JsString::default() + }; + ($s:literal) => { + $crate::string::JsString::from($crate::js_str!($s)) + }; + ($s:expr) => { + $crate::string::JsString::from($s) + }; + ( $x:expr, $y:expr ) => { + $crate::string::JsString::concat($crate::string::JsStr::from($x), $crate::string::JsStr::from($y)) + }; + ( $( $s:expr ),+ ) => { + $crate::string::JsString::concat_array(&[ $( $crate::string::JsStr::from($s) ),+ ]) + }; +} + +#[allow(clippy::redundant_clone)] +#[cfg(test)] +mod tests { + use std::hash::{BuildHasher, BuildHasherDefault, Hash}; + + use crate::{string::StaticJsStrings, JsStr}; + + use super::JsString; + use boa_macros::{js_str, utf16}; + use rustc_hash::FxHasher; + + fn hash_value(value: &T) -> u64 { + BuildHasherDefault::::default().hash_one(value) + } + + #[test] + fn empty() { + let s = js_string!(); + assert_eq!(&s, utf16!("")); + } + + #[test] + fn refcount() { + let x = js_string!("Hello world"); + assert_eq!(x.refcount(), Some(1)); + + { + let y = x.clone(); + assert_eq!(x.refcount(), Some(2)); + assert_eq!(y.refcount(), Some(2)); + + { + let z = y.clone(); + assert_eq!(x.refcount(), Some(3)); + assert_eq!(y.refcount(), Some(3)); + assert_eq!(z.refcount(), Some(3)); + } + + assert_eq!(x.refcount(), Some(2)); + assert_eq!(y.refcount(), Some(2)); + } + + assert_eq!(x.refcount(), Some(1)); + } + + #[test] + fn static_refcount() { + let x = js_string!(); + assert_eq!(x.refcount(), None); + + { + let y = x.clone(); + assert_eq!(x.refcount(), None); + assert_eq!(y.refcount(), None); + }; + + assert_eq!(x.refcount(), None); + } + + #[test] + fn as_str() { + const HELLO: &[u16] = utf16!("Hello"); + let x = js_string!(HELLO); + + assert_eq!(&x, HELLO); + } + + #[test] + fn hash() { + const HELLOWORLD: JsStr<'_> = js_str!("Hello World!"); + let x = js_string!(HELLOWORLD); + + assert_eq!(x.as_str(), HELLOWORLD); + + assert!(HELLOWORLD.is_latin1()); + assert!(x.as_str().is_latin1()); + + let s_hash = hash_value(&HELLOWORLD); + let x_hash = hash_value(&x); + + assert_eq!(s_hash, x_hash); + } + + #[test] + fn concat() { + const Y: &[u16] = utf16!(", "); + const W: &[u16] = utf16!("!"); + + let x = js_string!("hello"); + let z = js_string!("world"); + + let xy = js_string!(&x, &JsString::from(Y)); + assert_eq!(&xy, utf16!("hello, ")); + assert_eq!(xy.refcount(), Some(1)); + + let xyz = js_string!(&xy, &z); + assert_eq!(&xyz, utf16!("hello, world")); + assert_eq!(xyz.refcount(), Some(1)); + + let xyzw = js_string!(&xyz, &JsString::from(W)); + assert_eq!(&xyzw, utf16!("hello, world!")); + assert_eq!(xyzw.refcount(), Some(1)); + } + + #[test] + fn trim_start_non_ascii_to_ascii() { + let s = "\u{2029}abc"; + let x = js_string!(s); + + let y = js_string!(x.trim_start()); + + assert_eq!(&y, s.trim_start()); + } + + #[test] + fn conversion_to_known_static_js_string() { + const JS_STR_U8: &JsStr<'_> = &js_str!("length"); + const JS_STR_U16: &JsStr<'_> = &JsStr::utf16(utf16!("length")); + + assert!(JS_STR_U8.is_latin1()); + assert!(!JS_STR_U16.is_latin1()); + + assert_eq!(JS_STR_U8, JS_STR_U8); + assert_eq!(JS_STR_U16, JS_STR_U16); + + assert_eq!(JS_STR_U8, JS_STR_U16); + assert_eq!(JS_STR_U16, JS_STR_U8); + + assert_eq!(hash_value(JS_STR_U8), hash_value(JS_STR_U16)); + + let string = StaticJsStrings::get_string(JS_STR_U8); + + assert!(string.is_some()); + assert!(string.unwrap().as_str().is_latin1()); + + let string = StaticJsStrings::get_string(JS_STR_U16); + + assert!(string.is_some()); + assert!(string.unwrap().as_str().is_latin1()); + } +} diff --git a/core/engine/src/string/common.rs b/core/engine/src/string/common.rs deleted file mode 100644 index ba7785d7f63..00000000000 --- a/core/engine/src/string/common.rs +++ /dev/null @@ -1,950 +0,0 @@ -//! List of commonly used strings in Javascript code. - -use crate::{tagged::Tagged, JsStr}; - -use super::JsString; -use boa_macros::js_str; -use paste::paste; -use rustc_hash::{FxHashMap, FxHasher}; -use std::hash::BuildHasherDefault; - -macro_rules! well_known_statics { - ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { - $( - paste!{ - #[doc = "Gets the static `JsString` for `\"" $string "\"`."] - pub const $name: JsString = JsString { - ptr: Tagged::from_tag( - Self::find_index($string), - ), - }; - } - )+ - }; -} - -/// List of commonly used strings in Javascript code. -/// -/// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap. -#[derive(Debug, Clone, Copy)] -pub struct StaticJsStrings; - -impl StaticJsStrings { - // useful to search at compile time a certain string in the array - const fn find_index(candidate: &str) -> usize { - const fn const_eq(lhs: &[u8], rhs: &[u8]) -> bool { - if lhs.len() != rhs.len() { - return false; - } - - let mut i = 0; - while i < lhs.len() { - if lhs[i] != rhs[i] { - return false; - } - i += 1; - } - true - } - - let len = RAW_STATICS.len(); - let mut i = 0; - while i < len { - let Some(s) = RAW_STATICS[i].as_latin1() else { - // All static strings are latin1 encoded - unreachable!() - }; - if const_eq(s, candidate.as_bytes()) { - return i; - } - i += 1; - } - - panic!("couldn't find the required string on the common string array"); - } - - /// Gets the `JsString` corresponding to `string`, or `None` if the string - /// doesn't exist inside the static array. - pub(crate) fn get_string(string: &JsStr<'_>) -> Option { - if string.len() > MAX_STATIC_LENGTH { - return None; - } - - let index = RAW_STATICS_CACHE.with(|map| map.get(string).copied())?; - - Some(JsString { - ptr: Tagged::from_tag(index), - }) - } - - /// Gets the `&[u16]` slice corresponding to the provided index, or `None` if the index - /// provided exceeds the size of the static array. - pub(crate) fn get(index: usize) -> Option> { - RAW_STATICS.get(index).copied() - } - - // Some consts are only used on certain features, which triggers the unused lint. - well_known_statics! { - (EMPTY_STRING, ""), - (LENGTH, "length"), - // Symbols - (SYMBOL_ASYNC_ITERATOR, "Symbol.asyncIterator"), - (SYMBOL_HAS_INSTANCE, "Symbol.hasInstance"), - (SYMBOL_IS_CONCAT_SPREADABLE, "Symbol.isConcatSpreadable"), - (SYMBOL_ITERATOR, "Symbol.iterator"), - (SYMBOL_MATCH, "Symbol.match"), - (SYMBOL_MATCH_ALL, "Symbol.matchAll"), - (SYMBOL_REPLACE, "Symbol.replace"), - (SYMBOL_SEARCH, "Symbol.search"), - (SYMBOL_SPECIES, "Symbol.species"), - (SYMBOL_SPLIT, "Symbol.split"), - (SYMBOL_TO_PRIMITIVE, "Symbol.toPrimitive"), - (SYMBOL_TO_STRING_TAG, "Symbol.toStringTag"), - (SYMBOL_UNSCOPABLES, "Symbol.unscopables"), - (FN_SYMBOL_ASYNC_ITERATOR, "[Symbol.asyncIterator]"), - (FN_SYMBOL_HAS_INSTANCE, "[Symbol.hasInstance]"), - (FN_SYMBOL_IS_CONCAT_SPREADABLE, "[Symbol.isConcatSpreadable]"), - (FN_SYMBOL_ITERATOR, "[Symbol.iterator]"), - (FN_SYMBOL_MATCH, "[Symbol.match]"), - (FN_SYMBOL_MATCH_ALL, "[Symbol.matchAll]"), - (FN_SYMBOL_REPLACE, "[Symbol.replace]"), - (FN_SYMBOL_SEARCH, "[Symbol.search]"), - (FN_SYMBOL_SPECIES, "[Symbol.species]"), - (FN_SYMBOL_SPLIT, "[Symbol.split]"), - (FN_SYMBOL_TO_PRIMITIVE, "[Symbol.toPrimitive]"), - (FN_SYMBOL_TO_STRING_TAG, "[Symbol.toStringTag]"), - (FN_SYMBOL_UNSCOPABLES, "[Symbol.unscopables]"), - // Builtins - (ARRAY, "Array"), - (ARRAY_BUFFER, "ArrayBuffer"), - (SHARED_ARRAY_BUFFER, "SharedArrayBuffer"), - (ASYNC_FUNCTION, "AsyncFunction"), - (ASYNC_GENERATOR, "AsyncGenerator"), - (ASYNC_GENERATOR_FUNCTION, "AsyncGeneratorFunction"), - (ATOMICS, "Atomics"), - (BIG_INT, "BigInt"), - (BOOLEAN, "Boolean"), - (DATA_VIEW, "DataView"), - (DATE, "Date"), - (ERROR, "Error"), - (AGGREGATE_ERROR, "AggregateError"), - (EVAL_ERROR, "EvalError"), - (RANGE_ERROR, "RangeError"), - (REFERENCE_ERROR, "ReferenceError"), - (SYNTAX_ERROR, "SyntaxError"), - (TYPE_ERROR, "TypeError"), - (URI_ERROR, "URIError"), - (ESCAPE, "escape"), - (UNESCAPE, "unescape"), - (EVAL, "eval"), - (FUNCTION, "Function"), - (GENERATOR, "Generator"), - (GENERATOR_FUNCTION, "GeneratorFunction"), - (INTL, "Intl"), - (COLLATOR, "Collator"), - (LIST_FORMAT, "ListFormat"), - (LOCALE, "Locale"), - (PLURAL_RULES, "PluralRules"), - (SEGMENTER, "Segmenter"), - (DATE_TIME_FORMAT, "DateTimeFormat"), - (JSON, "JSON"), - (MAP, "Map"), - (MATH, "Math"), - (NUMBER, "Number"), - (NUMBER_FORMAT, "NumberFormat"), - (IS_FINITE, "isFinite"), - (IS_NAN, "isNaN"), - (PARSE_INT, "parseInt"), - (PARSE_FLOAT, "parseFloat"), - (OBJECT, "Object"), - (PROMISE, "Promise"), - (PROXY, "Proxy"), - (REFLECT, "Reflect"), - (REG_EXP, "RegExp"), - (SET, "Set"), - (STRING, "String"), - (SYMBOL, "Symbol"), - (TYPED_ARRAY, "TypedArray"), - (INT8_ARRAY, "Int8Array"), - (UINT8_ARRAY, "Uint8Array"), - (UINT8_CLAMPED_ARRAY, "Uint8ClampedArray"), - (INT16_ARRAY, "Int16Array"), - (UINT16_ARRAY, "Uint16Array"), - (INT32_ARRAY, "Int32Array"), - (UINT32_ARRAY, "Uint32Array"), - (BIG_INT64_ARRAY, "BigInt64Array"), - (BIG_UINT64_ARRAY, "BigUint64Array"), - (FLOAT32_ARRAY, "Float32Array"), - (FLOAT64_ARRAY, "Float64Array"), - (ENCODE_URI, "encodeURI"), - (ENCODE_URI_COMPONENT, "encodeURIComponent"), - (DECODE_URI, "decodeURI"), - (DECODE_URI_COMPONENT, "decodeURIComponent"), - (WEAK_REF, "WeakRef"), - (WEAK_MAP, "WeakMap"), - (WEAK_SET, "WeakSet"), - (TEMPORAL, "Temporal"), - (NOW, "Temporal.Now"), - (INSTANT, "Temporal.Instant"), - (DURATION, "Temporal.Duration"), - (PLAIN_DATE, "Temporal.PlainDate"), - (PLAIN_DATETIME, "Temporal.PlainDateTime"), - (PLAIN_TIME, "Temporal.PlainTime"), - (PLAIN_YM, "Temporal.PlainYearMonth"), - (PLAIN_MD, "Temporal.PlainMonthDay"), - (CALENDAR, "Temporal.Calendar"), - (TIMEZONE, "Temporal.TimeZone"), - (ZONED_DT, "Temporal.ZonedDateTime"), - } -} - -const MAX_STATIC_LENGTH: usize = { - let mut max = 0; - let mut i = 0; - while i < RAW_STATICS.len() { - // TOOD: Because `get_index` is not const, we are accessing doc hidden stuff, that may change. - let len = RAW_STATICS[i].len(); - if len > max { - max = len; - } - i += 1; - } - max -}; - -thread_local! { - /// Map from a string inside [`RAW_STATICS`] to its corresponding static index on `RAW_STATICS`. - static RAW_STATICS_CACHE: FxHashMap, usize> = { - let mut constants = FxHashMap::with_capacity_and_hasher( - RAW_STATICS.len(), - BuildHasherDefault::::default(), - ); - - for (idx, &s) in RAW_STATICS.iter().enumerate() { - constants.insert(s, idx); - } - - constants - }; -} - -/// Array of raw static strings that aren't reference counted. -const RAW_STATICS: &[JsStr<'_>] = &[ - js_str!(""), - // Well known symbols - js_str!("Symbol.asyncIterator"), - js_str!("[Symbol.asyncIterator]"), - js_str!("Symbol.hasInstance"), - js_str!("[Symbol.hasInstance]"), - js_str!("Symbol.isConcatSpreadable"), - js_str!("[Symbol.isConcatSpreadable]"), - js_str!("Symbol.iterator"), - js_str!("[Symbol.iterator]"), - js_str!("Symbol.match"), - js_str!("[Symbol.match]"), - js_str!("Symbol.matchAll"), - js_str!("[Symbol.matchAll]"), - js_str!("Symbol.replace"), - js_str!("[Symbol.replace]"), - js_str!("Symbol.search"), - js_str!("[Symbol.search]"), - js_str!("Symbol.species"), - js_str!("[Symbol.species]"), - js_str!("Symbol.split"), - js_str!("[Symbol.split]"), - js_str!("Symbol.toPrimitive"), - js_str!("[Symbol.toPrimitive]"), - js_str!("Symbol.toStringTag"), - js_str!("[Symbol.toStringTag]"), - js_str!("Symbol.unscopables"), - js_str!("[Symbol.unscopables]"), - js_str!("get [Symbol.species]"), - js_str!("get [Symbol.toStringTag]"), - // Well known builtins - js_str!("Array"), - js_str!("ArrayBuffer"), - js_str!("SharedArrayBuffer"), - js_str!("AsyncFunction"), - js_str!("AsyncGenerator"), - js_str!("AsyncGeneratorFunction"), - js_str!("Atomics"), - js_str!("BigInt"), - js_str!("Boolean"), - js_str!("DataView"), - js_str!("Date"), - js_str!("Error"), - js_str!("AggregateError"), - js_str!("EvalError"), - js_str!("RangeError"), - js_str!("ReferenceError"), - js_str!("SyntaxError"), - js_str!("TypeError"), - js_str!("URIError"), - js_str!("escape"), - js_str!("unescape"), - js_str!("eval"), - js_str!("Function"), - js_str!("Generator"), - js_str!("GeneratorFunction"), - js_str!("Intl"), - js_str!("Collator"), - js_str!("ListFormat"), - js_str!("Locale"), - js_str!("PluralRules"), - js_str!("Segmenter"), - js_str!("DateTimeFormat"), - js_str!("JSON"), - js_str!("Map"), - js_str!("Math"), - js_str!("Number"), - js_str!("NumberFormat"), - js_str!("isFinite"), - js_str!("isNaN"), - js_str!("parseInt"), - js_str!("parseFloat"), - js_str!("Object"), - js_str!("Promise"), - js_str!("Proxy"), - js_str!("Reflect"), - js_str!("RegExp"), - js_str!("Set"), - js_str!("String"), - js_str!("Symbol"), - js_str!("TypedArray"), - js_str!("Int8Array"), - js_str!("Uint8Array"), - js_str!("Uint8ClampedArray"), - js_str!("Int16Array"), - js_str!("Uint16Array"), - js_str!("Int32Array"), - js_str!("Uint32Array"), - js_str!("BigInt64Array"), - js_str!("BigUint64Array"), - js_str!("Float32Array"), - js_str!("Float64Array"), - js_str!("encodeURI"), - js_str!("encodeURIComponent"), - js_str!("decodeURI"), - js_str!("decodeURIComponent"), - js_str!("WeakRef"), - js_str!("WeakMap"), - js_str!("WeakSet"), - js_str!("Temporal"), - js_str!("Temporal.Now"), - js_str!("Temporal.Instant"), - js_str!("Temporal.Duration"), - js_str!("Temporal.Calendar"), - js_str!("Temporal.PlainDate"), - js_str!("Temporal.PlainDateTime"), - js_str!("Temporal.PlainMonthDay"), - js_str!("Temporal.PlainYearMonth"), - js_str!("Temporal.PlainTime"), - js_str!("Temporal.TimeZone"), - js_str!("Temporal.ZonedDateTime"), - // Misc - js_str!(","), - js_str!(":"), - // Generic use - js_str!("name"), - js_str!("length"), - js_str!("arguments"), - js_str!("prototype"), - js_str!("constructor"), - js_str!("return"), - js_str!("throw"), - js_str!("global"), - js_str!("globalThis"), - // typeof - js_str!("null"), - js_str!("undefined"), - js_str!("number"), - js_str!("string"), - js_str!("symbol"), - js_str!("bigint"), - js_str!("object"), - js_str!("function"), - // Property descriptor - js_str!("value"), - js_str!("get"), - js_str!("set"), - js_str!("writable"), - js_str!("enumerable"), - js_str!("configurable"), - // Object object - js_str!("assign"), - js_str!("create"), - js_str!("toString"), - js_str!("valueOf"), - js_str!("is"), - js_str!("seal"), - js_str!("isSealed"), - js_str!("freeze"), - js_str!("isFrozen"), - js_str!("isExtensible"), - js_str!("hasOwnProperty"), - js_str!("isPrototypeOf"), - js_str!("setPrototypeOf"), - js_str!("getPrototypeOf"), - js_str!("defineProperty"), - js_str!("defineProperties"), - js_str!("deleteProperty"), - js_str!("construct"), - js_str!("hasOwn"), - js_str!("ownKeys"), - js_str!("keys"), - js_str!("values"), - js_str!("entries"), - js_str!("fromEntries"), - js_str!("propertyIsEnumerable"), - js_str!("preventExtensions"), - js_str!("getOwnPropertyDescriptor"), - js_str!("getOwnPropertyDescriptors"), - js_str!("getOwnPropertyNames"), - js_str!("getOwnPropertySymbols"), - js_str!("__defineGetter__"), - js_str!("__defineSetter__"), - js_str!("__lookupGetter__"), - js_str!("__lookupSetter__"), - js_str!("__proto__"), - js_str!("get __proto__"), - js_str!("set __proto__"), - // Function object - js_str!("apply"), - js_str!("bind"), - js_str!("call"), - js_str!("caller"), - // Arguments object - js_str!("callee"), - // Array object - js_str!("at"), - js_str!("from"), - js_str!("isArray"), - js_str!("of"), - js_str!("copyWithin"), - js_str!("every"), - js_str!("fill"), - js_str!("filter"), - js_str!("find"), - js_str!("findIndex"), - js_str!("findLast"), - js_str!("findLastIndex"), - js_str!("flat"), - js_str!("flatMap"), - js_str!("forEach"), - js_str!("includes"), - js_str!("indexOf"), - js_str!("join"), - js_str!("map"), - js_str!("next"), - js_str!("reduce"), - js_str!("reduceRight"), - js_str!("reverse"), - js_str!("shift"), - js_str!("slice"), - js_str!("splice"), - js_str!("some"), - js_str!("sort"), - js_str!("unshift"), - js_str!("push"), - js_str!("pop"), - js_str!("groupBy"), - js_str!("toReversed"), - js_str!("toSorted"), - js_str!("toSpliced"), - js_str!("with"), - // String object - js_str!("charAt"), - js_str!("charCodeAt"), - js_str!("codePointAt"), - js_str!("concat"), - js_str!("endsWith"), - js_str!("fromCharCode"), - js_str!("fromCodePoint"), - js_str!("lastIndexOf"), - js_str!("match"), - js_str!("matchAll"), - js_str!("normalize"), - js_str!("padEnd"), - js_str!("padStart"), - js_str!("raw"), - js_str!("repeat"), - js_str!("replace"), - js_str!("replaceAll"), - js_str!("search"), - js_str!("split"), - js_str!("startsWith"), - js_str!("substr"), - js_str!("substring"), - js_str!("toLocaleString"), - js_str!("toLowerCase"), - js_str!("toUpperCase"), - js_str!("trim"), - js_str!("trimEnd"), - js_str!("trimStart"), - js_str!("isWellFormed"), - js_str!("localeCompare"), - js_str!("toWellFormed"), - js_str!("toLocaleLowerCase"), - js_str!("toLocaleUpperCase"), - js_str!("trimLeft"), - js_str!("trimRight"), - js_str!("anchor"), - js_str!("big"), - js_str!("blink"), - js_str!("bold"), - js_str!("fixed"), - js_str!("fontcolor"), - js_str!("fontsize"), - js_str!("italics"), - js_str!("link"), - js_str!("small"), - js_str!("strike"), - js_str!("sub"), - js_str!("sup"), - // Number object - js_str!("Infinity"), - js_str!("NaN"), - js_str!("EPSILON"), - js_str!("MAX_SAFE_INTEGER"), - js_str!("MIN_SAFE_INTEGER"), - js_str!("MAX_VALUE"), - js_str!("MIN_VALUE"), - js_str!("NEGATIVE_INFINITY"), - js_str!("POSITIVE_INFINITY"), - js_str!("isSafeInteger"), - js_str!("isInteger"), - js_str!("toExponential"), - js_str!("toFixed"), - js_str!("toPrecision"), - // BigInt object - js_str!("asIntN"), - js_str!("asUintN"), - // RegExp object - js_str!("exec"), - js_str!("test"), - js_str!("compile"), - js_str!("flags"), - js_str!("index"), - js_str!("lastIndex"), - js_str!("hasIndices"), - js_str!("ignoreCase"), - js_str!("multiline"), - js_str!("dotAll"), - js_str!("unicode"), - js_str!("sticky"), - js_str!("source"), - js_str!("get hasIndices"), - js_str!("get global"), - js_str!("get ignoreCase"), - js_str!("get multiline"), - js_str!("get dotAll"), - js_str!("get unicode"), - js_str!("get sticky"), - js_str!("get flags"), - js_str!("get source"), - // Symbol object - js_str!("for"), - js_str!("keyFor"), - js_str!("description"), - js_str!("asyncIterator"), - js_str!("hasInstance"), - js_str!("species"), - js_str!("unscopables"), - js_str!("iterator"), - js_str!("toStringTag"), - js_str!("toPrimitive"), - js_str!("isConcatSpreadable"), - js_str!("get description"), - // Map object - js_str!("clear"), - js_str!("delete"), - js_str!("has"), - js_str!("size"), - // Set object - js_str!("add"), - // Reflect object - // Proxy object - js_str!("revocable"), - // Error objects - js_str!("message"), - // Date object - js_str!("toJSON"), - js_str!("getDate"), - js_str!("getDay"), - js_str!("getFullYear"), - js_str!("getHours"), - js_str!("getMilliseconds"), - js_str!("getMinutes"), - js_str!("getMonth"), - js_str!("getSeconds"), - js_str!("getTime"), - js_str!("getYear"), - js_str!("getUTCDate"), - js_str!("getUTCDay"), - js_str!("getUTCFullYear"), - js_str!("getUTCHours"), - js_str!("getUTCMinutes"), - js_str!("getUTCMonth"), - js_str!("getUTCSeconds"), - js_str!("setDate"), - js_str!("setFullYear"), - js_str!("setHours"), - js_str!("setMilliseconds"), - js_str!("setMinutes"), - js_str!("setMonth"), - js_str!("setSeconds"), - js_str!("setYear"), - js_str!("setTime"), - js_str!("setUTCDate"), - js_str!("setUTCFullYear"), - js_str!("setUTCHours"), - js_str!("setUTCMinutes"), - js_str!("setUTCMonth"), - js_str!("setUTCSeconds"), - js_str!("toDateString"), - js_str!("toGMTString"), - js_str!("toISOString"), - js_str!("toTimeString"), - js_str!("toUTCString"), - js_str!("now"), - js_str!("UTC"), - js_str!("getTimezoneOffset"), - js_str!("getUTCMilliseconds"), - js_str!("setUTCMilliseconds"), - js_str!("toLocaleDateString"), - js_str!("toLocaleTimeString"), - // JSON object - js_str!("parse"), - js_str!("stringify"), - // Promise object - js_str!("promise"), - js_str!("resolve"), - js_str!("reject"), - js_str!("all"), - js_str!("allSettled"), - js_str!("any"), - js_str!("race"), - js_str!("then"), - js_str!("catch"), - js_str!("finally"), - js_str!("withResolvers"), - // Iterator object - js_str!("Array Iterator"), - js_str!("Set Iterator"), - js_str!("String Iterator"), - js_str!("Map Iterator"), - js_str!("For In Iterator"), - js_str!("RegExp String Iterator"), - // Iterator result object - js_str!("done"), - // Math object - js_str!("LN10"), - js_str!("LN2"), - js_str!("LOG10E"), - js_str!("LOG2E"), - js_str!("PI"), - js_str!("SQRT1_2"), - js_str!("SQRT2"), - js_str!("abs"), - js_str!("acos"), - js_str!("acosh"), - js_str!("asin"), - js_str!("asinh"), - js_str!("atan"), - js_str!("atanh"), - js_str!("atan2"), - js_str!("cbrt"), - js_str!("ceil"), - js_str!("clz32"), - js_str!("cos"), - js_str!("cosh"), - js_str!("exp"), - js_str!("expm1"), - js_str!("floor"), - js_str!("fround"), - js_str!("hypot"), - js_str!("imul"), - js_str!("log"), - js_str!("log1p"), - js_str!("log10"), - js_str!("log2"), - js_str!("max"), - js_str!("min"), - js_str!("pow"), - js_str!("random"), - js_str!("round"), - js_str!("sign"), - js_str!("sin"), - js_str!("sinh"), - js_str!("sqrt"), - js_str!("tan"), - js_str!("tanh"), - js_str!("trunc"), - // TypedArray object - js_str!("BYTES_PER_ELEMENT"), - js_str!("buffer"), - js_str!("byteLength"), - js_str!("byteOffset"), - js_str!("isView"), - js_str!("subarray"), - js_str!("get byteLength"), - js_str!("get buffer"), - js_str!("get byteOffset"), - js_str!("get size"), - js_str!("get length"), - // DataView object - js_str!("getBigInt64"), - js_str!("getBigUint64"), - js_str!("getFloat32"), - js_str!("getFloat64"), - js_str!("getInt8"), - js_str!("getInt16"), - js_str!("getInt32"), - js_str!("getUint8"), - js_str!("getUint16"), - js_str!("getUint32"), - js_str!("setBigInt64"), - js_str!("setBigUint64"), - js_str!("setFloat32"), - js_str!("setFloat64"), - js_str!("setInt8"), - js_str!("setInt16"), - js_str!("setInt32"), - js_str!("setUint8"), - js_str!("setUint16"), - js_str!("setUint32"), - // WeakRef object - js_str!("deref"), - // Atomic object - js_str!("and"), - js_str!("compareExchange"), - js_str!("exchange"), - js_str!("isLockFree"), - js_str!("load"), - js_str!("or"), - js_str!("store"), - js_str!("wait"), - js_str!("notify"), - js_str!("xor"), - // Intl object - js_str!("getCanonicalLocales"), - js_str!("get compare"), - js_str!("supportedLocalesOf"), - js_str!("Intl.Collator"), - js_str!("compare"), - js_str!("resolvedOptions"), - js_str!("Intl.ListFormat"), - js_str!("format"), - js_str!("formatToParts"), - js_str!("get baseName"), - js_str!("get calendar"), - js_str!("get caseFirst"), - js_str!("get collation"), - js_str!("get hourCycle"), - js_str!("get numeric"), - js_str!("get numberingSystem"), - js_str!("get language"), - js_str!("get script"), - js_str!("get region"), - js_str!("Intl.Locale"), - js_str!("maximize"), - js_str!("minimize"), - js_str!("baseName"), - js_str!("calendar"), - js_str!("caseFirst"), - js_str!("collation"), - js_str!("hourCycle"), - js_str!("numeric"), - js_str!("numberingSystem"), - js_str!("language"), - js_str!("script"), - js_str!("region"), - js_str!("Intl.Segmenter"), - js_str!("segment"), - js_str!("containing"), - js_str!("Segmenter String Iterator"), - js_str!("Intl.PluralRules"), - js_str!("select"), - // Temporal object - js_str!("get Id"), - js_str!("getOffsetNanosecondsFor"), - js_str!("getOffsetStringFor"), - js_str!("getPlainDateTimeFor"), - js_str!("getInstantFor"), - js_str!("getPossibleInstantFor"), - js_str!("getNextTransition"), - js_str!("getPreviousTransition"), - js_str!("id"), - js_str!("Now"), - js_str!("Calendar"), - js_str!("Duration"), - js_str!("Instant"), - js_str!("PlainDate"), - js_str!("PlainDateTime"), - js_str!("PlainMonthDay"), - js_str!("PlainTime"), - js_str!("PlainYearMonth"), - js_str!("TimeZone"), - js_str!("ZonedDateTime"), - js_str!("timeZoneId"), - js_str!("instant"), - js_str!("plainDateTime"), - js_str!("plainDateTimeISO"), - js_str!("zonedDateTime"), - js_str!("zonedDateTimeISO"), - js_str!("plainDate"), - js_str!("plainDateISO"), - js_str!("get epochSeconds"), - js_str!("get epochMilliseconds"), - js_str!("get epochMicroseconds"), - js_str!("get epochNanoseconds"), - js_str!("epochSeconds"), - js_str!("epochMilliseconds"), - js_str!("epochMicroseconds"), - js_str!("epochNanoseconds"), - js_str!("subtract"), - js_str!("until"), - js_str!("since"), - js_str!("equals"), - js_str!("toZonedDateTime"), - js_str!("toZonedDateTimeISO"), - js_str!("get Years"), - js_str!("get Months"), - js_str!("get Weeks"), - js_str!("get Days"), - js_str!("get Hours"), - js_str!("get Minutes"), - js_str!("get Seconds"), - js_str!("get Milliseconds"), - js_str!("get Microseconds"), - js_str!("get Nanoseconds"), - js_str!("get Sign"), - js_str!("get blank"), - js_str!("years"), - js_str!("months"), - js_str!("weeks"), - js_str!("days"), - js_str!("hours"), - js_str!("minutes"), - js_str!("seconds"), - js_str!("milliseconds"), - js_str!("microseconds"), - js_str!("nanoseconds"), - js_str!("blank"), - js_str!("negated"), - js_str!("total"), - js_str!("get calendarId"), - js_str!("get year"), - js_str!("get month"), - js_str!("get monthCode"), - js_str!("get day"), - js_str!("get dayOfWeek"), - js_str!("get dayOfYear"), - js_str!("get weekOfYear"), - js_str!("get yearOfWeek"), - js_str!("get daysInWeek"), - js_str!("get daysInMonth"), - js_str!("get daysInYear"), - js_str!("get monthsInYear"), - js_str!("get inLeapYear"), - js_str!("calendarId"), - js_str!("year"), - js_str!("month"), - js_str!("monthCode"), - js_str!("day"), - js_str!("dayOfWeek"), - js_str!("dayOfYear"), - js_str!("weekOfYear"), - js_str!("yearOfWeek"), - js_str!("daysInWeek"), - js_str!("daysInMonth"), - js_str!("daysInYear"), - js_str!("monthsInYear"), - js_str!("inLeapYear"), - js_str!("toPlainYearMonth"), - js_str!("toPlainMonthDay"), - js_str!("getISOFields"), - js_str!("getCalendar"), - js_str!("withCalendar"), - js_str!("dateFromFields"), - js_str!("yearMonthFromFields"), - js_str!("monthDayFromFields"), - js_str!("dateAdd"), - js_str!("dateUntil"), - js_str!("era"), - js_str!("eraYear"), - js_str!("fields"), - js_str!("mergeFields"), - // Console object - js_str!("console"), - js_str!("assert"), - js_str!("debug"), - js_str!("error"), - js_str!("info"), - js_str!("trace"), - js_str!("warn"), - js_str!("exception"), - js_str!("count"), - js_str!("countReset"), - js_str!("group"), - js_str!("groupCollapsed"), - js_str!("groupEnd"), - js_str!("time"), - js_str!("timeLog"), - js_str!("timeEnd"), - js_str!("dir"), - js_str!("dirxml"), - // Minified name - js_str!("a"), - js_str!("c"), - js_str!("d"), - js_str!("e"), - js_str!("f"), - js_str!("g"), - js_str!("h"), - js_str!("i"), - js_str!("j"), - js_str!("k"), - js_str!("l"), - js_str!("m"), - js_str!("n"), - js_str!("o"), - js_str!("p"), - js_str!("q"), - js_str!("r"), - js_str!("s"), - js_str!("t"), - js_str!("u"), - js_str!("v"), - js_str!("w"), - js_str!("x"), - js_str!("y"), - js_str!("z"), - js_str!("A"), - js_str!("C"), - js_str!("D"), - js_str!("E"), - js_str!("F"), - js_str!("G"), - js_str!("H"), - js_str!("I"), - js_str!("J"), - js_str!("K"), - js_str!("L"), - js_str!("M"), - js_str!("N"), - js_str!("O"), - js_str!("P"), - js_str!("Q"), - js_str!("R"), - js_str!("S"), - js_str!("T"), - js_str!("U"), - js_str!("V"), - js_str!("W"), - js_str!("X"), - js_str!("Y"), - js_str!("Z"), - js_str!("_"), - js_str!("$"), -]; diff --git a/core/engine/src/symbol.rs b/core/engine/src/symbol.rs index 11b04dd9e69..a25cf08855b 100644 --- a/core/engine/src/symbol.rs +++ b/core/engine/src/symbol.rs @@ -23,7 +23,7 @@ use crate::{ js_string, - string::{common::StaticJsStrings, JsString}, + string::{JsString, StaticJsStrings}, tagged::{Tagged, UnwrappedTagged}, }; use boa_gc::{Finalize, Trace}; diff --git a/core/engine/src/tagged.rs b/core/engine/src/tagged.rs index 83ec17c24b4..3415b38ed81 100644 --- a/core/engine/src/tagged.rs +++ b/core/engine/src/tagged.rs @@ -39,6 +39,7 @@ impl Clone for Tagged { impl Copy for Tagged {} +#[allow(dead_code)] impl Tagged { /// Creates a new, tagged `Tagged` pointer from an integer. /// diff --git a/core/engine/src/vm/opcode/push/array.rs b/core/engine/src/vm/opcode/push/array.rs index 9135f4109ba..480e979ecf8 100644 --- a/core/engine/src/vm/opcode/push/array.rs +++ b/core/engine/src/vm/opcode/push/array.rs @@ -1,6 +1,6 @@ use crate::{ builtins::Array, - string::common::StaticJsStrings, + string::StaticJsStrings, vm::{opcode::Operation, CompletionType}, Context, JsResult, JsValue, }; diff --git a/core/gc/ABOUT.md b/core/gc/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/gc/ABOUT.md +++ b/core/gc/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/gc/Cargo.toml b/core/gc/Cargo.toml index b21b61c44cb..af223d07677 100644 --- a/core/gc/Cargo.toml +++ b/core/gc/Cargo.toml @@ -15,12 +15,15 @@ rust-version.workspace = true thin-vec = ["dep:thin-vec"] # Enable default implementations of trace and finalize for some `ICU4X` types icu = ["dep:icu_locid"] +# Enable default implementations of trace and finalize for the `boa_string` crate +boa_string = ["dep:boa_string"] [dependencies] boa_profiler.workspace = true boa_macros.workspace = true hashbrown = { workspace = true, features = ["ahash", "raw"] } +boa_string = { workspace = true, optional = true } thin-vec = { workspace = true, optional = true } icu_locid = { workspace = true, optional = true } diff --git a/core/gc/src/trace.rs b/core/gc/src/trace.rs index b28d976e8d1..41ac9d91446 100644 --- a/core/gc/src/trace.rs +++ b/core/gc/src/trace.rs @@ -521,3 +521,15 @@ mod icu { empty_trace!(); } } + +#[cfg(feature = "boa_string")] +mod boa_string_trace { + use crate::{Finalize, Trace}; + + // SAFETY: `boa_string::JsString` doesn't have any traceable data. + unsafe impl Trace for boa_string::JsString { + empty_trace!(); + } + + impl Finalize for boa_string::JsString {} +} diff --git a/core/icu_provider/ABOUT.md b/core/icu_provider/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/icu_provider/ABOUT.md +++ b/core/icu_provider/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/interner/ABOUT.md b/core/interner/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/interner/ABOUT.md +++ b/core/interner/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/interop/ABOUT.md b/core/interop/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/interop/ABOUT.md +++ b/core/interop/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/macros/ABOUT.md b/core/macros/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/macros/ABOUT.md +++ b/core/macros/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/parser/ABOUT.md b/core/parser/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/parser/ABOUT.md +++ b/core/parser/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/profiler/ABOUT.md b/core/profiler/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/profiler/ABOUT.md +++ b/core/profiler/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/runtime/ABOUT.md b/core/runtime/ABOUT.md index c1de9c5a19f..c8c37206ac0 100644 --- a/core/runtime/ABOUT.md +++ b/core/runtime/ABOUT.md @@ -18,6 +18,7 @@ Try out the most recent release with Boa's live demo - [**`boa_profiler`**][profiler] - Boa's code profiler. - [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. - [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. [boa-conformance]: https://boajs.dev/conformance [boa-web]: https://boajs.dev/ @@ -30,3 +31,4 @@ Try out the most recent release with Boa's live demo [profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html [icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html [runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/string/ABOUT.md b/core/string/ABOUT.md new file mode 100644 index 00000000000..c8c37206ac0 --- /dev/null +++ b/core/string/ABOUT.md @@ -0,0 +1,34 @@ +# About Boa + +Boa is an open-source, experimental ECMAScript Engine written in Rust for +lexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some +of the [language][boa-conformance]. More information can be viewed at [Boa's +website][boa-web]. + +Try out the most recent release with Boa's live demo +[playground][boa-playground]. + +## Boa Crates + +- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree. +- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution. +- [**`boa_gc`**][gc] - Boa's garbage collector. +- [**`boa_interner`**][interner] - Boa's string interner. +- [**`boa_parser`**][parser] - Boa's lexer and parser. +- [**`boa_profiler`**][profiler] - Boa's code profiler. +- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. +- [**`boa_runtime`**][runtime] - Boa's WebAPI features. +- [**`boa_string`**][string] - Boa's ECMAScript string implementation. + +[boa-conformance]: https://boajs.dev/conformance +[boa-web]: https://boajs.dev/ +[boa-playground]: https://boajs.dev/playground +[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html +[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html +[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html +[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html +[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html +[profiler]: https://docs.rs/boa_profiler/latest/boa_profiler/index.html +[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html +[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html +[string]: https://docs.rs/boa_string/latest/boa_string/index.html diff --git a/core/string/Cargo.toml b/core/string/Cargo.toml new file mode 100644 index 00000000000..2a36d739c09 --- /dev/null +++ b/core/string/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "boa_string" +keywords = ["javascript", "js", "string"] +categories = ["parser-implementations", "compilers"] +readme = "../../README.md" +description.workspace = true +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +rustc-hash = { workspace = true, features = ["std"] } +sptr = "0.3.2" +static_assertions.workspace = true +paste = "1.0" +fast-float.workspace = true + +[dev-dependencies] +boa_macros.workspace = true + +[lints] +workspace = true + +[package.metadata.docs.rs] +all-features = true diff --git a/core/string/src/common.rs b/core/string/src/common.rs new file mode 100644 index 00000000000..8b420f7c682 --- /dev/null +++ b/core/string/src/common.rs @@ -0,0 +1,951 @@ +//! List of commonly used strings in Javascript code. + +use crate::{tagged::Tagged, JsStr}; + +use super::JsString; +use paste::paste; +use rustc_hash::{FxHashMap, FxHasher}; +use std::hash::BuildHasherDefault; + +macro_rules! well_known_statics { + ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { + $( + paste!{ + #[doc = "Gets the static `JsString` for `\"" $string "\"`."] + pub const $name: JsString = JsString { + ptr: Tagged::from_tag( + Self::find_index($string), + ), + }; + } + )+ + }; +} + +/// List of commonly used strings in Javascript code. +/// +/// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap. +#[derive(Debug, Clone, Copy)] +pub struct StaticJsStrings; + +impl StaticJsStrings { + // useful to search at compile time a certain string in the array + const fn find_index(candidate: &str) -> usize { + const fn const_eq(lhs: &[u8], rhs: &[u8]) -> bool { + if lhs.len() != rhs.len() { + return false; + } + + let mut i = 0; + while i < lhs.len() { + if lhs[i] != rhs[i] { + return false; + } + i += 1; + } + true + } + + let len = RAW_STATICS.len(); + let mut i = 0; + while i < len { + let Some(s) = RAW_STATICS[i].as_latin1() else { + // All static strings are latin1 encoded + unreachable!() + }; + if const_eq(s, candidate.as_bytes()) { + return i; + } + i += 1; + } + + panic!("couldn't find the required string on the common string array"); + } + + /// Gets the `JsString` corresponding to `string`, or `None` if the string + /// doesn't exist inside the static array. + #[inline] + #[must_use] + pub fn get_string(string: &JsStr<'_>) -> Option { + if string.len() > MAX_STATIC_LENGTH { + return None; + } + + let index = RAW_STATICS_CACHE.with(|map| map.get(string).copied())?; + + Some(JsString { + ptr: Tagged::from_tag(index), + }) + } + + /// Gets the `&[u16]` slice corresponding to the provided index, or `None` if the index + /// provided exceeds the size of the static array. + pub(crate) fn get(index: usize) -> Option> { + RAW_STATICS.get(index).copied() + } + + // Some consts are only used on certain features, which triggers the unused lint. + well_known_statics! { + (EMPTY_STRING, ""), + (LENGTH, "length"), + // Symbols + (SYMBOL_ASYNC_ITERATOR, "Symbol.asyncIterator"), + (SYMBOL_HAS_INSTANCE, "Symbol.hasInstance"), + (SYMBOL_IS_CONCAT_SPREADABLE, "Symbol.isConcatSpreadable"), + (SYMBOL_ITERATOR, "Symbol.iterator"), + (SYMBOL_MATCH, "Symbol.match"), + (SYMBOL_MATCH_ALL, "Symbol.matchAll"), + (SYMBOL_REPLACE, "Symbol.replace"), + (SYMBOL_SEARCH, "Symbol.search"), + (SYMBOL_SPECIES, "Symbol.species"), + (SYMBOL_SPLIT, "Symbol.split"), + (SYMBOL_TO_PRIMITIVE, "Symbol.toPrimitive"), + (SYMBOL_TO_STRING_TAG, "Symbol.toStringTag"), + (SYMBOL_UNSCOPABLES, "Symbol.unscopables"), + (FN_SYMBOL_ASYNC_ITERATOR, "[Symbol.asyncIterator]"), + (FN_SYMBOL_HAS_INSTANCE, "[Symbol.hasInstance]"), + (FN_SYMBOL_IS_CONCAT_SPREADABLE, "[Symbol.isConcatSpreadable]"), + (FN_SYMBOL_ITERATOR, "[Symbol.iterator]"), + (FN_SYMBOL_MATCH, "[Symbol.match]"), + (FN_SYMBOL_MATCH_ALL, "[Symbol.matchAll]"), + (FN_SYMBOL_REPLACE, "[Symbol.replace]"), + (FN_SYMBOL_SEARCH, "[Symbol.search]"), + (FN_SYMBOL_SPECIES, "[Symbol.species]"), + (FN_SYMBOL_SPLIT, "[Symbol.split]"), + (FN_SYMBOL_TO_PRIMITIVE, "[Symbol.toPrimitive]"), + (FN_SYMBOL_TO_STRING_TAG, "[Symbol.toStringTag]"), + (FN_SYMBOL_UNSCOPABLES, "[Symbol.unscopables]"), + // Builtins + (ARRAY, "Array"), + (ARRAY_BUFFER, "ArrayBuffer"), + (SHARED_ARRAY_BUFFER, "SharedArrayBuffer"), + (ASYNC_FUNCTION, "AsyncFunction"), + (ASYNC_GENERATOR, "AsyncGenerator"), + (ASYNC_GENERATOR_FUNCTION, "AsyncGeneratorFunction"), + (ATOMICS, "Atomics"), + (BIG_INT, "BigInt"), + (BOOLEAN, "Boolean"), + (DATA_VIEW, "DataView"), + (DATE, "Date"), + (ERROR, "Error"), + (AGGREGATE_ERROR, "AggregateError"), + (EVAL_ERROR, "EvalError"), + (RANGE_ERROR, "RangeError"), + (REFERENCE_ERROR, "ReferenceError"), + (SYNTAX_ERROR, "SyntaxError"), + (TYPE_ERROR, "TypeError"), + (URI_ERROR, "URIError"), + (ESCAPE, "escape"), + (UNESCAPE, "unescape"), + (EVAL, "eval"), + (FUNCTION, "Function"), + (GENERATOR, "Generator"), + (GENERATOR_FUNCTION, "GeneratorFunction"), + (INTL, "Intl"), + (COLLATOR, "Collator"), + (LIST_FORMAT, "ListFormat"), + (LOCALE, "Locale"), + (PLURAL_RULES, "PluralRules"), + (SEGMENTER, "Segmenter"), + (DATE_TIME_FORMAT, "DateTimeFormat"), + (JSON, "JSON"), + (MAP, "Map"), + (MATH, "Math"), + (NUMBER, "Number"), + (NUMBER_FORMAT, "NumberFormat"), + (IS_FINITE, "isFinite"), + (IS_NAN, "isNaN"), + (PARSE_INT, "parseInt"), + (PARSE_FLOAT, "parseFloat"), + (OBJECT, "Object"), + (PROMISE, "Promise"), + (PROXY, "Proxy"), + (REFLECT, "Reflect"), + (REG_EXP, "RegExp"), + (SET, "Set"), + (STRING, "String"), + (SYMBOL, "Symbol"), + (TYPED_ARRAY, "TypedArray"), + (INT8_ARRAY, "Int8Array"), + (UINT8_ARRAY, "Uint8Array"), + (UINT8_CLAMPED_ARRAY, "Uint8ClampedArray"), + (INT16_ARRAY, "Int16Array"), + (UINT16_ARRAY, "Uint16Array"), + (INT32_ARRAY, "Int32Array"), + (UINT32_ARRAY, "Uint32Array"), + (BIG_INT64_ARRAY, "BigInt64Array"), + (BIG_UINT64_ARRAY, "BigUint64Array"), + (FLOAT32_ARRAY, "Float32Array"), + (FLOAT64_ARRAY, "Float64Array"), + (ENCODE_URI, "encodeURI"), + (ENCODE_URI_COMPONENT, "encodeURIComponent"), + (DECODE_URI, "decodeURI"), + (DECODE_URI_COMPONENT, "decodeURIComponent"), + (WEAK_REF, "WeakRef"), + (WEAK_MAP, "WeakMap"), + (WEAK_SET, "WeakSet"), + (TEMPORAL, "Temporal"), + (NOW, "Temporal.Now"), + (INSTANT, "Temporal.Instant"), + (DURATION, "Temporal.Duration"), + (PLAIN_DATE, "Temporal.PlainDate"), + (PLAIN_DATETIME, "Temporal.PlainDateTime"), + (PLAIN_TIME, "Temporal.PlainTime"), + (PLAIN_YM, "Temporal.PlainYearMonth"), + (PLAIN_MD, "Temporal.PlainMonthDay"), + (CALENDAR, "Temporal.Calendar"), + (TIMEZONE, "Temporal.TimeZone"), + (ZONED_DT, "Temporal.ZonedDateTime"), + } +} + +const MAX_STATIC_LENGTH: usize = { + let mut max = 0; + let mut i = 0; + while i < RAW_STATICS.len() { + // TOOD: Because `get_index` is not const, we are accessing doc hidden stuff, that may change. + let len = RAW_STATICS[i].len(); + if len > max { + max = len; + } + i += 1; + } + max +}; + +thread_local! { + /// Map from a string inside [`RAW_STATICS`] to its corresponding static index on `RAW_STATICS`. + static RAW_STATICS_CACHE: FxHashMap, usize> = { + let mut constants = FxHashMap::with_capacity_and_hasher( + RAW_STATICS.len(), + BuildHasherDefault::::default(), + ); + + for (idx, &s) in RAW_STATICS.iter().enumerate() { + constants.insert(s, idx); + } + + constants + }; +} + +/// Array of raw static strings that aren't reference counted. +const RAW_STATICS: &[JsStr<'_>] = &[ + JsStr::latin1("".as_bytes()), + // Well known symbols + JsStr::latin1("Symbol.asyncIterator".as_bytes()), + JsStr::latin1("[Symbol.asyncIterator]".as_bytes()), + JsStr::latin1("Symbol.hasInstance".as_bytes()), + JsStr::latin1("[Symbol.hasInstance]".as_bytes()), + JsStr::latin1("Symbol.isConcatSpreadable".as_bytes()), + JsStr::latin1("[Symbol.isConcatSpreadable]".as_bytes()), + JsStr::latin1("Symbol.iterator".as_bytes()), + JsStr::latin1("[Symbol.iterator]".as_bytes()), + JsStr::latin1("Symbol.match".as_bytes()), + JsStr::latin1("[Symbol.match]".as_bytes()), + JsStr::latin1("Symbol.matchAll".as_bytes()), + JsStr::latin1("[Symbol.matchAll]".as_bytes()), + JsStr::latin1("Symbol.replace".as_bytes()), + JsStr::latin1("[Symbol.replace]".as_bytes()), + JsStr::latin1("Symbol.search".as_bytes()), + JsStr::latin1("[Symbol.search]".as_bytes()), + JsStr::latin1("Symbol.species".as_bytes()), + JsStr::latin1("[Symbol.species]".as_bytes()), + JsStr::latin1("Symbol.split".as_bytes()), + JsStr::latin1("[Symbol.split]".as_bytes()), + JsStr::latin1("Symbol.toPrimitive".as_bytes()), + JsStr::latin1("[Symbol.toPrimitive]".as_bytes()), + JsStr::latin1("Symbol.toStringTag".as_bytes()), + JsStr::latin1("[Symbol.toStringTag]".as_bytes()), + JsStr::latin1("Symbol.unscopables".as_bytes()), + JsStr::latin1("[Symbol.unscopables]".as_bytes()), + JsStr::latin1("get [Symbol.species]".as_bytes()), + JsStr::latin1("get [Symbol.toStringTag]".as_bytes()), + // Well known builtins + JsStr::latin1("Array".as_bytes()), + JsStr::latin1("ArrayBuffer".as_bytes()), + JsStr::latin1("SharedArrayBuffer".as_bytes()), + JsStr::latin1("AsyncFunction".as_bytes()), + JsStr::latin1("AsyncGenerator".as_bytes()), + JsStr::latin1("AsyncGeneratorFunction".as_bytes()), + JsStr::latin1("Atomics".as_bytes()), + JsStr::latin1("BigInt".as_bytes()), + JsStr::latin1("Boolean".as_bytes()), + JsStr::latin1("DataView".as_bytes()), + JsStr::latin1("Date".as_bytes()), + JsStr::latin1("Error".as_bytes()), + JsStr::latin1("AggregateError".as_bytes()), + JsStr::latin1("EvalError".as_bytes()), + JsStr::latin1("RangeError".as_bytes()), + JsStr::latin1("ReferenceError".as_bytes()), + JsStr::latin1("SyntaxError".as_bytes()), + JsStr::latin1("TypeError".as_bytes()), + JsStr::latin1("URIError".as_bytes()), + JsStr::latin1("escape".as_bytes()), + JsStr::latin1("unescape".as_bytes()), + JsStr::latin1("eval".as_bytes()), + JsStr::latin1("Function".as_bytes()), + JsStr::latin1("Generator".as_bytes()), + JsStr::latin1("GeneratorFunction".as_bytes()), + JsStr::latin1("Intl".as_bytes()), + JsStr::latin1("Collator".as_bytes()), + JsStr::latin1("ListFormat".as_bytes()), + JsStr::latin1("Locale".as_bytes()), + JsStr::latin1("PluralRules".as_bytes()), + JsStr::latin1("Segmenter".as_bytes()), + JsStr::latin1("DateTimeFormat".as_bytes()), + JsStr::latin1("JSON".as_bytes()), + JsStr::latin1("Map".as_bytes()), + JsStr::latin1("Math".as_bytes()), + JsStr::latin1("Number".as_bytes()), + JsStr::latin1("NumberFormat".as_bytes()), + JsStr::latin1("isFinite".as_bytes()), + JsStr::latin1("isNaN".as_bytes()), + JsStr::latin1("parseInt".as_bytes()), + JsStr::latin1("parseFloat".as_bytes()), + JsStr::latin1("Object".as_bytes()), + JsStr::latin1("Promise".as_bytes()), + JsStr::latin1("Proxy".as_bytes()), + JsStr::latin1("Reflect".as_bytes()), + JsStr::latin1("RegExp".as_bytes()), + JsStr::latin1("Set".as_bytes()), + JsStr::latin1("String".as_bytes()), + JsStr::latin1("Symbol".as_bytes()), + JsStr::latin1("TypedArray".as_bytes()), + JsStr::latin1("Int8Array".as_bytes()), + JsStr::latin1("Uint8Array".as_bytes()), + JsStr::latin1("Uint8ClampedArray".as_bytes()), + JsStr::latin1("Int16Array".as_bytes()), + JsStr::latin1("Uint16Array".as_bytes()), + JsStr::latin1("Int32Array".as_bytes()), + JsStr::latin1("Uint32Array".as_bytes()), + JsStr::latin1("BigInt64Array".as_bytes()), + JsStr::latin1("BigUint64Array".as_bytes()), + JsStr::latin1("Float32Array".as_bytes()), + JsStr::latin1("Float64Array".as_bytes()), + JsStr::latin1("encodeURI".as_bytes()), + JsStr::latin1("encodeURIComponent".as_bytes()), + JsStr::latin1("decodeURI".as_bytes()), + JsStr::latin1("decodeURIComponent".as_bytes()), + JsStr::latin1("WeakRef".as_bytes()), + JsStr::latin1("WeakMap".as_bytes()), + JsStr::latin1("WeakSet".as_bytes()), + JsStr::latin1("Temporal".as_bytes()), + JsStr::latin1("Temporal.Now".as_bytes()), + JsStr::latin1("Temporal.Instant".as_bytes()), + JsStr::latin1("Temporal.Duration".as_bytes()), + JsStr::latin1("Temporal.Calendar".as_bytes()), + JsStr::latin1("Temporal.PlainDate".as_bytes()), + JsStr::latin1("Temporal.PlainDateTime".as_bytes()), + JsStr::latin1("Temporal.PlainMonthDay".as_bytes()), + JsStr::latin1("Temporal.PlainYearMonth".as_bytes()), + JsStr::latin1("Temporal.PlainTime".as_bytes()), + JsStr::latin1("Temporal.TimeZone".as_bytes()), + JsStr::latin1("Temporal.ZonedDateTime".as_bytes()), + // Misc + JsStr::latin1(",".as_bytes()), + JsStr::latin1(":".as_bytes()), + // Generic use + JsStr::latin1("name".as_bytes()), + JsStr::latin1("length".as_bytes()), + JsStr::latin1("arguments".as_bytes()), + JsStr::latin1("prototype".as_bytes()), + JsStr::latin1("constructor".as_bytes()), + JsStr::latin1("return".as_bytes()), + JsStr::latin1("throw".as_bytes()), + JsStr::latin1("global".as_bytes()), + JsStr::latin1("globalThis".as_bytes()), + // typeof + JsStr::latin1("null".as_bytes()), + JsStr::latin1("undefined".as_bytes()), + JsStr::latin1("number".as_bytes()), + JsStr::latin1("string".as_bytes()), + JsStr::latin1("symbol".as_bytes()), + JsStr::latin1("bigint".as_bytes()), + JsStr::latin1("object".as_bytes()), + JsStr::latin1("function".as_bytes()), + // Property descriptor + JsStr::latin1("value".as_bytes()), + JsStr::latin1("get".as_bytes()), + JsStr::latin1("set".as_bytes()), + JsStr::latin1("writable".as_bytes()), + JsStr::latin1("enumerable".as_bytes()), + JsStr::latin1("configurable".as_bytes()), + // Object object + JsStr::latin1("assign".as_bytes()), + JsStr::latin1("create".as_bytes()), + JsStr::latin1("toString".as_bytes()), + JsStr::latin1("valueOf".as_bytes()), + JsStr::latin1("is".as_bytes()), + JsStr::latin1("seal".as_bytes()), + JsStr::latin1("isSealed".as_bytes()), + JsStr::latin1("freeze".as_bytes()), + JsStr::latin1("isFrozen".as_bytes()), + JsStr::latin1("isExtensible".as_bytes()), + JsStr::latin1("hasOwnProperty".as_bytes()), + JsStr::latin1("isPrototypeOf".as_bytes()), + JsStr::latin1("setPrototypeOf".as_bytes()), + JsStr::latin1("getPrototypeOf".as_bytes()), + JsStr::latin1("defineProperty".as_bytes()), + JsStr::latin1("defineProperties".as_bytes()), + JsStr::latin1("deleteProperty".as_bytes()), + JsStr::latin1("construct".as_bytes()), + JsStr::latin1("hasOwn".as_bytes()), + JsStr::latin1("ownKeys".as_bytes()), + JsStr::latin1("keys".as_bytes()), + JsStr::latin1("values".as_bytes()), + JsStr::latin1("entries".as_bytes()), + JsStr::latin1("fromEntries".as_bytes()), + JsStr::latin1("propertyIsEnumerable".as_bytes()), + JsStr::latin1("preventExtensions".as_bytes()), + JsStr::latin1("getOwnPropertyDescriptor".as_bytes()), + JsStr::latin1("getOwnPropertyDescriptors".as_bytes()), + JsStr::latin1("getOwnPropertyNames".as_bytes()), + JsStr::latin1("getOwnPropertySymbols".as_bytes()), + JsStr::latin1("__defineGetter__".as_bytes()), + JsStr::latin1("__defineSetter__".as_bytes()), + JsStr::latin1("__lookupGetter__".as_bytes()), + JsStr::latin1("__lookupSetter__".as_bytes()), + JsStr::latin1("__proto__".as_bytes()), + JsStr::latin1("get __proto__".as_bytes()), + JsStr::latin1("set __proto__".as_bytes()), + // Function object + JsStr::latin1("apply".as_bytes()), + JsStr::latin1("bind".as_bytes()), + JsStr::latin1("call".as_bytes()), + JsStr::latin1("caller".as_bytes()), + // Arguments object + JsStr::latin1("callee".as_bytes()), + // Array object + JsStr::latin1("at".as_bytes()), + JsStr::latin1("from".as_bytes()), + JsStr::latin1("isArray".as_bytes()), + JsStr::latin1("of".as_bytes()), + JsStr::latin1("copyWithin".as_bytes()), + JsStr::latin1("every".as_bytes()), + JsStr::latin1("fill".as_bytes()), + JsStr::latin1("filter".as_bytes()), + JsStr::latin1("find".as_bytes()), + JsStr::latin1("findIndex".as_bytes()), + JsStr::latin1("findLast".as_bytes()), + JsStr::latin1("findLastIndex".as_bytes()), + JsStr::latin1("flat".as_bytes()), + JsStr::latin1("flatMap".as_bytes()), + JsStr::latin1("forEach".as_bytes()), + JsStr::latin1("includes".as_bytes()), + JsStr::latin1("indexOf".as_bytes()), + JsStr::latin1("join".as_bytes()), + JsStr::latin1("map".as_bytes()), + JsStr::latin1("next".as_bytes()), + JsStr::latin1("reduce".as_bytes()), + JsStr::latin1("reduceRight".as_bytes()), + JsStr::latin1("reverse".as_bytes()), + JsStr::latin1("shift".as_bytes()), + JsStr::latin1("slice".as_bytes()), + JsStr::latin1("splice".as_bytes()), + JsStr::latin1("some".as_bytes()), + JsStr::latin1("sort".as_bytes()), + JsStr::latin1("unshift".as_bytes()), + JsStr::latin1("push".as_bytes()), + JsStr::latin1("pop".as_bytes()), + JsStr::latin1("groupBy".as_bytes()), + JsStr::latin1("toReversed".as_bytes()), + JsStr::latin1("toSorted".as_bytes()), + JsStr::latin1("toSpliced".as_bytes()), + JsStr::latin1("with".as_bytes()), + // String object + JsStr::latin1("charAt".as_bytes()), + JsStr::latin1("charCodeAt".as_bytes()), + JsStr::latin1("codePointAt".as_bytes()), + JsStr::latin1("concat".as_bytes()), + JsStr::latin1("endsWith".as_bytes()), + JsStr::latin1("fromCharCode".as_bytes()), + JsStr::latin1("fromCodePoint".as_bytes()), + JsStr::latin1("lastIndexOf".as_bytes()), + JsStr::latin1("match".as_bytes()), + JsStr::latin1("matchAll".as_bytes()), + JsStr::latin1("normalize".as_bytes()), + JsStr::latin1("padEnd".as_bytes()), + JsStr::latin1("padStart".as_bytes()), + JsStr::latin1("raw".as_bytes()), + JsStr::latin1("repeat".as_bytes()), + JsStr::latin1("replace".as_bytes()), + JsStr::latin1("replaceAll".as_bytes()), + JsStr::latin1("search".as_bytes()), + JsStr::latin1("split".as_bytes()), + JsStr::latin1("startsWith".as_bytes()), + JsStr::latin1("substr".as_bytes()), + JsStr::latin1("substring".as_bytes()), + JsStr::latin1("toLocaleString".as_bytes()), + JsStr::latin1("toLowerCase".as_bytes()), + JsStr::latin1("toUpperCase".as_bytes()), + JsStr::latin1("trim".as_bytes()), + JsStr::latin1("trimEnd".as_bytes()), + JsStr::latin1("trimStart".as_bytes()), + JsStr::latin1("isWellFormed".as_bytes()), + JsStr::latin1("localeCompare".as_bytes()), + JsStr::latin1("toWellFormed".as_bytes()), + JsStr::latin1("toLocaleLowerCase".as_bytes()), + JsStr::latin1("toLocaleUpperCase".as_bytes()), + JsStr::latin1("trimLeft".as_bytes()), + JsStr::latin1("trimRight".as_bytes()), + JsStr::latin1("anchor".as_bytes()), + JsStr::latin1("big".as_bytes()), + JsStr::latin1("blink".as_bytes()), + JsStr::latin1("bold".as_bytes()), + JsStr::latin1("fixed".as_bytes()), + JsStr::latin1("fontcolor".as_bytes()), + JsStr::latin1("fontsize".as_bytes()), + JsStr::latin1("italics".as_bytes()), + JsStr::latin1("link".as_bytes()), + JsStr::latin1("small".as_bytes()), + JsStr::latin1("strike".as_bytes()), + JsStr::latin1("sub".as_bytes()), + JsStr::latin1("sup".as_bytes()), + // Number object + JsStr::latin1("Infinity".as_bytes()), + JsStr::latin1("NaN".as_bytes()), + JsStr::latin1("EPSILON".as_bytes()), + JsStr::latin1("MAX_SAFE_INTEGER".as_bytes()), + JsStr::latin1("MIN_SAFE_INTEGER".as_bytes()), + JsStr::latin1("MAX_VALUE".as_bytes()), + JsStr::latin1("MIN_VALUE".as_bytes()), + JsStr::latin1("NEGATIVE_INFINITY".as_bytes()), + JsStr::latin1("POSITIVE_INFINITY".as_bytes()), + JsStr::latin1("isSafeInteger".as_bytes()), + JsStr::latin1("isInteger".as_bytes()), + JsStr::latin1("toExponential".as_bytes()), + JsStr::latin1("toFixed".as_bytes()), + JsStr::latin1("toPrecision".as_bytes()), + // BigInt object + JsStr::latin1("asIntN".as_bytes()), + JsStr::latin1("asUintN".as_bytes()), + // RegExp object + JsStr::latin1("exec".as_bytes()), + JsStr::latin1("test".as_bytes()), + JsStr::latin1("compile".as_bytes()), + JsStr::latin1("flags".as_bytes()), + JsStr::latin1("index".as_bytes()), + JsStr::latin1("lastIndex".as_bytes()), + JsStr::latin1("hasIndices".as_bytes()), + JsStr::latin1("ignoreCase".as_bytes()), + JsStr::latin1("multiline".as_bytes()), + JsStr::latin1("dotAll".as_bytes()), + JsStr::latin1("unicode".as_bytes()), + JsStr::latin1("sticky".as_bytes()), + JsStr::latin1("source".as_bytes()), + JsStr::latin1("get hasIndices".as_bytes()), + JsStr::latin1("get global".as_bytes()), + JsStr::latin1("get ignoreCase".as_bytes()), + JsStr::latin1("get multiline".as_bytes()), + JsStr::latin1("get dotAll".as_bytes()), + JsStr::latin1("get unicode".as_bytes()), + JsStr::latin1("get sticky".as_bytes()), + JsStr::latin1("get flags".as_bytes()), + JsStr::latin1("get source".as_bytes()), + // Symbol object + JsStr::latin1("for".as_bytes()), + JsStr::latin1("keyFor".as_bytes()), + JsStr::latin1("description".as_bytes()), + JsStr::latin1("asyncIterator".as_bytes()), + JsStr::latin1("hasInstance".as_bytes()), + JsStr::latin1("species".as_bytes()), + JsStr::latin1("unscopables".as_bytes()), + JsStr::latin1("iterator".as_bytes()), + JsStr::latin1("toStringTag".as_bytes()), + JsStr::latin1("toPrimitive".as_bytes()), + JsStr::latin1("isConcatSpreadable".as_bytes()), + JsStr::latin1("get description".as_bytes()), + // Map object + JsStr::latin1("clear".as_bytes()), + JsStr::latin1("delete".as_bytes()), + JsStr::latin1("has".as_bytes()), + JsStr::latin1("size".as_bytes()), + // Set object + JsStr::latin1("add".as_bytes()), + // Reflect object + // Proxy object + JsStr::latin1("revocable".as_bytes()), + // Error objects + JsStr::latin1("message".as_bytes()), + // Date object + JsStr::latin1("toJSON".as_bytes()), + JsStr::latin1("getDate".as_bytes()), + JsStr::latin1("getDay".as_bytes()), + JsStr::latin1("getFullYear".as_bytes()), + JsStr::latin1("getHours".as_bytes()), + JsStr::latin1("getMilliseconds".as_bytes()), + JsStr::latin1("getMinutes".as_bytes()), + JsStr::latin1("getMonth".as_bytes()), + JsStr::latin1("getSeconds".as_bytes()), + JsStr::latin1("getTime".as_bytes()), + JsStr::latin1("getYear".as_bytes()), + JsStr::latin1("getUTCDate".as_bytes()), + JsStr::latin1("getUTCDay".as_bytes()), + JsStr::latin1("getUTCFullYear".as_bytes()), + JsStr::latin1("getUTCHours".as_bytes()), + JsStr::latin1("getUTCMinutes".as_bytes()), + JsStr::latin1("getUTCMonth".as_bytes()), + JsStr::latin1("getUTCSeconds".as_bytes()), + JsStr::latin1("setDate".as_bytes()), + JsStr::latin1("setFullYear".as_bytes()), + JsStr::latin1("setHours".as_bytes()), + JsStr::latin1("setMilliseconds".as_bytes()), + JsStr::latin1("setMinutes".as_bytes()), + JsStr::latin1("setMonth".as_bytes()), + JsStr::latin1("setSeconds".as_bytes()), + JsStr::latin1("setYear".as_bytes()), + JsStr::latin1("setTime".as_bytes()), + JsStr::latin1("setUTCDate".as_bytes()), + JsStr::latin1("setUTCFullYear".as_bytes()), + JsStr::latin1("setUTCHours".as_bytes()), + JsStr::latin1("setUTCMinutes".as_bytes()), + JsStr::latin1("setUTCMonth".as_bytes()), + JsStr::latin1("setUTCSeconds".as_bytes()), + JsStr::latin1("toDateString".as_bytes()), + JsStr::latin1("toGMTString".as_bytes()), + JsStr::latin1("toISOString".as_bytes()), + JsStr::latin1("toTimeString".as_bytes()), + JsStr::latin1("toUTCString".as_bytes()), + JsStr::latin1("now".as_bytes()), + JsStr::latin1("UTC".as_bytes()), + JsStr::latin1("getTimezoneOffset".as_bytes()), + JsStr::latin1("getUTCMilliseconds".as_bytes()), + JsStr::latin1("setUTCMilliseconds".as_bytes()), + JsStr::latin1("toLocaleDateString".as_bytes()), + JsStr::latin1("toLocaleTimeString".as_bytes()), + // JSON object + JsStr::latin1("parse".as_bytes()), + JsStr::latin1("stringify".as_bytes()), + // Promise object + JsStr::latin1("promise".as_bytes()), + JsStr::latin1("resolve".as_bytes()), + JsStr::latin1("reject".as_bytes()), + JsStr::latin1("all".as_bytes()), + JsStr::latin1("allSettled".as_bytes()), + JsStr::latin1("any".as_bytes()), + JsStr::latin1("race".as_bytes()), + JsStr::latin1("then".as_bytes()), + JsStr::latin1("catch".as_bytes()), + JsStr::latin1("finally".as_bytes()), + JsStr::latin1("withResolvers".as_bytes()), + // Iterator object + JsStr::latin1("Array Iterator".as_bytes()), + JsStr::latin1("Set Iterator".as_bytes()), + JsStr::latin1("String Iterator".as_bytes()), + JsStr::latin1("Map Iterator".as_bytes()), + JsStr::latin1("For In Iterator".as_bytes()), + JsStr::latin1("RegExp String Iterator".as_bytes()), + // Iterator result object + JsStr::latin1("done".as_bytes()), + // Math object + JsStr::latin1("LN10".as_bytes()), + JsStr::latin1("LN2".as_bytes()), + JsStr::latin1("LOG10E".as_bytes()), + JsStr::latin1("LOG2E".as_bytes()), + JsStr::latin1("PI".as_bytes()), + JsStr::latin1("SQRT1_2".as_bytes()), + JsStr::latin1("SQRT2".as_bytes()), + JsStr::latin1("abs".as_bytes()), + JsStr::latin1("acos".as_bytes()), + JsStr::latin1("acosh".as_bytes()), + JsStr::latin1("asin".as_bytes()), + JsStr::latin1("asinh".as_bytes()), + JsStr::latin1("atan".as_bytes()), + JsStr::latin1("atanh".as_bytes()), + JsStr::latin1("atan2".as_bytes()), + JsStr::latin1("cbrt".as_bytes()), + JsStr::latin1("ceil".as_bytes()), + JsStr::latin1("clz32".as_bytes()), + JsStr::latin1("cos".as_bytes()), + JsStr::latin1("cosh".as_bytes()), + JsStr::latin1("exp".as_bytes()), + JsStr::latin1("expm1".as_bytes()), + JsStr::latin1("floor".as_bytes()), + JsStr::latin1("fround".as_bytes()), + JsStr::latin1("hypot".as_bytes()), + JsStr::latin1("imul".as_bytes()), + JsStr::latin1("log".as_bytes()), + JsStr::latin1("log1p".as_bytes()), + JsStr::latin1("log10".as_bytes()), + JsStr::latin1("log2".as_bytes()), + JsStr::latin1("max".as_bytes()), + JsStr::latin1("min".as_bytes()), + JsStr::latin1("pow".as_bytes()), + JsStr::latin1("random".as_bytes()), + JsStr::latin1("round".as_bytes()), + JsStr::latin1("sign".as_bytes()), + JsStr::latin1("sin".as_bytes()), + JsStr::latin1("sinh".as_bytes()), + JsStr::latin1("sqrt".as_bytes()), + JsStr::latin1("tan".as_bytes()), + JsStr::latin1("tanh".as_bytes()), + JsStr::latin1("trunc".as_bytes()), + // TypedArray object + JsStr::latin1("BYTES_PER_ELEMENT".as_bytes()), + JsStr::latin1("buffer".as_bytes()), + JsStr::latin1("byteLength".as_bytes()), + JsStr::latin1("byteOffset".as_bytes()), + JsStr::latin1("isView".as_bytes()), + JsStr::latin1("subarray".as_bytes()), + JsStr::latin1("get byteLength".as_bytes()), + JsStr::latin1("get buffer".as_bytes()), + JsStr::latin1("get byteOffset".as_bytes()), + JsStr::latin1("get size".as_bytes()), + JsStr::latin1("get length".as_bytes()), + // DataView object + JsStr::latin1("getBigInt64".as_bytes()), + JsStr::latin1("getBigUint64".as_bytes()), + JsStr::latin1("getFloat32".as_bytes()), + JsStr::latin1("getFloat64".as_bytes()), + JsStr::latin1("getInt8".as_bytes()), + JsStr::latin1("getInt16".as_bytes()), + JsStr::latin1("getInt32".as_bytes()), + JsStr::latin1("getUint8".as_bytes()), + JsStr::latin1("getUint16".as_bytes()), + JsStr::latin1("getUint32".as_bytes()), + JsStr::latin1("setBigInt64".as_bytes()), + JsStr::latin1("setBigUint64".as_bytes()), + JsStr::latin1("setFloat32".as_bytes()), + JsStr::latin1("setFloat64".as_bytes()), + JsStr::latin1("setInt8".as_bytes()), + JsStr::latin1("setInt16".as_bytes()), + JsStr::latin1("setInt32".as_bytes()), + JsStr::latin1("setUint8".as_bytes()), + JsStr::latin1("setUint16".as_bytes()), + JsStr::latin1("setUint32".as_bytes()), + // WeakRef object + JsStr::latin1("deref".as_bytes()), + // Atomic object + JsStr::latin1("and".as_bytes()), + JsStr::latin1("compareExchange".as_bytes()), + JsStr::latin1("exchange".as_bytes()), + JsStr::latin1("isLockFree".as_bytes()), + JsStr::latin1("load".as_bytes()), + JsStr::latin1("or".as_bytes()), + JsStr::latin1("store".as_bytes()), + JsStr::latin1("wait".as_bytes()), + JsStr::latin1("notify".as_bytes()), + JsStr::latin1("xor".as_bytes()), + // Intl object + JsStr::latin1("getCanonicalLocales".as_bytes()), + JsStr::latin1("get compare".as_bytes()), + JsStr::latin1("supportedLocalesOf".as_bytes()), + JsStr::latin1("Intl.Collator".as_bytes()), + JsStr::latin1("compare".as_bytes()), + JsStr::latin1("resolvedOptions".as_bytes()), + JsStr::latin1("Intl.ListFormat".as_bytes()), + JsStr::latin1("format".as_bytes()), + JsStr::latin1("formatToParts".as_bytes()), + JsStr::latin1("get baseName".as_bytes()), + JsStr::latin1("get calendar".as_bytes()), + JsStr::latin1("get caseFirst".as_bytes()), + JsStr::latin1("get collation".as_bytes()), + JsStr::latin1("get hourCycle".as_bytes()), + JsStr::latin1("get numeric".as_bytes()), + JsStr::latin1("get numberingSystem".as_bytes()), + JsStr::latin1("get language".as_bytes()), + JsStr::latin1("get script".as_bytes()), + JsStr::latin1("get region".as_bytes()), + JsStr::latin1("Intl.Locale".as_bytes()), + JsStr::latin1("maximize".as_bytes()), + JsStr::latin1("minimize".as_bytes()), + JsStr::latin1("baseName".as_bytes()), + JsStr::latin1("calendar".as_bytes()), + JsStr::latin1("caseFirst".as_bytes()), + JsStr::latin1("collation".as_bytes()), + JsStr::latin1("hourCycle".as_bytes()), + JsStr::latin1("numeric".as_bytes()), + JsStr::latin1("numberingSystem".as_bytes()), + JsStr::latin1("language".as_bytes()), + JsStr::latin1("script".as_bytes()), + JsStr::latin1("region".as_bytes()), + JsStr::latin1("Intl.Segmenter".as_bytes()), + JsStr::latin1("segment".as_bytes()), + JsStr::latin1("containing".as_bytes()), + JsStr::latin1("Segmenter String Iterator".as_bytes()), + JsStr::latin1("Intl.PluralRules".as_bytes()), + JsStr::latin1("select".as_bytes()), + // Temporal object + JsStr::latin1("get Id".as_bytes()), + JsStr::latin1("getOffsetNanosecondsFor".as_bytes()), + JsStr::latin1("getOffsetStringFor".as_bytes()), + JsStr::latin1("getPlainDateTimeFor".as_bytes()), + JsStr::latin1("getInstantFor".as_bytes()), + JsStr::latin1("getPossibleInstantFor".as_bytes()), + JsStr::latin1("getNextTransition".as_bytes()), + JsStr::latin1("getPreviousTransition".as_bytes()), + JsStr::latin1("id".as_bytes()), + JsStr::latin1("Now".as_bytes()), + JsStr::latin1("Calendar".as_bytes()), + JsStr::latin1("Duration".as_bytes()), + JsStr::latin1("Instant".as_bytes()), + JsStr::latin1("PlainDate".as_bytes()), + JsStr::latin1("PlainDateTime".as_bytes()), + JsStr::latin1("PlainMonthDay".as_bytes()), + JsStr::latin1("PlainTime".as_bytes()), + JsStr::latin1("PlainYearMonth".as_bytes()), + JsStr::latin1("TimeZone".as_bytes()), + JsStr::latin1("ZonedDateTime".as_bytes()), + JsStr::latin1("timeZoneId".as_bytes()), + JsStr::latin1("instant".as_bytes()), + JsStr::latin1("plainDateTime".as_bytes()), + JsStr::latin1("plainDateTimeISO".as_bytes()), + JsStr::latin1("zonedDateTime".as_bytes()), + JsStr::latin1("zonedDateTimeISO".as_bytes()), + JsStr::latin1("plainDate".as_bytes()), + JsStr::latin1("plainDateISO".as_bytes()), + JsStr::latin1("get epochSeconds".as_bytes()), + JsStr::latin1("get epochMilliseconds".as_bytes()), + JsStr::latin1("get epochMicroseconds".as_bytes()), + JsStr::latin1("get epochNanoseconds".as_bytes()), + JsStr::latin1("epochSeconds".as_bytes()), + JsStr::latin1("epochMilliseconds".as_bytes()), + JsStr::latin1("epochMicroseconds".as_bytes()), + JsStr::latin1("epochNanoseconds".as_bytes()), + JsStr::latin1("subtract".as_bytes()), + JsStr::latin1("until".as_bytes()), + JsStr::latin1("since".as_bytes()), + JsStr::latin1("equals".as_bytes()), + JsStr::latin1("toZonedDateTime".as_bytes()), + JsStr::latin1("toZonedDateTimeISO".as_bytes()), + JsStr::latin1("get Years".as_bytes()), + JsStr::latin1("get Months".as_bytes()), + JsStr::latin1("get Weeks".as_bytes()), + JsStr::latin1("get Days".as_bytes()), + JsStr::latin1("get Hours".as_bytes()), + JsStr::latin1("get Minutes".as_bytes()), + JsStr::latin1("get Seconds".as_bytes()), + JsStr::latin1("get Milliseconds".as_bytes()), + JsStr::latin1("get Microseconds".as_bytes()), + JsStr::latin1("get Nanoseconds".as_bytes()), + JsStr::latin1("get Sign".as_bytes()), + JsStr::latin1("get blank".as_bytes()), + JsStr::latin1("years".as_bytes()), + JsStr::latin1("months".as_bytes()), + JsStr::latin1("weeks".as_bytes()), + JsStr::latin1("days".as_bytes()), + JsStr::latin1("hours".as_bytes()), + JsStr::latin1("minutes".as_bytes()), + JsStr::latin1("seconds".as_bytes()), + JsStr::latin1("milliseconds".as_bytes()), + JsStr::latin1("microseconds".as_bytes()), + JsStr::latin1("nanoseconds".as_bytes()), + JsStr::latin1("blank".as_bytes()), + JsStr::latin1("negated".as_bytes()), + JsStr::latin1("total".as_bytes()), + JsStr::latin1("get calendarId".as_bytes()), + JsStr::latin1("get year".as_bytes()), + JsStr::latin1("get month".as_bytes()), + JsStr::latin1("get monthCode".as_bytes()), + JsStr::latin1("get day".as_bytes()), + JsStr::latin1("get dayOfWeek".as_bytes()), + JsStr::latin1("get dayOfYear".as_bytes()), + JsStr::latin1("get weekOfYear".as_bytes()), + JsStr::latin1("get yearOfWeek".as_bytes()), + JsStr::latin1("get daysInWeek".as_bytes()), + JsStr::latin1("get daysInMonth".as_bytes()), + JsStr::latin1("get daysInYear".as_bytes()), + JsStr::latin1("get monthsInYear".as_bytes()), + JsStr::latin1("get inLeapYear".as_bytes()), + JsStr::latin1("calendarId".as_bytes()), + JsStr::latin1("year".as_bytes()), + JsStr::latin1("month".as_bytes()), + JsStr::latin1("monthCode".as_bytes()), + JsStr::latin1("day".as_bytes()), + JsStr::latin1("dayOfWeek".as_bytes()), + JsStr::latin1("dayOfYear".as_bytes()), + JsStr::latin1("weekOfYear".as_bytes()), + JsStr::latin1("yearOfWeek".as_bytes()), + JsStr::latin1("daysInWeek".as_bytes()), + JsStr::latin1("daysInMonth".as_bytes()), + JsStr::latin1("daysInYear".as_bytes()), + JsStr::latin1("monthsInYear".as_bytes()), + JsStr::latin1("inLeapYear".as_bytes()), + JsStr::latin1("toPlainYearMonth".as_bytes()), + JsStr::latin1("toPlainMonthDay".as_bytes()), + JsStr::latin1("getISOFields".as_bytes()), + JsStr::latin1("getCalendar".as_bytes()), + JsStr::latin1("withCalendar".as_bytes()), + JsStr::latin1("dateFromFields".as_bytes()), + JsStr::latin1("yearMonthFromFields".as_bytes()), + JsStr::latin1("monthDayFromFields".as_bytes()), + JsStr::latin1("dateAdd".as_bytes()), + JsStr::latin1("dateUntil".as_bytes()), + JsStr::latin1("era".as_bytes()), + JsStr::latin1("eraYear".as_bytes()), + JsStr::latin1("fields".as_bytes()), + JsStr::latin1("mergeFields".as_bytes()), + // Console object + JsStr::latin1("console".as_bytes()), + JsStr::latin1("assert".as_bytes()), + JsStr::latin1("debug".as_bytes()), + JsStr::latin1("error".as_bytes()), + JsStr::latin1("info".as_bytes()), + JsStr::latin1("trace".as_bytes()), + JsStr::latin1("warn".as_bytes()), + JsStr::latin1("exception".as_bytes()), + JsStr::latin1("count".as_bytes()), + JsStr::latin1("countReset".as_bytes()), + JsStr::latin1("group".as_bytes()), + JsStr::latin1("groupCollapsed".as_bytes()), + JsStr::latin1("groupEnd".as_bytes()), + JsStr::latin1("time".as_bytes()), + JsStr::latin1("timeLog".as_bytes()), + JsStr::latin1("timeEnd".as_bytes()), + JsStr::latin1("dir".as_bytes()), + JsStr::latin1("dirxml".as_bytes()), + // Minified name + JsStr::latin1("a".as_bytes()), + JsStr::latin1("c".as_bytes()), + JsStr::latin1("d".as_bytes()), + JsStr::latin1("e".as_bytes()), + JsStr::latin1("f".as_bytes()), + JsStr::latin1("g".as_bytes()), + JsStr::latin1("h".as_bytes()), + JsStr::latin1("i".as_bytes()), + JsStr::latin1("j".as_bytes()), + JsStr::latin1("k".as_bytes()), + JsStr::latin1("l".as_bytes()), + JsStr::latin1("m".as_bytes()), + JsStr::latin1("n".as_bytes()), + JsStr::latin1("o".as_bytes()), + JsStr::latin1("p".as_bytes()), + JsStr::latin1("q".as_bytes()), + JsStr::latin1("r".as_bytes()), + JsStr::latin1("s".as_bytes()), + JsStr::latin1("t".as_bytes()), + JsStr::latin1("u".as_bytes()), + JsStr::latin1("v".as_bytes()), + JsStr::latin1("w".as_bytes()), + JsStr::latin1("x".as_bytes()), + JsStr::latin1("y".as_bytes()), + JsStr::latin1("z".as_bytes()), + JsStr::latin1("A".as_bytes()), + JsStr::latin1("C".as_bytes()), + JsStr::latin1("D".as_bytes()), + JsStr::latin1("E".as_bytes()), + JsStr::latin1("F".as_bytes()), + JsStr::latin1("G".as_bytes()), + JsStr::latin1("H".as_bytes()), + JsStr::latin1("I".as_bytes()), + JsStr::latin1("J".as_bytes()), + JsStr::latin1("K".as_bytes()), + JsStr::latin1("L".as_bytes()), + JsStr::latin1("M".as_bytes()), + JsStr::latin1("N".as_bytes()), + JsStr::latin1("O".as_bytes()), + JsStr::latin1("P".as_bytes()), + JsStr::latin1("Q".as_bytes()), + JsStr::latin1("R".as_bytes()), + JsStr::latin1("S".as_bytes()), + JsStr::latin1("T".as_bytes()), + JsStr::latin1("U".as_bytes()), + JsStr::latin1("V".as_bytes()), + JsStr::latin1("W".as_bytes()), + JsStr::latin1("X".as_bytes()), + JsStr::latin1("Y".as_bytes()), + JsStr::latin1("Z".as_bytes()), + JsStr::latin1("_".as_bytes()), + JsStr::latin1("$".as_bytes()), +]; diff --git a/core/engine/src/string/iter.rs b/core/string/src/iter.rs similarity index 97% rename from core/engine/src/string/iter.rs rename to core/string/src/iter.rs index 03dedcb6b8d..ead0e8c94c7 100644 --- a/core/engine/src/string/iter.rs +++ b/core/string/src/iter.rs @@ -29,6 +29,7 @@ impl<'a> Iter<'a> { impl Iterator for Iter<'_> { type Item = u16; + #[inline] fn next(&mut self) -> Option { match &mut self.inner { IterInner::U8(iter) => iter.map(u16::from).next(), @@ -40,6 +41,7 @@ impl Iterator for Iter<'_> { impl FusedIterator for Iter<'_> {} impl ExactSizeIterator for Iter<'_> { + #[inline] fn len(&self) -> usize { match &self.inner { IterInner::U8(v) => v.len(), @@ -75,6 +77,7 @@ impl<'a> Windows<'a> { impl<'a> Iterator for Windows<'a> { type Item = JsStr<'a>; + #[inline] fn next(&mut self) -> Option { match &mut self.inner { WindowsInner::U8(iter) => iter.next().map(JsStr::latin1), @@ -86,6 +89,7 @@ impl<'a> Iterator for Windows<'a> { impl FusedIterator for Windows<'_> {} impl ExactSizeIterator for Windows<'_> { + #[inline] fn len(&self) -> usize { match &self.inner { WindowsInner::U8(v) => v.len(), diff --git a/core/engine/src/string/mod.rs b/core/string/src/lib.rs similarity index 82% rename from core/engine/src/string/mod.rs rename to core/string/src/lib.rs index 16f21727310..2daae7d5c6a 100644 --- a/core/engine/src/string/mod.rs +++ b/core/string/src/lib.rs @@ -1,10 +1,4 @@ //! A Latin1 or UTF-16 encoded, reference counted, immutable string. -//! -//! This module contains the [`JsString`] type, the [`js_string`][crate::js_string] macro and the -//! [`js_str`][crate::js_str] macro. -//! -//! The [`js_string`][crate::js_string] macro is used when you need to create a new [`JsString`], -//! and the [`js_str`][crate::js_str] macro is used for const conversions of string literals to [`JsStr`]. // Required per unsafe code standards to ensure every unsafe usage is properly documented. // - `unsafe_op_in_unsafe_fn` will be warn-by-default in edition 2024: @@ -20,22 +14,24 @@ // Right now this allows us to use the stable polyfill from the `sptr` crate, which uses // the same names from the unstable functions of the `std::ptr` module. #![allow(unstable_name_collisions)] +#![allow(clippy::module_name_repetitions)] -pub mod common; +mod common; mod iter; mod str; +mod tagged; -use self::{common::StaticJsStrings, iter::Windows, str::JsSliceIndex}; +#[cfg(test)] +mod tests; + +use self::{iter::Windows, str::JsSliceIndex}; +use crate::tagged::{Tagged, UnwrappedTagged}; #[doc(inline)] -pub use crate::string::{ +pub use crate::{ + common::StaticJsStrings, iter::Iter, str::{JsStr, JsStrVariant}, }; -use crate::{ - builtins::string::is_trimmable_whitespace, - tagged::{Tagged, UnwrappedTagged}, -}; -use boa_gc::{Finalize, Trace}; use std::{ alloc::{alloc, dealloc, Layout}, cell::Cell, @@ -51,65 +47,41 @@ fn alloc_overflow() -> ! { panic!("detected overflow during string allocation") } -/// Utility macro to create a [`JsString`]. -/// -/// # Examples -/// -/// You can call the macro without arguments to create an empty `JsString`: -/// -/// ``` -/// use boa_engine::js_string; -/// -/// let empty_str = js_string!(); -/// assert!(empty_str.is_empty()); -/// ``` -/// -/// -/// You can create a `JsString` from a string literal, which completely skips the runtime -/// conversion from [`&str`] to [&\[u16\]][slice]: -/// -/// ``` -/// # use boa_engine::js_string; -/// let hw = js_string!("Hello, world!"); -/// assert_eq!(&hw, "Hello, world!"); -/// ``` -/// -/// Any `&[u16]` slice is a valid `JsString`, including unpaired surrogates: -/// -/// ``` -/// # use boa_engine::js_string; -/// let array = js_string!(&[0xD8AFu16, 0x00A0, 0xD8FF, 0x00F0]); -/// ``` -/// -/// You can also pass it any number of `&[u16]` as arguments to create a new `JsString` with -/// the concatenation of every slice: -/// -/// ``` -/// # use boa_engine::{js_string, js_str, JsStr}; -/// const NAME: JsStr<'_> = js_str!("human! "); -/// let greeting = js_string!("Hello, "); -/// let msg = js_string!(&greeting, NAME, js_str!("Nice to meet you!")); -/// -/// assert_eq!(&msg, "Hello, human! Nice to meet you!"); -/// ``` -#[macro_export] -#[allow(clippy::module_name_repetitions)] -macro_rules! js_string { - () => { - $crate::string::JsString::default() - }; - ($s:literal) => { - $crate::string::JsString::from($crate::js_str!($s)) - }; - ($s:expr) => { - $crate::string::JsString::from($s) - }; - ( $x:expr, $y:expr ) => { - $crate::string::JsString::concat($crate::string::JsStr::from($x), $crate::string::JsStr::from($y)) - }; - ( $( $s:expr ),+ ) => { - $crate::string::JsString::concat_array(&[ $( $crate::string::JsStr::from($s) ),+ ]) - }; +/// Helper function to check if a `char` is trimmable. +pub(crate) const fn is_trimmable_whitespace(c: char) -> bool { + // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does + // + // Rust uses \p{White_Space} by default, which also includes: + // `\u{0085}' (next line) + // And does not include: + // '\u{FEFF}' (zero width non-breaking space) + // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space + matches!( + c, + '\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' | + // Unicode Space_Separator category + '\u{1680}' | '\u{2000}' + ..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' | + // Line terminators: https://tc39.es/ecma262/#sec-line-terminators + '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' + ) +} + +/// Helper function to check if a `u8` latin1 character is trimmable. +pub(crate) const fn is_trimmable_whitespace_latin1(c: u8) -> bool { + // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does + // + // Rust uses \p{White_Space} by default, which also includes: + // `\u{0085}' (next line) + // And does not include: + // '\u{FEFF}' (zero width non-breaking space) + // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space + matches!( + c, + 0x09 | 0x0B | 0x0C | 0x20 | 0xA0 | + // Line terminators: https://tc39.es/ecma262/#sec-line-terminators + 0x0A | 0x0D + ) } /// Represents a Unicode codepoint within a [`JsString`], which could be a valid @@ -127,6 +99,7 @@ pub enum CodePoint { impl CodePoint { /// Get the number of UTF-16 code units needed to encode this code point. + #[inline] #[must_use] pub const fn code_unit_count(self) -> usize { match self { @@ -136,6 +109,7 @@ impl CodePoint { } /// Convert the code point to its [`u32`] representation. + #[inline] #[must_use] pub fn as_u32(self) -> u32 { match self { @@ -146,6 +120,7 @@ impl CodePoint { /// If the code point represents a valid 'Unicode scalar value', returns its [`char`] /// representation, otherwise returns [`None`] on unpaired surrogates. + #[inline] #[must_use] pub const fn as_char(self) -> Option { match self { @@ -161,6 +136,8 @@ impl CodePoint { /// /// Panics if the buffer is not large enough. A buffer of length 2 is large enough to encode any /// code point. + #[inline] + #[must_use] pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { match self { Self::Unicode(c) => c.encode_utf16(dst), @@ -218,9 +195,6 @@ const DATA_OFFSET: usize = std::mem::size_of::(); /// /// We define some commonly used string constants in an interner. For these strings, we don't allocate /// memory on the heap to reduce the overhead of memory allocation and reference counting. -#[derive(Trace, Finalize)] -// Safety: `JsString` does not contain any objects which needs to be traced, so this is safe. -#[boa_gc(unsafe_empty_trace)] #[allow(clippy::module_name_repetitions)] pub struct JsString { ptr: Tagged, @@ -230,6 +204,7 @@ pub struct JsString { static_assertions::assert_eq_size!(JsString, *const ()); impl<'a> From<&'a JsString> for JsStr<'a> { + #[inline] fn from(value: &'a JsString) -> Self { value.as_str() } @@ -237,8 +212,9 @@ impl<'a> From<&'a JsString> for JsStr<'a> { impl<'a> IntoIterator for &'a JsString { type IntoIter = Iter<'a>; - type Item = u16; + + #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } @@ -260,6 +236,7 @@ impl JsString { } /// Obtains the underlying [`&[u16]`][slice] slice of a [`JsString`] + #[inline] #[must_use] pub fn as_str(&self) -> JsStr<'_> { match self.ptr.unwrap() { @@ -298,6 +275,7 @@ impl JsString { } /// Creates a new [`JsString`] from the concatenation of `x` and `y`. + #[inline] #[must_use] pub fn concat(x: JsStr<'_>, y: JsStr<'_>) -> Self { Self::concat_array(&[x, y]) @@ -305,6 +283,7 @@ impl JsString { /// Creates a new [`JsString`] from the concatenation of every element of /// `strings`. + #[inline] #[must_use] pub fn concat_array(strings: &[JsStr<'_>]) -> Self { let mut latin1_encoding = true; @@ -373,6 +352,7 @@ impl JsString { /// Decodes a [`JsString`] into a [`String`], replacing invalid data with its escaped representation /// in 4 digit hexadecimal. + #[inline] #[must_use] pub fn to_std_string_escaped(&self) -> String { self.to_string_escaped() @@ -383,6 +363,7 @@ impl JsString { /// # Errors /// /// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data. + #[inline] pub fn to_std_string(&self) -> Result { match self.as_str().variant() { JsStrVariant::Latin1(v) => Ok(v.iter().copied().map(char::from).collect()), @@ -392,6 +373,7 @@ impl JsString { /// Decodes a [`JsString`] into an iterator of [`Result`], returning surrogates as /// errors. + #[inline] pub fn to_std_string_with_surrogates(&self) -> impl Iterator> + '_ { struct WideStringDecoderIterator { codepoints: Peekable, @@ -443,6 +425,7 @@ impl JsString { } /// Maps the valid segments of an UTF16 string and leaves the unpaired surrogates unchanged. + #[inline] #[must_use] pub fn map_valid_segments(&self, mut f: F) -> Self where @@ -457,10 +440,11 @@ impl JsString { } } - js_string!(&text[..]) + Self::from(&text[..]) } /// Gets an iterator of all the Unicode codepoints of a [`JsString`]. + #[inline] pub fn code_points(&self) -> impl Iterator + Clone + '_ { char::decode_utf16(self.iter()).map(|res| match res { Ok(c) => CodePoint::Unicode(c), @@ -477,6 +461,7 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-stringindexof + #[inline] #[must_use] pub fn index_of(&self, search_value: JsStr<'_>, from_index: usize) -> Option { // 1. Assert: Type(string) is String. @@ -522,6 +507,7 @@ impl JsString { /// # Panics /// /// If `position` is smaller than size of string. + #[inline] #[must_use] pub fn code_point_at(&self, position: usize) -> CodePoint { // 1. Let size be the length of string. @@ -569,6 +555,8 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-stringtonumber + #[inline] + #[must_use] pub fn to_number(&self) -> f64 { // 1. Let text be ! StringToCodePoints(str). // 2. Let literal be ParseText(text, StringNumericLiteral). @@ -781,12 +769,14 @@ impl JsString { } /// Convert the [`JsString`] into a [`Vec`]. + #[inline] #[must_use] pub fn to_vec(&self) -> Vec { self.as_str().to_vec() } /// Check if the [`JsString`] contains a byte. + #[inline] #[must_use] pub fn contains(&self, element: u8) -> bool { match self.as_str().variant() { @@ -796,30 +786,35 @@ impl JsString { } /// Trim whitespace from the start and end of the [`JsString`]. + #[inline] #[must_use] pub fn trim(&self) -> JsStr<'_> { self.as_str().trim() } /// Trim whitespace from the start of the [`JsString`]. + #[inline] #[must_use] pub fn trim_start(&self) -> JsStr<'_> { self.as_str().trim_start() } /// Trim whitespace from the end of the [`JsString`]. + #[inline] #[must_use] pub fn trim_end(&self) -> JsStr<'_> { self.as_str().trim_end() } /// Check if the [`JsString`] is static. + #[inline] #[must_use] pub fn is_static(&self) -> bool { self.ptr.is_tagged() } /// Get the element a the given index, [`None`] otherwise. + #[inline] #[must_use] pub fn get<'a, I>(&'a self, index: I) -> Option where @@ -833,6 +828,7 @@ impl JsString { /// # Panics /// /// If the index is out of bounds. + #[inline] #[must_use] pub fn get_expect<'a, I>(&'a self, index: I) -> I::Value where @@ -840,6 +836,20 @@ impl JsString { { self.get(index).expect("Index out of bounds") } + + /// Gets the number of `JsString`s which point to this allocation. + #[inline] + #[must_use] + pub fn refcount(&self) -> Option { + match self.ptr.unwrap() { + UnwrappedTagged::Ptr(inner) => { + // SAFETY: The reference count of `JsString` guarantees that `inner` is always valid. + let inner = unsafe { inner.as_ref() }; + Some(inner.refcount.get()) + } + UnwrappedTagged::Tag(_inner) => None, + } + } } impl Clone for JsString { @@ -866,6 +876,7 @@ impl Default for JsString { } impl Drop for JsString { + #[inline] fn drop(&mut self) { if let UnwrappedTagged::Ptr(raw) = self.ptr.unwrap() { // See https://doc.rust-lang.org/src/alloc/sync.rs.html#1672 for details. @@ -947,6 +958,7 @@ impl From<&str> for JsString { } impl From> for JsString { + #[inline] fn from(value: JsStr<'_>) -> Self { StaticJsStrings::get_string(&value) .unwrap_or_else(|| JsString::from_slice_skip_interning(value)) @@ -954,6 +966,7 @@ impl From> for JsString { } impl From<&[JsString]> for JsString { + #[inline] fn from(value: &[JsString]) -> Self { Self::concat_array( &value @@ -972,36 +985,42 @@ impl From for JsString { } impl From<&[u16; N]> for JsString { + #[inline] fn from(s: &[u16; N]) -> Self { Self::from(&s[..]) } } impl Hash for JsString { + #[inline] fn hash(&self, state: &mut H) { self.as_str().hash(state); } } impl PartialOrd for JsStr<'_> { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for JsString { + #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_str().cmp(&other.as_str()) } } impl PartialEq for JsString { + #[inline] fn eq(&self, other: &Self) -> bool { self.as_str() == other.as_str() } } impl PartialEq for [u16] { + #[inline] fn eq(&self, other: &JsString) -> bool { if self.len() != other.len() { return false; @@ -1016,24 +1035,28 @@ impl PartialEq for [u16] { } impl PartialEq for [u16; N] { + #[inline] fn eq(&self, other: &JsString) -> bool { self[..] == *other } } impl PartialEq<[u16]> for JsString { + #[inline] fn eq(&self, other: &[u16]) -> bool { other == self } } impl PartialEq<[u16; N]> for JsString { + #[inline] fn eq(&self, other: &[u16; N]) -> bool { *self == other[..] } } impl PartialEq for JsString { + #[inline] fn eq(&self, other: &str) -> bool { let utf16 = self.code_points(); let mut utf8 = other.chars(); @@ -1052,24 +1075,28 @@ impl PartialEq for JsString { } impl PartialEq for str { + #[inline] fn eq(&self, other: &JsString) -> bool { other == self } } impl PartialEq> for JsString { + #[inline] fn eq(&self, other: &JsStr<'_>) -> bool { self.as_str() == *other } } impl PartialEq for JsStr<'_> { + #[inline] fn eq(&self, other: &JsString) -> bool { other == self } } impl PartialOrd for JsString { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -1078,6 +1105,7 @@ impl PartialOrd for JsString { impl FromStr for JsString { type Err = Infallible; + #[inline] fn from_str(s: &str) -> Result { Ok(Self::from(s)) } @@ -1091,6 +1119,7 @@ pub(crate) trait ToStringEscaped { } impl ToStringEscaped for [u16] { + #[inline] fn to_string_escaped(&self) -> String { char::decode_utf16(self.iter().copied()) .map(|r| match r { @@ -1100,187 +1129,3 @@ impl ToStringEscaped for [u16] { .collect() } } - -#[allow(clippy::redundant_clone)] -#[cfg(test)] -mod tests { - use std::hash::{BuildHasher, BuildHasherDefault, Hash}; - - use crate::{string::common::StaticJsStrings, tagged::UnwrappedTagged, JsStr}; - - use super::JsString; - use boa_macros::{js_str, utf16}; - use rustc_hash::FxHasher; - - impl JsString { - /// Gets the number of `JsString`s which point to this allocation. - fn refcount(&self) -> Option { - match self.ptr.unwrap() { - UnwrappedTagged::Ptr(inner) => { - // SAFETY: The reference count of `JsString` guarantees that `inner` is always valid. - let inner = unsafe { inner.as_ref() }; - Some(inner.refcount.get()) - } - UnwrappedTagged::Tag(_inner) => None, - } - } - } - - fn hash_value(value: &T) -> u64 { - BuildHasherDefault::::default().hash_one(value) - } - - #[test] - fn empty() { - let s = js_string!(); - assert_eq!(&s, utf16!("")); - } - - #[test] - fn refcount() { - let x = js_string!("Hello world"); - assert_eq!(x.refcount(), Some(1)); - - { - let y = x.clone(); - assert_eq!(x.refcount(), Some(2)); - assert_eq!(y.refcount(), Some(2)); - - { - let z = y.clone(); - assert_eq!(x.refcount(), Some(3)); - assert_eq!(y.refcount(), Some(3)); - assert_eq!(z.refcount(), Some(3)); - } - - assert_eq!(x.refcount(), Some(2)); - assert_eq!(y.refcount(), Some(2)); - } - - assert_eq!(x.refcount(), Some(1)); - } - - #[test] - fn static_refcount() { - let x = js_string!(); - assert_eq!(x.refcount(), None); - - { - let y = x.clone(); - assert_eq!(x.refcount(), None); - assert_eq!(y.refcount(), None); - }; - - assert_eq!(x.refcount(), None); - } - - #[test] - fn ptr_eq() { - let x = js_string!("Hello"); - let y = x.clone(); - - assert!(!x.ptr.is_tagged()); - - assert_eq!(x.ptr.addr(), y.ptr.addr()); - - let z = js_string!("Hello"); - assert_ne!(x.ptr.addr(), z.ptr.addr()); - assert_ne!(y.ptr.addr(), z.ptr.addr()); - } - - #[test] - fn static_ptr_eq() { - let x = js_string!(); - let y = x.clone(); - - assert!(x.ptr.is_tagged()); - - assert_eq!(x.ptr.addr(), y.ptr.addr()); - - let z = js_string!(); - assert_eq!(x.ptr.addr(), z.ptr.addr()); - assert_eq!(y.ptr.addr(), z.ptr.addr()); - } - - #[test] - fn as_str() { - const HELLO: &[u16] = utf16!("Hello"); - let x = js_string!(HELLO); - - assert_eq!(&x, HELLO); - } - - #[test] - fn hash() { - const HELLOWORLD: JsStr<'_> = js_str!("Hello World!"); - let x = js_string!(HELLOWORLD); - - assert_eq!(x.as_str(), HELLOWORLD); - - assert!(HELLOWORLD.is_latin1()); - assert!(x.as_str().is_latin1()); - - let s_hash = hash_value(&HELLOWORLD); - let x_hash = hash_value(&x); - - assert_eq!(s_hash, x_hash); - } - - #[test] - fn concat() { - const Y: &[u16] = utf16!(", "); - const W: &[u16] = utf16!("!"); - - let x = js_string!("hello"); - let z = js_string!("world"); - - let xy = js_string!(&x, &JsString::from(Y)); - assert_eq!(&xy, utf16!("hello, ")); - assert_eq!(xy.refcount(), Some(1)); - - let xyz = js_string!(&xy, &z); - assert_eq!(&xyz, utf16!("hello, world")); - assert_eq!(xyz.refcount(), Some(1)); - - let xyzw = js_string!(&xyz, &JsString::from(W)); - assert_eq!(&xyzw, utf16!("hello, world!")); - assert_eq!(xyzw.refcount(), Some(1)); - } - - #[test] - fn trim_start_non_ascii_to_ascii() { - let s = "\u{2029}abc"; - let x = js_string!(s); - - let y = js_string!(x.trim_start()); - - assert_eq!(&y, s.trim_start()); - } - - #[test] - fn conversion_to_known_static_js_string() { - const JS_STR_U8: &JsStr<'_> = &js_str!("length"); - const JS_STR_U16: &JsStr<'_> = &JsStr::utf16(utf16!("length")); - - assert!(JS_STR_U8.is_latin1()); - assert!(!JS_STR_U16.is_latin1()); - - assert_eq!(JS_STR_U8, JS_STR_U8); - assert_eq!(JS_STR_U16, JS_STR_U16); - - assert_eq!(JS_STR_U8, JS_STR_U16); - assert_eq!(JS_STR_U16, JS_STR_U8); - - assert_eq!(hash_value(JS_STR_U8), hash_value(JS_STR_U16)); - - let string = StaticJsStrings::get_string(JS_STR_U8); - - assert!(string.is_some()); - assert!(string.unwrap().as_str().is_latin1()); - - let string = StaticJsStrings::get_string(JS_STR_U16); - - assert!(string.is_some()); - assert!(string.unwrap().as_str().is_latin1()); - } -} diff --git a/core/engine/src/string/str.rs b/core/string/src/str.rs similarity index 96% rename from core/engine/src/string/str.rs rename to core/string/src/str.rs index 6ce3c4cabd4..ef31ac7a276 100644 --- a/core/engine/src/string/str.rs +++ b/core/string/src/str.rs @@ -1,7 +1,4 @@ -use crate::{ - builtins::string::{is_trimmable_whitespace, is_trimmable_whitespace_latin1}, - string::Iter, -}; +use crate::{is_trimmable_whitespace, is_trimmable_whitespace_latin1, Iter}; use std::{ hash::{Hash, Hasher}, slice::SliceIndex, @@ -195,6 +192,8 @@ impl<'a> JsStr<'a> { } /// Convert the [`JsStr`] into a [`Vec`]. + #[inline] + #[must_use] pub fn to_vec(&self) -> Vec { match self.variant() { JsStrVariant::Latin1(v) => v.iter().copied().map(u16::from).collect(), @@ -203,13 +202,19 @@ impl<'a> JsStr<'a> { } /// Returns true if needle is a prefix of the [`JsStr`]. + #[inline] #[must_use] + // We check the size, so this should never panic. + #[allow(clippy::missing_panics_doc)] pub fn starts_with(&self, needle: JsStr<'_>) -> bool { let n = needle.len(); self.len() >= n && needle == self.get(..n).expect("already checked size") } /// Returns `true` if `needle` is a suffix of the [`JsStr`]. + #[inline] #[must_use] + // We check the size, so this should never panic. + #[allow(clippy::missing_panics_doc)] pub fn ends_with(&self, needle: JsStr<'_>) -> bool { let (m, n) = (self.len(), needle.len()); m >= n && needle == self.get(m - n..).expect("already checked size") @@ -217,6 +222,7 @@ impl<'a> JsStr<'a> { } impl Hash for JsStr<'_> { + #[inline] fn hash(&self, state: &mut H) { // NOTE: The hash function has been inlined to ensure that a hash of latin1 and U16 // encoded strings remains the same if they have the same characters @@ -238,6 +244,7 @@ impl Hash for JsStr<'_> { } impl Ord for JsStr<'_> { + #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { match (self.variant(), other.variant()) { (JsStrVariant::Latin1(x), JsStrVariant::Latin1(y)) => x.cmp(y), @@ -250,6 +257,7 @@ impl Ord for JsStr<'_> { impl Eq for JsStr<'_> {} impl PartialEq for JsStr<'_> { + #[inline] fn eq(&self, other: &Self) -> bool { match (self.variant(), other.variant()) { (JsStrVariant::Latin1(lhs), JsStrVariant::Latin1(rhs)) => return lhs == rhs, @@ -269,6 +277,7 @@ impl PartialEq for JsStr<'_> { } impl<'a> PartialEq> for [u16] { + #[inline] fn eq(&self, other: &JsStr<'a>) -> bool { if self.len() != other.len() { return false; diff --git a/core/string/src/tagged.rs b/core/string/src/tagged.rs new file mode 100644 index 00000000000..83ec17c24b4 --- /dev/null +++ b/core/string/src/tagged.rs @@ -0,0 +1,109 @@ +// Remove when/if https://github.com/rust-lang/rust/issues/95228 stabilizes. +// Right now this allows us to use the stable polyfill from the `sptr` crate, which uses +// the same names from the unstable functions of the `std::ptr` module. +#![allow(unstable_name_collisions)] + +use sptr::Strict; +use std::ptr::NonNull; + +/// A pointer that can be tagged with an `usize`. +/// +/// Only pointers with a minimum alignment of 2-bytes are valid, and the tag must have its most +/// significant bit (MSB) unset. In other words, the tag must fit inside `usize::BITS - 1` bits. +/// +/// # Representation +/// +/// If the least significant bit (LSB) of the internal [`NonNull`] is set (1), then the pointer +/// address represents a tag where the remaining bits store the tag. Otherwise, the whole pointer +/// represents the pointer itself. +/// +/// It uses [`NonNull`], which guarantees that [`Tagged`] can use the "null pointer optimization" +/// to optimize the size of [`Option`]. +/// +/// # Provenance +/// +/// This struct stores a [`NonNull`] instead of a [`NonZeroUsize`][std::num::NonZeroUsize] +/// in order to preserve the provenance of our valid heap pointers. +/// On the other hand, all index values are just casted to invalid pointers, because we don't need to +/// preserve the provenance of [`usize`] indices. +/// +/// [tagged_wp]: https://en.wikipedia.org/wiki/Tagged_pointer +#[derive(Debug)] +pub(crate) struct Tagged(NonNull); + +impl Clone for Tagged { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Tagged {} + +impl Tagged { + /// Creates a new, tagged `Tagged` pointer from an integer. + /// + /// # Requirements + /// + /// - `T` must have an alignment of at least 2. + /// - `tag` must fit inside `usize::BITS - 1` bits + pub(crate) const fn from_tag(tag: usize) -> Self { + debug_assert!(std::mem::align_of::() >= 2); + let addr = (tag << 1) | 1; + // SAFETY: `addr` is never zero, since we always set its LSB to 1 + unsafe { Self(NonNull::new_unchecked(sptr::invalid_mut(addr))) } + } + + /// Creates a new `Tagged` pointer from a raw pointer. + /// + /// # Requirements + /// + /// - `T` must have an alignment of at least 2. + /// + /// # Safety + /// + /// - `T` must be non null. + pub(crate) const unsafe fn from_ptr(ptr: *mut T) -> Self { + debug_assert!(std::mem::align_of::() >= 2); + // SAFETY: the caller must ensure the invariants hold. + unsafe { Self(NonNull::new_unchecked(ptr)) } + } + + /// Creates a new `Tagged` pointer from a `NonNull` pointer. + /// + /// # Requirements + /// + /// - `T` must have an alignment of at least 2. + pub(crate) const fn from_non_null(ptr: NonNull) -> Self { + debug_assert!(std::mem::align_of::() >= 2); + Self(ptr) + } + + /// Unwraps the `Tagged` pointer. + pub(crate) fn unwrap(self) -> UnwrappedTagged { + let addr = self.0.as_ptr().addr(); + if addr & 1 == 0 { + UnwrappedTagged::Ptr(self.0) + } else { + UnwrappedTagged::Tag(addr >> 1) + } + } + + /// Gets the address of the inner pointer. + #[allow(unused)] + pub(crate) fn addr(self) -> usize { + self.0.as_ptr().addr() + } + + /// Returns `true` if `self ` is a tagged pointer. + #[allow(unused)] + pub(crate) fn is_tagged(self) -> bool { + self.0.as_ptr().addr() & 1 > 0 + } +} + +/// The unwrapped value of a [`Tagged`] pointer. +#[derive(Debug, Clone, Copy)] +pub(crate) enum UnwrappedTagged { + Ptr(NonNull), + Tag(usize), +} diff --git a/core/string/src/tests.rs b/core/string/src/tests.rs new file mode 100644 index 00000000000..2022994b483 --- /dev/null +++ b/core/string/src/tests.rs @@ -0,0 +1,166 @@ +#![allow(clippy::redundant_clone)] + +use std::hash::{BuildHasher, BuildHasherDefault, Hash}; + +use crate::{JsStr, JsString, StaticJsStrings}; + +use boa_macros::utf16; +use rustc_hash::FxHasher; + +fn hash_value(value: &T) -> u64 { + BuildHasherDefault::::default().hash_one(value) +} + +#[test] +fn empty() { + let s = StaticJsStrings::EMPTY_STRING; + assert_eq!(&s, utf16!("")); +} + +#[test] +fn refcount() { + let x = JsString::from("Hello world"); + assert_eq!(x.refcount(), Some(1)); + + { + let y = x.clone(); + assert_eq!(x.refcount(), Some(2)); + assert_eq!(y.refcount(), Some(2)); + + { + let z = y.clone(); + assert_eq!(x.refcount(), Some(3)); + assert_eq!(y.refcount(), Some(3)); + assert_eq!(z.refcount(), Some(3)); + } + + assert_eq!(x.refcount(), Some(2)); + assert_eq!(y.refcount(), Some(2)); + } + + assert_eq!(x.refcount(), Some(1)); +} + +#[test] +fn static_refcount() { + let x = StaticJsStrings::EMPTY_STRING; + assert_eq!(x.refcount(), None); + + { + let y = x.clone(); + assert_eq!(x.refcount(), None); + assert_eq!(y.refcount(), None); + }; + + assert_eq!(x.refcount(), None); +} + +#[test] +fn ptr_eq() { + let x = JsString::from("Hello"); + let y = x.clone(); + + assert!(!x.ptr.is_tagged()); + + assert_eq!(x.ptr.addr(), y.ptr.addr()); + + let z = JsString::from("Hello"); + assert_ne!(x.ptr.addr(), z.ptr.addr()); + assert_ne!(y.ptr.addr(), z.ptr.addr()); +} + +#[test] +fn static_ptr_eq() { + let x = StaticJsStrings::EMPTY_STRING; + let y = x.clone(); + + assert!(x.ptr.is_tagged()); + + assert_eq!(x.ptr.addr(), y.ptr.addr()); + + let z = StaticJsStrings::EMPTY_STRING; + assert_eq!(x.ptr.addr(), z.ptr.addr()); + assert_eq!(y.ptr.addr(), z.ptr.addr()); +} + +#[test] +fn as_str() { + const HELLO: &[u16] = utf16!("Hello"); + let x = JsString::from(HELLO); + + assert_eq!(&x, HELLO); +} + +#[test] +fn hash() { + const HELLOWORLD: JsStr<'_> = JsStr::latin1("Hello World!".as_bytes()); + let x = JsString::from(HELLOWORLD); + + assert_eq!(x.as_str(), HELLOWORLD); + + assert!(HELLOWORLD.is_latin1()); + assert!(x.as_str().is_latin1()); + + let s_hash = hash_value(&HELLOWORLD); + let x_hash = hash_value(&x); + + assert_eq!(s_hash, x_hash); +} + +#[test] +fn concat() { + const Y: &[u16] = utf16!(", "); + const W: &[u16] = utf16!("!"); + + let x = JsString::from("hello"); + let z = JsString::from("world"); + + let xy = JsString::concat(x.as_str(), JsString::from(Y).as_str()); + assert_eq!(&xy, utf16!("hello, ")); + assert_eq!(xy.refcount(), Some(1)); + + let xyz = JsString::concat(xy.as_str(), z.as_str()); + assert_eq!(&xyz, utf16!("hello, world")); + assert_eq!(xyz.refcount(), Some(1)); + + let xyzw = JsString::concat(xyz.as_str(), JsString::from(W).as_str()); + assert_eq!(&xyzw, utf16!("hello, world!")); + assert_eq!(xyzw.refcount(), Some(1)); +} + +#[test] +fn trim_start_non_ascii_to_ascii() { + let s = "\u{2029}abc"; + let x = JsString::from(s); + + let y = JsString::from(x.trim_start()); + + assert_eq!(&y, s.trim_start()); +} + +#[test] +fn conversion_to_known_static_js_string() { + const JS_STR_U8: &JsStr<'_> = &JsStr::latin1("length".as_bytes()); + const JS_STR_U16: &JsStr<'_> = &JsStr::utf16(utf16!("length")); + + assert!(JS_STR_U8.is_latin1()); + assert!(!JS_STR_U16.is_latin1()); + + assert_eq!(JS_STR_U8, JS_STR_U8); + assert_eq!(JS_STR_U16, JS_STR_U16); + + assert_eq!(JS_STR_U8, JS_STR_U16); + assert_eq!(JS_STR_U16, JS_STR_U8); + + assert_eq!(hash_value(JS_STR_U8), hash_value(JS_STR_U16)); + + let string = StaticJsStrings::get_string(JS_STR_U8); + + assert!(string.is_some()); + assert!(string.unwrap().as_str().is_latin1()); + + let string = StaticJsStrings::get_string(JS_STR_U16); + + assert!(string.is_some()); + assert!(string.unwrap().as_str().is_latin1()); +} From 314ff860f9bede802a862499d99f7a9ceb849362 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 21:25:20 +0200 Subject: [PATCH 024/212] Bump getrandom from 0.2.14 to 0.2.15 (#3850) Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.14 to 0.2.15. - [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-random/getrandom/compare/v0.2.14...v0.2.15) --- updated-dependencies: - dependency-name: getrandom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- ffi/wasm/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a012fc6025..66a1a5b1847 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1605,9 +1605,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -2271,9 +2271,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" diff --git a/ffi/wasm/Cargo.toml b/ffi/wasm/Cargo.toml index 2445c26a696..25c18cdbc3e 100644 --- a/ffi/wasm/Cargo.toml +++ b/ffi/wasm/Cargo.toml @@ -14,7 +14,7 @@ rust-version.workspace = true [dependencies] boa_engine = { workspace = true, features = ["js"] } wasm-bindgen = { version = "0.2.91", default-features = false } -getrandom = { version = "0.2.14", features = ["js"] } +getrandom = { version = "0.2.15", features = ["js"] } console_error_panic_hook = "0.1.7" [dev-dependencies] From 52bc17d1a887fa2ea81b1efc6f16aaaf2c93c737 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 21:25:41 +0200 Subject: [PATCH 025/212] Bump thiserror from 1.0.59 to 1.0.60 (#3851) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.59 to 1.0.60. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.59...1.0.60) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- core/engine/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66a1a5b1847..b4a80e68ea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3812,18 +3812,18 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 73502507c17..934363976dc 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -90,7 +90,7 @@ fast-float.workspace = true once_cell = { workspace = true, features = ["std"] } tap = "1.0.1" sptr = "0.3.2" -thiserror = "1.0.59" +thiserror = "1.0.60" dashmap = "5.5.3" num_enum = "0.7.2" pollster.workspace = true From e17d660352997b3a0bfe80ae9d5f33b2ad87fa32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 19:26:22 +0000 Subject: [PATCH 026/212] Bump num-bigint from 0.4.4 to 0.4.5 (#3854) Bumps [num-bigint](https://github.com/rust-num/num-bigint) from 0.4.4 to 0.4.5. - [Changelog](https://github.com/rust-num/num-bigint/blob/master/RELEASES.md) - [Commits](https://github.com/rust-num/num-bigint/compare/num-bigint-0.4.4...num-bigint-0.4.5) --- updated-dependencies: - dependency-name: num-bigint dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 5 ++--- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4a80e68ea5..047906812e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2512,12 +2512,11 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ "arbitrary", - "autocfg", "num-integer", "num-traits", "serde", diff --git a/Cargo.toml b/Cargo.toml index ac1c3bf75d1..b913271e0b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ hashbrown = { version = "0.14.5", default-features = false } indexmap = { version = "2.2.6", default-features = false } indoc = "2.0.5" jemallocator = "0.5.4" -num-bigint = "0.4.4" +num-bigint = "0.4.5" num-traits = "0.2.19" once_cell = { version = "1.19.0", default-features = false } phf = { version = "0.11.2", default-features = false } From e8f673c26bce50e0d8d58961a2fcf1685ec18632 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 20:35:40 +0000 Subject: [PATCH 027/212] Bump paste from 1.0.14 to 1.0.15 (#3852) Bumps [paste](https://github.com/dtolnay/paste) from 1.0.14 to 1.0.15. - [Release notes](https://github.com/dtolnay/paste/releases) - [Commits](https://github.com/dtolnay/paste/compare/1.0.14...1.0.15) --- updated-dependencies: - dependency-name: paste dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 047906812e5..9d8ba5a1dbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2687,9 +2687,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" From 0a5721bbc89d8b7e936c606cf09e8aaa4f56a6c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 23:47:20 +0200 Subject: [PATCH 028/212] Bump proc-macro2 from 1.0.81 to 1.0.82 (#3853) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.81 to 1.0.82. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.81...1.0.82) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d8ba5a1dbb..620e1775767 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2932,9 +2932,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] From 2e40dbd2931e1627d2e6a2f6dc69ade41b91c32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Mon, 20 May 2024 14:49:26 +0000 Subject: [PATCH 029/212] Fix CI for nextest step (#3862) --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 189da64b52e..10bc3589d7a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -45,6 +45,8 @@ jobs: name: Test runs-on: ${{ matrix.os }} timeout-minutes: 60 + env: + RUSTUP_WINDOWS_PATH_ADD_BIN: 1 strategy: matrix: os: From 6130adf6d9b4db3fbc9de7491995c5e43925a94b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Mon, 20 May 2024 10:43:10 -0600 Subject: [PATCH 030/212] Group dependabot updates (#3863) * Group dependabot updates * Add missing winapi feature --- .github/dependabot.yml | 86 ++--- Cargo.lock | 535 ++++++++++++++----------------- Cargo.toml | 46 +++ cli/Cargo.toml | 4 +- core/engine/Cargo.toml | 46 +-- core/macros/Cargo.toml | 8 +- core/profiler/Cargo.toml | 2 +- core/string/Cargo.toml | 4 +- examples/Cargo.toml | 6 +- ffi/wasm/Cargo.toml | 8 +- tests/macros/Cargo.toml | 2 +- tests/tester/Cargo.toml | 12 +- tools/gen-icu4x-data/Cargo.toml | 6 + tools/gen-icu4x-data/src/main.rs | 3 + tools/scripts/Cargo.toml | 2 +- 15 files changed, 372 insertions(+), 398 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5108f6684dc..409f8acea52 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,76 +4,32 @@ updates: directory: / schedule: interval: weekly + groups: + ci-dependencies: + applies-to: version-updates + patterns: ["*"] + update-types: + - "minor" + - "patch" - package-ecosystem: cargo directory: / schedule: interval: weekly - # CLI - - package-ecosystem: cargo - directory: /cli/ - schedule: - interval: weekly - # CORE - - package-ecosystem: cargo - directory: /core/ast/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/engine/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/gc/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/icu_provider/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/interner/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/macros/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/parser/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/profiler/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/runtime/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /core/temporal/ - schedule: - interval: weekly - # TESTS - - package-ecosystem: cargo - directory: /tests/tester/ - schedule: - interval: weekly - - package-ecosystem: cargo - directory: /tests/macros/ - schedule: - interval: weekly + groups: + rust-dependencies: + applies-to: version-updates + patterns: ["*"] + update-types: + - "minor" + - "patch" - package-ecosystem: cargo directory: /tests/fuzz/ schedule: interval: weekly - # FFI - - package-ecosystem: cargo - directory: /ffi/wasm/ - schedule: - interval: weekly - # TOOLS - - package-ecosystem: cargo - directory: /tools/ - schedule: - interval: weekly + groups: + fuzz-dependencies: + applies-to: version-updates + patterns: ["*"] + update-types: + - "minor" + - "patch" diff --git a/Cargo.lock b/Cargo.lock index 620e1775767..fa79bc6121c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anes" @@ -63,47 +63,48 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -137,38 +138,36 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.2.0", - "event-listener-strategy 0.5.1", + "event-listener-strategy 0.5.2", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.9.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b3e585719c2358d2660232671ca8ca4ddb4be4ce8a1842d6c2dc8685303316" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.2", + "fastrand 2.1.0", "futures-lite 2.3.0", "slab", ] [[package]] name = "async-fs" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ - "async-lock 3.3.0", + "async-lock", "blocking", "futures-lite 2.3.0", ] @@ -179,28 +178,19 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ - "async-lock 3.3.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.6.0", + "polling 3.7.0", "rustix", "slab", "tracing", "windows-sys 0.52.0", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.3.0" @@ -225,18 +215,18 @@ dependencies = [ [[package]] name = "async-process" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d999d925640d51b662b7b4e404224dd81de70f4aa4a199383c2c5e5b86885fa3" +checksum = "a53fc6301894e04a92cb2584fedde80cb25ba8e02d9dc39d4a87d036e22f397d" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.3.1", "async-io", - "async-lock 3.3.0", + "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.2.0", + "event-listener 5.3.0", "futures-lite 2.3.0", "rustix", "tracing", @@ -245,12 +235,12 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ "async-io", - "async-lock 2.8.0", + "async-lock", "atomic-waker", "cfg-if", "futures-core", @@ -258,14 +248,14 @@ dependencies = [ "rustix", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "atomic-waker" @@ -275,9 +265,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -302,9 +292,9 @@ checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" @@ -335,18 +325,16 @@ dependencies = [ [[package]] name = "blocking" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" dependencies = [ - "async-channel 2.2.0", - "async-lock 3.3.0", + "async-channel 2.3.1", + "async-lock", "async-task", - "fastrand 2.0.2", "futures-io", "futures-lite 2.3.0", "piper", - "tracing", ] [[package]] @@ -522,7 +510,7 @@ version = "0.18.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "synstructure", ] @@ -604,7 +592,7 @@ dependencies = [ "serde_repr", "serde_yaml", "time 0.3.36", - "toml 0.8.12", + "toml 0.8.13", ] [[package]] @@ -620,9 +608,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bus" @@ -659,9 +647,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" dependencies = [ "bytemuck_derive", ] @@ -674,7 +662,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -701,9 +689,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -725,7 +713,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.22", + "semver 1.0.23", "serde", "serde_json", "thiserror", @@ -745,9 +733,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" @@ -819,7 +807,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -830,9 +818,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clipboard-win" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d517d4b86184dbb111d3556a10f1c8a04da7428d2987bf1081602bf11c3aa9ee" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" dependencies = [ "error-code", ] @@ -872,9 +860,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colored" @@ -900,9 +888,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -919,9 +907,9 @@ dependencies = [ [[package]] name = "const_fn" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" [[package]] name = "core_maths" @@ -947,9 +935,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "58ebf8d6963185c7625d2c3c3962d99eb8936637b1427536d21dc36ae402ebad" dependencies = [ "cfg-if", ] @@ -998,9 +986,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -1026,9 +1014,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -1091,9 +1079,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -1101,26 +1089,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -1155,7 +1143,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "synstructure", ] @@ -1185,7 +1173,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -1218,7 +1206,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -1249,9 +1237,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elsa" @@ -1270,19 +1258,13 @@ checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "enum-iterator" version = "0.7.0" @@ -1321,7 +1303,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -1341,9 +1323,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1374,9 +1356,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -1395,11 +1377,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.2.0", + "event-listener 5.3.0", "pin-project-lite", ] @@ -1436,20 +1418,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" - -[[package]] -name = "fd-lock" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.52.0", -] +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fixed_decimal" @@ -1465,9 +1436,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -1536,7 +1507,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.2", + "fastrand 2.1.0", "futures-core", "futures-io", "parking", @@ -1551,7 +1522,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -1592,6 +1563,7 @@ dependencies = [ "icu_segmenter", "log", "simple_logger", + "winapi", ] [[package]] @@ -1641,9 +1613,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -2062,7 +2034,7 @@ checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -2150,9 +2122,9 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] @@ -2177,6 +2149,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "isahc" version = "1.7.2" @@ -2271,9 +2249,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" @@ -2293,9 +2271,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libnghttp2-sys" -version = "0.1.9+1.58.0" +version = "0.1.10+1.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57e858af2798e167e709b9d969325b6d8e9d50232fcbc494d7d54f976854a64" +checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" dependencies = [ "cc", "libc", @@ -2315,9 +2293,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" @@ -2330,9 +2308,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -2457,9 +2435,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -2489,15 +2467,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - [[package]] name = "nix" version = "0.28.0" @@ -2524,9 +2493,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -2583,7 +2552,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -2664,9 +2633,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -2674,15 +2643,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -2736,7 +2705,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -2765,7 +2734,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -2782,12 +2751,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" dependencies = [ "atomic-waker", - "fastrand 2.0.2", + "fastrand 2.1.0", "futures-io", ] @@ -2843,9 +2812,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if", "concurrent-queue", @@ -2932,9 +2901,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -2974,16 +2943,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -3042,11 +3001,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] @@ -3171,9 +3130,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -3192,9 +3151,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -3219,15 +3178,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", "rustls-pki-types", @@ -3236,9 +3195,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rustyline" @@ -3249,13 +3208,10 @@ dependencies = [ "bitflags 2.5.0", "cfg-if", "clipboard-win", - "fd-lock", - "home", "libc", "log", "memchr", "nix", - "radix_trie", "rustyline-derive", "unicode-segmentation", "unicode-width", @@ -3271,14 +3227,14 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "ryu-js" @@ -3342,9 +3298,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -3357,9 +3313,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] @@ -3385,20 +3341,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -3413,14 +3369,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -3464,9 +3420,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3536,11 +3492,11 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e635339259e51ef85ac7aa29a1cd991b957047507288697a690e80ab97d07cad" dependencies = [ - "async-channel 2.2.0", + "async-channel 2.3.1", "async-executor", "async-fs", "async-io", - "async-lock 3.3.0", + "async-lock", "async-net", "async-process", "blocking", @@ -3549,9 +3505,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3661,7 +3617,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -3683,9 +3639,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", @@ -3700,7 +3656,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -3731,7 +3687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.2", + "fastrand 2.1.0", "rustix", "windows-sys 0.52.0", ] @@ -3777,7 +3733,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -3788,7 +3744,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "test-case-core", ] @@ -3811,22 +3767,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -3965,21 +3921,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -3997,15 +3953,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.5", + "winnow 0.6.8", ] [[package]] @@ -4028,7 +3984,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -4074,16 +4030,16 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddb747392ea12569d501a5bbca08852e4c8cd88b92566074b2243b8846f09e6" +checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ "glob", "serde", "serde_derive", "serde_json", "termcolor", - "toml 0.8.12", + "toml 0.8.13", ] [[package]] @@ -4131,9 +4087,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unsafe-libyaml" @@ -4149,9 +4105,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.6" +version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" +checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ "base64", "flate2", @@ -4219,9 +4175,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" @@ -4260,7 +4216,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "wasm-bindgen-shared", ] @@ -4294,7 +4250,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4327,7 +4283,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -4665,11 +4621,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4706,7 +4662,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -4726,17 +4682,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -4747,9 +4704,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4765,9 +4722,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4783,9 +4740,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4801,9 +4764,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4819,9 +4782,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4831,9 +4794,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4849,9 +4812,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -4864,9 +4827,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -4912,28 +4875,28 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] @@ -4953,7 +4916,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", "synstructure", ] @@ -4999,7 +4962,7 @@ checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.65", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b913271e0b4..83b82e7e273 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,52 @@ time = {version = "0.3.36", default-features = false, features = ["local-offset" tinystr = "0.7.5" log = "0.4.21" simple_logger = "4.3.3" +cargo_metadata = "0.18.1" +trybuild = "1.0.95" +rayon = "1.10.0" +toml = "0.8.12" +color-eyre = "0.6.3" +comfy-table = "7.1.1" +serde_repr = "0.1.19" +bus = "2.4.1" +wasm-bindgen = { version = "0.2.91", default-features = false } +getrandom = { version = "0.2.15", default-features = false } +console_error_panic_hook = "0.1.7" +wasm-bindgen-test = "0.3.42" +smol = "2.0.0" +futures-util = "0.3.30" +isahc = "1.7.2" +rustyline = { version = "14.0.0", default-features = false } +dhat = "0.3.3" +quote = "1.0.36" +syn = { version = "2.0.60", default-features = false } +proc-macro2 = "1.0" +synstructure = "0.13" +measureme = "11.0.1" +sptr = "0.3.2" +paste = "1.0" +rand = "0.8.5" +num-integer = "0.1.46" +ryu-js = "1.0.1" +tap = "1.0.1" +thiserror = "1.0.60" +dashmap = "5.5.3" +num_enum = "0.7.2" +itertools = { version = "0.12.1", default-features = false } +portable-atomic = "1.6.0" +bytemuck = { version = "1.15.0", default-features = false } +arrayvec = "0.7.4" +intrusive-collections = "0.9.6" +cfg-if = "1.0.0" +either = "1.12.0" +sys-locale = "0.3.1" +temporal_rs = "0.0.2" +web-time = "1.1.0" +criterion = "0.5.1" +float-cmp = "0.9.0" +futures-lite = "2.3.0" +test-case = "3.3.1" +winapi = { version = "0.3.9", default-features = false } # ICU4X diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 0a73fb677a2..e9310bf6d9f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -16,14 +16,14 @@ boa_engine = { workspace = true, features = ["deser", "flowgraph", "trace"] } boa_parser.workspace = true boa_gc.workspace = true boa_runtime.workspace = true -rustyline = { version = "14.0.0", features = ["derive"]} +rustyline = { workspace = true, features = ["derive"]} clap = { workspace = true, features = ["derive"] } serde_json.workspace = true colored.workspace = true regex.workspace = true phf = { workspace = true, features = ["macros"] } pollster.workspace = true -dhat = { version = "0.3.3", optional = true } +dhat = { workspace = true, optional = true } [features] default = ["boa_engine/annex-b", "boa_engine/experimental", "boa_engine/intl_bundled"] diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index 934363976dc..bbe97f7f43f 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -77,38 +77,38 @@ boa_parser.workspace = true boa_string.workspace = true serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true -rand = "0.8.5" +rand.workspace = true num-traits.workspace = true regress.workspace = true rustc-hash = { workspace = true, features = ["std"] } num-bigint = { workspace = true, features = ["serde"] } -num-integer = "0.1.46" +num-integer.workspace = true bitflags.workspace = true indexmap = { workspace = true, features = ["std"] } -ryu-js = "1.0.1" +ryu-js.workspace = true fast-float.workspace = true once_cell = { workspace = true, features = ["std"] } -tap = "1.0.1" -sptr = "0.3.2" -thiserror = "1.0.60" -dashmap = "5.5.3" -num_enum = "0.7.2" +tap.workspace = true +sptr.workspace = true +thiserror.workspace = true +dashmap.workspace = true +num_enum.workspace = true pollster.workspace = true thin-vec.workspace = true -itertools = { version = "0.12.1", default-features = false } +itertools = { workspace = true, default-features = false } icu_normalizer = { workspace = true, features = ["compiled_data"] } -portable-atomic = "1.6.0" -bytemuck = { version = "1.15.0", features = ["derive"] } -arrayvec = "0.7.4" -intrusive-collections = "0.9.6" -cfg-if = "1.0.0" +portable-atomic.workspace = true +bytemuck = { workspace = true, features = ["derive"] } +arrayvec.workspace = true +intrusive-collections.workspace = true +cfg-if.workspace = true time.workspace = true hashbrown.workspace = true -either = { version = "1", optional = true } +either = { workspace = true, optional = true } # intl deps boa_icu_provider = { workspace = true, features = ["std"], optional = true } -sys-locale = { version = "0.3.1", optional = true } +sys-locale = { workspace = true, optional = true } icu_provider = { workspace = true, optional = true } icu_locid = { workspace = true, features = ["serde"], optional = true } icu_locid_transform = { workspace = true, default-features = false, features = ["std", "serde"], optional = true } @@ -127,21 +127,21 @@ fixed_decimal = { workspace = true, features = ["ryu", "experimental"], optional tinystr = { workspace = true, optional = true } # temporal deps -temporal_rs = { version = "0.0.2", optional = true } +temporal_rs = { workspace = true, optional = true } [target.'cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies] -web-time = { version = "1.1.0", optional = true } +web-time = { workspace = true, optional = true } [dev-dependencies] -criterion = "0.5.1" -float-cmp = "0.9.0" +criterion.workspace = true +float-cmp.workspace = true indoc.workspace = true textwrap.workspace = true -futures-lite = "2.3.0" -test-case = "3.3.1" +futures-lite.workspace = true +test-case.workspace = true [target.x86_64-unknown-linux-gnu.dev-dependencies] -jemallocator = "0.5.4" +jemallocator.workspace = true [lib] crate-type = ["cdylib", "lib"] diff --git a/core/macros/Cargo.toml b/core/macros/Cargo.toml index f877acd4723..6f43d7dd8cb 100644 --- a/core/macros/Cargo.toml +++ b/core/macros/Cargo.toml @@ -12,10 +12,10 @@ rust-version.workspace = true proc-macro = true [dependencies] -quote = "1.0.36" -syn = { version = "2.0.60", features = ["full"] } -proc-macro2 = "1.0" -synstructure = "0.13" +quote.workspace = true +syn = { workspace = true, features = ["full"] } +proc-macro2.workspace = true +synstructure.workspace = true [lints] workspace = true diff --git a/core/profiler/Cargo.toml b/core/profiler/Cargo.toml index 20911a6c8a5..4f94ca8c05f 100644 --- a/core/profiler/Cargo.toml +++ b/core/profiler/Cargo.toml @@ -14,7 +14,7 @@ rust-version.workspace = true profiler = ["dep:measureme", "dep:once_cell", "dep:rustc-hash"] [dependencies] -measureme = { version = "11.0.1", optional = true } +measureme = { workspace = true, optional = true } once_cell = { workspace = true, optional = true, features = ["std"] } rustc-hash = { workspace = true, optional = true } diff --git a/core/string/Cargo.toml b/core/string/Cargo.toml index 2a36d739c09..81ef5534570 100644 --- a/core/string/Cargo.toml +++ b/core/string/Cargo.toml @@ -13,9 +13,9 @@ rust-version.workspace = true [dependencies] rustc-hash = { workspace = true, features = ["std"] } -sptr = "0.3.2" +sptr.workspace = true static_assertions.workspace = true -paste = "1.0" +paste.workspace = true fast-float.workspace = true [dev-dependencies] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index e81873483a3..e1c4e17b19f 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -17,9 +17,9 @@ boa_gc.workspace = true boa_parser.workspace = true boa_runtime.workspace = true time.workspace = true -smol = "2.0.0" -futures-util = "0.3.30" -isahc = "1.7.2" +smol.workspace = true +futures-util.workspace = true +isahc.workspace = true # use explicit lints for examples, since we don't need to lint for docs [lints.rust] diff --git a/ffi/wasm/Cargo.toml b/ffi/wasm/Cargo.toml index 25c18cdbc3e..ec2c2c23a05 100644 --- a/ffi/wasm/Cargo.toml +++ b/ffi/wasm/Cargo.toml @@ -13,12 +13,12 @@ rust-version.workspace = true [dependencies] boa_engine = { workspace = true, features = ["js"] } -wasm-bindgen = { version = "0.2.91", default-features = false } -getrandom = { version = "0.2.15", features = ["js"] } -console_error_panic_hook = "0.1.7" +wasm-bindgen = { workspace = true, default-features = false } +getrandom = { workspace = true, features = ["js"] } +console_error_panic_hook.workspace = true [dev-dependencies] -wasm-bindgen-test = "0.3.42" +wasm-bindgen-test.workspace = true [features] default = ["boa_engine/annex-b", "boa_engine/intl_bundled", "boa_engine/experimental"] diff --git a/tests/macros/Cargo.toml b/tests/macros/Cargo.toml index 030308047d4..3826548ad56 100644 --- a/tests/macros/Cargo.toml +++ b/tests/macros/Cargo.toml @@ -11,7 +11,7 @@ repository.workspace = true rust-version.workspace = true [dev-dependencies] -trybuild = "1.0.95" +trybuild.workspace = true boa_engine.workspace = true [lints] diff --git a/tests/tester/Cargo.toml b/tests/tester/Cargo.toml index 050f79dfd95..0216531282c 100644 --- a/tests/tester/Cargo.toml +++ b/tests/tester/Cargo.toml @@ -22,13 +22,13 @@ serde_json.workspace = true bitflags.workspace = true colored.workspace = true rustc-hash = { workspace = true, features = ["std"] } -rayon = "1.10.0" -toml = "0.8.12" -color-eyre = "0.6.3" +rayon.workspace = true +toml.workspace = true +color-eyre.workspace = true phf = { workspace = true, features = ["macros"] } -comfy-table = "7.1.1" -serde_repr = "0.1.19" -bus = "2.4.1" +comfy-table.workspace = true +serde_repr.workspace = true +bus.workspace = true time.workspace = true [features] diff --git a/tools/gen-icu4x-data/Cargo.toml b/tools/gen-icu4x-data/Cargo.toml index 04cdb1158da..d54c2f153e5 100644 --- a/tools/gen-icu4x-data/Cargo.toml +++ b/tools/gen-icu4x-data/Cargo.toml @@ -28,6 +28,12 @@ icu_normalizer = { workspace = true, features = ["datagen"] } icu_plurals = { workspace = true, features = ["datagen", "experimental"] } icu_segmenter = { workspace = true, features = ["datagen"] } +[target.'cfg(windows)'.dependencies] +# wasmer-wasi apparently has a wrong deps config... +# This dep patches that. +winapi = { workspace = true, features = ["sysinfoapi"] } + + [lints] workspace = true diff --git a/tools/gen-icu4x-data/src/main.rs b/tools/gen-icu4x-data/src/main.rs index 4226025a3fe..1ce90601463 100644 --- a/tools/gen-icu4x-data/src/main.rs +++ b/tools/gen-icu4x-data/src/main.rs @@ -11,6 +11,9 @@ use icu_provider::{ }; use icu_provider_blob::export::BlobExporter; +#[cfg(target_os = "windows")] // wasmer-wasi is a really fun dependency to maintain :) +use winapi as _; + /// Hack that associates the `und` locale with an empty plural ranges data. /// This enables the default behaviour for all locales without data. #[derive(Debug)] diff --git a/tools/scripts/Cargo.toml b/tools/scripts/Cargo.toml index f40c7befba7..593f6e50c00 100644 --- a/tools/scripts/Cargo.toml +++ b/tools/scripts/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] -cargo_metadata = "0.18.1" +cargo_metadata.workspace = true log.workspace = true simple_logger.workspace = true From b11726d5de766b5e01fa13750e7d56890e973626 Mon Sep 17 00:00:00 2001 From: Kevin Ness <46825870+nekevss@users.noreply.github.com> Date: Mon, 27 May 2024 04:05:05 -0400 Subject: [PATCH 031/212] Add matrix badge and update communication to include matrix (#3865) --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 63443bb7a5f..ce164b72b6b 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Currently, it has support for some of the language. [![Crates.io](https://img.shields.io/crates/v/boa_engine.svg)](https://crates.io/crates/boa_engine) [![Docs.rs](https://docs.rs/boa_engine/badge.svg)](https://docs.rs/boa_engine) [![Discord](https://img.shields.io/discord/595323158140158003?logo=discord)](https://discord.gg/tUFFk9Y) +[![Matrix](https://img.shields.io/matrix/boa:matrix.org)](https://matrix.to/#/#boa:matrix.org) [build_badge]: https://github.com/boa-dev/boa/actions/workflows/rust.yml/badge.svg?event=push&branch=main [build_link]: https://github.com/boa-dev/boa/actions/workflows/rust.yml?query=event%3Apush+branch%3Amain @@ -177,7 +178,8 @@ See [CHANGELOG.md](./CHANGELOG.md). ## Communication -Feel free to contact us on [Discord](https://discord.gg/tUFFk9Y). +Feel free to contact us on [Discord](https://discord.gg/tUFFk9Y) for any questions or issues. The general +contributor chat for Boa occurs on [Matrix](https://matrix.to/#/#boa:matrix.org) if you're interested in contributing. ## License From 26b1f699a31d3e8a97772e5f923ca9554f02e38f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 17:29:57 +0200 Subject: [PATCH 032/212] Bump the rust-dependencies group with 5 updates (#3866) * Bump the rust-dependencies group with 5 updates Bumps the rust-dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [serde](https://github.com/serde-rs/serde) | `1.0.202` | `1.0.203` | | [syn](https://github.com/dtolnay/syn) | `2.0.65` | `2.0.66` | | [proc-macro2](https://github.com/dtolnay/proc-macro2) | `1.0.83` | `1.0.84` | | [icu_properties](https://github.com/unicode-org/icu4x) | `1.4.0` | `1.4.1` | | [icu_normalizer](https://github.com/unicode-org/icu4x) | `1.4.1` | `1.4.2` | Updates `serde` from 1.0.202 to 1.0.203 - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.202...v1.0.203) Updates `syn` from 2.0.65 to 2.0.66 - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.65...2.0.66) Updates `proc-macro2` from 1.0.83 to 1.0.84 - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.83...1.0.84) Updates `icu_properties` from 1.4.0 to 1.4.1 - [Release notes](https://github.com/unicode-org/icu4x/releases) - [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md) - [Commits](https://github.com/unicode-org/icu4x/compare/icu@1.4.0...ind/icu_properties@1.4.1) Updates `icu_normalizer` from 1.4.1 to 1.4.2 - [Release notes](https://github.com/unicode-org/icu4x/releases) - [Changelog](https://github.com/unicode-org/icu4x/blob/ind/icu_normalizer@1.4.2/CHANGELOG.md) - [Commits](https://github.com/unicode-org/icu4x/compare/ind/icu_normalizer@1.4.1...ind/icu_normalizer@1.4.2) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: icu_properties dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: icu_normalizer dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] * Bump icu_properties_data --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: jedel1043 --- Cargo.lock | 88 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 8 ++--- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa79bc6121c..12aff3e47de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -510,7 +510,7 @@ version = "0.18.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "synstructure", ] @@ -662,7 +662,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -807,7 +807,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1097,7 +1097,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1108,7 +1108,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1143,7 +1143,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "synstructure", ] @@ -1173,7 +1173,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1206,7 +1206,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1303,7 +1303,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1522,7 +1522,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1913,9 +1913,9 @@ checksum = "545c6c3e8bf9580e2dafee8de6f9ec14826aaf359787789c7724f1f85f47d3dc" [[package]] name = "icu_normalizer" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c183e31ed700f1ecd6b032d104c52fe8b15d028956b73727c97ec176b170e187" +checksum = "183072b0ba2f336279c830a3d594a04168494a726c3c94b50c53d788178cf2c2" dependencies = [ "databake", "displaydoc", @@ -1954,9 +1954,9 @@ dependencies = [ [[package]] name = "icu_properties" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976e296217453af983efa25f287a4c1da04b9a63bf1ed63719455068e4453eb5" +checksum = "3a89401989d8fdf571b829ce1022801367ec89affc7b1e162b79eff4ae029e69" dependencies = [ "databake", "displaydoc", @@ -1971,9 +1971,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a86c0e384532b06b6c104814f9c1b13bcd5b64409001c0d05713a1f3529d99" +checksum = "e70a8b51ee5dd4ff8f20ee9b1dd1bc07afc110886a3747b1fec04cc6e5a15815" [[package]] name = "icu_provider" @@ -2034,7 +2034,7 @@ checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2552,7 +2552,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2705,7 +2705,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2734,7 +2734,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2901,9 +2901,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -3227,7 +3227,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3313,9 +3313,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -3341,13 +3341,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3369,7 +3369,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3617,7 +3617,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3639,9 +3639,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -3656,7 +3656,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3733,7 +3733,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3744,7 +3744,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "test-case-core", ] @@ -3782,7 +3782,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3984,7 +3984,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4216,7 +4216,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -4250,7 +4250,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4283,7 +4283,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4875,7 +4875,7 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "synstructure", ] @@ -4896,7 +4896,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4916,7 +4916,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "synstructure", ] @@ -4962,7 +4962,7 @@ checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 83b82e7e273..304b3395af5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,7 +68,7 @@ regex = "1.10.4" regress = { version="0.9.1", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } serde_json = "1.0.116" -serde = "1.0.201" +serde = "1.0.203" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" @@ -94,7 +94,7 @@ isahc = "1.7.2" rustyline = { version = "14.0.0", default-features = false } dhat = "0.3.3" quote = "1.0.36" -syn = { version = "2.0.60", default-features = false } +syn = { version = "2.0.66", default-features = false } proc-macro2 = "1.0" synstructure = "0.13" measureme = "11.0.1" @@ -138,8 +138,8 @@ icu_segmenter = { version = "~1.4.0", default-features = false } icu_datagen = { version = "~1.4.1", default-features = false } icu_provider_adapters = { version = "~1.4.0", default-features = false } icu_provider_blob = { version = "~1.4.0", default-features = false } -icu_properties = { version = "~1.4.0", default-features = true } -icu_normalizer = { version = "~1.4.1", default-features = false } +icu_properties = { version = "~1.4.1", default-features = true } +icu_normalizer = { version = "~1.4.2", default-features = false } icu_decimal = { version = "~1.4.0", default-features = false } writeable = "~0.5.4" yoke = "~0.7.3" From fd1a34860127e165babafd29f978acaddb0036f0 Mon Sep 17 00:00:00 2001 From: leoflalv <47084241+leoflalv@users.noreply.github.com> Date: Tue, 28 May 2024 17:47:47 +0200 Subject: [PATCH 033/212] add group_collapsed function executing group inside it (#3867) --- core/runtime/src/console/mod.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/core/runtime/src/console/mod.rs b/core/runtime/src/console/mod.rs index 607eee6b50c..8953e322287 100644 --- a/core/runtime/src/console/mod.rs +++ b/core/runtime/src/console/mod.rs @@ -218,7 +218,7 @@ impl Console { 0, ) .function( - console_method_mut(Self::group, state.clone()), + console_method_mut(Self::group_collapsed, state.clone()), js_string!("groupCollapsed"), 0, ) @@ -658,6 +658,25 @@ impl Console { Ok(JsValue::undefined()) } + /// `console.groupCollapsed(...data)` + /// + /// Adds new group collapsed with name from formatted data to stack. + /// + /// More information: + /// - [MDN documentation][mdn] + /// - [WHATWG `console` specification][spec] + /// + /// [spec]: https://console.spec.whatwg.org/#groupcollapsed + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupcollapsed_static + fn group_collapsed( + _: &JsValue, + args: &[JsValue], + console: &mut Self, + context: &mut Context, + ) -> JsResult { + Console::group(&JsValue::Undefined, args, console, context) + } + /// `console.groupEnd(label)` /// /// Removes the last group from the stack. From 8c727f6d525bbfead52bfc4622e6c43acd0811fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Wed, 29 May 2024 18:35:34 +0000 Subject: [PATCH 034/212] Bump ICU4X to 1.5 and cleanup Intl (#3868) --- Cargo.lock | 1407 ++++------------- Cargo.toml | 46 +- core/engine/Cargo.toml | 1 + core/engine/src/builtins/intl/collator/mod.rs | 15 +- .../src/builtins/intl/list_format/mod.rs | 8 +- core/engine/src/builtins/intl/locale/mod.rs | 2 +- core/engine/src/builtins/intl/locale/tests.rs | 17 +- core/engine/src/builtins/intl/locale/utils.rs | 460 ++---- core/engine/src/builtins/intl/mod.rs | 10 + .../src/builtins/intl/number_format/mod.rs | 8 +- .../src/builtins/intl/plural_rules/mod.rs | 8 +- .../engine/src/builtins/intl/segmenter/mod.rs | 19 +- core/engine/src/builtins/string/mod.rs | 34 +- core/engine/src/context/mod.rs | 22 + core/icu_provider/data/icudata.postcard | Bin 16417284 -> 16164195 bytes test262_config.toml | 2 +- tests/tester/src/edition.rs | 4 + tools/gen-icu4x-data/Cargo.toml | 18 +- tools/gen-icu4x-data/src/main.rs | 142 +- 19 files changed, 630 insertions(+), 1593 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12aff3e47de..099f5867102 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.1", + "gimli", ] [[package]] @@ -17,17 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -150,9 +139,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ "async-task", "concurrent-queue", @@ -280,16 +269,10 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.32.2", + "object", "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base64" version = "0.22.1" @@ -311,26 +294,13 @@ dependencies = [ "serde", ] -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blocking" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel 2.3.1", - "async-lock", "async-task", "futures-io", "futures-lite 2.3.0", @@ -345,7 +315,7 @@ dependencies = [ "bitflags 2.5.0", "boa_interner", "boa_macros", - "indexmap 2.2.6", + "indexmap", "num-bigint", "rustc-hash", "serde", @@ -393,7 +363,7 @@ dependencies = [ "fixed_decimal", "float-cmp", "futures-lite 2.3.0", - "hashbrown 0.14.5", + "hashbrown", "icu_calendar", "icu_casemap", "icu_collator", @@ -406,10 +376,10 @@ dependencies = [ "icu_plurals", "icu_provider", "icu_segmenter", - "indexmap 2.2.6", + "indexmap", "indoc", "intrusive-collections", - "itertools 0.12.1", + "itertools 0.13.0", "jemallocator", "num-bigint", "num-integer", @@ -425,6 +395,7 @@ dependencies = [ "serde", "serde_json", "sptr", + "static_assertions", "sys-locale", "tap", "temporal_rs", @@ -432,7 +403,7 @@ dependencies = [ "textwrap", "thin-vec", "thiserror", - "time 0.3.36", + "time", "tinystr", "web-time", "writeable", @@ -453,7 +424,7 @@ dependencies = [ "futures-util", "isahc", "smol", - "time 0.3.36", + "time", ] [[package]] @@ -463,7 +434,7 @@ dependencies = [ "boa_macros", "boa_profiler", "boa_string", - "hashbrown 0.14.5", + "hashbrown", "icu_locid", "thin-vec", ] @@ -485,8 +456,8 @@ dependencies = [ "arbitrary", "boa_gc", "boa_macros", - "hashbrown 0.14.5", - "indexmap 2.2.6", + "hashbrown", + "indexmap", "once_cell", "phf", "rustc-hash", @@ -510,7 +481,7 @@ version = "0.18.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "synstructure", ] @@ -591,7 +562,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_yaml", - "time 0.3.36", + "time", "toml 0.8.13", ] @@ -623,28 +594,6 @@ dependencies = [ "parking_lot_core", ] -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "bytemuck" version = "1.16.0" @@ -656,13 +605,13 @@ dependencies = [ [[package]] name = "bytemuck_derive" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -679,9 +628,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "calendrical_calculations" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d" +checksum = "cec493b209a1b81fa32312d7ceca1b547d341c7b5f16a3edbf32b1d8b455bbdf" dependencies = [ "core_maths", "displaydoc", @@ -713,7 +662,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.23", + "semver", "serde", "serde_json", "thiserror", @@ -807,7 +756,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -905,12 +854,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "const_fn" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" - [[package]] name = "core_maths" version = "0.1.0" @@ -920,24 +863,11 @@ dependencies = [ "libm", ] -[[package]] -name = "corosensei" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" -dependencies = [ - "autocfg", - "cfg-if", - "libc", - "scopeguard", - "windows-sys 0.33.0", -] - [[package]] name = "crc32fast" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ebf8d6963185c7625d2c3c3962d99eb8936637b1427536d21dc36ae402ebad" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -1077,40 +1007,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "darling" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 2.0.66", -] - -[[package]] -name = "darling_macro" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.66", -] - [[package]] name = "dashmap" version = "5.5.3" @@ -1118,7 +1014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.5", + "hashbrown", "lock_api", "once_cell", "parking_lot_core", @@ -1126,9 +1022,9 @@ dependencies = [ [[package]] name = "databake" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82175d72e69414ceafbe2b49686794d3a8bed846e0d50267355f83ea8fdd953a" +checksum = "6a04fbfbecca8f0679c8c06fef907594adcc3e2052e11163a6d30535a1a5604d" dependencies = [ "databake-derive", "proc-macro2", @@ -1137,21 +1033,21 @@ dependencies = [ [[package]] name = "databake-derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43" +checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "synstructure", ] [[package]] name = "deduplicating_array" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a636096586ca093a10ac0175bfb384d024089dca0dae54e3e69bc1c1596358e8" +checksum = "1d579f0dbb23612a78e2d3a483dd1bff5edafa84592d15ba5eb5e3fd689b7d98" dependencies = [ "serde", ] @@ -1173,7 +1069,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1192,12 +1088,6 @@ dependencies = [ "thousands", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "displaydoc" version = "0.2.4" @@ -1206,34 +1096,14 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "dynasm" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", + "syn", ] [[package]] -name = "dynasmrt" -version = "1.2.3" +name = "downcast-rs" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" -dependencies = [ - "byteorder", - "dynasm", - "memmap2 0.5.10", -] +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "either" @@ -1265,47 +1135,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enumset" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1395,12 +1224,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fast-float" version = "0.2.0" @@ -1424,9 +1247,9 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fixed_decimal" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc7fdec9d7f6671a3ebb3282c969962aba67c49f6abac5311959b65cafabc10" +checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8" dependencies = [ "displaydoc", "ryu", @@ -1468,12 +1291,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures-core" version = "0.3.30" @@ -1522,7 +1339,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1559,20 +1376,9 @@ dependencies = [ "icu_normalizer", "icu_plurals", "icu_provider", - "icu_provider_blob", "icu_segmenter", "log", "simple_logger", - "winapi", -] - -[[package]] -name = "generational-arena" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7" -dependencies = [ - "cfg-if", ] [[package]] @@ -1588,17 +1394,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.28.1" @@ -1621,31 +1416,13 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] @@ -1667,15 +1444,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "http" version = "0.2.12" @@ -1687,11 +1455,35 @@ dependencies = [ "itoa", ] +[[package]] +name = "icu" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff5e3018d703f168b00dcefa540a65f1bbc50754ae32f3f5f0e43fe5ee51502" +dependencies = [ + "icu_calendar", + "icu_casemap", + "icu_collator", + "icu_collections", + "icu_datetime", + "icu_decimal", + "icu_experimental", + "icu_list", + "icu_locid", + "icu_locid_transform", + "icu_normalizer", + "icu_plurals", + "icu_properties", + "icu_provider", + "icu_segmenter", + "icu_timezone", +] + [[package]] name = "icu_calendar" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb932a690c92f87955e923106181ee0d5682e688ff37fb5c7b296e1fe806edb" +checksum = "4231cddf3dcbfc21e04bb52768f18e11a077d2935774eabdf7b239bb83bf3882" dependencies = [ "calendrical_calculations", "databake", @@ -1708,15 +1500,15 @@ dependencies = [ [[package]] name = "icu_calendar_data" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2" +checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0" [[package]] name = "icu_casemap" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7988d4f2655012592ac5b027722a93fbe12ff2a86d3e0f9ae686aedba0984f5e" +checksum = "a7701c9ff229dae365623cfb1b24c86552fe3a8e82877f9a75400b95452a9c02" dependencies = [ "databake", "displaydoc", @@ -1731,27 +1523,25 @@ dependencies = [ [[package]] name = "icu_codepointtrie_builder" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6030944910d61b4d2832e89b866f5f7961c22c70a73c9aeccffe57eaa3707418" +checksum = "c41aa8d27817174c88860a3227fa5cc50cf2d915367f626d56f5711dd8a2e22a" dependencies = [ "icu_collections", - "once_cell", "toml 0.5.11", - "wasmer", - "wasmer-wasi", + "wasmi", + "zerovec", ] [[package]] name = "icu_collator" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2a45056e541cffde068f5c81ac1c0503b9ee2a4b967546422e509c5c653750" +checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2" dependencies = [ "databake", "displaydoc", "icu_collections", - "icu_locid", "icu_normalizer", "icu_properties", "icu_provider", @@ -1764,9 +1554,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "137d96353afc8544d437e8a99eceb10ab291352699573b0de5b08bda38c78c60" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "databake", "displaydoc", @@ -1778,13 +1568,15 @@ dependencies = [ [[package]] name = "icu_datagen" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a21dec81a41daa5848ed26b230c90656616d4e35557a88a683e311773faa01d" +checksum = "d1e80c1b833419752592da4d8a37239f6f11dd04dc3c0048f90acc000b17f4a9" dependencies = [ + "calendrical_calculations", "displaydoc", "either", "elsa", + "icu", "icu_calendar", "icu_casemap", "icu_codepointtrie_builder", @@ -1792,21 +1584,29 @@ dependencies = [ "icu_collections", "icu_datetime", "icu_decimal", + "icu_experimental", "icu_list", "icu_locid", "icu_locid_transform", "icu_normalizer", + "icu_pattern", "icu_plurals", "icu_properties", "icu_provider", "icu_provider_adapters", + "icu_provider_blob", "icu_segmenter", "icu_timezone", "itertools 0.10.5", + "litemap", "log", "memchr", "ndarray", + "num-bigint", + "num-rational", + "num-traits", "once_cell", + "rayon", "serde", "serde-aux", "serde_json", @@ -1822,9 +1622,9 @@ dependencies = [ [[package]] name = "icu_datetime" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1508c7ed627cc0b031c81203eb98f34433e24b32b39d5b2c0238e4962a00957d" +checksum = "ea57d14f11efa0425216b4e183db61dd23e1bb1357a86009308de18812bb860e" dependencies = [ "databake", "displaydoc", @@ -1846,24 +1646,55 @@ dependencies = [ [[package]] name = "icu_decimal" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb" +dependencies = [ + "databake", + "displaydoc", + "fixed_decimal", + "icu_provider", + "serde", + "writeable", +] + +[[package]] +name = "icu_experimental" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf994f9ed8061c17bb313f28fba6cffc736f0a16c7fab827efc9b73fd3f7778" +checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7" dependencies = [ "databake", "displaydoc", "fixed_decimal", + "icu_collections", + "icu_decimal", "icu_locid", + "icu_locid_transform", + "icu_normalizer", + "icu_pattern", + "icu_plurals", + "icu_properties", "icu_provider", + "litemap", + "log", + "num-bigint", + "num-rational", + "num-traits", "serde", + "smallvec", + "tinystr", "writeable", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] name = "icu_list" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6c04ec71ad1bacdbfb47164d4801f80a0533d9340f94f1a880f521eff59f54" +checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365" dependencies = [ "databake", "deduplicating_array", @@ -1876,9 +1707,9 @@ dependencies = [ [[package]] name = "icu_locid" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0aa2536adc14c07e2a521e95512b75ed8ef832f0fdf9299d4a0a45d2be2a9d" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "databake", "displaydoc", @@ -1891,9 +1722,9 @@ dependencies = [ [[package]] name = "icu_locid_transform" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c17d8f6524fdca4471101dd71f0a132eb6382b5d6d7f2970441cb25f6f435a" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "databake", "displaydoc", @@ -1907,15 +1738,15 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c6c3e8bf9580e2dafee8de6f9ec14826aaf359787789c7724f1f85f47d3dc" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] name = "icu_normalizer" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183072b0ba2f336279c830a3d594a04168494a726c3c94b50c53d788178cf2c2" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "databake", "displaydoc", @@ -1933,20 +1764,34 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_pattern" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22026918a80e6a9a330cb01b60f950e2b4e5284c59528fd0c6150076ef4c8522" +checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4" +dependencies = [ + "databake", + "displaydoc", + "either", + "serde", + "writeable", + "yoke", + "zerofrom", +] [[package]] name = "icu_plurals" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d807b123eb2a9ae8f12080fb8cce479f5c8a761fba0bb5ab52da6dd5e31a03" +checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114" dependencies = [ "databake", "displaydoc", "fixed_decimal", - "icu_locid", "icu_provider", "serde", "zerovec", @@ -1954,9 +1799,9 @@ dependencies = [ [[package]] name = "icu_properties" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a89401989d8fdf571b829ce1022801367ec89affc7b1e162b79eff4ae029e69" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" dependencies = [ "databake", "displaydoc", @@ -1971,15 +1816,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70a8b51ee5dd4ff8f20ee9b1dd1bc07afc110886a3747b1fec04cc6e5a15815" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba58e782287eb6950247abbf11719f83f5d4e4a5c1f2cd490d30a334bc47c2f4" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "databake", "displaydoc", @@ -1999,9 +1844,9 @@ dependencies = [ [[package]] name = "icu_provider_adapters" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a229f978260da7c3aabb68cb7dc7316589936680570fe55e50fdd3f97711a4dd" +checksum = "d6324dfd08348a8e0374a447ebd334044d766b1839bb8d5ccf2482a99a77c0bc" dependencies = [ "icu_locid", "icu_locid_transform", @@ -2013,9 +1858,9 @@ dependencies = [ [[package]] name = "icu_provider_blob" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a7202cddda672db167c6352719959e9b01cb1ca576d32fa79103f61b5a73601" +checksum = "c24b98d1365f55d78186c205817631a4acf08d7a45bdf5dc9dcf9c5d54dccf51" dependencies = [ "icu_provider", "log", @@ -2028,20 +1873,20 @@ dependencies = [ [[package]] name = "icu_provider_macros" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] name = "icu_segmenter" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2dc1e8f4ba33a6a4956770ac5c08570f255d6605519fb3a859a0c0a270a2f8f" +checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de" dependencies = [ "core_maths", "databake", @@ -2056,14 +1901,13 @@ dependencies = [ [[package]] name = "icu_timezone" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35aabe571a7c653c0f543ff1512b8a1b2ad481cfa24b3d25115298d2ff3b50f" +checksum = "aa91ba6a585939a020c787235daa8aee856d9bceebd6355e283c0c310bc6de96" dependencies = [ "databake", "displaydoc", "icu_calendar", - "icu_locid", "icu_provider", "serde", "tinystr", @@ -2071,12 +1915,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.5.0" @@ -2093,17 +1931,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.2.6" @@ -2111,9 +1938,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown", ] +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + [[package]] name = "indoc" version = "2.0.5" @@ -2135,7 +1968,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b694dc9f70c3bda874626d2aed13b780f137aab435f4e9814121955cf706122e" dependencies = [ - "memoffset 0.9.1", + "memoffset", ] [[package]] @@ -2193,9 +2026,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -2241,28 +2074,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libm" version = "0.2.8" @@ -2281,9 +2098,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -2299,9 +2116,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" dependencies = [ "serde", ] @@ -2322,45 +2139,6 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -[[package]] -name = "loupe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" -dependencies = [ - "indexmap 1.9.3", - "loupe-derive", - "rustversion", -] - -[[package]] -name = "loupe-derive" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "mach2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" -dependencies = [ - "libc", -] - [[package]] name = "matrixmultiply" version = "0.3.8" @@ -2378,7 +2156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa4a40f09af7aa6faef38285402a78847d0d72bf8827006cd2a332e1e6e4a8d" dependencies = [ "log", - "memmap2 0.2.3", + "memmap2", "parking_lot", "perf-event-open-sys", "rustc-hash", @@ -2400,24 +2178,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -2448,12 +2208,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07" -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - [[package]] name = "ndarray" version = "0.15.6" @@ -2515,6 +2269,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2552,7 +2317,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -2564,18 +2329,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "crc32fast", - "hashbrown 0.11.2", - "indexmap 1.9.3", - "memchr", -] - [[package]] name = "object" version = "0.32.2" @@ -2633,9 +2386,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2705,7 +2458,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -2734,7 +2487,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -2768,9 +2521,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -2781,15 +2534,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] @@ -2869,36 +2622,6 @@ dependencies = [ "toml_edit 0.21.1", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "1.0.84" @@ -2908,26 +2631,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.36" @@ -2937,12 +2640,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -3053,37 +2750,16 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "region" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach2", - "windows-sys 0.52.0", -] - [[package]] name = "regress" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eae2a1ebfecc58aff952ef8ccd364329abe627762f5bf09ff42eb9d98522479" dependencies = [ - "hashbrown 0.14.5", + "hashbrown", "memchr", ] -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - [[package]] name = "ring" version = "0.17.8" @@ -3099,35 +2775,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rkyv" -version = "0.7.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3140,15 +2787,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustix" version = "0.38.34" @@ -3227,7 +2865,7 @@ checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -3281,21 +2919,6 @@ dependencies = [ "simple_logger", ] -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.23" @@ -3305,12 +2928,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.203" @@ -3330,15 +2947,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "serde_bytes" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.203" @@ -3347,7 +2955,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -3369,7 +2977,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -3387,28 +2995,13 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.6", + "indexmap", "itoa", "ryu", "serde", "unsafe-libyaml", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sharded-slab" version = "0.1.7" @@ -3427,21 +3020,15 @@ dependencies = [ "libc", ] -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - [[package]] name = "simple_logger" -version = "4.3.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" dependencies = [ "colored", "log", - "time 0.3.36", + "time", "windows-sys 0.48.0", ] @@ -3531,70 +3118,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strsim" version = "0.11.1" @@ -3617,7 +3146,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.66", + "syn", ] [[package]] @@ -3626,17 +3155,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.66" @@ -3656,7 +3174,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -3674,29 +3192,10 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "target-lexicon" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand 2.1.0", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "temporal_rs" version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460e114a8c5282e7370dff89d53c48f347b3d405cfb8397073eb41a937b189a9" +source = "git+https://github.com/boa-dev/temporal.git?rev=61a05fbb7c72353deda72a3df0e6887d65b840d2#61a05fbb7c72353deda72a3df0e6887d65b840d2" dependencies = [ "bitflags 2.5.0", "icu_calendar", @@ -3733,7 +3232,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -3744,7 +3243,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "test-case-core", ] @@ -3782,7 +3281,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -3801,21 +3300,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros 0.1.1", - "version_check", - "winapi", -] - [[package]] name = "time" version = "0.3.36" @@ -3831,7 +3315,7 @@ dependencies = [ "powerfmt", "serde", "time-core", - "time-macros 0.2.18", + "time-macros", ] [[package]] @@ -3840,16 +3324,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - [[package]] name = "time-macros" version = "0.2.18" @@ -3860,24 +3334,11 @@ dependencies = [ "time-core", ] -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "tinystr" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "databake", "displaydoc", @@ -3946,7 +3407,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap", "toml_datetime", "winnow 0.5.40", ] @@ -3957,11 +3418,11 @@ version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ - "indexmap 2.2.6", + "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.9", ] [[package]] @@ -3984,7 +3445,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -4049,6 +3510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", + "rand", "static_assertions", ] @@ -4149,12 +3611,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" - [[package]] name = "valuable" version = "0.1.0" @@ -4216,7 +3672,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn", "wasm-bindgen-shared", ] @@ -4250,7 +3706,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4283,285 +3739,49 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "wasmer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" -dependencies = [ - "cfg-if", - "indexmap 1.9.3", - "js-sys", - "loupe", - "more-asserts", - "target-lexicon", - "thiserror", - "wasm-bindgen", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-compiler-singlepass", - "wasmer-derive", - "wasmer-engine", - "wasmer-engine-dylib", - "wasmer-engine-universal", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" -dependencies = [ - "enumset", - "loupe", - "thiserror", - "wasmer-compiler", - "wasmer-types", + "syn", ] [[package]] -name = "wasmer-compiler" -version = "2.3.0" +name = "wasmi" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" +checksum = "77a8281d1d660cdf54c76a3efa9ddd0c270cada1383a995db3ccb43d166456c7" dependencies = [ - "enumset", - "loupe", - "rkyv", - "serde", - "serde_bytes", "smallvec", - "target-lexicon", - "thiserror", - "wasmer-types", - "wasmparser", -] - -[[package]] -name = "wasmer-compiler-singlepass" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ca2a35204d8befa85062bc7aac259a8db8070b801b8a783770ba58231d729e" -dependencies = [ - "byteorder", - "dynasm", - "dynasmrt", - "gimli 0.26.2", - "lazy_static", - "loupe", - "more-asserts", - "rayon", - "smallvec", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-derive" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "wasmer-engine" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" -dependencies = [ - "backtrace", - "enumset", - "lazy_static", - "loupe", - "memmap2 0.5.10", - "more-asserts", - "rustc-demangle", - "serde", - "serde_bytes", - "target-lexicon", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-engine-dylib" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" -dependencies = [ - "cfg-if", - "enum-iterator", - "enumset", - "leb128", - "libloading", - "loupe", - "object 0.28.4", - "rkyv", - "serde", - "tempfile", - "tracing", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-engine", - "wasmer-object", - "wasmer-types", - "wasmer-vm", - "which", -] - -[[package]] -name = "wasmer-engine-universal" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" -dependencies = [ - "cfg-if", - "enumset", - "leb128", - "loupe", - "region", - "rkyv", - "wasmer-compiler", - "wasmer-engine", - "wasmer-engine-universal-artifact", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-engine-universal-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" -dependencies = [ - "enum-iterator", - "enumset", - "loupe", - "rkyv", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-object" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" -dependencies = [ - "object 0.28.4", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-types" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" -dependencies = [ - "backtrace", - "enum-iterator", - "indexmap 1.9.3", - "loupe", - "more-asserts", - "rkyv", - "serde", - "thiserror", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", ] [[package]] -name = "wasmer-vfs" -version = "2.3.0" +name = "wasmi_arena" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9302eae3edc53cb540c2d681e7f16d8274918c1ce207591f04fed351649e97c0" -dependencies = [ - "slab", - "thiserror", - "tracing", -] +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] -name = "wasmer-vm" -version = "2.3.0" +name = "wasmi_core" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" dependencies = [ - "backtrace", - "cc", - "cfg-if", - "corosensei", - "enum-iterator", - "indexmap 1.9.3", - "lazy_static", - "libc", - "loupe", - "mach", - "memoffset 0.6.5", - "more-asserts", - "region", - "rkyv", - "scopeguard", - "serde", - "thiserror", - "wasmer-artifact", - "wasmer-types", - "winapi", -] - -[[package]] -name = "wasmer-wasi" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadbe31e3c1b6f3e398ad172b169152ae1a743ae6efd5f9ffb34019983319d99" -dependencies = [ - "cfg-if", - "generational-arena", - "getrandom", - "libc", - "thiserror", - "tracing", - "wasm-bindgen", - "wasmer", - "wasmer-vfs", - "wasmer-wasi-types", - "winapi", + "downcast-rs", + "libm", + "num-traits", + "paste", ] [[package]] -name = "wasmer-wasi-types" -version = "2.3.0" +name = "wasmparser-nostd" +version = "0.100.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22dc83aadbdf97388de3211cb6f105374f245a3cf2a5c65a16776e7a087a8468" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" dependencies = [ - "byteorder", - "time 0.2.27", - "wasmer-types", + "indexmap-nostd", ] -[[package]] -name = "wasmparser" -version = "0.83.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" - [[package]] name = "web-sys" version = "0.3.69" @@ -4591,18 +3811,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -4634,19 +3842,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" -dependencies = [ - "windows_aarch64_msvc 0.33.0", - "windows_i686_gnu 0.33.0", - "windows_i686_msvc 0.33.0", - "windows_x86_64_gnu 0.33.0", - "windows_x86_64_msvc 0.33.0", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -4708,12 +3903,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4726,12 +3915,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4750,12 +3933,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4768,12 +3945,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4798,12 +3969,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4827,9 +3992,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -4842,24 +4007,18 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad7bb64b8ef9c0aa27b6da38b452b0ee9fd82beaf276a87dd796fb55cbae14e" - -[[package]] -name = "wyz" -version = "0.5.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" dependencies = [ - "tap", + "either", ] [[package]] name = "yoke" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" dependencies = [ "serde", "stable_deref_trait", @@ -4869,13 +4028,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "synstructure", ] @@ -4896,41 +4055,41 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] name = "zerofrom" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", "synstructure", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zerotrie" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0594125a0574fb93059c92c588ab209cc036a23d1baeb3410fa9181bea551a0" +checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f" dependencies = [ "databake", "displaydoc", @@ -4943,9 +4102,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff4439ae91fb5c72b8abc12f3f2dbf51bd27e6eadb9f8a5bc8898dddb0e27ea" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" dependencies = [ "databake", "serde", @@ -4956,13 +4115,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 304b3395af5..022a139bae8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,7 @@ thin-vec = "0.2.13" time = {version = "0.3.36", default-features = false, features = ["local-offset", "large-dates", "wasm-bindgen", "parsing", "formatting", "macros"]} tinystr = "0.7.5" log = "0.4.21" -simple_logger = "4.3.3" +simple_logger = "5.0.0" cargo_metadata = "0.18.1" trybuild = "1.0.95" rayon = "1.10.0" @@ -107,7 +107,7 @@ tap = "1.0.1" thiserror = "1.0.60" dashmap = "5.5.3" num_enum = "0.7.2" -itertools = { version = "0.12.1", default-features = false } +itertools = { version = "0.13.0", default-features = false } portable-atomic = "1.6.0" bytemuck = { version = "1.15.0", default-features = false } arrayvec = "0.7.4" @@ -115,7 +115,7 @@ intrusive-collections = "0.9.6" cfg-if = "1.0.0" either = "1.12.0" sys-locale = "0.3.1" -temporal_rs = "0.0.2" +temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "61a05fbb7c72353deda72a3df0e6887d65b840d2" } web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.9.0" @@ -125,26 +125,26 @@ winapi = { version = "0.3.9", default-features = false } # ICU4X -icu_provider = { version = "~1.4.0", default-features = false } -icu_locid = { version = "~1.4.0", default-features = false } -icu_locid_transform = { version = "~1.4.0", default-features = false } -icu_datetime = { version = "~1.4.0", default-features = false } -icu_calendar = { version = "~1.4.0", default-features = false } -icu_collator = { version = "~1.4.0", default-features = false } -icu_plurals = { version = "~1.4.0", default-features = false } -icu_list = { version = "~1.4.0", default-features = false } -icu_casemap = { version = "~1.4.0", default-features = false } -icu_segmenter = { version = "~1.4.0", default-features = false } -icu_datagen = { version = "~1.4.1", default-features = false } -icu_provider_adapters = { version = "~1.4.0", default-features = false } -icu_provider_blob = { version = "~1.4.0", default-features = false } -icu_properties = { version = "~1.4.1", default-features = true } -icu_normalizer = { version = "~1.4.2", default-features = false } -icu_decimal = { version = "~1.4.0", default-features = false } -writeable = "~0.5.4" -yoke = "~0.7.3" -zerofrom = "~0.1.3" -fixed_decimal = "~0.5.5" +icu_provider = { version = "~1.5.0", default-features = false } +icu_locid = { version = "~1.5.0", default-features = false } +icu_locid_transform = { version = "~1.5.0", default-features = false } +icu_datetime = { version = "~1.5.0", default-features = false } +icu_calendar = { version = "~1.5.0", default-features = false } +icu_collator = { version = "~1.5.0", default-features = false } +icu_plurals = { version = "~1.5.0", default-features = false } +icu_list = { version = "~1.5.0", default-features = false } +icu_casemap = { version = "~1.5.0", default-features = false } +icu_segmenter = { version = "~1.5.0", default-features = false } +icu_datagen = { version = "~1.5.0", default-features = false } +icu_provider_adapters = { version = "~1.5.0", default-features = false } +icu_provider_blob = { version = "~1.5.0", default-features = false } +icu_properties = { version = "~1.5.0", default-features = true } +icu_normalizer = { version = "~1.5.0", default-features = false } +icu_decimal = { version = "~1.5.0", default-features = false } +writeable = "~0.5.5" +yoke = "~0.7.4" +zerofrom = "~0.1.4" +fixed_decimal = "~0.5.6" [workspace.metadata.workspaces] allow_branch = "main" diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index bbe97f7f43f..547b455a2a7 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -105,6 +105,7 @@ cfg-if.workspace = true time.workspace = true hashbrown.workspace = true either = { workspace = true, optional = true } +static_assertions.workspace = true # intl deps boa_icu_provider = { workspace = true, features = ["std"], optional = true } diff --git a/core/engine/src/builtins/intl/collator/mod.rs b/core/engine/src/builtins/intl/collator/mod.rs index edbb9314362..2181de61820 100644 --- a/core/engine/src/builtins/intl/collator/mod.rs +++ b/core/engine/src/builtins/intl/collator/mod.rs @@ -2,8 +2,7 @@ use boa_gc::{custom_trace, Finalize, Trace}; use boa_macros::js_str; use boa_profiler::Profiler; use icu_collator::{ - provider::CollationMetadataV1Marker, AlternateHandling, CaseFirst, Collator as NativeCollator, - MaxVariable, Numeric, + provider::CollationMetadataV1Marker, AlternateHandling, CaseFirst, MaxVariable, Numeric, }; use icu_locid::{ @@ -35,7 +34,7 @@ use crate::{ }; use super::{ - locale::{canonicalize_locale_list, resolve_locale, supported_locales, validate_extension}, + locale::{canonicalize_locale_list, filter_locales, resolve_locale, validate_extension}, options::{coerce_options_to_object, IntlOptions}, Service, }; @@ -53,7 +52,7 @@ pub(crate) struct Collator { usage: Usage, sensitivity: Sensitivity, ignore_punctuation: bool, - collator: NativeCollator, + collator: icu_collator::Collator, bound_compare: Option, } @@ -277,7 +276,7 @@ impl BuiltInConstructor for Collator { // 18. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]]. // 19. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, relevantExtensionKeys, localeData). let mut locale = resolve_locale::( - &requested_locales, + requested_locales, &mut intl_options, context.intl_provider(), ); @@ -337,7 +336,7 @@ impl BuiltInConstructor for Collator { .unzip(); let collator = - NativeCollator::try_new_unstable(context.intl_provider(), &collator_locale, { + icu_collator::Collator::try_new_unstable(context.intl_provider(), &collator_locale, { let mut options = icu_collator::CollatorOptions::new(); options.strength = strength; options.case_level = case_level; @@ -395,8 +394,8 @@ impl Collator { // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). let requested_locales = canonicalize_locale_list(locales, context)?; - // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). - supported_locales::<::LangMarker>(&requested_locales, options, context) + // 3. Return ? FilterLocales(availableLocales, requestedLocales, options). + filter_locales::<::LangMarker>(requested_locales, options, context) .map(JsValue::from) } diff --git a/core/engine/src/builtins/intl/list_format/mod.rs b/core/engine/src/builtins/intl/list_format/mod.rs index 6bc069570ef..58695ab1e5c 100644 --- a/core/engine/src/builtins/intl/list_format/mod.rs +++ b/core/engine/src/builtins/intl/list_format/mod.rs @@ -23,7 +23,7 @@ use crate::{ }; use super::{ - locale::{canonicalize_locale_list, resolve_locale, supported_locales}, + locale::{canonicalize_locale_list, filter_locales, resolve_locale}, options::IntlOptions, Service, }; @@ -122,7 +122,7 @@ impl BuiltInConstructor for ListFormat { // 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, opt, %ListFormat%.[[RelevantExtensionKeys]], localeData). // 10. Set listFormat.[[Locale]] to r.[[locale]]. let locale = resolve_locale::( - &requested_locales, + requested_locales, &mut IntlOptions { matcher, ..Default::default() @@ -204,8 +204,8 @@ impl ListFormat { // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). let requested_locales = canonicalize_locale_list(locales, context)?; - // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). - supported_locales::<::LangMarker>(&requested_locales, options, context) + // 3. Return ? FilterLocales(availableLocales, requestedLocales, options). + filter_locales::<::LangMarker>(requested_locales, options, context) .map(JsValue::from) } diff --git a/core/engine/src/builtins/intl/locale/mod.rs b/core/engine/src/builtins/intl/locale/mod.rs index 9c1afe0bd2a..d04437148f2 100644 --- a/core/engine/src/builtins/intl/locale/mod.rs +++ b/core/engine/src/builtins/intl/locale/mod.rs @@ -7,7 +7,7 @@ use icu_locid::{ extensions::unicode::Value, extensions_unicode_key as key, extensions_unicode_value as value, }; -#[cfg(test)] +#[cfg(all(test, feature = "intl_bundled"))] mod tests; mod utils; diff --git a/core/engine/src/builtins/intl/locale/tests.rs b/core/engine/src/builtins/intl/locale/tests.rs index 969c9cb4563..ba74d540c07 100644 --- a/core/engine/src/builtins/intl/locale/tests.rs +++ b/core/engine/src/builtins/intl/locale/tests.rs @@ -11,7 +11,7 @@ use icu_provider::{DataLocale, DataProvider, DataRequest, DataRequestMetadata}; use crate::{ builtins::intl::{ - locale::{best_locale_for_provider, default_locale, resolve_locale}, + locale::{default_locale, resolve_locale}, options::{IntlOptions, LocaleMatcher}, Service, }, @@ -88,7 +88,7 @@ fn locale_resolution() { hc: Some(HourCycle::H11), }, }; - let locale = resolve_locale::(&[], &mut options, &provider); + let locale = resolve_locale::([], &mut options, &provider); assert_eq!(locale, default); // test best fit @@ -99,15 +99,8 @@ fn locale_resolution() { }, }; - let locale = resolve_locale::(&[], &mut options, &provider); - let best = best_locale_for_provider::<::LangMarker>( - default.id.clone(), - &provider, - ) - .unwrap(); - let mut best = Locale::from(best); - best.extensions = locale.extensions.clone(); - assert_eq!(locale, best); + let locale = resolve_locale::([], &mut options, &provider); + assert_eq!(locale, default); // requested: [es-ES] let mut options = IntlOptions { @@ -115,6 +108,6 @@ fn locale_resolution() { service_options: TestOptions { hc: None }, }; - let locale = resolve_locale::(&[locale!("es-AR")], &mut options, &provider); + let locale = resolve_locale::([locale!("es-AR")], &mut options, &provider); assert_eq!(locale, "es-u-hc-h23".parse().unwrap()); } diff --git a/core/engine/src/builtins/intl/locale/utils.rs b/core/engine/src/builtins/intl/locale/utils.rs index 24f4c0ad2c5..3e496804e03 100644 --- a/core/engine/src/builtins/intl/locale/utils.rs +++ b/core/engine/src/builtins/intl/locale/utils.rs @@ -14,18 +14,13 @@ use crate::{ }; use boa_macros::js_str; -use icu_collator::provider::CollationMetadataV1Marker; use icu_locid::{ extensions::unicode::{Key, Value}, subtags::Variants, LanguageIdentifier, Locale, }; use icu_locid_transform::LocaleCanonicalizer; -use icu_provider::{ - DataError, DataErrorKind, DataLocale, DataProvider, DataRequest, DataRequestMetadata, - KeyedDataMarker, -}; -use icu_segmenter::provider::WordBreakDataV1Marker; +use icu_provider::{DataLocale, DataProvider, DataRequest, DataRequestMetadata, KeyedDataMarker}; use indexmap::IndexSet; use tap::TapOptional; @@ -153,212 +148,108 @@ pub(crate) fn canonicalize_locale_list( Ok(seen.into_iter().collect()) } -/// Abstract operation `BestAvailableLocale ( availableLocales, locale )` -/// -/// Compares the provided argument `locale`, which must be a String value with a -/// structurally valid and canonicalized Unicode BCP 47 locale identifier, against -/// the locales in `availableLocales` and returns either the longest non-empty prefix -/// of `locale` that is an element of `availableLocales`, or undefined if there is no -/// such element. -/// -/// We only work with language identifiers, which have the same semantics -/// but are a bit easier to manipulate. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma402/#sec-bestavailablelocale -pub(crate) fn best_available_locale( - candidate: LanguageIdentifier, - provider: &(impl DataProvider + ?Sized), -) -> Option { - // 1. Let candidate be locale. - let mut candidate = candidate.into(); - // 2. Repeat - loop { - // a. If availableLocales contains an element equal to candidate, return candidate. - // ICU4X requires doing data requests in order to check if a locale - // is part of the set of supported locales. - let response = DataProvider::::load( - provider, - DataRequest { - locale: &candidate, - metadata: { - let mut metadata = DataRequestMetadata::default(); - metadata.silent = true; - metadata - }, - }, - ); - - match response { - Ok(req) => { - // `metadata.locale` returns None when the provider doesn't have a fallback mechanism, - // but supports the required locale. However, if the provider has a fallback mechanism, - // this will return `Some(locale)`, where the locale is the used locale after applying - // the fallback algorithm, even if the used locale is exactly the same as the required - // locale. - match req.metadata.locale { - // TODO: ugly hack to accept locales that fallback to "und" in the collator/segmenter services - Some(loc) - if loc == candidate - || (loc.is_empty() - && [ - CollationMetadataV1Marker::KEY.path(), - WordBreakDataV1Marker::KEY.path(), - ] - .contains(&M::KEY.path())) => - { - return Some(candidate.into_locale().id) - } - None => return Some(candidate.into_locale().id), - _ => {} - } - } - Err(DataError { - kind: DataErrorKind::ExtraneousLocale, - .. - }) => { - // This is essentially the same hack as above but for singleton keys - return Some(candidate.into_locale().id); - } - Err(_) => {} - } - - // b. Let pos be the character index of the last occurrence of "-" (U+002D) within candidate. If that character does not occur, return undefined. - // c. If pos ≥ 2 and the character "-" occurs at index pos-2 of candidate, decrease pos by 2. - // d. Let candidate be the substring of candidate from position 0, inclusive, to position pos, exclusive. - // - // Since the definition of `LanguageIdentifier` allows us to manipulate it - // without using strings, we can replace these steps by a simpler - // algorithm. - - if candidate.has_variants() { - let mut variants = candidate - .clear_variants() - .iter() - .copied() - .collect::>(); - variants.pop(); - candidate.set_variants(Variants::from_vec_unchecked(variants)); - } else if candidate.region().is_some() { - candidate.set_region(None); - } else if candidate.script().is_some() { - candidate.set_script(None); - } else { - return None; - } - } -} - -/// Returns the locale resolved by the `provider` after using the ICU4X fallback -/// algorithm with `candidate` (if the provider supports this), or None if the locale is not -/// supported. -pub(crate) fn best_locale_for_provider( - candidate: LanguageIdentifier, - provider: &(impl DataProvider + ?Sized), -) -> Option { - // another hack to the list... - // This time is because markers like `WordBreakDataV1Marker` throw an error if they receive - // a request with a locale, because they don't really need it. In this case, we can - // check if the key is one of those kinds and return the candidate as it is. - if M::KEY.metadata().singleton { - return Some(candidate); - } - - let response = DataProvider::::load( - provider, - DataRequest { - locale: &DataLocale::from(&candidate), - metadata: { - let mut md = DataRequestMetadata::default(); - md.silent = true; - md - }, - }, - ) - .ok()?; - - if candidate == LanguageIdentifier::UND { - return Some(LanguageIdentifier::UND); - } - - response - .metadata - .locale - .map(|dl| { - // TODO: ugly hack to accept locales that fallback to "und" in the collator/segmenter services - if [ - CollationMetadataV1Marker::KEY.path(), - WordBreakDataV1Marker::KEY.path(), - ] - .contains(&M::KEY.path()) - && dl.is_empty() - { - candidate.clone() - } else { - dl.into_locale().id - } - }) - .or(Some(candidate)) - .filter(|loc| loc != &LanguageIdentifier::UND) -} - -/// Abstract operation [`LookupMatcher ( availableLocales, requestedLocales )`][spec] +/// Abstract operation [`LookupMatchingLocaleByPrefix ( availableLocales, requestedLocales )`][prefix] +/// and [`LookupMatchingLocaleByBestFit ( availableLocales, requestedLocales )`][best] /// /// Compares `requestedLocales`, which must be a `List` as returned by `CanonicalizeLocaleList`, /// against the locales in `availableLocales` and determines the best available language to /// meet the request. /// -/// # Note +/// # Notes /// -/// This differs a bit from the spec, since we don't have an `[[AvailableLocales]]` +/// - This differs a bit from the spec, since we don't have an `[[AvailableLocales]]` /// list to compare with. However, we can do data requests to a [`DataProvider`] /// in order to see if a certain [`Locale`] is supported. /// -/// [spec]: https://tc39.es/ecma402/#sec-lookupmatcher -fn lookup_matcher( - requested_locales: &[Locale], +/// - Calling this function with a singleton `KeyedDataMarker` will always return `None`. +/// +/// [prefix]: https://tc39.es/ecma402/#sec-lookupmatchinglocalebyprefix +/// [best]: https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit +pub(crate) fn lookup_matching_locale_by_prefix( + requested_locales: impl IntoIterator, provider: &IntlProvider, -) -> Locale +) -> Option where IntlProvider: DataProvider, { - // 1. Let result be a new Record. - // 2. For each element locale of requestedLocales, do + // 1. For each element locale of requestedLocales, do for locale in requested_locales { - // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale - // extension sequences removed. + // a. Let extension be empty. + // b. If locale contains a Unicode locale extension sequence, then + // i. Set extension to the Unicode locale extension sequence of locale. + // ii. Set locale to the String value that is locale with any Unicode locale extension sequences removed. let mut locale = locale.clone(); let id = std::mem::take(&mut locale.id); locale.extensions.transform.clear(); locale.extensions.private.clear(); - // b. Let availableLocale be ! BestAvailableLocale(availableLocales, noExtensionsLocale). - let available_locale = best_available_locale::(id, provider); - - // c. If availableLocale is not undefined, then - if let Some(available_locale) = available_locale { - // i. Set result.[[locale]] to availableLocale. - // Assignment deferred. See return statement below. - // ii. If locale and noExtensionsLocale are not the same String value, then - // 1. Let extension be the String value consisting of the substring of the Unicode - // locale extension sequence within locale. - // 2. Set result.[[extension]] to extension. - locale.id = available_locale; - - // iii. Return result. - return locale; + // c. Let prefix be locale. + let mut prefix = id.into(); + + // d. Repeat, while prefix is not the empty String, + // We don't use a `while !prefix.is_und()` because it could be that prefix is und at the start, + // so we need to make the request at least once. + loop { + // i. If availableLocales contains prefix, return the Record { [[locale]]: prefix, [[extension]]: extension }. + // ICU4X requires doing data requests in order to check if a locale + // is part of the set of supported locales. + let response = DataProvider::::load( + provider, + DataRequest { + locale: &prefix, + metadata: { + let mut metadata = DataRequestMetadata::default(); + metadata.silent = true; + metadata + }, + }, + ); + + if let Ok(req) = response { + // `metadata.locale` returns None when the provider doesn't have a fallback mechanism, + // but supports the required locale. However, if the provider has a fallback mechanism, + // this will return `Some(locale)`, where the locale is the used locale after applying + // the fallback algorithm, even if the used locale is exactly the same as the required + // locale. + match req.metadata.locale { + Some(loc) if loc.get_langid() == prefix.get_langid() => { + locale.id = loc.into_locale().id; + return Some(locale); + } + None => { + locale.id = prefix.into_locale().id; + return Some(locale); + } + _ => {} + } + } + + // ii. If prefix contains "-" (code unit 0x002D HYPHEN-MINUS), let pos be the index into prefix of the last occurrence of "-"; else let pos be 0. + // iii. Repeat, while pos ≥ 2 and the substring of prefix from pos - 2 to pos - 1 is "-", + // 1. Set pos to pos - 2. + // iv. Set prefix to the substring of prefix from 0 to pos. + // Since the definition of `LanguageIdentifier` allows us to manipulate it + // without using strings, we can replace these steps by a simpler + // algorithm. + if prefix.has_variants() { + let mut variants = prefix.clear_variants().iter().copied().collect::>(); + variants.pop(); + prefix.set_variants(Variants::from_vec_unchecked(variants)); + } else if prefix.region().is_some() { + prefix.set_region(None); + } else if prefix.script().is_some() { + prefix.set_script(None); + } else { + break; + } } } - // 3. Let defLocale be ! DefaultLocale(). - // 4. Set result.[[locale]] to defLocale. - // 5. Return result. - default_locale(provider.locale_canonicalizer()) + // 2. Return undefined. + None } -/// Abstract operation [`BestFitMatcher ( availableLocales, requestedLocales )`][spec] +/// Abstract operation [`LookupMatchingLocaleByBestFit ( availableLocales, requestedLocales )`][spec] /// /// Compares `requestedLocales`, which must be a `List` as returned by `CanonicalizeLocaleList`, /// against the locales in `availableLocales` and determines the best available language to @@ -367,31 +258,50 @@ where /// produced by the `LookupMatcher` abstract operation. /// /// [spec]: https://tc39.es/ecma402/#sec-bestfitmatcher -fn best_fit_matcher( - requested_locales: &[Locale], +fn lookup_matching_locale_by_best_fit( + requested_locales: impl IntoIterator, provider: &IntlProvider, -) -> Locale +) -> Option where IntlProvider: DataProvider, { - for mut locale in requested_locales - .iter() - .cloned() - .chain(std::iter::once_with(|| { - default_locale(provider.locale_canonicalizer()) - })) - { + for mut locale in requested_locales { let id = std::mem::take(&mut locale.id); + + // Only leave unicode extensions when returning the locale. locale.extensions.transform.clear(); locale.extensions.private.clear(); - if let Some(available) = best_locale_for_provider(id, provider) { - locale.id = available; + let Ok(response) = DataProvider::::load( + provider, + DataRequest { + locale: &DataLocale::from(&id), + metadata: { + let mut md = DataRequestMetadata::default(); + md.silent = true; + md + }, + }, + ) else { + continue; + }; + + if id == LanguageIdentifier::UND { + return Some(locale); + } - return locale; + if let Some(id) = response + .metadata + .locale + .map(|dl| dl.into_locale().id) + .or(Some(id)) + .filter(|loc| loc != &LanguageIdentifier::UND) + { + locale.id = id; + return Some(locale); } } - Locale::default() + None } /// Abstract operation `ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData )` @@ -406,7 +316,7 @@ where /// /// [spec]: https://tc39.es/ecma402/#sec-resolvelocale pub(in crate::builtins::intl) fn resolve_locale( - requested_locales: &[Locale], + requested_locales: impl IntoIterator, options: &mut IntlOptions, provider: &IntlProvider, ) -> Locale @@ -416,15 +326,16 @@ where { // 1. Let matcher be options.[[localeMatcher]]. // 2. If matcher is "lookup", then - // a. Let r be ! LookupMatcher(availableLocales, requestedLocales). + // a. Let r be LookupMatchingLocaleByPrefix(availableLocales, requestedLocales). // 3. Else, - // a. Let r be ! BestFitMatcher(availableLocales, requestedLocales). - // 4. Let foundLocale be r.[[locale]]. + // a. Let r be LookupMatchingLocaleByBestFit(availableLocales, requestedLocales). + // 4. If r is undefined, set r to the Record { [[locale]]: DefaultLocale(), [[extension]]: empty }. let mut found_locale = if options.matcher == LocaleMatcher::Lookup { - lookup_matcher::(requested_locales, provider) + lookup_matching_locale_by_prefix::(requested_locales, provider) } else { - best_fit_matcher::(requested_locales, provider) - }; + lookup_matching_locale_by_best_fit::(requested_locales, provider) + } + .unwrap_or_else(|| default_locale(provider.locale_canonicalizer())); // From here, the spec differs significantly from the implementation, // since ICU4X allows us to skip some steps and modularize the @@ -485,62 +396,18 @@ where found_locale } -/// Abstract operation [`LookupSupportedLocales ( availableLocales, requestedLocales )`][spec] +/// Abstract operation [`FilterLocales ( availableLocales, requestedLocales, options )`][spec] /// /// Returns the subset of the provided BCP 47 language priority list requestedLocales for which -/// `availableLocales` has a matching locale when using the BCP 47 Lookup algorithm. Locales appear -/// in the same order in the returned list as in `requestedLocales`. +/// availableLocales has a matching locale. /// /// # Note /// -/// This differs a bit from the spec, since we don't have an `[[AvailableLocales]]` -/// list to compare with. However, we can do data requests to a [`DataProvider`] -/// in order to see if a certain [`Locale`] is supported. -/// -/// [spec]: https://tc39.es/ecma402/#sec-lookupsupportedlocales -fn lookup_supported_locales( - requested_locales: &[Locale], - provider: &(impl DataProvider + ?Sized), -) -> Vec { - // 1. Let subset be a new empty List. - // 2. For each element locale of requestedLocales, do - // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed. - // b. Let availableLocale be ! BestAvailableLocale(availableLocales, noExtensionsLocale). - // c. If availableLocale is not undefined, append locale to the end of subset. - // 3. Return subset. - requested_locales - .iter() - .filter(|loc| best_available_locale(loc.id.clone(), provider).is_some()) - .cloned() - .collect() -} - -/// Abstract operation [`BestFitSupportedLocales ( availableLocales, requestedLocales )`][spec] -/// -/// Returns the subset of the provided BCP 47 language priority list `requestedLocales` for which -/// `availableLocales` has a matching locale when using the Best Fit Matcher algorithm. Locales appear -/// in the same order in the returned list as in requestedLocales. -/// -/// [spec]: https://tc39.es/ecma402/#sec-bestfitsupportedlocales -fn best_fit_supported_locales( - requested_locales: &[Locale], - provider: &(impl DataProvider + ?Sized), -) -> Vec { - requested_locales - .iter() - .filter(|loc| best_locale_for_provider(loc.id.clone(), provider).is_some()) - .cloned() - .collect() -} - -/// Abstract operation [`SupportedLocales ( availableLocales, requestedLocales, options )`][spec] -/// -/// Returns the subset of the provided BCP 47 language priority list requestedLocales for which -/// availableLocales has a matching locale +/// Calling this function with a singleton `KeyedDataMarker` will always return `None`. /// /// [spec]: https://tc39.es/ecma402/#sec-supportedlocales -pub(in crate::builtins::intl) fn supported_locales( - requested_locales: &[Locale], +pub(in crate::builtins::intl) fn filter_locales( + requested_locales: Vec, options: &JsValue, context: &mut Context, ) -> JsResult @@ -553,22 +420,36 @@ where // 2. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). let matcher = get_option(&options, js_str!("localeMatcher"), context)?.unwrap_or_default(); - let elements = match matcher { - // 4. Else, - // a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales). - LocaleMatcher::Lookup => { - lookup_supported_locales(requested_locales, context.intl_provider()) - } - // 3. If matcher is "best fit", then - // a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales). - LocaleMatcher::BestFit => { - best_fit_supported_locales(requested_locales, context.intl_provider()) + // 3. Let subset be a new empty List. + let mut subset = Vec::with_capacity(requested_locales.len()); + + // 4. For each element locale of requestedLocales, do + for locale in requested_locales { + // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed. + let mut no_ext_loc = locale.clone(); + no_ext_loc.extensions.unicode.clear(); + let loc_match = match matcher { + // b. If matcher is "lookup", then + // i. Let match be LookupMatchingLocaleByPrefix(availableLocales, noExtensionsLocale). + LocaleMatcher::Lookup => { + lookup_matching_locale_by_prefix([no_ext_loc], context.intl_provider()) + } + // c. Else, + // i. Let match be LookupMatchingLocaleByBestFit(availableLocales, noExtensionsLocale). + LocaleMatcher::BestFit => { + lookup_matching_locale_by_best_fit([no_ext_loc], context.intl_provider()) + } + }; + + // d. If match is not undefined, append locale to subset. + if loc_match.is_some() { + subset.push(locale); } - }; + } - // 5. Return CreateArrayFromList(supportedLocales). + // 5. Return CreateArrayFromList(subset). Ok(Array::create_array_from_list( - elements + subset .into_iter() .map(|loc| js_string!(loc.to_string()).into()), context, @@ -577,6 +458,10 @@ where /// Validates that the unicode extension `key` with `value` is a valid extension value for the /// `language`. +/// +/// # Note +/// +/// Calling this function with a singleton `KeyedDataMarker` will always return `None`. pub(in crate::builtins::intl) fn validate_extension( language: LanguageIdentifier, key: Key, @@ -597,54 +482,47 @@ pub(in crate::builtins::intl) fn validate_extension( .is_some() } -#[cfg(test)] +#[cfg(all(test, feature = "intl_bundled"))] mod tests { use icu_locid::{langid, locale, Locale}; use icu_plurals::provider::CardinalV1Marker; - use icu_provider::AsDeserializingBufferProvider; use crate::{ builtins::intl::locale::utils::{ - best_available_locale, best_fit_matcher, default_locale, lookup_matcher, + lookup_matching_locale_by_best_fit, lookup_matching_locale_by_prefix, }, context::icu::IntlProvider, }; #[test] - fn best_avail_loc() { - let provider = boa_icu_provider::buffer(); - let provider = provider.as_deserializing(); + fn best_fit() { + let icu = &IntlProvider::try_new_with_buffer_provider(boa_icu_provider::buffer()).unwrap(); assert_eq!( - best_available_locale::(langid!("en"), &provider), - Some(langid!("en")) + lookup_matching_locale_by_best_fit::([locale!("en")], icu), + Some(locale!("en")) ); assert_eq!( - best_available_locale::(langid!("es-ES"), &provider), - Some(langid!("es")) + lookup_matching_locale_by_best_fit::([locale!("es-ES")], icu), + Some(locale!("es")) ); assert_eq!( - best_available_locale::(langid!("kr"), &provider), + lookup_matching_locale_by_best_fit::([locale!("kr")], icu), None ); } #[test] fn lookup_match() { - let icu = IntlProvider::try_new_with_buffer_provider(boa_icu_provider::buffer()).unwrap(); - - // requested: [] - - let res = lookup_matcher::(&[], &icu); - assert_eq!(res, default_locale(icu.locale_canonicalizer())); - assert!(res.extensions.is_empty()); + let icu = &IntlProvider::try_new_with_buffer_provider(boa_icu_provider::buffer()).unwrap(); // requested: [fr-FR-u-hc-h12] let requested: Locale = "fr-FR-u-hc-h12".parse().unwrap(); - let result = lookup_matcher::(&[requested.clone()], &icu); + let result = + lookup_matching_locale_by_prefix::([requested.clone()], icu).unwrap(); assert_eq!(result.id, langid!("fr")); assert_eq!(result.extensions, requested.extensions); @@ -655,7 +533,7 @@ mod tests { let uz = locale!("uz-Cyrl"); let requested = vec![kr, gr, es.clone(), uz]; - let res = best_fit_matcher::(&requested, &icu); + let res = lookup_matching_locale_by_best_fit::(requested, icu).unwrap(); assert_eq!(res.id, langid!("es")); assert_eq!(res.extensions, es.extensions); } diff --git a/core/engine/src/builtins/intl/mod.rs b/core/engine/src/builtins/intl/mod.rs index 5f033a75682..a3de9e830d4 100644 --- a/core/engine/src/builtins/intl/mod.rs +++ b/core/engine/src/builtins/intl/mod.rs @@ -28,6 +28,7 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use icu_provider::KeyedDataMarker; +use static_assertions::const_assert; pub(crate) mod collator; pub(crate) mod date_time_format; @@ -44,6 +45,15 @@ pub(crate) use self::{ mod options; +// No singletons are allowed as lang markers. +// Hopefully, we'll be able to migrate this to the definition of `Service` in the future +// (https://github.com/rust-lang/rust/issues/76560) +const_assert! {!::LangMarker::KEY.metadata().singleton} +const_assert! {!::LangMarker::KEY.metadata().singleton} +const_assert! {!::LangMarker::KEY.metadata().singleton} +const_assert! {!::LangMarker::KEY.metadata().singleton} +const_assert! {!::LangMarker::KEY.metadata().singleton} + /// JavaScript `Intl` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] diff --git a/core/engine/src/builtins/intl/number_format/mod.rs b/core/engine/src/builtins/intl/number_format/mod.rs index a94d8e56ccd..b96e1054f4c 100644 --- a/core/engine/src/builtins/intl/number_format/mod.rs +++ b/core/engine/src/builtins/intl/number_format/mod.rs @@ -39,7 +39,7 @@ use crate::{ }; use super::{ - locale::{canonicalize_locale_list, resolve_locale, supported_locales, validate_extension}, + locale::{canonicalize_locale_list, filter_locales, resolve_locale, validate_extension}, options::{coerce_options_to_object, IntlOptions}, Service, }; @@ -237,7 +237,7 @@ impl BuiltInConstructor for NumberFormat { // 9. Let localeData be %Intl.NumberFormat%.[[LocaleData]]. // 10. Let r be ResolveLocale(%Intl.NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.NumberFormat%.[[RelevantExtensionKeys]], localeData). let locale = resolve_locale::( - &requested_locales, + requested_locales, &mut intl_options, context.intl_provider(), ); @@ -465,8 +465,8 @@ impl NumberFormat { // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). let requested_locales = canonicalize_locale_list(locales, context)?; - // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). - supported_locales::<::LangMarker>(&requested_locales, options, context) + // 3. Return ? FilterLocales(availableLocales, requestedLocales, options). + filter_locales::<::LangMarker>(requested_locales, options, context) .map(JsValue::from) } diff --git a/core/engine/src/builtins/intl/plural_rules/mod.rs b/core/engine/src/builtins/intl/plural_rules/mod.rs index 917e85c1917..cae8cd17c9f 100644 --- a/core/engine/src/builtins/intl/plural_rules/mod.rs +++ b/core/engine/src/builtins/intl/plural_rules/mod.rs @@ -26,7 +26,7 @@ use crate::{ }; use super::{ - locale::{canonicalize_locale_list, resolve_locale, supported_locales}, + locale::{canonicalize_locale_list, filter_locales, resolve_locale}, number_format::{DigitFormatOptions, Extrema, NotationKind}, options::{coerce_options_to_object, IntlOptions}, Service, @@ -136,7 +136,7 @@ impl BuiltInConstructor for PluralRules { // 10. Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]], localeData). // 11. Set pluralRules.[[Locale]] to r.[[locale]]. let locale = resolve_locale::( - &requested_locales, + requested_locales, &mut IntlOptions { matcher, ..Default::default() @@ -292,8 +292,8 @@ impl PluralRules { // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). let requested_locales = canonicalize_locale_list(locales, context)?; - // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). - supported_locales::<::LangMarker>(&requested_locales, options, context) + // 3. Return ? FilterLocales(availableLocales, requestedLocales, options). + filter_locales::<::LangMarker>(requested_locales, options, context) .map(JsValue::from) } diff --git a/core/engine/src/builtins/intl/segmenter/mod.rs b/core/engine/src/builtins/intl/segmenter/mod.rs index 7353acfef5f..e39581191d6 100644 --- a/core/engine/src/builtins/intl/segmenter/mod.rs +++ b/core/engine/src/builtins/intl/segmenter/mod.rs @@ -3,10 +3,9 @@ use std::ops::Range; use boa_gc::{Finalize, Trace}; use boa_macros::js_str; use boa_profiler::Profiler; +use icu_collator::provider::CollationDiacriticsV1Marker; use icu_locid::Locale; -use icu_segmenter::{ - provider::WordBreakDataV1Marker, GraphemeClusterSegmenter, SentenceSegmenter, WordSegmenter, -}; +use icu_segmenter::{GraphemeClusterSegmenter, SentenceSegmenter, WordSegmenter}; use crate::{ builtins::{ @@ -30,7 +29,7 @@ pub(crate) use options::*; pub(crate) use segments::*; use super::{ - locale::{canonicalize_locale_list, resolve_locale, supported_locales}, + locale::{canonicalize_locale_list, filter_locales, resolve_locale}, options::IntlOptions, Service, }; @@ -79,7 +78,9 @@ impl NativeSegmenter { } impl Service for Segmenter { - type LangMarker = WordBreakDataV1Marker; + // TODO: Track https://github.com/unicode-org/icu4x/issues/3284 + // and replace when segmenters are locale-aware. + type LangMarker = CollationDiacriticsV1Marker; type LocaleOptions = (); } @@ -134,7 +135,7 @@ impl BuiltInConstructor for Segmenter { let options = args.get_or_undefined(1); // 4. Let requestedLocales be ? CanonicalizeLocaleList(locales). - let locales = canonicalize_locale_list(locales, context)?; + let requested_locales = canonicalize_locale_list(locales, context)?; // 5. Set options to ? GetOptionsObject(options). let options = get_options_object(options)?; @@ -148,7 +149,7 @@ impl BuiltInConstructor for Segmenter { // 10. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]], requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]], localeData). // 11. Set segmenter.[[Locale]] to r.[[locale]]. let locale = resolve_locale::( - &locales, + requested_locales, &mut IntlOptions { matcher, ..Default::default() @@ -214,8 +215,8 @@ impl Segmenter { // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales). let requested_locales = canonicalize_locale_list(locales, context)?; - // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options). - supported_locales::<::LangMarker>(&requested_locales, options, context) + // 3. Return ? FilterLocales(availableLocales, requestedLocales, options). + filter_locales::<::LangMarker>(requested_locales, options, context) .map(JsValue::from) } diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index f0a3d8ce34b..9dd89e98309 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -1743,10 +1743,12 @@ impl String { #[cfg(feature = "intl")] { use super::intl::locale::{ - best_available_locale, canonicalize_locale_list, default_locale, + canonicalize_locale_list, default_locale, lookup_matching_locale_by_prefix, }; - use icu_casemap::provider::CaseMapV1Marker; - use icu_locid::LanguageIdentifier; + // TODO: Small hack to make lookups behave. + // We would really like to be able to use `icu_casemap::provider::CaseMapV1Marker` + use icu_locid::Locale; + use icu_plurals::provider::OrdinalV1Marker; // 1. Let O be ? RequireObjectCoercible(this value). let this = this.require_object_coercible()?; @@ -1762,19 +1764,25 @@ impl String { // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales). // 2. If requestedLocales is not an empty List, then // a. Let requestedLocale be requestedLocales[0]. - let lang = canonicalize_locale_list(args.get_or_undefined(0), context)? + let mut requested_locale = canonicalize_locale_list(args.get_or_undefined(0), context)? .into_iter() .next() // 3. Else, // a. Let requestedLocale be ! DefaultLocale(). - .unwrap_or_else(|| default_locale(context.intl_provider().locale_canonicalizer())) - .id; + .unwrap_or_else(|| default_locale(context.intl_provider().locale_canonicalizer())); // 4. Let noExtensionsLocale be the String value that is requestedLocale with any Unicode locale extension sequences (6.2.1) removed. - // 5. Let availableLocales be a List with language tags that includes the languages for which the Unicode Character Database contains language sensitive case mappings. Implementations may add additional language tags if they support case mapping for additional locales. - // 6. Let locale be ! BestAvailableLocale(availableLocales, noExtensionsLocale). - // 7. If locale is undefined, set locale to "und". - let lang = best_available_locale::(lang, context.intl_provider()) - .unwrap_or(LanguageIdentifier::UND); + requested_locale.extensions.unicode.clear(); + + // 5. Let availableLocales be a List with language tags that includes the languages for which the Unicode + // Character Database contains language sensitive case mappings. Implementations may add additional + // language tags if they support case mapping for additional locales. + // 6. Let match be LookupMatchingLocaleByPrefix(availableLocales, noExtensionsLocale). + // 7. If match is not undefined, let locale be match.[[locale]]; else let locale be "und". + let locale = lookup_matching_locale_by_prefix::( + [requested_locale], + context.intl_provider(), + ) + .unwrap_or(Locale::UND); let casemapper = context.intl_provider().case_mapper(); @@ -1784,11 +1792,11 @@ impl String { // 10. Else, // a. Assert: targetCase is upper. // b. Let newCodePoints be a List whose elements are the result of an uppercase transformation of codePoints according to an implementation-derived algorithm using locale or the Unicode Default Case Conversion algorithm. - casemapper.uppercase_to_string(&segment, &lang) + casemapper.uppercase_to_string(&segment, &locale.id) } else { // 9. If targetCase is lower, then // a. Let newCodePoints be a List whose elements are the result of a lowercase transformation of codePoints according to an implementation-derived algorithm using locale or the Unicode Default Case Conversion algorithm. - casemapper.lowercase_to_string(&segment, &lang) + casemapper.lowercase_to_string(&segment, &locale.id) } }); diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index 99278a173c9..db2fa8b0fae 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -940,6 +940,14 @@ impl ContextBuilder { /// /// This function is only available if the `intl` feature is enabled. /// + /// # Additional considerations + /// + /// If the data was generated using `icu_datagen`, make sure that the deduplication strategy is + /// not set to [`Maximal`]. Otherwise, `icu_datagen` will delete base locales such as "en" from + /// the list of supported locales if the required data for "en" is the same as "und". + /// We recommend [`RetainBaseLanguages`] as a nice default, which will only deduplicate locales + /// if the deduplication target is not "und". + /// /// # Errors /// /// This returns `Err` if the provided provider doesn't have the required locale information @@ -947,6 +955,9 @@ impl ContextBuilder { /// mean that the provider will successfully construct all `Intl` services; that check is made /// until the creation of an instance of a service. /// + /// [`Maximal`]: https://docs.rs/icu_datagen/latest/icu_datagen/enum.DeduplicationStrategy.html#variant.Maximal + /// [`RetainBaseLanguages`]: https://docs.rs/icu_datagen/latest/icu_datagen/enum.DeduplicationStrategy.html#variant.RetainBaseLanguages + /// [`ResolveLocale`]: https://tc39.es/ecma402/#sec-resolvelocale /// [`LocaleCanonicalizer`]: icu_locid_transform::LocaleCanonicalizer /// [`LocaleExpander`]: icu_locid_transform::LocaleExpander /// [`BufferProvider`]: icu_provider::BufferProvider @@ -963,6 +974,14 @@ impl ContextBuilder { /// /// This function is only available if the `intl` feature is enabled. /// + /// # Additional considerations + /// + /// If the data was generated using `icu_datagen`, make sure that the deduplication strategy is + /// not set to [`Maximal`]. Otherwise, `icu_datagen` will delete base locales such as "en" from + /// the list of supported locales if the required data for "en" is the same as "und". + /// We recommend [`RetainBaseLanguages`] as a nice default, which will only deduplicate locales + /// if the deduplication target is not "und". + /// /// # Errors /// /// This returns `Err` if the provided provider doesn't have the required locale information @@ -970,6 +989,9 @@ impl ContextBuilder { /// mean that the provider will successfully construct all `Intl` services; that check is made /// until the creation of an instance of a service. /// + /// [`Maximal`]: https://docs.rs/icu_datagen/latest/icu_datagen/enum.DeduplicationStrategy.html#variant.Maximal + /// [`RetainBaseLanguages`]: https://docs.rs/icu_datagen/latest/icu_datagen/enum.DeduplicationStrategy.html#variant.RetainBaseLanguages + /// [`ResolveLocale`]: https://tc39.es/ecma402/#sec-resolvelocale /// [`LocaleCanonicalizer`]: icu_locid_transform::LocaleCanonicalizer /// [`LocaleExpander`]: icu_locid_transform::LocaleExpander /// [`AnyProvider`]: icu_provider::AnyProvider diff --git a/core/icu_provider/data/icudata.postcard b/core/icu_provider/data/icudata.postcard index cf7fab9f0ab1dc78b7a8a549b6ce8aba1e80d89a..266f6ba5f166e69384a050dfc25852c41377971f 100644 GIT binary patch delta 1225789 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 O_ESXEq?uCiI8ian?r| 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 yZn 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 ekKM9NkwLIQ-rcqqdqO@ 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
-S+jsVWdL!bYHe8yKm5*|rD>^&ZI|usD+C{r+caHX- zwM*DI{#Peck0gibf#KHt{1aJiCm|6g56G`$erecsxDS#w^BS1(1$J_!q0yqLV0yt(H zz!AU^z{&grNZI5#h{VL=;EMDD2mQ|lmI7-5I5Dr5XzXJxb-Isy2vJ}uuoehTynpoC zFI)mh0!RXc%ubjK$Z>0x2Tp5f190@9BY^Yxnr&?W&ZY~J01|71CCu7jcP1~)+C{r+ zcP28-+C{r+cjht7+C{r+cV;up+65c^zY|o!&b!|NOM$fj9P=9H0&w)e5x~iJy9MCn ztK9-{VkNa3;aE%UuJRq`0!x9l036c;;0WLd;0WNb04!1Tv6NOmau*a>$Uo4v7Jzh` z6fvKhTFy-|%PF(Z*IL(Nt%+x+`g{@N`mbbYa^unN4m&@&CQiTEuke8TVgZ4W7aO(RohA>&zaq^3&4rB))I}i*6z&qn6*n- z&i_vAO!}C$i+0sgk=TctbAhG6S|FTB5N< zwOJB)HmPLMuG+{MF@5Aoj3B4}>$RIPfc)rom>XYWz6*2n76#wVhhgsA!+ag)%df)R zJ;r<+=H3zJ&tZ-wgZbn@?g?`uETJ$nx)> zfB!lLe&4zOeGHS1QPJ0KggG+~JEp#P80OBS7#IDQ82$Y8$x}u;KYbSCc&|MQb96Jz z_1j_YFnR9|=5Y+=eU`&^V|Xp&Z@-Q4x6k5_5w&4slx)~*H!-&`cQ7ZIN0`T9j*c); zj))!|W1t*;i}^0h^=tq5AOH2A>%Tny#}EJd&;RQ`-456BSC|L?_^|9;5Wlbqve?2K8@P;Q)k5C8mMw`cF4eTY5JtC0C+yr-Do#Y3c{ zo0u;!cQ9XK?uEI24RZrS^!hE#Z4A8Y$6;+%fFqhZuPGq1>l!58ypO;USX`zYTLj^n@jy{5i}cTKNd6NBF)$ z;9F+<^P}fa;>p&nqvubb$CIuPFP_BXqg%Jb+`swa*^8I$Y0!&z@$ly8T0EwC@!`df zFQ2}A{xTk8#AA!=x5Jzqhxz8(U!VN-hgVNty?Pao58@`ChX-NqJ_!8k$K}oPe)%CD zIK-{<`fkme5mw{rdHr zH}Bp)c^h{vamVuD?%VfqL4572FxPKmz6o>dYs|M{?mZ53O!WA>Fb^JuIl(--_wG$x z4Ikfl_u<{f^>AEF-o1tSE)4F2$Jb)e`^(*{U0jI8Mc9om!`yrn=I+hif5cVO@!c?g zy1RQ9mj_4J!f?gY{+&8x#N|f>g8M_9>aYDN%olfde~UBj^&4Tf5aa~?{z;tv?%fS@ z{O2$yx86T{|Kk0t_wPJceRvh;m=Dq1*h(LM`r+k|Z|vlwC$4V#rx($?-M)*7jx_!q ze--BB)=wYK_G`cF*0%qq{n{_Pwe8)L-K+m*{n{_PwVTm>?fbQTzqaq!_WfF1a$WLz zZQrl$uh;h1Yy0c9{q>qT!&i16y1!nF`?dXj=>9%*e;<0K&)4?XYy0c9{q@@ZdToEb zw!dE6zc<*wH;9)4`}YR>_XfZDK6HN{y1x(IzYqQY%lpt){leiMj4vrbk2?KJm>xKnsvo-L7o`#M z)6xn(WF!q901e8TO$Ffmtbk~F(D_64L$@!#Y6}yvFabgW7MAID1>j`5T>&}YHXRFz z%E5z<9yq^JKXf~4q)#g#8ZVNXPNwa}!c2T)EB^M> zAZ6xS0XUhtRzQwPX?Or~0O({=T7ciEAG$&M^a7%nt{=L^a0u4aa-o+mAR24!Krz<3 zz=i6EZdX%4^wRZ1WB>d1fc;kB5(PvrT|YD`KVC%vQFdVry^EiY_Tu$Jx2*O^EzV4b zDL_Ha%!VldCsQ#Bz{ymM0&xCN{m{*Fp$G{sTR`;E^+Or?9D@)Yx%&wPMB{>;U0}7) zKU6=I{+G%sSzeU#D_J6xA}xt1z>)}FB6#Nth{j4Z52Qa(KQt;o`L%GCCs4?|{qndl zyHh-Nrv+S?-6@_&9DfDW!6OHt&g0?$f)mFdND=_jB?^c#*UhFQP>0o^LBCT!bn_eI zlB`Go+m!193f0>GW&zQd%kw{T0a*JB)eqgwV1W%_VOjg3076;&p@5vM=@KD;8~|HU zj~wbUuwwo~<=~M6kaO4bKcu_ik_32by(5*^C!1Aj&SY zq^LS|pjkFl9XxUXa59I0{zn8!07(GJ+*o!;rp*?Bqel+ijvN3wcd8>2$hlV>fE)li zTi&fOTdnbQW7_Ubr1r}aZK^R--%E>%FZH49b|63(d7G_CN7Iv@td;tp+$f+}) zRZD2J0FD5T0G;FNh6Qq}8^)8BFU>MhsPxWwT z^0>~^0)^^4Z9x=+;i;MxP^)LGH8p>n({rqKW^UuJ$6E_SD++OL`4 z6(Jv!u!RJ01S~9{pa3jFQ$UVIXv#x_EE1{?rwl7!>QV@or24&HxPYh&<7tID0Cj3B zS~gTpZACr7i7(%C;57! z{m`vQ4*`$_Y(@cVe}Mv`m#!aL*WW&FaADP-TP~Ca324yo6cEi^FgSG#U9f&=^grzT zPc0x?zkADu+GspBx;m?@Ex@ws5)F`*FLk;0bud~tRG~T;^}zYH0-|kUf4qLEl`pLu z%EB&PKs2|a)k4*&j_9NHL%IHW#4568naNrJq0D40Ag4~#RtuGbhfRH%`k`_Ca}@}O4?VnqZU2y3FqA{msLn&JLhw6uJ)(xfl;U^Uk zZAC)S`D67%^UFufmgo6P77)F3{m{JsYvn?z{b=1#Hlu)Dax-r>8<3MXo6Q3!OKTK> zqX(U=tO3a90}aS|Wae?j5VA@roAHko5UmFN;rgN6|6M@=(Kv?Dv1}-7cH#P=asL-= zPTj1%=T3!u^HCl-c~KFZyb5l>h2_;!^PqF_`k_?by@CRwuD#VlY0w|2AIkQBA8Y@K z1w<(lYHiU!T0eBVngXKie_vU%Dh1X8)QS6z`k_|7%&*x%x@-Z_=t7yD>sA(a@%o`u zzPy40qOSdA>W9YrN1spGYMwA;3(W#@ywIWY<&_i= z4e86(58W&&N{c?JfGCOHZS8N{&4!?WwHL631T5>ce(1(VgY;;eSyEII{jDUsVmJUcqd<=L zcX$Bmcz;(OI3KAWnw2jmo1qDPe zT|YGXA79bZ>Q5*j%D%r;{ZOyJV&>d{d3?pbv|d6uFIqtK()B~*_`iw*qL;288s~qD zhJIoJ(M#74<^KB$3W&y5yj=az?P>~$Qjvl_;3btZIBp6o1%9J`=*GYeNS{_f^wRZ1 zIsUJpfM~4oD=Uej*1(18hi<{Xf&!u}_QzP2KU_a_yP5)`m#!ZgU%oi<(R-9g5nQf- z=%wq2dj7Xu=qD8ry>$Ihe*afcK-70X)R6JrPk{^64~_GGus^wgXdHSL8RgNQMMizb zXB8Wc$^vkb5C^PyBgV`;hMBdODIgllxLp0v&B8h?yTGLih+evW=w`PWC*~`Ub=qhb~Ob=EsGUvLix-i_C+t}=~S=i=~OT3 z8B=lmGtZ|M5IwU%HhvRi%XZioliXH6H10pHqJXG7k-G?%!U8T@Ka}JD3JQp_rmdvt zAFCg_#cWqlK=jh}LpPqCetH4XOVi)ORTWp8R7Z8mD?{f7+`Tbd2 zj@@9n&`T8%y>$K1SiXfp9rb2OI~vl;+6BB|0nx^Eu*M&%AG%Q`G+!DA$mL~|V#Db9 z%>trPqVM->?OmkQ-WJ2pucn6RFKZlJq}1N_m#eBF`pcZT7?S$YQhWb7F4^N1}G^>~rRRcoO5+{_>K%F_PcTRCW&!S#ak)Q3O8fy_+-H zkh9^EEr666U>e%AoIQ41l#w@(Z)9?6&3`-7G*}17{?Y8u!RJS65i*`*@x_N=KKZibLQ+n zOrGP{A>bFZ&zbZ1hy*#FnZ^b&RgO)x&zZ9WaS$L~>^^7CDpq5Iq^u!RI1 zLjpO39ljAqEO83k-20h@83Gv|SJ!oUQ!kbo^Da0`(b$HBPI znR5%7D#sSu=gip=?Q`b%<;18!64S^)5|CZA&zYm+Xc=;BqJ7Ss2gv;_9^0B zBj3s7Ig)S$B=ZQ!iOSUE0&>XzlC#g5GmVXlWK-R|T8>R+79CqiAZMR5#}+bEZl5!^ z&zXDf2ygA8U9~$uZOqT9z*1l>04EbZ3&7C>M*t^9;`}cr6z6}E<@Pyq&tp?8X3nkJ z_;fOxOS@=SE%|UJ#*q(~br1`X54X>m+vm(3>{e5swDIBaIF45BZgnhzcG0fd-MY`2 z+vm*D7w>cCD6_TCnKOBgOh3U|fPA<|URoD`qX&*)pEI}5nR`QDvipyNV~hV5SPHBK z;QR^BK4)&9Gq=x~d&SS@eUyf>9$JB=z*+!KoTaoxAYbT~UF^5X6~{v%Or%L4zO$(j4~w7D&cW&QU|o7-Z@c8r?-;$fIOk79WA zUv_`_@#&NJ^2e=vPoKrG;A@Y<9Ni3a{dSl;Oy0YLdHgiK^pYREh;LQAeY5;{iL|-@ z?I_+7@6+ZeD;JLh_i1ylUj4Yd*{99@zaeezNu)(Ri7c1zpMC%Q``6z`qQz13@GAP{5AQzw^ux;^ULJfn_{*Dv_wSxO z{qaqlf;bCtn)>MlCmTK+`r)rnDl_n~z|a9-)3Yf2*a5Em)ZSFSDL=Z${C_`S=CJ8v zw!a^YIRXFg2SHxr-=TI$o37D`AQPX`@=Y+m!E;z(2(amjWwhYfL3d5%isi*CPHNL2 zAwVKQkD;I|7T*vGx>T_82MT~K)-n+*+7J@5ln^12Ko*5jlmMt8=rI*=r2&g=bcI5c`nbbeG$jB^2;_s55OkLkxNL;qBt$`gLIUU_ z6cTh73gL=jyGmH;6A1wl3E+#6NYGtK=t>1@9H{_MK_DHZf}p!5*55B7Dq-p-5&|R= zz!xErpu1>2>}ubdJF|!Du_@>Ad8}YDgad2L_Meku9N^NA%NvqDUlE$k)X#=(4~S1g#@s& z1-bZmY~?nLcn{6r9nz+2bRx({Br@oSQf$Mrb@68qbBMv!RJl%BEh}bfLhu^&j)Z`8U|SxjfVkY11`25p3d9 zTE3s@SDg+E3?1~?Sgx42(Wee@u{iATeSB)d*71+oY#slYfqmeoI1jZ$+H{Rh!~)#< zX-VO|#_#U1z|cXDjpZ6~ZStq`P590y7^8LGgXqsZznqQ5oF}=w^RDSH+s|3Hz@%js zEMR#FSd0ku1l`3t=u*LyW)uV{B!DhLAwZmeZI?bI5I_wEU|Z-h~=3Y zeVG4-_OE%zoQ#D?n%ck2oIdcxjJ^?Is zr-W5Hkq{t}0KN!`1l@&%E)^U(fPw&p1kgn&BJQ1WJIE5OkLcxKzm5bKbD9axV8B6YwE#ar+tI zF#iqhU)&tx9oxS)IpyTU7gc@0dstxTpvT5?jomesOND&SW&ejxd+ZfUMeNKJv!6~P zduHlZRu4hAxaHF&0E$2=(Ic5La69cLxe;E6}Z8pDc8Ky>%iECPYFTiD}l@H;zwwe zU}UR}5J(59Am}a?aHYaYAQb>A2sZgZB?R521g?~zDy9+uB?QtzDhRqu1zf4X4@3ok z3Y$niNC`oADS=A~Q~Hq*AdvvN2!#aQg@UeBxY-(E$F(VeD~`>efs_z*mlC*Ag3W3p z04fNigH#Z7mkPL2fqLX9vDIrh-q=Y~hJH|!;R1ox-3b;Hlk3cF2WW~+s zmJ;5nvi^b!W+U>JwSr3E%JtxZhK&H&2!RS98zJZ}6>y=jWl;hotWJPL0{9{%5_A_5 zx@?4B_f>%}9;qOZ4pKqTT`Gi&>rcdZCZYsD2|{}%ON(khGln`{6>wzm3sBoo^9grLWiz?BLh6$G$+s)j;7UhAO8M%Le;5+WoLK=Yf)NC<`w zdW=RGQ$d760$F@MNd78Bs19 zVNeBPTS|Dl2n}E(1WJHxgrK{Wz?BN@7#jglK_DHZf}p!l*!-||O586AfD!`vASDFd zWg~E<0uKyr1V9CWbdU;ya>e0}vBPcLS7* z-LYj<34%)axo#u`LkCFcq7ezY3k6-Nz;8|kf0@Jt(m^T&;Qqg&N)Sv5HmeeV0g^yE zNC`oAsemgL_?@T#P(dIa6bdnHL)3jr;8NlqS4~I=kVpVsL?aS(7Ye#kfoJ+E08|i2 z_x+O+_}mirKuQRtamA_xKnX#QsemgLKq?4e`6VKHY(4|EGb@-KYCHTs@jS$#okP3qCQUO=4 z2HQjhz`*r~pGD6`;8Q}N1V{;iF7D@)01T{$pvP3e!VQY8RN2;_s55OkLcxK!|~^e6~WNB~`gLIPZy5QzlR zs;lu6xJvY{Mwb%3tI?%G?`r&hy^ZiX9d&`~AQb?c2Zd23@a1}N8c_mZBLqr-ln`{6 z3b;~%9isw31%Y%B3Y%Z|%|_r;LLklGD9!hm*+Gxli2f6mr9%ISTKk7a2xvqC6hLwQ zEuW1ribMivRt-WzfJA~GLqV4cA`}wHs1F{47x{yfFT}bFk33?0~@dz+iQSG@mV2Ci?Es0$-K?hcU> z{klk(68*a95tkM!cqM|Sz$JoI5OkLck68a)4=xOp01QY7 zf}qD#@clzdcoD#Q2%uT*3JC!c33?0(T`GuZL;_gfU2BaHppc-)HYD^ZAwnX7G|=T`1^M;b#6QEm7wAzZENs!@40PWVta#NFWw1_=QY z3E+#6NYGtK=&}(OSwlg9LIUU_6cTh73it1{^M5PGhJk7=;JYk z5&$IxJ*EUMkL?E_6$G$OxF$kDfI@;ELqXRV=l=$g(C2zQ=0X_>0TKzIi;zgrT`1^E z1)8G*Km~zxkP3e4(DitXPYD4utIi=IKq5hpA)!kJ5ef-lWealgVbjH2ZR7F&*{Nvq zd)e4Q9Bd}Wv^g<35#$e&v4c3sOpIxBVsc{I%`qyGhdqY{h7NXH!LftRpUR~~&L7JQ zFRS7|=3-U+2cN@me(A!ngE+`cjA?UXaw48TMY>JW`4@cuHE+zpWa#@p=3z3H5cwSZ zXbuYu9rV~(u9&;grw(+nO*SP!N(g#T30x@wQbHh$t4$REDhPT^1zf2BQb7P~JK`;? z5&$IxN`RCQbe9sia!cGR1%L_y=^zyZ-K7Gqc>ffwa4ZFl5FnAD$B@wFdWeunAdBY= zDgaav^q2~`QUQd*c3mWm&8h@I2|*8C zB@_fGBiQmkPJM&I(Zh3JIW#LSbvD`;ai0 z66{+VaSNb?Ksty<7;`m5DiBKrE^Jir3J1DCh5L>_E?-IzRYD+*w_`ZZNYG;_=u$z1LIPN3385euSpP&lh=eXBL`Wo%Zj>|tnxKsv}q2)auJTppOmbiaoi zbAoJyKt4zbL3b&EDiQS1Ne@rv%r3N(khGln`{661Y-=J2Mpk zDhQ;5R1kER3b;7`zdN|koabv0n2Ko#(KXzNk|S1N#1*hEM? zsDna)M1meeLYEREBofG?`(-sA08|k4m^{ZmDi zz?TwSg)0FVAPJ;{ln`{63b<4-?+XP13JIW#P}mZ69}>D!f?ul=03`&{K}b9jbe9Ua zQh^mx0ic3FdZ0M}B7x5>AyNXD5z3?1~?Sgx_V zrgEu}v&WLc%kuokTrAFi@EQNQd;ZjgVFz)rnHbaN#N0&f^)Sks=J3D{dTcD$*j-b(RIs~4A-lp3pl4UuLAc_xG4_Dv zQzAB9Oh=Q`GG{b7ZBDRAwj7mLgNz;0rfYN}nn+|yyK7YxoFfY=`2O+KJ)EJ8r|ys` zdT52gL3d5%inaA9)-rP}HC@c* z^7see zd-2)BmzJ94`ZEz*v(VT$iIZGb4t7W>kqQAgLp{S z=tPi-Olfzol-3Su(=|FV=EUU0w0rdmW$lo5OW){Jupvxocdu62*g-s`Yjh&WM5eU6 zSFa^w2XT;@7}Ms&TP&=ee*XYEU6O$7`=D<4C4r$XhIuT?KytKQ;14=ujP1m^mkni7gPHA^5jX%^5 zY11`2G3LbN#I##I8)}EN=^C8~GVv*G78O3UknoUpxA}ien}o@Ukft9!)DCIWH98T} z_OoesYrL`Jis`Vx&_R!l<%)S5ecC$0=b&yR1V|+4F(h;;A)3k+EsiJHT=}7pBV#(~ zF%*ocaAN)+68eDxV=HY=z~}f{J-)O<+H{Rh!~#44q}{##{@{dXhKB_P zz=mt+!Lfp4cTMG*aOM0li`W^XaZWf(G|Pz~M^>(Tq`=TYkBzUl#)_F6nmWM6^1Ql8 zo37CbXJL6>W9(ip2iLfTIV>=A&|_n{V&0_3^S|;%lb7~bxd0MAXpR+a2#L%R>@E~^ zr2;qCG~x)Lfg10kf6sA1c>yhJ=I*{(Lq;jQ#!;;FSLT-lK1|DQyCI^MC(tp$I&Hf>)Mju)`dPtkD z(TQMlDw)#mUfWS>hqUP$od~i$cG}$=dlBuBHeI6=@&3vGQ`+5I&!*ZTZMsG$#+;a( zn09Yz`=NG7o37D`AiHj9cW>i8-(C3)40?S2)PrNOV&+DhI>5#9>=n|ci@DgO{(~Qz ziWE-(4~aWMGwOzKq3JR%x9(@bQcQ7Qo$b-E`SPx-P^eS)5m%wP=cru0$e`A zq!9un67(1fx?Bwr3JG8x*X|IY5WwfJ6-2_I5+WoLD8aLJ8gUFzLeOI>;7SFM3IbRU z9yJBQ!1^cZK_ql3AwnVnEDL(m2*J=nkD*{p1rZ7fU|HoGg-7>1{>$Y-VGxM~osWbr z_h@d0+y?Dvij@vZTlHM;u)B9}4vufI(glVNBDQ64EZ^AqQ@NDL`D01pWqJN%E|%v% z_%83m!O@>xUIRl^^VSn{H8ggxb>CFJSl)+&yIw~WKthiaA|w)YItsc{fqj063!s8P zI!J|0)O||eQlf3Q54nwy&|@P+NF+c(ghB#bkFT{x2vA7SV;d6sVly^`gf1JAE0Js9 zmiPHdP~7s~4?8jKcHZJ@qvS@!hjc~B6UqLI`W!J4WS<@EHXj~15$t?ykk*N~&r7z~ z#5-)VU+e+g*v8jTPj(Lf<2bSWW1qJcoMr?kxL#?N0zT7B@{ z_^q(};cjbasDkd%Wjx z|Cqrh*W;t8)ai4i_!Sf=6o+uSVhc4Chj6-LdGQ`BiiqaqVVjEUKYk*Jod#nK&Q2nG z8dUpqk_DWds!3O}mQow0QH|w7Hd@rS(b?$fQrgx!8$4YXc6p1OSqXQ3clYMIO}jfA zzQy9(<~_a{2sr^;q=7;R>9TPmL>dUx*SW1=^SGvc9UEx$xc;vhU8`(Cu1i}D`UL>t}STuQXj?adXt>2J|u=BM3h{l|h`Y5uWD?E5DN_wEKdEHHFH zY%@5PZ|wZ3TuNl7U$`>CuLJ11P)N{SDCkll9}I*m9}Ec=tgaKbJ66|;Se`e|+97SaMkj*oEi;zm|NRT4v4c3InHbaN#NXVI%u)j|6}=L=0=-3z{T>g%*-zLA9Jy@{Rf|&?bizqwL{u; zjZTa?F*y6%tFtmT^J{WT{W`T7uwST)0QLomg8U=<9dTcCL%-!fy2e@K| zv@vhq|2f5Mtdaki!8Q6xHg@=+-T7}!n-h~0@iXUQ(m}d7{*Y+BOrTt$ z+#>8zT`ZVA>Oc5yMJ~P%f5HxFcm5mG=EUTLzqTkEZrXAbSx>M$ndAK<*FS5yvc5Qs z_?V%VP<)ERBI2`l(XLu5s#z_Bzm9042w908oK zIh^4L;0WLd;8@)m&dD`?e~@%R5=i2@OcFp2Kn_4o?XnYq9Dp2voRa|R0OSDVoY4PE zs!i=68%edP^}umh3_=1p0)zx|I5)}x$N|U!z_AoE9Ka??7bF3sR7r`PpP6~IIcpc~ zs?E~IvjvV9?W#Rjp?HpJ!&&k;G?qTW_Fwx{Ok{0h*h$MUeqMRc+C{r+dxhaS3I=;= zuoPGeWXWL&XkrPW8Tn6$vR;dJ)l%!$!oYKZrNCMM&iQJ-kk5OV>%52A&wAMZ;j(Zq z^9z_w09ydZ8mn*wa0GA!aAIY&L}M+rt@7@im3No?x8}dyW`W&VyJ%N!R@9xXsJm!a zEtPLAt~(c43akaPIxd9Rs#-#^qEU0zLb-E+036=tI^uX~^Hyi;R z0UQCG++qdbB3M&STf9dF0@M6PsR(LTq|%D;zs#;n*er zt@)4YtJnYR@h3vKz*^vZeb+hMr}bT7pH4B$=~UKvg`cX?ATf!f|NifXG&Yg_Uw5t@ zLCh+w5ECsO!Al>!JpUJfr3aEg4$o7OvIwgjJS+Lvx+HNAE(stBpb$U~ou?dt9Dp2v z9G=F>0muQ!0mxy$&E)@IbDz=6c&u8AkN_b8905XkuojRL{i-^7uo9{j6Ub>R z7SEq*&5jsT5Yza@s>h$o<6Sgv_PTynOUJuAv_Az&+Pb{@Yo=L9Dp2vI-L6C)LGGj za+{<&E&8}v61!8O+OLKX%10;#;N%mO0&*DekB-&eOiwHnw;u}^~h=Gmn}!-NW!I1onRi+Pf;MJeu{b&s=-h$B|Q4M0(_KdQ)esGoGK2QV5Tv>d&bY z=I#*4ss7x@h6>eLQIDKDD_ZJH4tv1{)$TaCP58;C*JxEQg{o(?BA1*x81=}h zgVDlVa`=_Iu=?E_a#>u5$@vrgkEFUrbFiDFIv7vtU=+A8JaDXng^&Op0UUvxcA#Z_ zEID)$3X0N}=lYtY{^tIx9VL@VKz*h^$@Z!m1 zN1;4;!UK?#$4>LWvE&d76TlJBAc34b;CGS#pGSPe0FtUfmK#!tb|_R0GNceYR!;8o z(8+z?030j-z!9J$U|Bc({vD*7*P4(3k^quGPIj%W*pHm-TAK%sgAQ4k0FD41fgFCX za;!fmDK9*Nl)vRxVh577cHjsQ5+Gy|9XJ9w0yqLVR>^@QfFpntfaiaKtc?Rn07(Ez z0LMx%J%LTEstIvCq&HA1!T1#9|K?~15gJ5C!Zk|fRoRV3YdTH5Ez66lDz*fk3zL(*K5rLEKDGWvz0oY$Or-C zcp}5&(O+O8|1}>R7f`4+k;Or55FUkU3;F2Dg?R#Ec7PmDK;g+3ng!6w7n%jEKVhK{ zganf8)5}AHZrM8)fTM?n3FOokvI>Ws+V^_octy=zfZ#;^4=<_9CCU3cJl@|GaADrx z;qi@T0T<>Q&GMjgo34}<6TlImBY?Bz8_huZMza8Hx^2qTtjK|Y(0k)7HI96+5cL>l4!2N%@BzXeC;|ZjI98VzOvEl+k z0)zx`1n5|TK~DWT^vJ1S2SpR&{y*53Pe4cjNq|t^mKBiWHC%b%cwdE1-f|T{CvUj` zb>s`s5p0r(Da4sXAx|=p0CGIZgePCa7CoqRL3v3Xz#;)GOaMm!M}UrH0N@DV2v+L9h#=*v=OLCW9a|-G^z#{Rfu+D& zAhY@Bn2dR~gqZg!n%YIXYBNQDHbsA1{I_Z|H-9!8f6=a5a^8J_I~Q0AtOekB%)-fo zw*VYHnL7_58me74|7D*1Y@Yn0UA1JeJGeJi;}kBZa6N^Y?G6obEWy|ck&k|4_g!Eq zu)c`?*I4GU&!(;uW@c>5SD!OKeaU}o{>xofU62Hj;&{^%jblOEvYR9DifxhqBE(eYHP2=; zFWOa`Nz3zs%A#GhnV&o_k$*;j4Z3PGF?lu>dC{)g%sQUUI$pG^HWQ3z6O0$_s?Aj5 z*;L|1yJ|CQcs6Tz3CsD%2{LPs+_@ z{^egZ!utSob!Y9OU9~+KcaG#*vv`*RYk|zpg%Dd*ODJ|tj__Spd$KHy`DaR}07wFa1aR{5y8xVg%27a$PrvX01_+J-j(`RUzF@;ap%Tuoi%01|S>( z9044G9Jdx8^Zz8_lH}%!SZiqmaP+_t^n5)Q_LwUSI085VII$(PL~{|bGG^h;JJ0`% z6RS3J@MhEQ7VWCd47=G3yG6Tdcc#(J+C{r+$#ToP-vUd4wE&#Wq_{bd0FnTb08+l& zEdVFq?G}I&YpEp~YpLz2Y;z=$9rM_KDX=yW=l?+FBta5D5a6Xr84)3ao&QvqI0Tf|Mgy3P8${D*;#l z772C%mTaVaepFy7uomcfP;+=Zbl?c0{zyZR^0i9=Ncq~O02~^DB}yZ(+RPuC%^zB{tJYMZ zxc@d+Xwj}(a(c`bnhPui)&iOL10lAAmQZX7lY3_EqFuEuk7v$2o+baSA6{?VBK9<$ zIkIkY3~zz8K&ItDhy`j1G4D@dXYHb0wV7-)n{2aaS8dOunIkXe=u5AEMWM15$jliS zu`n&6SQvRMkukzKe?EhkGk86NTi%OQl+1#W5L%JNar-XIpG7RHz*<03X0FIeC@-PR zgmCjzT>xI5stdr$3cqAD?o$td2RtX`8>CP zoQJJJzmix&2+l|HO5*$A=jHh>15)IJl)!Ncy*Bj+SM>{ka? z$iX8AAcyTL2Oy`mbmo3AIU3;{vQ&rBpFO5ut9j(XL{uLL=%rsGz-Y_CKivU z+XCu%x-Abn=krP&|Mxi(OLFbaB4NdTE3bs{$6j$l642_r7%f1ny$6Srx90^|`@B5| zIA){(I(p>%R$fVr|NA_NB{cd1c_lIa!FTzv0krx`5=+pbYhmpLHmHs>vq)IhCX(c9 zaoE*?d{SabOCDjD{a#*)y!et+UyZI@xtCMq=!ZeI?burL8a`S7s- zoP79LKn{Bv9WzYi)V|jvht|uf6(gNwbIm?OVo6(=StK+_z={cI&^@~RjTVU{;gSS^ z4kW<}oKA3=0yDT3}(Eo@Oa^Mjj2Obqr$bmO}RC#1c2P zStK^7PSZcnD~bN+^CXtoXl|HYu{tm0b3az2ofo41AqdjdCYB&r9h!g!{W7m)`&@}7 z7tAa1{ReA*)rln+%qyY);Q)ESkCII!ut7dtzypv2kOPq86A?H7IRH7e?;jRN07n2P zK=eNdU43E+PZC(zNAgM-{~IJac3LE`L3LL2iJ%-jasYDbcW;J?oci7CkyAaR=_GQh zXVhc<=jTW)X=~4wnT@VXG_y$P&@!)t{^zqKmbit?B4J?y*8b;tCDH$Up2QLt<~v1} z>N~{%`GTWBaJKe_qg?E>D@iN~or~m^Y@aK!Q%(k|=hwL*ep+J51@lVs{;y?^Fm(Q^5=$;SF4%EXd5uY_Qp4;o+#c`|ibc~*_@Z-1V|5*BuWyprhu^4hFrn9%AgNh}E+cENi$ zE%w**N<98QMEI%`OJ1V+@w^hoALrn)0t^n$!D9s&8*Dxa1Bfr0SmHo+Ynw0-Ig6YC zewJ4f&;NBv@*x2vZzc=aAa5qiLxVn+SR#juAR7JKc_mSQwZ{8jZDI*a_2@5PgS;BT z17O8A5o<4y49{~#F7i< zmBjr&%itJvVP1CQ0nqBa-7X-fejUG?SkkuQ$MQ-x*8X!OmeBjOR1+GVL-Px;VtQ8k zpB$SX;3^YK+^rwaD~bLm@5^HGd0$q5R)0cb32SeLi8`KR@Bmo*$}8CtLAu(+5(ItF zRDcHMtJ(lPuVfR^qAN};xnN#Nj6bS0lB-QDxnN#NJpc7BHqt<{Xi$MqO)R-!UP-L~ zX$r|zCYD?J_!$yQ=+G{ZSF(Ms#F7iAs5Lj z@%}G!NUk=qgnsSf~<%uQytS^vP(&`^wePYQ4^Gf3Sm)wY}Pb~2z zW+>;Cbpe)@SJnY|?-S?0hRjgOmqO(skHdTtIXuH8n?nym0yw{xSmO5ocwWhd(C0`j ziJXy^uF=4+=at0vCr9Jz6HDA$q3qYEemk!up8p5wGbEOpK z!u?<5inOGFD@-i8U|vale~A`gNjr&O;}s>A#2sw^*ildTIO0j)xxAA2{Y9RJ)SlUK zI@M?FIUX&Aq|ZdN+x)b>Ll$E4$Vc)@HkBdu#S4(?3uOzva|>Y!u@=9dSmOJh;N-xS z0%VTpsl1Zt|HH)st~jyef_WwF{ma!SmgEcONDRsM%K>uSe}R@)LjPZ`D@`o9U|vbw ze>-UE$`ebTa*R~+$ZzMB#QrypIxgkbscD5NjBr82?f$Eg)vf2D1s6O+d&*4MGA!0z!V>gOGrbfRITTgam{Hgf_=V z7~^GuF@Ur24$%<65WoXCBHcmTgr${i^OnDdr3C04%&VDB&w0_}%e4fKxraV9vz!4BK zt$~n$(4GALW}sX^G*&$2=P`!-H|D>|oQ6R=Y9~_{2JNVwp0@8}3}6aA8ebMd~=5Wu;r^^Wr&slO^RX8+Gp?f(lH z@}!5ylU@NrzKCUE0v0AP#7i(k07C#no(~y+lm{@gc|rt`sqbt6X9GAJ>tU$Y%$KBw zxZ*bia4vu$yJVCBhU}8VvpM#j*#yJ{%;wh}3~^W(s^h~?I2fuX>T#}WqC@VTt2Ta~ z=tEgR=%)Ff%;sGQiUMK+L%bI^1TX|JRGZ2{^oD9v`B4Vvs!gSbxh&{K=++U>f0<2} zG0uL*JRcJBdYi~OE-MQ+gq{q3AdH-sx+URh2WA%e^6ugRf z4At)R8w!SMcY3*tpn#Boxg6I{NI(b>-`@oBqcT=MGd+fQ+iD2lYyd;Gryb93sP=T8 z6;b9SZV2F9UR4kRY{tB-2$zG_S(t#3fY}_6Zm2eu9zs66AS7TefuZUo9ENVFItj;| z5A;7GW7S4KVxVIEVDzBqw+jdfm`h;DYdSn$(iJdN-Ht=eohv&{#$VTAB_JkXw&Oai z1cvIc@<}1>5E!bh=;(1n3@R`LP_WwJ=xCsfEOJJz>ro+MvS%2PGb!1jv zGFBa#BeGSj-;y5Zsw2)ZWX^@hP;GpNW*e%F?|Xg(bNn?iUCib{Y+?dJ0z-8+b5ww# zI-BK91mx;81S8LA`SanpvXL)GKLYEL_& z+PP{^neT7jXlJWEt%tcRu}?_ATmnP2Hy;5E)!uXfv~%%u zjqAUdt$w(Ugf>>!PkL0WpP6;^Rji+xgPWbJny81l9PUg=z+3`D*?C9*<8WtV)q7hS zpVq$Fl_dSU_;1%^0840+TL%7Z3AEll8C*#|@@d$0x!We?UW(1+lb zwdjdil%9p%4L)mYKC;OrX4AsVO z9dLwd<69Koh2=Fwgz^m6D!&st`6jPFn+rw9>=F~uMuDL`X|@5nhDy=$?#v}1Bw()G zl^pZn$q~7Ob_^TWpOYos2koex=2q2xC@>Zv6pC7$F}s6o2kod0&mzfx;bUrwAy#=e zS6~EKf4~?g@&T8p_5z0NSn$~47ck_l0UmD+3K;TSfCu2h0EF_EP2lz~mod&@w8Js8 z%qB2YO?0HJA+J8*&>K3J-C}v1%cBR6M^6Dmcf9_`Rcj_NW(P;e4z2*9d^lTxcI3lZ zfZa<0LwK0$mcQx(gz&`mKd9DBV64{6^60FYz>rtTc(|%G?6^NBK{y!t4>TzT~o;M=W^ zd`pEB6EK@~!BG?t5)jHI<(7!W*9gv`z*vA#khM4=Kh=XBpId)&viSF)9kr8XzX$E8 zooZa>P+%-T$b*1T9t{Nu#p1Q_WAQJZ>A{YH>Hkj$U=F8nJcSTY!Qy6?=vJ@kY+x)8 zA;?&qkn?$I{u}e(WU21q#r8oScCt$MpdGc7Rk{c5sGS^AIcP`iWJ&HpJ8GvG(r8~h z6c`H-vh+3~!A49(%t4dH1cU^HVx6=&qpjB39<-x&vdZ?L9kr8Hwg>H~oh+<9Xh-d| zy;_mhj@rp;+Jkn~PBE16x`%gxu>c{9W)B4j=^=zN7H2d%$2o@URr=yz^#2yh9<-x& zTAWimY9~iB4%$&WSrvQGj@rq3*n@V|PFBJmw4-)fU(+V-sNE(*l{drx+qljsL?>5S)&I%Iq0E)Qyps!&XBiF zN@P+JFH%W^2DrDF5{qj|yR=AENt1XCy+lKA(~r^baP0ejH_r_2izYB7IYkJw&# zOM_hlM5Rpr=hC*^wi(+*9e(Tb4vq6 zGKlh$xdEcQWG+yBwS=UvBiHiMVAlYVPF;uuyR{KADx$=IiuxwoVAlZAg?rnw*+S_; zHZ@1PLzc!%bG>kOo2qnU=+T!6QP8Iiu>hd}(QIK>Et14J^2stL>8i`7Y)d5Rayo#oyP#f#<6a&Kzl#d2r4?_9)7gIxne zwBaPoa&H#m#d2r4HwST@f5}C>%fEfQj4cgz4G>kOmXI|zW*S~Bcb0o|3@?^D%YFNP zEe&=J5b^!1{?%w(?k^<|UM}I?C9Do+W)oIYU1=AurNOR&qWE_y3{?E&>($F-spbOS z0BJ7Z1@c2SK$IV{Ky||sl1_NxvfNoR$WP97+ImvCLd#?2+N~Kr}2UV;$KdvF1eDGlvch4w3t>WHwiqEPzyGDWXhe$tqSjES14=rwjuziT&Ar_C| z9^J3vvs+J2Pk#9QQ`}w3|Eio{e*fj~zb+@0_wWDy^oJ+q0CMeS6_;E~YU_6os(AlV zd4l}m^oQ@icv21=x9@%Nw7es(J*wjRttxKZsp5S$@4Zj)xO^j?Ci&upo5Rzu>pgC{ zG*nq`3css=*KSeVrg)#?A;lw#$5mXvPJzmGq}R_VP`Umo#phMrxc1|J`um@bfA{#u z@BaR$|M?&96m|9?#YaE>@4x@)gZ~Wi(OruB6c2v<@Bi@Am;d82HGOh}g0?@Q_D^W> zQ)>VG8U-q!fBN)P@6PJ=@Q?rNAAb7B|9Y)9Zr!Nj_Qw>Ds;?teXViK|b7yFuJ)l7E zJ}UR=+eheqL=6wveDG-%50O5krH7wY@rYhNqN+!5pHRW4n0@x>%O^hge)+|hhxN;s zUp=Wu&f9mYxPR-br(ZoAPn2JM>$74#2Y&VPtM8wE@$AcIr)MwfvFgU1DjuFy@yWG+ ze)7-XJ%952`SV&_sGe7N0IK5dM}?n%fB5F`;_zKXjn&FJ$8V2BFI~T1#rrp_czFBx zeI3jO7S*A~;xk_p2y^YzDz4w6__&H2Y(9KI@lRiW{q;BBeEY{If2=i4oQeMNMNOZs zeOSefI~1Q(ar0j!$e|EQuf4Y16w(gqOuT{bQdHlN| z)Tlde2}<|7x*1;krz(E;{^?)qrgh_H6>C+x7l44S#N)VuB9AYa$87~r z1i<6!t_c*XuIZghAK$`Fm7UgKm0$C_eO92&6C#qePfazz{04wY(lFq4C*mo=$11sL0o9g(<{8&R{Kkubl z*PnOjrkb68z#WfKd<`$v`mlwk2V*7BOy3?DfFdXMFt>_f&;GodYD7ANprg|PppSfk z6~fTg`cBd;iJL0*^m+SwUaC4)VdghCKu=BtjFo^c`s$s*&&&7* zinxp_>g%cQs)+u&mntuoQR?69?c7wa?xmW*3Y}8v$6LCoUfoMI^Ur!QmJ9FzI03$i zn`&9{^WjhUnqI2Be?U7&O@Um(22hksSTUJ&51=TM?h7~7t9z+3|9p3Ds#o_?t@-CW zbW^=H24Ck?cI;=pR84G~i_vgO)hzdqE4!&y^ZYIkb27h}S1HO)Sf^pJs3CrpiuNtBEWGcqeP1$U7NK zJlaSs0a6B_A8+iYS{GBy*&i$H9rLkHk(Xz50M%AGmiJBzFne=1ReOIWFV$6lRd)Ja zxT(_nJH8|+@LFD~E-a4OJ8@I3damfDn&ZFB@J^*40>8@ z{*TOgRRhdq5M9Mh_3B=#HUF85oXU>mmKWfPdLb)%lMf8nxhST7frpQ}wQ+ z%j)ZTsWSh3cW$az_fm~x!@F`*y}FkwU)vJ4{uHSF~zAHD?t9z-g zcwp+did*K}x~cLJnFRzhr zJ9SgNx|izuPTf>-EzRs%e-VC;28jNQm#Tqv$li&Ys!l1@>g#){di}X@T(QUTW*K+l zjcod!PAAd}ztri0I^9W?F$AN+IyWxh@KWXeTjV=(Q@y&E>iSOIRIl!(y1r94RldCM zujHj#ZW>iFV$5qEq=BF)qTsq=%(79TN@Xy zxANes@lU3y@`KTH>*_9CzA!HHH}c?G%b%7bpzH;B?_AM^i@_@^{k5*_!L`1<3s-vo zh8|oAY_IMo^t8cMT)5uKgUk7UwK_8mUfqRDH!Z&eQjcyN4HWr(83ypG2N&1>l3ipP z^!*r&k#r3(OQ2|hcbRCg-v8rY-MIxxBse#IiA00z@=B(`tGjT$l?NB&ug5sQO@phs zaJ`iWSH1r)7xik}5?`!eHqju*|0dJm)m^yW%7csVADc{ro2}P!;qnXVn|g57_up-% zLB4`r&4sI-0N%)hs{)IBQAw-5s4Q?r7q0pR`Of%e9$c#qTkN!e@10k5;i`idt9*DX z_b6UqQK0})J_i6ybI?H1YMuiqZiAY6KWv6Q`xzImSoL?;D|&Ew{ils~n7FzN7hQZ~ z4=%1hWq*-rFkc(5;=)ztZ|=dB@4uHa4c4!~4|4?b9E?jTr!By8E9kw_K1PuPwT)6tke*qK2OhEUq;==V-9$Z|1^yOlN6T#Vle!Pwg*IRjT)%7QjEHuWcXg{vx!qva@8+mZ8 zaoA$;_1t2oujs;6o!07&Jh&2A%}(pEML&MgM1%SMUafYf!K=G)`IEh=2bcbz89;vf zPA`ZP&M%T^aCP2Da{}COuIj=?@5?Fc%{;gqfA-t7g7iZ`jlW2u!OTA|Wg5J?3)fqD za5?`hKP$gF*G>zl@%3G}P-f1q6?cuRcyKZQ91dHU>AA(=U%`dztvtBu`p-+ti%f&; z$4eQ?*Ky%O`Hele82@sCNKTywxShPd3m3}m=wyCDD}2pFgEjtTpJ%oPIBw*b4e+xr zT&45o9$Y5YORx@&u^;+r0qjTb*E>=5Jenl4;*#+LFMdT<&4 zLSvn2@aisHZ{@+|{J)y_pzZP7nugH?!wiMTW{pSRoB0=H}@d@&~xjGE?jTr!L`1c zY4GYUTyN#UwVH84490U!1kwk;#f0zJ>a=}UfM1x-@(cs$o_&U?z)m^w4y!NZo zdwM!t#g}>`o0`LetLEQt>%vt(*_(TC>Hl*%)8N%zxZcWx%j?hSQl`PHyKudg2N%~L za}0K-K|Y3G&4sJ(SZ|(aaINIR<$pz)OoLZ<;bQvx#vWXZ|0dI59Zgqs;d(0%u3Ff2 zEGmoB)qwb7{f!b0zM5r_{6iOM27mc1gX<>E;4hzL@Zl!SV7&+aEz2M;pX!)Q6Rw*I==Q)~I?Up0H`(_6n?*M7^M`djwYe}~yqf4i=gYv6@X>NYF% zZ`ZZovO@os724pve{Wf#|IBr*{J>wn_w~#7>LGCbeb3)2CP?y7`K^mH8GgMd+i*xv>#b0Em2ODV>{p1-WzMYom5cc@mn8gW zTaKLkcR$^5Nzs3O%aMQo-e<+F83uZo4F1=mIC^bD|N35gxncABf4FVfbpMrG`Ldqb z|34n}jZw{F0GBx43OsaifSViRtQD}`<18JpZN!xX>Pzb7hO@B1wm#PxsJI&S#?IBU z1Cw9D*-j{`0qgX@P@hQTgKO zcTZk>v4dJN&U*j($@<-svGTz&)bp>7-_|M!<7j_gD~i0jB0_y;)tDAt9^sp8AD(=3 zu~fn#-0|@J(Pb09ZJifiZShm$yTC8%gVx)v&hS?jXQ(9^XG_1xN)4|r z)UdMNkX?B3Qt@YIB!BhA`1(t(zw^osj^%eMbJ8!j{?4Ddi!Bc!A5lE6 z;XrF#>+k%w{!X0=y&%v3FV^4r>({m4-i-hD#gC7DzkTtm2%c?OPG;FT{RGh{pm=rA{E5E+U(WLq|X_}k`%AirT zKw1hWrJ>=4P}}v;?5h-p0aI(w8#FnaG_%fvnn?y!Gs{3z_VptFj=QUt;Y?@=CS{XQ zF$B?9wQTYIm4I5B%%W>pim(ir6qZu4+$_ZAUT&9Uv&p$QTL5)!muXwI+^*QBvhXWwkvi4WVNAnSO0l2B+njc`1qtz*2-|z@+G_R1EtdNQxr0{O!@22SLJDn%b|Z z)$7oVPr#&TQp(PXXfgmT*P~k|m=uOtt8P*lO4-+Xv6>dwAk;)y222V|DJ@Nav=kM9 zmI%XuNnt1zL;cU81Zv%MU8m;UOQl+(7E3}lZBn2m$R-7o(hw?!rd*+Vfm)^!Xli%z z4YKjf&Bd}0pTyf}=~08$JbXkpIgg(0VtXEU+ni1Aq!fma%&TgX+eA*WB)1cLsVtUI zS_kwMk3RGYLkPK>nSFrKQxrCw%N$vbV-EVHq9qpJHsHOp-5 zq^i?!`;@hQ?nT-tb4ba6f=SU) zWmYAkh5)o`&A{6KC8)sQYDk*W(xnBl3_vSZ!zB6)wGuQL>RQ`bz5E-SyRfAyy$J?qPO8-4_9;&;CynL=lOluHZL`=zt-^hCd$^dfb8aN% z*Df1qvU7iu0IZZpPB4~bG&xwA3^enFX_(x{B`d)Aqn5zp2_}W5GA)yQsi*+7L~|ub zn@pH)%QW1s4rz;0yS3DUs4PG51W@J~TPBzkO)9g=i)>`Z0jp(rC|ZI^X$Ym^s+7G9 zK#lRY%mxNSyGGuP+Naa+)yu!JxeHqrOOJ$S`&0RajKu&OOseu44Q@9%vY92%6x1KT zIeh=(8?P?gt*wxHE>x}TVv`y#ZDCnENm+SSvV}c+>A&PttB$+0xw;cwkYv+HKD9Dy zNQ#V+WDWAwbuz29CNclVb^rgQ*=Dhik8Fyn zO=^YHQXpq=IhKM+(O)U|R-|E$ID`+oNr8qS4F!|Z5Ngd{vKGfe6#~^jS_&qmB~%PO zwlT~>-blk7X*YZU{Y90#q!qZYc3;dL1`$Ml!j0->~T#7ICP6kSkYaMa(2ihI8m@2|ACQ&;)V4YPY*Gr#fT@L{lzVxzFFpZk^~FcZ z7D36YCl|{e*klUSvIkZwmX0S)&Vjwz0~?^B!->`$SCM@k<63dWd1p~-$*9e1 z^F;zis2KLZnu;-oQ0}F`>K#t%5=eQ+;{4C5yOYy$-n$e!JoDbQU5PhMdKy#LJfB4x z&hzECZ-agk^w~I|n70l!j3Db>_MZK+6RrmIn3yvmurWwahvb zP5E1z0aHs)77hCxQ5Hi_4GiZwvdx&879CE`|76j$vL0BeG|91RlLECo!l_>{DTYT% zL$hpR7=YF`@uq#fcfp! zGkI#cU6xr(DVhwRV`pCGCYTg`RTjg(-bvZlnFVwW($WJJ%X!3#Y;qp4uI{{`<$BK= zZWeq_lTaE8)av0egJ^d0q3?gF3}GyLe5BG@9}3VCSeXw6O~ufMf~I2VB^FAcp%>Ps z>iuU!J3BA2XCz5nkO{N|Ik19BX$X}jd)|CD=glGwoj_~Y^X9WvB-e*}Qk+go%Xz5J zY^MQeZI`ZgV5Kk&m|FBz%D$2eHF-r%iyBK)l%Xcj5~QU-s&CL)|9Vn3iOgR_p@#DY zZT{4S>c>UBuD?Pp=T-e8&FZ|WZxgPjuYFo(8Et#fr=?W(vJZkRt*urc1ot2N{53f{ zI}d_ww$?P+k1|Q%sv(qy0;p{+R}H1u$$+V4UR60eYl6T9wK;*==6rQvM;gJQ6~n%@ zCn2o$_rx%(T}_ILJmf|fwayhb(&7fYXaC)Yej< zB}hZTr0gq{hTB}f9(Ph03hMhyLoDTD*(dZPfm(e+OT`dMLxEb{su$`KOv)zboxO7h zkG#TZvhTB55?;%`&q}4qzRya<(3>Pp3N-X4*;EX@Nj4S3&wc)dn*+ODPd=88XKQJ> zU1Q#X-L5vTp%lXtK*zS}ix=l#+f2r>^r4{jZF0Lny-i9numRLBqR9ZMJ}q6eK8Bu+ zrL|^7`>CcS!ZhhgMP&lDJgJ(puL?DsXP0>j7qq3!-+Rh9u* zirCA5Nnt2uUq8P#j=KP~L>LB43PY*A^RI1AX2(ZL%lRlHrKLZOmGzC}bHSuEoc9rh z8qWJjPLL{XKj-l{`!gPnf2VdP;ck*VSKk_ve0OAiz97lb8?uJv^xn1HAsb8zTV>2Y z2gFoP%j-FbE6MXXi3h?`s(!zLFqDd6H+fy47Dz+Eq%>UNV(A%3W#d#FNK2?RnNxAd zG`x{3d8L*f7xIBHl+sW{lL7RCRMgBrgP3{@q~*9&4N6O>H0jQvF3`}OYbs5;Z%xI} z(*bIn7Zn{Nv`NnpEAw<{H7&P!ukGtL1GY`hT-X8Bbt~sZgGtfit!rccIi&2R`Zjqh zQ;(tqT7t9`OiDv24U?s=>Iy(hgkiv>YW_L6SmqW`lt8uI0tV$?D%8+>0b1L1+zyWv zO$JOY45etXqBGpN;sQ_;VHq$fET!!0S#Ia2_X5xoVHhwe45c)@pR4NWy#jv!EvX!q z0a%K#41hB0_cFnxFidLjNnw~I;Zm8>6*Z=G1J(Sq(NxpY$(>YYW~C*VlueGA*$`^z z6iux@M?RY8h)6?E7PMA5Ej?Sb%#&!j`PrC?GTLe*}Rz9;ttlhP1b^>b)pxaM=K zoL^-S3MQqcUuPy|lldrv{=KSpnLsTl4F!|Z5X!#RCrqwCW$DMv`h^CU(zf|g)XHklda5Z3jl)$DBiwTlYC zQiNr|q%f3w+jH+u}dT$N$>LiRNrF9#cgP*QrKWO4(!_ zrkc`FXXvG<0GbqG7%(YzvM9el0-Xg-pqf*>rfl+J4T8+|5b~?qq(DQEO$sJulThia zr!nV$K0m0|Cv-kPOiD}7Y^lE9xs-vwh{z`A^-fB|dA&R9gCKyqR-u*%vzPw%bZj03 zaVdw&WTugZnanh0lbOpvF_T{=u=`LN7U271OSMGk_}uSbBmgZDmI0H(P%4JbETA&i zYosBREh?=0?@-h7BY(ou51@V#mI0HZNvRn2_0H89{6)6wG}xq+h92_N^&sCLL9??W znha14A(m1$nOCt;dG>CkVV=F4(lF27MddwegZlp15XdDV&&JBH2{HFR#s`9Iv*TdHMdiABYoeF7}w&OX7UFpOWjlxG)|U{dU47NH2m z^;gSzcEt}KwYisZe{agYl+tkSWnT4^)_CXBh$hE7pGFu;Rs2Q4Py4tFm=u;$HYvg~ z0Ikn>{#z!P6o$$)6k!;ER;;SD{hW^D#K?GX68_2Z7|Wz~E!$0MuBM@3Rb47oBCncL zW&=&@|HAhVSy$uazg@~~WW|lD?m$H;v$1od+D4LPI7;oMYL?m9NtGJhKIQ4Fu@1)~ z$o;pu-2_u@-_#x|my`)6g`u+Dq%h1y2C8--Wj4@fyuZr)->quksd|>#*h!ThT$TVS zwP){{3!FMH;3ax@L6_-NxwK%2m0-Uz3`>K0MnT?$r zrG{jm^7QNr=l}JOG4S6mWj6M1>`yLRlEq6(?aR{MrOd|OjZ*WeG-Wn+ZtSwLccau^ zjF~vV`FB-1I94@@NBgAqO=-DZ1Vuxsxg1J0HkL}kP??6G@QW=)1)v40`)_kvD#bDY zwU{|VY@lCdBxrJ!Tv}FT$;M7(*rTnk5^%EM;?A{l8Cn zI(oHAnT?$rRh?I>l&4z6=DJ6~1e0Q!8f9fN(0&4_c=AoKPHwJZq z6HE$2Wg6z|a#I?{ibJR98t;_ZSm_Bpa}&{Ntb$f}M{FsZqqkA^gmo2UQK=N$1Ln$|y7@L95ykMEy* zRiC-MPuW~r=qD@wK;?bf1e3xr@6#qVR|u+}mCz@z2`05$o3lyPw9J!N32!*Pp8!iK znhcoKTwcf~A3kuvYsDY|S|SVspjIK40jl|8A4)JOEurkILJgs8lJWP1#Dw3=G8<^B z=H>Nk%hT88|AHP{RVn}WDNkQ}*-4p=og1a*#V+Nka-UVJDYLP2W0#G+8~f8Ay`iMc z#?Fn3zwrM)<>`-hKV>#{ZtSwLccZGS*Y9^}c!EjIC6UTxXt6lbaQG<{t16Lm{L86b zd+$ayKt?uI8%&CIh?JGdK&(h4rD0z46*b=XHNtSrEgJ3rSnI0E#qyKUt3}zOR2i24*P|X-5taco z`4HhU!K7$XnSB*u7=YHJd&4kbQW#2U_~@SVpXU5Y(w@gBH*O5Z56|JrMy*Zs&3k9O z(q1N*)Lf@(H>rJVX&5W&X9=r1{`pr&mj6(-0Wu2OK!Z~)Hzmb#Qvs7=O)9Bs;ANJ% zVs)y!dGv3Dp;Was(Ei)Ck(|FP!u-IEw9F6O6iupSlVdPmj!~g5DGUQ9g`t#&k5{MV z#XT0FCc-jcQdml9$uQ1!-7R?WP+)L1n(C@fXikUss3t@@9a>>2;iv}fZ{oh870 zc(3|F-hZ2`28Of_v8Yu8_0GMr1Gm+7Ya^`l>XNFJU071%<*l(f*Hs(RF$k^f=B#&Y zZG^Ml9*h_OLSc9MUzVb8wH%4^GMf=0Uw&}gpMRz3SJ5pWn93Rg!!J%5cr zxxv+sHV~F0Vy+N}x zR~LkKV^LOUl!W zm93pyrN>4VwJW`^)U(XSPO8-4_9;&;X}6@zMpn5iHE#zgPcQ0Qus8GxLV5jZu55DzFRSJ4vay%ipDKYZ)s26rm#f>mY?rQ^V}-rau8ddr z)o(BgRMr0j`@g*WfwH#h-!5e~_HNV(=B+tpHg;~* zdFZV<<*63ri|@n)lbUPy?IyKHYmFC0!RkRu6sjL7vw>>$`ZV(rqg?5TE5EQ0G?xXt zOM+_bLd*64hDEBXT2>U;`TW?Ej?Ak)Mp zsHSwyKQH*y5OwPq5Op5|s3Txfm`iD#hlQr>cTVP_0x--uEXe)0IT{^jbD{K^!Eu9@ z#$MKM;YKy!957`zc5YN}oC&6!+5OHtyWe4e^}SHyF&nI1X< zbOh)K)VVY2sZ4rx|4|7_C4fo~S_z;IKpg-&M#j>Z038830(Cwf^_T%q9T;^AtXA~F zheJt#k^m(E73n8IV*+#p=m^yLbkt)~J9S{x0ia{DI_eRuD2Y%KsKm8eC4f2rbpYt- zP=SsB9RWH5b$S%_8&Ic5QO5jd=98l$0ZIZY5>Sz;CwmA`2cQl>oreW%44@7`9YE=f@82r*Q2Rw( zr5^I^40;0jL8&$J}e^2+&zYC<#zXms3UTF+Ryt9>hn>WLG;k zyV}9>Xi4(37oA$|{PM=(^2YJ^HUF4;$(V+AHDYZm~WY?XH7aX z?9EGtnE-78jhUAU9RWH5bOh+s&t{3VcE)mK89Pssu|xhH^DlYD)^ih!9W0NQ$s~6E zhwm8~%cCU;z|0nQ-r&&S*dR&5P$*kVL^a%%w#wW>=M8El2AI139-BhRx9BJdP!gac zK*txB=m^jepd&yhBh~<&j93G7NZPf&M@6RZLPs&Uhe-5(2_w(pboF$sLHfg>cFUT+xtJV`YoaaPzj(Cz=|HW z4nQ4%IskQeF=S%^bpYxB)bS^!QRA;v@;40?B@<3jkpLY56$z*)2Wta$ZjHurup;Um z6R6WW22=NclyC^MF@Q<{8w04*dv7i&b$ah*)bajLg#dND|2LzKvnUwAs*=;Frclz; znGOli5zrxlIymU515gK`4nQ5JT37+pai-Ob@n7a}vLdGODgmqrU`0JbdK9bEBP62^ zAEK-Wpbo%#0P1i{RmZz6$G>NyO5Sd(YbH`cNkBycbOcmn$|Q9-nAPc{T9G>RUY*`C z^Cel&*s=9JQzEIv4p~v}nIbAO`4Kt-bOcl+K*yIg=m^jepd(O+r#*E5=&U9=QVB|> z?zP#AtO!P((d+ljQ)ETmX&H53sK-o1=m@AsfQ~>N^Utcp#7ZTAN=~)V?hA-OogQi# zE9wKyd_(H=fhMC)pNh8u>X`q{#-LQ9`*x`J%#=au%suN1h{=JhsIMO~*3$=?d4SY` zv7SE8>KV1}e>CRPDoO&B1XQFa06GG61n3CV;j*I+K%Kr2nb}93d#wXdr$9abQ4y3% zz4s>NQ3*z!-g{?SHpaA49e_Fj>v48W&CZS+sN?Lo7<1{YR3xGz0ZIZYGH;GLG_Ou~ zND(@|3`9qOj(~au>hy1KRvdL;j^rPZQi<+cQSX_VaIC2J%oI23@GDwT|E`Lt$4ocq z2+$Ewk3b!tjJW>X1F7Vr5lR4+`kH?apiW=&Wzf-8oW=y`2xv^8PM;lT0CmRMQSTo? zD)n#XOAM7@tf+r8lg`)}j!fzR)B#veciIdx>U5`N)al#wde--8ffehxVP`CnAxSG1!3U40*GMKI`?x&<8p6$#K0sDm%B zIskP5>HyT~3)+3>KPvSV&7>_V_4#Tt7Ik2(s884XIf$zHK86J9^f6@G6+6_UD5H+^ z9~#80Zz_ZNI3R)Dk%Wo_=m^jesM9kd zedg8a8Ig=S+|{hd8=m=3`i|#Y29^4HZf+4P>g&0TI(-oN!cd()hGf*~pTmS9>h#Yc zb29%&Um8szqLP@!))ZreePTv(xp`i{8I_AGXM?gIS zbOh>n{ZWZyN2NZ9OpKvYA0OtqP^XU%8Fjd}SWjPM3~#V87<5ckfsTNB3h?~j6iTM0 zKuLg-03Crkz4xZ1PzOex-g}cySWln#GU~uk4?V8?Z}vO(2s(wt`9z0_juWA0z6jI= zXbDgfpkrzXbOh)K&;juLSBT4%O1Y8^Mx|WIipjwtN8)(+nK}Is)nusFPE<7(l)M7)Yg@%Og7i6`3Z1iUjBgsK`7B z=m^jepd&!Xln3Yt&=H^`D4kk=DW}&+bAXZzN&+e}kpUG6&=H^`P$&0{VgTyoo-r78 zaz7{rfX*tiA}E#mxA$&pMg7~$sFT@JRg~G%0M?V)(nvi(r%gCOJpyzD)bn`FO=F}K zK*`(!C<#>R#!Mri4vae8nCSs%On{C69Rc;^u+M(S-249v1(XMq0~{@r0N~s-00+yX z<&5$_KOeaO4h@bC65NkM)oF>SM%OU@bIXI}arNH_-7m>O6aO8vMYvX8G&MLhI5tqn zi6@K`&j#u^@r3bMYd}4TzgG#F75S0?C0`OWP{(%!FwO@W*dgZw%}`I?AvHiJ?~od( zljnaZr$Y^BOh%>NGvni}2u7XWGlSyKvGPXNKphzC@#sh7(J$crqe>qAP(4Hf8-r1& zN60r$HpcBn9dAIGdjl$v51I|o$p_5_bf`krdHxSdMMkgFm;j~QUL6}ykqkNlb$W!1 zF;}NYNJbr|&9r6oxjMzv^M6w+@erqy&v%G?zH6Y4Uo>F=R^%7WX3)9ArIdOE=m=;_ zfR0}+SCsNavjIx^qFF${U33IgBtS<%MSg8WM}Uq19f3Md&0zrQc>gf|8l+MWqe0y& z^)Sk)!_j18eW2xWo2vUjGxS;=P73SkvqO>fWEAWBqfyo>^)Sk)=-k~f(ap_9+B4baJF*aqtK@nMv)IxsXQqfU3|k@+7~f>NnFa9Rc+S&@t#29RWH5bPCk<2c`UA+3)nD_zKg_JKP3`2FC^o=smB^vb98n?HAN( zd9XZMCSv#8xZML~U4Pt2jPAKHx(CanCE>EZz&&qpXmD(RP7dA%=;YvSkg!`Ssg7D= z&i&ue+H=c;<KSZN`y5E72|sST7mNhhX%(1YW`m+8W@9- zVVn<$`FF^_WBw)D_1tLJgXPgO;jQO}w;n8ymI-G)H=Olgd9*w|Tfd}n16U81N6SR4 zo*S`xusm84_37R7yuqQtu>m^9ouVT^M}Uq19V1L@{xc9tQYZ-$Vv0hIj3uHP8KX(h zEf1DQ%fyhL8$)`qJX$7b^xUA)gXPhZC{gaehKU|5kCqh|%BWZ#ERU9n13fnm^k8|k zBntC}=X8TZgJT19jOILVfX?03+iO!O(J&?AIu}Cm`N5a1n@h{Pu=SFKDERU86 z&pba^m*vqi@tEhvV;&9_|5G)&kpRqd127MkN6SQ9o*Qv_usm8O#PZw_%Y)_7G69z7 zhEyIbkCus{JU52&V0k>&{ipVzLJ*CaJa2GlaBP51zJxSDCtpGupyR7A>d9NN2GoW2{cM(qYR4+(Fked=92y)4 zsPn&2^~e%&b;y#iIU^6x8yp%O8=#YCg9hlxpp%c#04Ey_)N!uy^`X{Z0#T9Cgfu2V zNkBycbPN(iM}Uq19f3N&GJU0OFV0pAm#NW9Qe+SE> zWkUSU4e>iz9xW5ncm7AO^_EA=#PFRP!*{Se{;~Wos^N;d^A+@YgF}O31EclcL!o+Y zi3r;fdw1U8(BRkrom@2=pd+)QBvOetg;Zo{ok}q3_!6;1&brab^t%B%dCLJ{dZZ43 zdSujb=2T2()@vh_GPQ0%hx`;oMFMmLR3uQx7gI0*b$l_^3_3=%QI7x}L5=@FC>gzm zk^m(EjS0{(b`2c?Is$YA=w#Q^l?-0}OchvW*wuN1gTUv%rV@jOMj2ZkEeXLgzU;ig zp~0~MI!2eFBS1%hjzAqp3&zn}8>s|^(&*^`R3w9r0G-E9EE=FAgN^{58VO6JH4>JI z3OhFf>~LC{rybom8S`~+d9XZMo{Z}{w>(%LEl&n=om(C(kCuc)<>|Mu?O z=Z#R3LP>y9zU?+ZC*O7(B>sv@>L;_ryhgF&o(!!zZ*XXEY=F*c^bJY^lmsXVP^w|H zM9OGU&QPiI3e0hmcW7{Iu#KHU@gf2W?b@pM&l+QMA)JPzcSAP1czJ8ujm{1kJ)Uqh z^|gJUENz_K;`m3^@Ff%l)G9z}_F*(%LEfW-TZcxm@@@P3D zU(PF>B`-I>uly^bV^axuL8BV8L{yDkiex#zgb$bS@e&>pEUHqIVJ<3ET@}2cf6w_3 z>p+L5jtx{w0LnRBpn8F72BMo=bp!Nrt8Rc!j;{vjtU17%La7cgOT=}6Stgp~+-Q=6 z<fb2Rh(U;K>I_aA3dz%W4MPFH$fObMDP%cM{tksSMk}cC*_p>`=`^*{P$n}{_Ar2 ze*gaO%S*bvqOaYoV)|jP&)|Odpo;e&m1FcD%KQ0?Ctu*{{KeC9?Y;J>itD$kxN)b7 z_u0JnKE>nm<$W5r-3$NQr(f51#d4^vvYceAympJ?HpTlC4=El|Jg(yU_5Yg^o$H9N zpHZN5{Zoq1tGIFP$N%*AKOO(>@sHpA{ZIe%Ki(_du=e*E8m|I-Km8RDb66!$3} z{P^Gh;ioVE$75>xmPpl$Nze*Hg4Uh z;`YZBkE*zHzl!&7{P=(V;iv!dcXxjLyZ`Xhe_Nlvc>1z_dA@+0KdbK)=ik;#r0cgR zen;^>#RnAksz~+Nbt9Xd?AuT=ptcpkU@)1=% zg8PIDKE>>_M_)eit=E@de0f;EeEHRrdVh5LP8IiWef9LKXX9Ek4Zr@kPy%uYFj>jXM;dRB`)bichP!_qd8Pq-UR3@zJ9y9#TBI z_w6@Ml)rsR)`aVy)ok+aZHmvUIJ;TJ<7?&h{_OPpR5P)fh28w1id&DWxO?mLFEwjA zyIaLS-93F<^MmWxs$j-5{#_7i)byhSrTbmo>aYD%6~BA`^sjZty>YXOwJPKW{o+a8 z{_fqa;_S039^QWO^u<>%p1=6kd)3S5b(48n-K|l2`NMb5zW>IHd|itBI{m{}b#=RQ zm!dAD_3!M%Djwec;pJbyuB|_RVf&k2*Vb>JoSy&n3)|oHx^}CsUq5qUt82?|*R|iS zYrkFB=7sLJ>)OxG*M7UM{Wf3wZNB!~eCu`6D;@UFIX&h zmiu~gG+=RQuxo(GgawEMhy;jy(}_rcNT7(ja~yciQv-SP2jburezEB0y#NXbPFx0FVg~2@shB z0FeNZKoK5liU5iLih2|u0u%uhRrBwynt_x7l=XNd2R@DUc<5Myl>jRNR>@1>KoQ@b z5gEgel>m`ozZOd0(*v|mGL$><-oX9Ea%Z`p?@&;^ys^8yfm-BMm3VJ>{$jbKtW{oo z1*0DCVraD)MA%b8R_BbwUo3Z)dtcn3)?n8F(R`irjCZ2wY?&49Erpr!^=J#w5g;;J z9FYK#08tvKzKZvWreE@Jmw$T$=@-jgl^p-Z&@Yxd%e`-zsII}T0iy8LjCfzKoD85} z8tfV%@=Grw0U`k+J}%ZOe*;x@BT61w_Obz@>}3H1;IR@Q5@6+LLPa>5DFPs()0B`f zy09!?tp49+b6>B^k_)QAt^uNpNcLs3h0=u#&1%dNNi|kaoC#%LCSoBwg1eGKz5F!p;zOMKFqR3RMJ91W?3zu1GIuWuE#}^r&S3MF7nhOwAl#lpWpz zMxG-QU?spRM?eEbw?>86jdInj`=}$ZW_aW&0%!(M)ZI6{Tv7Ml@N%uZ{?n@0e}Ff= z23qOmN{#yeVkST)P=ptvB7h=*BJYPNdp~TTh%g?l09pYQ^^W0@Ti1Wgde3g~RZ25} zB7kOIl2HaI@{(MPq2E{u5DBm{;#(1ig`z$_jQC!abswcP>pmLjtyy=`ux_jbhy+;q zaSV|Fkw6jtDT)A!0DGd$jOJF>{gBbD`(Y5bX59}R0I?Dv5@2=P`(Xn`-Vb5=d=c!3 zvJq+4Kc{Yen!zaQbLn;;rvgRYMWeB`nqBPk#n@{_eZClbUAR`+f& z|3oH*nGw#)n93<~PKT&3I|4<0+0nroD*++_6`u7`B~a8yRec6y{*O`*qd+s@(2l z;f3puNSV(-#TYeAjRHghYRqD*4HWgysiT%weI1k0s*lr;`ZyKrx&9csq*-@gMzbD# zLxXLs?`98LBtRs<%BWu|6d)2P>YJG^=q0Ks*S?z>M5{;{C$}~718dg5w1K>e`p7qISF1kqb?MWp@0>DP^^M;c zU9I|uezf+07@LbsfSIwm)F@EYw{YWc74>;5qg9`QM&xSMXP{xXTJ>4>=o79#nspx^ z0W|AA>g|D<0FeNz?UMD1`g&?4Emi_V0<5ywd_Y})l=W|x<>Zm|wN{{6pG$iCYt`qH zakDfgKqSD**jPjYL;^+CS62Bhs_zFEOS$*qZE&n+edOohNWU4pj*49-Vxiu#*m@xY zUJ(kA2{63=q3<6=4m*^;NP%2c2Ea^4v;0`afJlJIuu?<hJ#`zz)XNy`ko$eSiF#-%<9ou4tg;TH0wmqrJfgkA<%4qs4BFqRaV)^GR})3 zpIP5|EY@7qLE9|dTmy@M|BCq#Exg|4j17$uNz<4ic zpa_N~1M|q%08t*<0M7p#DDqAQ(_I`(8&{N@$Qr$sp#;#(%OZ@2PXk3BJ}|v!0!8i_ zA}(v}v_O%U=VJ15mMFKpoo$MKWS!s!n8_fz?P=Wrk&L3=v+YX1iky9*?ChffqBYC) zLOIWDpc$`X>3%K-4G`Vo)JG(s9|A;qIokkHUd|TC>zM|K%3r1`!Yr>@8z9STR)80* z28x`s!1PWF6!j68DLFd*Yi3Fg(?`{rtBh#Yhm{eI?3qB3Z=_+oPBc*DbppnVbOS}b zV@5Gjp#YJ9eXc+-=Kn!C;*1SLX3!!c0cHY4eOPT*az$i>B31&d1d6<)!T=N%$ST*) z0Wr(8umNTUBr57-M@Es?GIYF_H9(Y?#0_XHFNpztmI++tS)^GXsM~c-5jp9gADMkL zKx9B6A_0vF5E)R2NPtLyCWa%1CQD#0M*C%L%zeW9#$uRb5zM`zeOdG}t6gUO%B*jh zg)OsEf%E@D2H+vfQi~0;ej>Z5cd^C$Mwfpx~z4W)e9G+=(4Qg-pIMdGAkHnNy4mIn8gbB#>_32 zS(b3FEVz_w;B|Mm0iwLd0+?lCE-;A9=Sa-*K-nOx0m9c_Tkeg5%ff(J5pXW*w{-m5 zT=_4H_`#RCWfti3_}5!e0d9+or*UpsvM+1(u}GXTZ%YH#kjwge1u`jWfGCq9fOpXb zioBr|Q^9MDe-Ql|@oaO^J48mWArj1G?G*W_i^!M61yWn8VmH2)q0CZtd&AQfOINL{ z>%Wm{S*R{6)Md%J#mKZQMwiv)mMl1zmF9HVr1l1LdAY0<2VbpPvN+dv(Ky!bl39^u zcGUoxj3Q^*Fg`OkK$K^d23X};1>n7|fg%{JG6!j}n^9)SeEGICz$~xh06y|HP~`J@ zF~*BwB|s#gLW9E)2@nYo856b;WhO!ean+Qo8qH6Uzh z!1`M$S=TEob=69;?2u&^SlY@kBSeBMX$4O`l&p>99d&8I(n-0;7P$9h{qyTr&846& zsg|a#P?Z5$qw&jImzIi}YV>93q(jC}AuhoDCn&Pn!mM17HJ-4EKFV{Irvc_7Pji7L zR%XhlpaxlyDJwR)R8w7lPIFPF0p^-ab3LV{bjEKsz+5p2QC^HS$O=jD^p$e1ezcS; zwjoh-iKKO|Z-gu_vK!15j1c8+)F4Y1!B-oWS)3@V7iHC=tV6UI)|3T`_C_*gNusPp zv|sfHuUc3~C|~v(Wc?s`+Mvu*L0L*DYY2_?fX1ppW3iyQ+7HY8#G|1>miNi>Kd$>z z<8NeAR{F^jKYL@67R#*W*g= zjm355>Ntze@7OfBwCIh(L>DOzL?PGj1_*QgE|6J814J_WwNUlL61E1}@S(*rE7str zl@A&dup&)9ZA>t!slJD*%2Q?|ZeF`=l&15)tYZ_euO%CC{wlpAT}pW>Uzx2dSSb$* zj~ZoVG7xSz@e_dBI*Mrs75}3+k4-f!<3-1?x|*{wI4dwJoP=4%@2uXS@6@CK8jNo6rai@x4+m7g^Epe+Sjg0vJ& zN=qmW^KrE*3qUKMStpnjhRR~dG*JI57vOO(MKl?prL)DRwEW=1(boX9L>LB4iYBEr z%)6PQ0!pjy|GZ7WG5|{vmH|@EsGtOs(h$mlRj479O>VvJ+{st`@3fS|GJyK;`Fdf3 zNztUT7^0=20Ij@AnqX2GD$_8pm+(IeQqA8UN-!xc=Ru&5CIisgy8F48TgN|}oGyR? z7_J*{z*fvw92%%)Z?x89LjZv7-}G9Hs< z89-$jn<}$G7ss!VlfuC`sb-nglHyibb;kcMPL^3PDPERU-4BkHWj5l4x!>55oc7W_-Rfssp*%h z;q*`qK&!@>lIxFK@mW>7#!#wy<{QZ2!CH!WAvyky&Ee)+%Kk1Xvw?#vr-T3E`C4k< z+qUKD4=exU^;&vt#2vPtR*AGmb%IGvZ`gW{9a__6R>N3v7?r&0PnnH)(3V=SGKF7i zoGu54!Jw*vf$(8fOUt}(Dk=ai5rzSi!ca=XylN>bpt7tCh^3rO=KV`kTIQE7DuDV$ z7zRv=CZ#mo`s>z^T=v{u`Br9=7f!r>K2y!FD(DL)MUzr7L`aiTHp%!u<+sBm!#P@s zW|dN&O$=np}3x;+qjt`&WIFwSU!3S@R2z z<$KCX5(oird4WTex<3hb)nS;EMmQWcU5Av4TJ|m_Vw;-t{GKV0p35;vs^SLm=v8IR}YZmIe}U|u*%Y;!`WKttDh{P1RDC>f&ZOc zEa7ZYpqf7fT7pS2JW?_AhiEEI`k(@pCzeJU=82`K^};Flt9DPPZ-U`(vKNU#qzVZ6le*u zNx`J-E0l&?&vm$F~aTQm=t>{Ws@Qeb?8ycQ9(=KDmOwc=lEPi0LVXYV zs|QxfzKU!TS(@xa-7pk)@^V0m-`}Xe5A^`*zZ9jyk2)!SOJz2B5xC;yKrLThq9vG= zhEOpC8GogkoxRtStpF{dv=mH=y;MuXINKE!fR+fufJtE}rQv$k*Si4K`g$j2XGL1h z>s>zfP?<-rhJC#D64IXL4HhIS_y_gf{izuKuZ}#iyf|kd;b%AVBFezJvs`sB|kgsbKObW|_ofN|d zTz|Ek=jBE0k|(H^{`TzUJP5Y&OdhaOHaQQ1#5iH-BaLRnMJlpuF820(BEQX%O)v>Jdyf&A=KhM8#{x*!yhyiFYDTYT`n(TALz^pXc=SX6;H1yQKaGoO%+AzTRmym-X z080@~20(42x@>YA-{oFjM0(9hY@B+%9XFXoj%Wc+2~;-;ES&MSfOa7$SRpe5oL378ayQtst8 ze$KvbBkAbtn&>($=ab$xye^h~Yggu8UWD9vkGB_npEdX{hPlsH#{^T$7F}f~o%JqB z{{^an+)Kfvw1kRb53GTEX%b39frbtz)eE2!*2nv=pvhr5JenHOq?Ao=qyB7CO2hed zAf=%bXsX}F1Zuc$!vh`Ii$K9SrMw(6I1Y6wnhdah&&Ho%UyCO46V_57t}&$tt9 zDAhN;c>YhyCXvO^dmT;Ao8C4U(I&THi5iXo#XA3ksDB%)sO2_lQOk??#cO>K7`C`j z17jUSe~6}H=nv79!}Iyt_XFb`V+qAxCiKycdH-li%UtY6W6>W&l8dp2XNN%&RF;6q zYM~(kX1nzt|KAK{vqYPX*hy5k!IN5V!zgRdE(KI3vJ%QT%4LE{(W){HudkV(53csC zQ07SuCZ*+=CJiB-O;n~@oLdW&U{V@FrAg1g)?)rSVl=T#_~xXrOd#i^Xfh$4QgmNK z!K5^VvPoAl)zH0FYwYCuo)Mo8>^Al@mZ)hd&@$&m^+GTyn}mv?rvrTzpk-|6GC`=! zKRbeSs?lU(Nuh3Wpko;@DVmhBNfCwtYB}S85==@%s9nuJhg8dP!5LwvOUdHpjaH)2 zCYTh4@%)y`EubjDq-avgzKSqhgHSVOb(vsNSSGM_QdlO$6>1yf>QA|icD2bF_Zom< zg}XZc6nQESM3ctCYB{4~1F+o2$J*pJQdYwmGaG>6HhOk(mD_C~t(F%dwc{E$LTMMO zaB^p5Ao`kk+etAzQl4DniJ+)4GaE?5)j67$nVJn@EXPSQr{^Vv(o&$Irv^<5qRFx)I8LZ>RSK6FVnfBlz`sP|KZc zs#+`mKc!mc-2_r5Ls|;71lgouQW`>~uYQZ6hJ2K1q#=||D%7wjzCWVILyAobCZ#1* zEJ0cd(8?#z2_}W%_~h9L!+iTxi>JorocL?e zhIKFWf_Z8kOodjTDVuXIm1>!XON3szCeYFgS5qnZwZz*My9t~FJd5qyaq4Tk!sTg`1HiM_1SUQJkDwfRPwG?Ox(oird`wFGuHuHez^EL-TwOH5R zi*yA3HdJN+I!y*liY61BJ}H`%vdL}M0-N0CFK{O>G8uUO$Fi??CN+qqm$|Axc@Ah& zM3Vtf+mr_yZj&F_VCU+b%mxF=QPvqGW=@G^ zQX{*BQl3wn?;8_82qwH->5sRw#>?Spn zVrW@=P`V7b|B?zrY=`yO~$?q<~OZ25Y8`eT& z&(bIUVO1ptG-f%M+9sXGb!SSGs{9{=E1Q9mmjin!8nDbpl5wa^13AhzsX1yVlG1qm z8=E74N7d<%o3b{1{&K4-MUiy410D`FWd^k;+jidvq>X7VHo}P8ifC(l=ZV#vHWzu;BCV(d^Ml$wc1G-QPG9PPiPA5od-FS&h|4o@%?d$g+ZJ1||QpzIn2) zR6jT^l6<8UJx5Yzx$~rAsXPXBVVANtIRBEXr4k)@jHJv)^0ZXi1K+r$%wp(C+EUed z1fuIMEo+rq)fNt_ctw7!!iIhGwxs+FsYf1N5hM}JP$Y;)ZzoY zhJs0H==b`j82`@S0~fO@vw=bV3fVx@20Rs$+b1j2C%I3lc`ByN+Vn~FQ?+^7NtunL z0;=y{m-7Eq3KUP-l-WprpnaCEPg0;I8_5l{%SMt0m74pIG8?<34a!C`36)yCbiZ4B zv(EfMRq5bnCZVc&P~;R!GG(Y5o|M^0k|C9Ac$8EFucjQT`Ugd>9g`20N#)-Wl6EHm zwY=|}U{Y9)$wea!rEJnHMGMp?^io5RhJs0HnEbzR`OY=Lq_E7FuSsD!K6rtAc%48k zpJJP;y&PH^`Zx>ad!QQs&WSWRnj9~Q8eu8rUg}GUVE~3A3;4P@Ly6w zmD;O$ph|gAwAH2mE8 z51f0xOfV@dGb)q9Qp&zQ_K~Wn0JKCH222V=DGeD#pMTuH8o-h|j6Mdru|Hi(TD4@O zLPJ&aUxkq}cqda>)e}K&lv*AFQ@T-40a0^uXZ)r?uyr%SoB)Yo!2-H$9< z8piv+)CXuZlWu9q*yOPFpP9Ds|8XlhxJvKU#9P(Cpk@lLG8xp&)1~1^UNy3DX*kkY z%}iZoI1*X~@cpYex|Y&^N#12M{JKa=Z9cwF&=e`Oe6%lRLIKnXqo6TA67>*0!IR7r0UewE=j7mMMC!W=L;7)q>Z;aOE zJY%B*Da;2r)%y=2wgVIz`!GRMSYGUc2*UvirOTr+Pv)ZhQRtITAg4{@GUE^HNO?7f zS=kI~COWHu8R^WH3($yUW+Py((M*G;A${V(%(mzg8}WhgcCs@31Wl1VZ8#z}nrYW;lD+xY>=XZzY|Z{L z+EDw`i;;1STxq6WTP8)~v^DA2CO1>HX_-KRQX3Hkjj)v3VJMn;+%(JsUdhIjULy?0 zgI*Bdznass_NlhnGyf&Io6SVieewzEMV^!VmyB z>~hP=jU<`dWh2?=_NQ99T)q4onqU_ zce|-I^Wte(-8Bu~g1q)BWj2x~uhcGb;F%k5t>xYGV&Rg+ba+Ehja`^rPOX=^XwwvW z)#&9^dNHg054%5AZo9-zP9U}3rKip9rrJ!!7t7?>BTXL5(KF&|{z(?TRB(}zZ)jUd z9u}&K04g|h{f(KZPwPA_w3?PViHjO%bEBEYPeX7BxbB8(}D=VIqB-(r``gKR8}knaAsw zOaeIazr~rPdNI688j(|Nc$I*7cd$%A&M_(WGGI~~qV8V)uAe*PTdZ!R22f3E=u}x(wH1$?V0JHx0ua^YS{1cax0{+5D0KNWHVBxQo z1d#aGk+-}NF{>kQIS98xLM#6^NdQ;#0or7aI9g)0s(Cwc16hxiF5MU&%0Jcxl>CQu8C zzFs*ApzA+tdDeSrIzXF*%3k&_^3Ib0*8GQV`=l~~)aSjIf=Ss|s2GCms{pM;#7rlqBA+rQm=t|g z=D;TRA5`Ma8u5#~W)eW(KYIB5I!OSN|LxaH0$A5y^8qrj0gQFLt0$Ng1N&~10CN8O zTmDK(0L}kqdOr_>ikHptRONEaO9n z<^M5`Uut#yM`8V2Ws1b)FFs%@ZK2FWB)-38mNR1e2wqSFR#ge{%mD`p+Bs96$<64cu84*Wuws1?ywxCp>wJ|t zPY}R*|E;4mGKiPYZ3h9Y{@1V}Tm%8M^@uXkV2Z43zW2cL)|fz6-y^>@2%zs@SUzpb zfGMI#MK;e{4+5C@Z}a6cYgVhs0BUiwK>+LflS3s6nq#JX=0`MdOA+@-fE7bY^C(L5 z^mF7l2La^$%glOEaLYjex&OyktwtUt6CU;iDm}_M&Ga^d0IrGsN7=I{5Lx7$)JW{V zVg=A*iTyXh6k+(=g8+{BZw3a`a=r-PP7uJl{|yZ2MUdEk)Su^|V*(LpB*3HeA=#oB z(ohQ`fh?P|MV`vU{%etj&y7EV<#57lM3KYyt`W_nD3<+pkZ6Dud6gi5wf?az&ld!6 zy_0Z&)`EMGqrAl+fWH4$k>?5m==dwK|EdbFcVMYPb31p##YS@|=ls9SL$iyVW&A)NJGIC zX^2R}p9TS3D_dn)24I=ke-j`|?7s=72*axf0qpq0=L!PI_;UmXw0&+n2w=ruGpgTG zwFsbLMKo-{6j4`2*7a=!0UYDcT3yFy2#O4#$lC}4X#dl3!K=u8qiA3)yfvuKyhq0H ze2DC~6wy2Z8s<&3M1m>uAblk*(HgCmzLP>Ts^2of6k++6g8*jy*|GmB|62|MNdLp} z<=i{wLD$X_r; z7KtcD>ZJ`U?%Iv4DLYeA_Ji5e~o3p6j9`G*KUO2 zaMvyrhp7oLjOWw@#(z_WWdJmtp(!$8iYQW%b&bn(i#*E2{zDWOhDI7@mZ?RQ`1ro> zU*>o4NhPA%faK7k@49Hi7`U{*U%KQxL%V{#b>dF9_gT;qut2ry}ePbh|+S>3^&29sNww+YJJ^GU;zb zzr+=t(Ex8L2;lnaTPjeY`dmtHT_(PTAb_@ihyFW55J38$g#SBF5Ww{-FTk?}0bCOf z@N7W~EwkoWHjz<0JFfUD8}&KCrb?_ay`xB`;ib`U`0KURo4 z9suVE0_gLf*m_3%TZpJ9x~(99_5G`g%tz%3rs(E_0In6m?^(QO8{KLU!1908OYK}i z0GQ+ojm1=yJG=JW55Wv-tfaePWxW?h9!~0cywwkYZ;lTt`bQ3`UegCbPZ08CBxK`Q5 z3N$`mCY4GwW|tZfa!yfV`EkV)NG{V%1eUvU5WqE)M3qGVnTaSeV2UVG(OV1x$oR9e zHR9hnf&fEY`e-lj6O#}h-`mb1b#sefiUH~EU zZY2odN0`PJ7Z%^%u>n%!U!z;%TGdr8@62m+Yr@A-lN*7q+) zzjf#if=5~r_a5^QASmAejoxw)z;y(jCd-f)t z_4CH5`RYvYOi)%M_MgE1r)AjQauC28|BpC-brQ}H1Tgb&=L-Tz`Nr}a{)e|61hC$J zE6(2ef&kY1Yuty=6a;WJ3SgarGX();{vqN2&JzT1H3}fFpyvq!xElTMd_e$L!}pyp z2;fR&ysVj=V2W-b2;f?!X2sS76t9Tl0}y$eE)z`A^@0FWK0V;Mf&dbK>4F-ZDF`6% zUln`GKz~HV3Ya2dTis|7!213e_Y#UcLl8jsALb~}6a+Bw-_92VaBZ)WYJxbEOu!k5 zGsy&sT=j%VFhyKFZyp5D@lRQvD+r+R-{QzpwFuylbjDaFm?DnyZ3Y2c4H|g9Ab`C8 z)*+oO2q5t{6Z>xh+FFqj`Lbdc5$m6C&=zfH{3z(wK z+Oo}*5%(_901=(W_alp+NdA%ZCP4t#A71Rf{Pui706G3@!=xNaU5rWd=ca=I*7(Qh zf9DDUSp9G9A=0t`CYU0^;~7@WD0d>l;|0J<^sWh}2*Z3+n<5NH7_UN!6}3(TEwTS5 zn4+5t0=Pb6(ck%k0H*!jG4m`z0M}RF+1DJtJWmk7TK_si|JCxtGXw$D|BJ=0X9@yX z{Xd2G%F_Tvj2{+sf+@1Dh++t0$*Qbcl~qVb?+KTBJZrkd}fe zvPeW0d5s`|&i_~0=|;#uoyG)HM3JNS0QGwL09oG2t@9{VYG?z?>UGP8`CDB7D&!~w zCQVEDIm$jGZ!-wsYUsaImiHNp6j%!vLr^in6j@h9G4%UYv#jfH4gyI3Qk^)HR|-X;{bbh%%UOWL-0sZ;^G)SiVHR z5CpL1KU?X!yzL->)&C~;AGMebdwW3uGyh+OpD75S@!t&nXCQ)G4+6Lj_v>nbvjhRG z``_#CTtNW2{~ylI`SybVu8I9eE#?iK*ncfb^LQV($ezGS>!m~i^cR`Oo?wdT3Djwr z*ncGgF#kEAJ%LJ#Ozgkb#nNw?i1Jah5nV{40h*WIYAuT4qk2{>hy=1g8{)r>{Xtqw zWwTL;w0>OPP!PbZzo1BFDpFadT&%3XT7W!C!4z3nL>gWt2w>vBCH7y{B7po8`)`6N zqR6)x1hD2m9m$aYIf4K>|4G^N*(KsA1E|;y1_5;ZmDqn2l=Wty9&80z3rD@J^Bz%2 zT?1e}cAd}!Q^eJyPD2rfYml@K-t<^7MOq??B?wDdUUz;YS`f_(R;1y)VDlxZSuuP< z{7o&h3>2-aKPRvjuKsLMEW3Fg3uJZkC`w%sSyzFqemgMCU+uq1w!RfiFlk!)o}kE= z0$746@+jxqfl3YM+kw&j77S-pBawzHvQ)Oi8mY`tI>V1;rurLci718+ms=M@`*K9H zQ~!k^fQkQhz94|r|0ecdRrVY~0Db@BT%IWipz+@f{pY#7^&o&1f1_{cvjhQj{->Hh z!9^zEjEpEF4W@{@tD}q*>U$(WmbV71d5?%R^k#vT^`_kaW94Zh?yf9Boir@R%F{w` zGYH_CrK;q5mLP!mpHO&@nQl7>VBP=53bflw>_4hA#{?qINPu;9xZI)`_W17E9N&pF zbfBjph*PO9hO5sHWCCd*Ed^7gC8AjJZoFV9kkxO{iqfwA20a!R!0$N?LE^t9_FpSw z*^?TH21x!l5d?6(W9UDNJVy{f-+wu_`JBDoAb^g);(J%M7*2SNxCj*4JR&ZF0M1Ba z|4lGO8uI24L#NQ1<<(=rzokTL5SC{N0?7L>Wphti*~I>9Q7YRbpHJq8`j<#~btuD|;qw0wnoO1!^12Mvhl$**D*ft z*nd^#tp)*1{I~N30j&6MiTzgxevTl3WBl2%|Ed;D#m0A~EzvHz;)3x)_U#1$tohF>uSSv_a+<{l zF@Ii8TGHC@Fe=*Ee8Qy4gF^brn3YA zr2nY^IA;q2Nc^Y7{+ob+Jc@`h6fi~9RgqzP?sv?v09jr=u*PLHkcNn2*int{C;qmU z$ShJYMOq??C0)`&{sLKcTrKics?;z&7)oC$C(y7&)&I0CmcCrJC`I}S0FgjT$M!9X zp@aMuX=qSC>KcHdh!O*g|0ble0n&U&L?oCZEfHB{+_jr!kq=qFMv(y+iZBeAB1(LC z*Y_`FeJ&FXaEi2?=Q6SXu$<@edO-k<|7OHL<8USRANe~CY%QGqZBdSr1=tHBfh>@9 z6-<#wiAY0(`e7JQtThlzWj3q`%K$Rd5gW^ZDZ(&*+f#&L{I(HgS@#4}gkkA;|C%x^ z#{%y{IzulLz*2-|z!YJq$fJCdAb{Ne@?+T-!DE5>d+uQgrpO`@#jtPae14GC_w)Gb zKo;J{6Tn&%`wv;BZW?)%nYw9_MLLIL{+@euf+^B)UIZ#NoEJe{%Q37`3f~_G^zs?L z4gy%~FH~xIh9H2er?4t}rXYZc|CSlps>PU! zZN#}8Q?ZRWBV#JIkdvm%1e}q~qfRhI7^?G(bP&Lb{}C5)v>=MS#UOz6zZ4n2b}Ab$ z_6ZdA-Szf^02+TcvHvJCAE66ns%HXfkvX3Urig|eb3TPW<$_u!fTf6o445JeS7n}( zvjhRO{X6vE8G-=P{~P!3OhEwGr6c~GCkWtrDe~VXcsSyl4Fb5%*nblo8f_;)R9AJ< z{Mq=&h=KJC^w0M2bsU+;&-lB^-=eDBj-Q16tM-3Q*5?WW==E>(zs=zsK>+Ld`%MLY z$;i6VW`Y?PkN|zMwunA)m^;A~VW_SSW*`g|X;>{?|7mZ3Hs4hI8Af3KJ4X<}n*V&s zqKk6{0bD!w-VP+T-SuZ2eOKyuRy6{s~${fW0x>->Aa9_GJHB@Bv@Imm_F|r8=YYiLe}?&<9xt zF+qzkRHvZ`!vPAVI8w^z(3AQDl_Tqje>WNg(C_d6-XZV|^+$Q06n<_x2;e%S{+%ZX z;M!6A&J+Z&=ATD6z+>#cAur2fG43j5*C zdN=Nq=L!Po_m>~6;8}tIu804a_vMWJH$f?f|KS8J+8#k6T05yfo00khlh*Dyf6D|V zT1V`^mQ^&m5e{%W0irAr2d!ufM!=mLPyD?^FxH^s;zzf+@P$Ab=VFRH<39H37wE#nuEf;@?uD z+}2ZcU6(qQS1gz5yJA@xc+m5x^0^akK|V07n2vAcwcE;;eD;kc3DQ0MY}#`;n0VjsT88 z4jwLY0CE6w0CJu-2OtLkXMF&IL+*Io;Gs;P`!;N()v~{Q&D7NT(sqV4q4bJC$I6Dj}cWdxtc@N(p2EocDO!r3k?aNd!p% zDU3QwF{(PsKlzpQB0szQ?DKQ@ljqcQXSz2%7+mbabZ5FZJ$zBS{XMEo*NaBG2Kxq( zsv@RntGP`H%Vm_Iiw3&}`vx=U7aV8P;RxUe;Ho7Ha^NBLT<($N|W~hm?95%}NduIRH6ynZ>F2Qy@tINdQUSDr5wZ1CRrd<2i-nIc*@v zQ(6+^Q&AxS`3T^wA~N!ll=NT}maDZ@a{9;&EM-Q0WJ=`lE#G_qasU)&cquvK=n%m-j$064~Wf+K(^+#W|De9A1(N!zRwAd5%rHrE`pA^X=_50MlAJy;C2$;YQX#<#Nkok@ z7%gBlBuD}{0yqLP`aPd+p~t(B0G!HFnL07pP! z0yz$~kT}$8AP0$@ZfOG!?T9}klGGi@!@kf27KTJlUuXsmvO?T}a{5+OWnq0_#ubv& z2d1Prwf?F*5P>8l7SRB5@YX> zkAQpxa0KLI*c>_3L(XH~KO`Z7L0VDw&ft>D!jhG{+&%F{k)ZTphz&_{$6Uh0D z{zp=uSA*VISf5ve-@p-2m;jD|jEq|&hXJ4*-;7!%r~7-% zz!8wqRKCBMh+4=v(o*THjs>* zeoQMdpT5iV4wvIyX8f7H&5ZnFg@^?s31DFWGvXO2r=N-jhmnIs4(CozpH}B%<@9NF z;^cziikjUv~{19MaHX~NuipEf}(mf_nVm>`4c+!<#MPwv^B%m-?M3Rv}PG4wB;Kadnb5=iP1kxuh+dPJl|PCxN7xqvwm^XVs^5k)MlyK^OS9?<`oQJ)Xz6)mhU zA|-PAzBgcqoIV{&Ie?%76FBQh%u(0lkl*s9h=;J;zfrTNF1E4TJUXhOgjsT9}K>uS94N3i2R3fRL zipIu(BOoII907%W($|ncPG3VxP|)p979!5Apsl# z`3T^Aez!7&BY-1-BY?Ah&LK+bqgNuSkKWJ}a{9hkBB$^BWRkO=RwZ- z0C1GR5y(l;T9W&D{}ZK&48;J+rHvFOAR_@83FPEnE(t&mKu&Ju5lex545|P}00+SP z-{H=CT_XV`0VDw&10#@;0FD5T0M3WGy#|1z1dafXF%HN_Q15>OK~f|s?*JvqLt_9r zc?T#-o(BV%Po4)uVm^KD$5XXbeeMPI{#T+5mj*B+Bo>z8(uhVtt)Hbw8~~0I3Nw%Z z90B_rKD0VFE~>^wA5*NI*scINa}~Xj4~ityjuPcjCS2;mg;`Nq6GC zX)W@v`6tJC7h}2quED+mkpQYNQ?yl>X)V}SPP!BCO~*q1i!9_X%}x`4So)QDrUvV- z(Y}GCRI1njIRH5TIbLj70ptMW0OUAF0LM9k26CJuVEyYoJDLV&ghUd6j53MT08S>6 z8pv@pgajbR(NIg|^ntlPes@v+HzEoXNb*UB1Ykx!$yy@Es~-tKj#qz6dw%m+{dE93*HB>AG*fQ<4*vjK&<8l8Lua_rJu0_UNLzlH zIV6FcKKCvuCKD@)p@%0XsuXhdPtbWl%040=9EZ(RC?o0yRp}ak_jz?CFeCvP z2{GN6jK5G$x)bkB4=&uj zFx{E%O%GqamQT79?@bT2)HYMQ4R&AG{=|I@*F{P6{PTsK#a#kw=yn;yP#v}C$7 z-J7!L^S=8}UVwazHrO|SEYjg;OI?vrhC(a%SA6tccy#O!^@ZOO?Rey)5FWXa?+i6Z+dvi$-Y?q*_rN54==}( z(2EAU2KxqZKJf9{0FDwk0ytEN6m4A#rX^2J{aH)J-_r4Oc%PDsa_6^|VOUVAPj!z*if|M^I1)TaKqs)pmfRkCV0#w@f08>=eihEnp zbZ5FZovRx!dYR*HZM1IysWLU)nWCypYt5m4DHjcP4fYMPR1rd*b5m5cA2OBMcCSX-dT^=Vh3T$RoGnE&MG4!Sx|wGzukcoz+J4fYM-*jd6!uh{^O5;?pm z%khmF6+jLE&ci=2{)Lp`cLPY7l)FNiwu<07nTN0fqS~h_VE53ef&7 zlH|-35`ZM%Vp;;{iSGjq;3y#<0UXz}QJ4UZ0FD68$B$@#NFWOnKoUSo^F*7vR@3!j zw2KD22KxqZ%GMNZ*_zIEW*4>M>Yo0OJCTgB25qi9gX9u4ID&lxIkpxOTdRQ_Byb+{ z4op4*@)5u(!1KS*a|ad;ASr<)fRkLRCB&%8_TX5+bZ5FZJscdwnC?vXrdjEA;o`0x zY4tz;Ik=GP!gOc4H=QfHE^1YjFTJ}4`v!1alm$lsM*v3vC!cm3!1>^T?|&_VR7Yuw zy3U#DT#9wkYqE9?_6^{;`UZ{wjsT7T4*8R!t^8MiSfi7%U4vbNeFHcS5hwBZ5BBjN zCdYr%Zq#n*MiuM*L+YsL<$u$VDh~zqo^Y1Kbx^O24?MU5;3C~H~GqYuzE*frQUfaB+6I088Nz}x^%1}_cZ zC?TH=UJCI1^#^seglU%UT)1>+XSz4d!kr5j?CebUriWLI94}0FrhC&vt*nV-VuM|S zeS=#6A|%z7p6PYz3Jh{?=q=*(O}nL-vCY(W{S4TGCdrZyVUdN_~*{<65e0J z8jw>hld0nFN2 zV7qZ8s9YX`N`MNG{l|T(6f^T%}=mn6hD`)0Vts-^wTJ8?X%YQchS$pq!S4Tjo+)cMGi&#uI zrfmCe|H{tr0(ONLWJh@9n3HDn_iPTIUE$}x?n^Bva9Kp{bY9=TZ2uCSE@yyrD*4B4 z-O15)4Uh$J1gLbBJtFGC9eVbJbOMW)_*}8&ZG2NJMUAvGC(>LO6El6-3 z!_r{W09BnGQ-swd<=vm%ksEBjSbHz?68WmVG?*Kh&&|hC`Fe*+fGS7X09B5%fC~?> z63k7;vGT2~RJH#2IqlFQbky4Gz;v5s24i>dP5y0bEkJglZLn#8s#h0OPAC5$9d$74 z`3t3LV9G8eSy!-3#BL>#q0Pzxv_WWt(5C-yfG+(%fSGgb1gc7s{nuP5pzb}ZQY3&m zdh286t=~Wu@1a@&%n_i9Mpb3&Vg*2@!#^s4`;Up#Js4(Q3gjqIMTIo$DjqkN=0k`o zfK~vly7qeYt3skG{Xb>c|2I&_BhDNFn)%Qxj-Opo2~Y{Jy3e;SRRC20RXz{l_&jW| z#sBDZ`(@1-0&CVs=C@H*ePnKzY+=j?sRC$amrNM|Rd&fG(f1xJL8)r}CvA?Ip7!dv zQZQ4ZiVCR$sOl=7ZdI(RtEe|URsvK4GW4Yc)ha?KP{)UHbUN6pLZYhcak@{is%|P> z=C$g2=pavq0#pL592YJnqsOr<{w-Z$kZ!qucRG_MFrPJMYRq20i zj(x-E1+T7em~4zo)_pn!SUK+H$OKpkP&tW zDtoy$P}PmE)45h%MJ20deZyoIR_yzR(Zw7y0XYh=dgPd{fhxyzNV?k*sOoOV58PxZ z*yw-Vm(}CGtOS?|D9pDG%^aMyL=_)GQ~|UCsOoxTPg1OAJ<@qs|MR$eZ-HigG5QtU z9Q$H?%+#rc0aO9BvO}ee?oS1(x`BO7*J?Cy`CmJbEi(Z+0cJXTQ`j+U(l>O)F)h@n)QXMM6<5GzGGVT6{jP%R^9k>%gLvGUIbVP$T0rbnCW(nPN1%z zW`6ot)lV~D8MNw7GcOUCci$q=s=F57jpIXW38*uo9^1&RgGRRraU!KYB1@ zCO{{^Ovhzaea@7q>IYr+?t|QYtu7T>aR-@U-%2_sqY}9PxriKfN=7HZOrWZ(uXD1h zejJ?co2QBk9;;j&sR5GqMqVbW{mRV2>yPD*cO52~Y`C<+dzIdX)iGagMag z$to29l|IA^<%`Hspe_&1lH{rxKvfr}H?daf1B#O#Yyeeh_Te8)J#-XCC8+V|@C!!w zfSwqgfWq_!R+SO$kf8GO7%Bl)0#$jm47XraNKl>btX8Vp|AcN1ou0nrD8NjBN(Wz5 z0#pK2`t_m`pc0_cn-`S;l|WUxsl%OB2VZsRr;3!mXaH5|i%QasKRVzcF~>aON&?U- zkGSEWOFi_|MJ2%M`Plz;c<7?jRaaf-s9&zC&QUL1aulEvV3o@)HK1KzDIwj6w|d?# zrkgKc^ZjRTv9*+AZMNTPfU0sdWpggb-zXv7h)c4Ih@X=2!`=#cpKrhh40)Ra@El(X zjQy`#q?sKH5`bm^RlXXa0;uxU011x(auldSf>mDG8lcK68zAFFP$A6B!D_QxRPXYwgIY4&I0US8mO{+Dak{J zRSi%n;lOlfrKt)q{%?^wM;}NWeKb(zI1`EE%m%8QoJHc~Yy(v9+8H#UEG4Lzimmbg zK-MF(vn9#=Oasi6V3ujt2Be^n}dMNtV* z39!;vR8?P9N>J$;y5N5)!c3sf5hD@@lMPTg7$nDx3LB{EOXqYS8&!5?kO7n>C{^wM z>>GHAGE3Y5vuroh05c`3y0FY2qe6lzqmKqyDM6(lC$$%#8vDN#(ti`30G$A{bS$ZO zmC^p1OM^`VR7EyL2>A;oq#N;8AI-&d^VMo+se9&Px-s3BKc#aEY>$#jh6bo|H8j{X zKvl(?BCO)|zg$c=rrV>Zvz@dD272Uq=?C-y4AZ12VnORCE0l-JCkHflCi(Y z5+}>C`N&p}%Ir0=5pVUSTvmTJrd!=87t@XDRtL((bYr^Jb#gJ?n6hg|I^72B!jb9W z0(wX;4d%`sy)8%f|IkHpZr+i7IIyV(Fx~1BxtMNDw>m>ErW@1jqp|1=xiQ`90J)fM zOt-o|E~XpPb*t0kV!AQimYd>zFYgBIPoV^r&W%e0R7z0QSu@Rk7A1KZ5kDp4XRABo zV!Elc`v2`tOVjK|vDJrhG2NJM^|H>O+N78lcv=~jou#dKr3)m3pZ-I#85Qd~?o zq&5FsE`#?x+y?BypafOsu^OPtJXV3cP&Sx5H(*sQVT!PhQ18RVbYr?Lm%?WZ9oD7M z+;IV2*_$FP`}QwnM}$mqHONi~$g7D>=Pn0JIpSppga&gP15}yfp8FrD^D&CfCrSfU zdJSSGnEMr=(pM0b0F^)$@3pD`s(jNePUO7*`e&Be-c zE8t?fG2Q9}xR`EC3B#{L;L>15^)D6U9~4zPQ$pdNM(ZaA{S3^X8@(g~RJqXszA`sZ z6M30fKGr)Up!O-R032qp52t`j19k(tDRnMGk`p*BAX(tLd$_I{&$P0M8rM6 z=Q~D$)5!0rl+0M|Sm}1B%0n!PrJ@?e4}jHkpCApeDhd5hA*x1-v?Th_Ev6gO+B^x4 zY3-II$IeaXxkOG+Ec8XUxdcg{an6@=V~cA;gAQa~{eOvck(=SpXRLBmnPhH|*yPBo zuBO{^V0-+5l^cMN;Yv{H)I~&b0V)A1j_2RG{u5)IpwxNDHXvqo9GuCUX>iSvQz9|4TiFhPq4$L&r7L0+a$&v~ z4r)t@@+qhRW>k$712P&eYD)v+Opon=haVcMamogmfvQnuVyr=_?0@P%WsuIO(@Xh+ z>54|&XTgjsjVcq_4QAkIR6drl5+qVI@+!hKL7^9Y$~LP%iT|7c(TNPb)rD*^-I#96 zFD##&8xXo#394!;)5LS$>MXXHZcMlF5nJ-J$2MZ`O;w109Dn}6k%ofiaLlAV>m9 zbmo!(WaK=993*h`^nxRRBY-22!^DRifE<7v0G!1CmGrbp07(Eyev5=7AR_@BfgFZR zasYAwasY7N|7dUoaNb`LK~e{924LAZg0yqLWG?kogMQ0x5bSvsr21h_X1?u?=Nf8<8Uj|74NdQLxM@KU_ z0yqLV0yuh{!4bd_z!Au)H~;egM8q6`B!DDy{Za*IzxGJUJ+f9ZBuJiw=;SINIK)7AK;tmB)ec;}DXyEl%VgpkleaUVxH1PT> zv4L+RH1O(cD4}=-C$WKjwYuTZ!0UT;r5~zzU#(7K12Zjp)1iThKXch9?{aM5x&=>k zHxU|m{qNlG^{p~Kd<1C=Y(v-uTw;X^RfE*4(4nR&H z*sp~KHvYdEF+*-fee}8vno%FU5;;EqshQ9J2Ik}QABm%=2IhlAj-#kjCH^n9_KOrG z0VDy1=~gI*Zy9m`asYB1YC(3W)j*CzEhN09nh(HyKJfY9Iy0gfBmpD=%&1#P*F!l> zCdmQF0ho`ETXObs+dz&_+ma;yFB$2BNJauk0y5GEQ4W{49Dp2voIbKQ4-LGUQ6HK9 zi4-QFFaaC^h542WM*v3vM<9nWwH$yPN7--ya;!A|7rzu67!@@$BY+tJEUYW7Gb0=U z9042wh3VZW$B|Y`_QwBpsD-FIkP#brNX)34+HVnN)EAl(Ienq&`6#DvMI~~OSfO!X z75}%7Odx3^n3G7`WMkdZ(RCs zG7`WMz!8v-UYc@zGir&Pk|h54qwezsX4HMY5;F3WFc}Hp2*^kvhg(2S-yM$ua{BIY zo44KpQJ6UpVLY# zte?~Reac}{-HZTo02bCyMg2bIAd%BgMd!`Thm&ah@hsh>8T-6u3Z*0hG7^xHfWmYM zB_n|xzVOQd$mtI4mqP=O_`hb<_iM)nzKzhp>#xKH){~SY6L4e#j%>sp96(NAXsYD& zQ}NeA1JC&5I+>bL--=4ih&zWC((6eM5-S9d)8}5dQ#pO^mB4x0908oCpECbSMv5Tm ze@bBjNCG$lIShN`^oU4r1UWq-a!Nr?Kk<~v=?7lir?|Z#g<#f-8q}Km)WZgCDzbdIOMkR9k;h{&X9By|j)c5_HhXywO zxIV6C)FV_SG_^ji6efTpph5yUeWB_8DhG)ifE;=@^8v^K?2Z51y`Ju`lIWx)b#JB0 zjOcCTbVqcp(7?q1?ei+2)&dH<`Ov@>f2=Pe-Df$njtzVpp@COjY-wr%M=#*$ZzD9Y z_g}l<5=(URyZ^y;G0~xrPPl{{6~Gce62Q?L7mfgq0FFRTIK?4ipp>JW12T%csHz|PQL=l|KAvJ)LIV1o> z9yyMNTCz9(ufrikePjZTUSLKJsgdxsG9#awEs^7c42f5N1M~6fZ;6#=#8dfH{9lJd z=m2KqXb6cfr47u;m(rGyQNCz4fRhiJ4dmFRBe6?wKv}CINV-RJWCAi0KoZF57J6%; zfmh$}D8u)=1{UTQO(cHNY+!}_qFIu6=}W1!fJzJC2*@WNG#kLl2TcLbA4r*gZvaUN z842LzLu3Ou`4HJaj?WJyK0g|ej}kfEisg!3_n#RdlGGPkUbivp3oWnPEit3MJ9N%A zpT0Yk$mv%6`Ov`i{!fnf#SP2|i50RdM#Zz1rWVLSA_qV|x@*G`z!AU^$np7s1YrDe zkt81=rPG6(j09vPfFmHIw-Oq7#{Z?Tyq7hgu)LNP@C6HwfC}Z6s{s|dN@(EnKk+*( zg$c+gpJ5wNSU$rxkkcK}^+E%W_`e*PUg8ucph5x)(_I{n0FD5T0FK_`a0GA!a0GC2 z0p*gR^wz=;QZv!Q|O{*R;j&%_35a{5f6be=as~XJ)ZBPz;OhUJp8mPmz_$?^nEsFGxIY&gm#USx5rU4aF0>^~~R9Zkj0yqLVt}E!! zz{Fo3B1q~Wxx(NULIbbA5*wIPQ|kd-kwCaz!J&W)5#CN{V4gpfqpKBu8XLGyO{t!< zYypnoZHES4zp31RAvSQydH&pJXkefJy}E(OzxlS?fcU@pwp*Z@!Srp01}6TmD+zuk zHn3|X4z-fto~NL}TMZ3d@!uF`T7?K%}Py#wB8Xy9u9UyKcWIW%ys*Xgw- zGdl1x0|w7H!J8?XOHsCnYW+;o{7DF5$Xx#kE?m65Jk$J1VBz8++a|?#UFXwff+-?c ztGY57h_J1SD*x)qd8frN;P9g6e}q^DOd6Jov~*S%%K%s+3C$&kMe;)7Dz+E6s=k!i{;Zo3z-R|fwUA%k%ovg zbR7(a0kA|E222r#iZHDGUzi}(QXmbarC^G*L}Zb!n87drmI%XuDZ)^ZhE6|YSfH-| zx{Sw^G?pqoBO)vVWHH{>QZPjtB8nkMLjf#T++Y|mMH}KjE7MYhWq>rClC%^|5k)>i z6hn}P0$9$CV;C?+7%I|m6=E46&2x`PFhyF+g}d zwg=0ADZ)~bl@(zb0LukG7zRuchKe*4VHg0*RX_{(f6$a+sYpu^mI1Q(UbP?+Op%6& zVhGYuAdAoL8VVG-{!2?_u>@%;koK%C1yiIUq8NfS6v*NP|&Xn2`a{9T=T zXJ~OPsmHezgH5$$()>xJ@uJz1N%JRB#w$}a2|vDN1(i#w2=^T@MFa%DYy7Ltl+76B zTAr@GB8#6$usap~V~}z)KP6GhkgVVS=^Mt{mUkM>(m3y1KHt6sBEK74Xx5?HcMYfnmU85m#N2hI$ArCCdJXS}Mab080^; z0f=&@Czv7()mhh^=@wb!X)Jh4L{$Ax(Gnk?%tQu=#|a^peBE8p31opZ6iktZh%}6q z?N8qVpKHq>n9$af=<-#q+LID$e#YMsf_}?VwS)keV2Wn^`OOr~AoUs^yG5v3-S{uF75&Lchzs$b1h zd@d>t1tl8m@3hPn2kluGDbNz6rC^FQM3f?N2U*BLAPc0SV2U*SnDKWky|%FwNb}lm zkxe^;_y@ppk=`=F6j7u)4Mh|gAnO5j#sAlS!S+d~(V|#F(^8-%4P}vnDYD3J*hG)i zaJK8c8t}$YfZ?i4OJpe$q@}?8JrPt)Fhv^9Gon(%c}83yxL}w!mPQ(ui1AO$w9G3D zBHlx_6vzT;DVQP+5qTcc&#jJ6Z~)fwUA%5kUoe!M4pwAk0LE+!z!|_wlEdTl*I&7grPd?D#9?J{6ELK zNh<3!v_*=r445n|6?v2{rqmG6$ryq(6le(2P@u?Gsk~MfU&hjAA^+JvA}we0xFXXc zIq(?TDj3e@5otJ^$F-Ok22hJa)&EjiWwwt9%K&M<=OPkJk%ow3=zDInY#!HfTBMhe zh9C_EuK$u|kzPmA`i4#r$|=&)J9~;WM3f@CZ+6M2%_Guq_RWekbp3xUZBObiFn`;# zML9~_vqdrNN0+NaQ)EB7vYUo^A+kt;qPjtA{7+`ws<8xFSwYiciO3=odZ-KoV2Lmc zm?DZ)q@j)!lo)Ve{jCto04zmV1}O4ziAXR-8X~fJRBAY%mYER55Z_1-G82M|GXL3H zEup2deMVfH8p}Q-*#pE{^chhUL#7lKG8M=ISy#anx&KC9ft5KkgQZ}Kv_zDnbYiAi z7U`1Kr9=T3iZBeAA`Vg!hU-su8u2Jy@v5bVt7RXhI*atX=u(*lz!G5?FhvxpD29EH z)Y90Dzf0@A0d3wSA}tZcvK!WAves2(k+WeHX^1F>Uf^Ueuwq`|jK8w$$&_h1FLf8v zF2xFfC8EfHDZ)^ZMY`}-!^~o$bn&enUk}t;B%&1AU97&(vFt8Z zQHty?R#6ODq-XO8JaCXD0&4z~Isz?Hpvdtux;bz@-B%`t4wsviBKuNzIdSUhr4CDA z_FmxJ|H`y=)6!`)}UVw#utoNeDVq{ak%nW)RQ<17E$5g?Z;*u| z=WQv{5>bxQyRliRYhQP+|IShNb(gJ;tOX)VeAo9cWj!cxEqE+@OrR)5IuxMDIVNy{ zxOL?okD(m^EP>hE0kkM}<-y?oL)k1zT0cm#ZPFB3+4&$j*+FR(*>BM5QrCWi&hAR) z)jdJ>St?QOKa0$U6-=5%+9e=zvH3E=6k({&B1ISm$m;vq#pyPWNJHZv#nSs2%Xvee zZrv11pXjjWqy4y@(|okMR^7V#z_k`WT``=GS<%+@@AEl(y6sbn?8jwxf-v`@ek(Xfgv(zWp#GBmcXr+Z6Tq#_L+K$F+W zc2mS(ZW%>p3#uu?GMiB;@@_;lZ%dJe^R_(hQ04upVc%RXtxt)$x$6E`W@SYdIqxo) z04)?bA9^B-^yz2*KI@ug^PFyUWszgctKRzx!%yA+ha$5J7Bbi0Q)IvtQKTa4D#9>8 zmL~$yJR_&uWyR1F(X7?Vp6;P#WffWEe2^%z$oU}2uxFh9+{UAGOb2Hd-i!K|S{t0@ z{Z+z%^#3ib>NR91qfo1UW0gPVF89}3gl)iNVW~(%m;PfI0850Sfbr*mSSpJpk6SGT zvhq+amSBoBL=?l^ip3Jh0$HSBipsFoUm(-c&;d);A^@5Q%YZ4ONJTNE>(Njk%MJ>b zV2U(E6hq?=XgQDabYC%>N0FBEC{Onq^Nc96uH(L_!gF6VpcaNBpu~V0e-4PHGK&;Z zWB{xO%u{0-Fhv+D(olq9fUFEXN+g&f3|D2bbi{$>7w_LFh$f=k%l5leEw{VKMPt%WCAU%V~b*G6|#;$6ECi~kJ?X@)1{HeX| z$TNSk>D~5lQ64Y8qEE0+8Qa@s^SddU8{=tN^(acE=qu}_`ICL}iuMhJr1_IQ@;3RC z&GVq`!rSHe^M7oe2N(6&SFhOR2kX^7!4$DgRX&-Y#jWARc!_)o$>eQCjcghK-NXxujqNJgg31lI%NWm0oINQ|-8X7V@t?!TI z;ox9G*a3KDK5X%5)@tK}rim)8c(~U43zSAIwcoS%dNJGICX}D&rhe2Ni0n$A8t+RPl zYB?{0d~ATlo$5W3j}0w~p|^31JR^^*|El+2Wb|aQw3};DEbZo66id%NB7t@F+_xx( zp8FQX&>;+>jAiJ5g|<%yGp+R~5yg_Bymb{=BuGQS6lsV^Lj#{+7ywIz;fMFR|E;Vt zECZzVLoeGMPLY-w>%n)3Hvud|q%0Fm5r*n4QRM!gA}f2kVWww9k(RT+o116`kpI|D zv%wT$m<=|kh=a^lnB)OOazk=out7qOOW;SdoT~7BP$y`vgG+g8UPG<8cGu2a~G3V0=%S`@|zlgd9ATk!rGQkvK zsLmoq7y|hISkMSDSO!cHmWs3#VHp4`_vGk2(kkP~>h@E=o$+PZmsGEkWr{Xy%QjOs z_cztDvUVd=Z7OU3NUj3>Q?xIrN7DSsR;NWnlRZ$2wv%R>KV$#X;vvk=sKt9N&oqCs zDQeM>ZId2qgVT7YOfW@keyVOWMRP~hEu!=&1=i8Gv~2FBs%83?)@fM>TK{Qqf3nT0 zUdD{I+K@ufXn&4t3gNuBt~ zrmY%K7^0%pK-&S*1d4qBp1Zl0&7jRKU$vwFuQl`(V46SK0k&wmERStp>mcI6JbuRC zu{UgyJ-LeUDfJqOR zc9SjISJ2k{pKPLAG&I>=w`ku=GtHlDtXni>*>SgMY^v+~w+Nfa z3e6pN%jVMFyZ7^qDADOIyc#N+`|)ZxcjgTsFAl&of7a~PE1RmJqG}v=5&ao|#}2+w z(cMTn#=e#2Pj>vR%`q)CY5uU;FXR9G%QnEp^BPH-KiLAfXlSxGaM8R1 zlIBnL1umLbK+?oC$}Yi6{$#V@P5x}xY#n^Kw0Cgvcp792;o|vdNP4)mp>WYSo7q>m zXr9`nhrOLu()@9=;W|`h*@bvZjH2~)>*30@Chj@Qa}(mS8KSvUv6i~#Eu{;9C7N3l zYZwQ<)@hh;$|VBu{cAvr%xC5)qDVzr)+JH@xlaAb=EfFN=S5M`IQ7};xM*GnN%JRL zA#Z22rQ`m`%aqNXkT+8__e<8Y3dkPG=&Pxc=1+D{F51{Bd2X3p`TunDEv%Q2tU z2+J{_2J(e#0`foItl1(JY3M5$tkVsgtt+CMe`}?d(y$D`@^pV^El+oP*6_IJ^V2#G zk20U>$$>BNG4wNii^{O}pU>y{mT5`*XPD+swurWv%34v;xZ$#4bY<)yg zc9P~#Hj}R0(5%^Jy7txemQ9*J*Y~}5Bt5YIbo_jJZ|bFF5zSqy%Vvn?R@PO3_hHif$^O-ntGa56_S4<0EB_&4ALqLt z<^8v1Z0Vf6uAya2F~JmJsjdp^!NmlMKH=0L5=@bXh++s@DN>r1&FoT%0hwvR~5e4#CtF|;T$fc!-i88Af{DzZr9YGD`vOSD-hnlda^YI(Z(x0lc9 zHsIEEZU`PgUTM@y^M@*q6VK)8qx|swcjf>14%FHbJfLJjbL;Th^?8Wq{^GU&v$u89 z{K>B3lB)nUMH^AO*xlRkUA33=Ky3A${m08@$g(GS)kn+I9mus*RD_}9&k}H+ z{K0_pZrcE^FaUOxdnS2T**GR$`iB* zOLbZfr~46}OvB-HKSWi_fiN82_l0Qbr2O#xi+{3dYW`FKKi$;53i#>v?qzamqxZ4n zdx;FzwH!gCx%s<>vg-T$+z`HKR4u7L*%w|mbwX1$>-YF0XjB(X-to(%U*nIUkTVRK z1?Ye4_}l;eOGZboxgG!3ReMQkg;b8i9?J2rx&EEmj(;ef|M36Jcl>Kp?~LQ*$Wc7A z^X!dsX`)N8u(8+b=4#Lg^T+Og<^f8>c7Q@w1j_*$VW~*NQQaZ3JyK=TVtO;1`iCMs zj|tkw_8@n04oAd798*$% z;Cn^z1C#XCi#v}#fH}bs@&4Pg&7^Hxq-7Qolqdj}2*ZFW!cdWhdDAOV0Ia;kHn zVmaRQ3Z4$HYs~^2+q!&n9X9Z2eR9_*$B2{%jOgZY8VvXa5z?=Rwl^ z+4!e;J`R!|I!xU%!4z#;R3`N;_%mub0V1kvnm_QJe^3aO!+LE!lIG9GKb!p7`lo2$ z^2}St&-lB^-=eDBjvoTsWhvSOEvo*%Wo0#J+X)g9Z>j&B3xD8qF8qOMrT=PWOcO3| z^O`?J<7b=n@Pm)$r1`UM{8u-w&*r3uAHN&U6BA6)rbU}6+O}wms4AK?f8bLOe_&GG zA76h-bh~vQSwBtWfi|Fu@dIJ6;}4!$E>7t5_Rgr9>5zE!4x1cB+t|djF%L5}*=ni9Z(ih7!e>p#f$ed^*eo zm)$KY}FeAFZH;Tm8SzlH{7RqtQCIjHM{&Ifr&eb9;LR+7_jZdfH?TLbDb zqqG@pjd}=B39w4cwE}BGt+hyKu!IInu&>3yUx@-X<9HFk%SXLNxcfSJ+Erbz)z}>ByZ9nh5PNC{p_W5($p+kI45l z))96}RJ})IAOWZXsPfiF1yJR!kA!!5t$e9(iB`5Q>z@oJq?yfIlGC6%%x>4ApG2Vw)wd>$fk%+x>?66z5T%?7G+WTkT2ACZC za0@aNP(^{NbegsP@KJX=0(5aCZGf2)Ref1K2B_*zA}=(MyA|{9f&}+r>LJidhhpO& zq#~v3dL&{5?C!ktH>I+b1p??EZ$JPD)G>hLUs(zUH%D{o;C8B{DcGtqf`o=5usbW?Pmb%~)o}}$V?R3mIIaqbsy@@F3)fM( z>YRfXV3l>~fIgc7Rh~`Pe|PtnRpqd|`#W7qu36uKr)$Mk^=;w(D$xJGu?odzHadTuRYqrfXtFlq`ZFag8TdO`VSz-;j&r4iu>3`x;i>{w$r%R`) zeila;$dc#=sE+HPyY{DRo~g&_@@B1i7{~ipG15w$;9hskingpkW+B^Hi(DuxkQ<=O zYU2V~W845$mJF zE(>29pvofG0$JMH09BT;7RcJw2B@-5wLn&1=nP=u$w5cl3%wCOI zgDic_(#Wid%qqvlRghW!IF>S& zXh$KvzOkkk<9}6->Uo`Ofa`izEH=Os{&kpRpP;QE_)d8SdA0G$A{OwKkymE~mx;xE(yReXaAWSX@Bs?4$i?5`TQ{zMUl zoh|{>tWRf#Y|6E!>lZ7)`3A4B=W;52t zyrvA7zGOAbT+qULluR--Ks8smKpB9E;5*j`%c zktH5kv$13uhiR5)WckL%bsKZ(1q&eTedqd&buO+zce(;&E}uYky2c{QC6HJ3O|vp0 zD<`sKB8wohNFu8vYP|y|(KHJow(F&(4_Wh&RSb(O8nV103ljYejP)h^? z=7I%Or;8Kjl7y@-z@#c|x_!A`UTH8^7-Yr3a%m|+)&?v)ElslwU~x%6)(33$_+L!3 z6kw}wf1Lm0^}oI3<{$t5b*sz%Vj5rmc;_#A>c?$=I^N^{e$TF>0V*f0Pzk30{q&tj zmG{X8ah6BU^S6HLF1X}4zY$NLcbwgyF^HIs@o{pG6MVei@k-AWR|A~ZmBe8^4(;(| z$AvmB>-b;CQ@urcjoXu#B!i*`s(hj#0qCyo6Qv}l9_G`n9II0oa#ZauUh9v1)kWki zaRbciS~ZRTcRae|@x9U2JHFeCj@_68cbnz^1fLXkV3!FL;ql%kwfm1JGRMR84 zPGbF~@kpl&?@}JR<-W@G9~51&>u-ce+HJQ1?x{NRqB?cZMRjz>b=ZjnpvqxqNpzZB z2B`U;7EQlcbkBW+G>9K8^5j)DyW|oZU)WmXR@z!f2gfvitZ{U$#ZV=$WlM5MYoSu5 z760s~?yJ*jbvm3*|5Q}N1$DTTq8ff})5(pr;--e{Qxvu8Ih5O5uJ-8VE zm;H5IxXS$M9$eM`m;JR|xO$SH@_BO)u2ny+dv!lhk1M%wjk$#~Pf(QxWcZ(YaN+-U zO&6|fd2q4+=124fRS!Ri&>nB*!sYZ>SzXD4YaKgFmi<*-xTyV=J-Ak#x31;FRXz9B zJ-F8EyKp&q_NOa)aIM#O;i~q}K7-j{@Xb89*ncn%JOwgzZ-8%AhVBJ^#f9ry9$eM` zRPJ>Jo$SLm_TT$$E?gY{l|8uX``dM0xcHux{vW`2f-hRxnyvv~!A|$1(@OsnbkE5;Du5~`&n(PYG&lfMztw{)-+!;=!ZjSXsK=XoaQXfBdM;eo^5Ek8?=@Yx zuI0g1*Pp&zoU<#sa5<4(=2!CITI0*bWu;RW^>{-Uu5ra(&4X*bz6)1wiYt0>IseQ- zU)_bvJ1|pUmCqY`a5?_O?9pgO9k#faezOY~wa<1<4LF-hQ2n|GSB*a@>^d%7Wq#Z$ z$vJ-{&Js4D9@+V*0aZj&>z@i;%Z2M&9$fn0>dQqv^2*VGI_8xF@QW^7cl6hy6|dmI zMgMb67p`l0a8>_vT^BB{T$<`i9$agjx@i3DPSd~~ow6#)uexwm@ihL`Jh;~DyKv=! z`}AraT*kko`d4@1vhlC#!R7Zaiub#B0~-G~yKq_i9=2rB3Fg1egNyl3*nOt2;KEhr z`fE{-H}>F)|84g6!2Sv@T-Wm8(*O2)E?n30;G+MzrVCeYznn+zh4$9M$>l%w;9~rF zO&6|fd2rSEb9io151qO=0zJ1>F}hL(P?bUV(S_?;9$du#&$b8+C@ec56v$VR2B?%! zkLkj7Ef22pf4i;=7hei!sw;VLmH$KBNz49G=by`?tL#nR#D&W_^>Tr@k_T7qe?rfXn}q;hiZ^uODs!x^ z=E1dI--Ro+&rAjN@JZHy13Si5*F_ZxK-Jp5HdlAyx|Ro*K?xE;3M(1nZUzp@9H$q^$<#%-tuJste^r|jg*Ye<6 zukXTjEf22sQhd%Ux^P{~gKHh0TX-j3(S_?;9$Y_-KS@2_(1nZsudHUc$T#xfTCeZI zRmET3gG>Jt9IH+vE9%|FoQNvGFS~GE%Y&=@kG>%H5yz0H(VMz(UCV=u@#i&NxXPDn zI64jRCLUZh|4slUM_5J<2E?n30;9~r!at$NPs#oByChIP|Mbg4O{>-9@ZbKw zfBF0G{_nf>=fV9-9{z#k<4WHBu#)%g|LuSM%isU&KfU|6fBHZE{{O6BzWVa@#fzFB zyZif#Z)y$W#rO5rc<%wp?@8Vxd7tD_CHL==yhDQa{zH;?N#NaoT**6--}$_fhmaq_ zd-x&A6Ounv^6tl#d;s~;`n=M|5Fg(sf&G|VA5+|8*pHu*!21x&ht%x}yeH)Fl)q0u zt>hWnXO#5pvr0aumLHSV$H+b*gHJL0?BlOq_#XV#7hmnxuU>!s!WUH+6Mg;V*WZjS zX}1oPl4p-A`Q+}uz4*5uUcPww^5y>Jx3z-> z>sBiH;7Q?^KkmNUz1sb-f4Sf7zukYoKh%K!-iN*yRPyZM{>QqSYpDM0{kqgxR`Knw z7AD;Nw32%dNd8dCef~asO7cIy{r21MzWe^qFaEsyGtt?feDLR24lpaZ|1QZVl|1|d z$)}Y(dS1!nk4QeRGOxLUX-KoqYo;1{8=T>9=`hW)z`0HzWQEY-`6iI=;iCGZne_uzy9#ekKgHT zTVWYHYU5wOE`QZ`KOiX|*!p?=kq)eXeO;FAidxrsqHSFKYeyJd@9bK*o5DN$mhF$S z8Kv-`>@Ro7tr@+U4R3UpR$!Jc3?78$aOm)87kxIEy%hoGZdr{pcx8|GV?>03TkV9$a28% zulVVb;rjxOg=84;zu9}+s7tT&u5X2;t6j|#JtHe_=?h`;zFN>)(VXY`(8X%IMx(Lb zW+YiNvSQKF%9f5K>ztA8BgvL1+y#=97$@N+pa5}5fKp1SOMy~oS_%{>q-}tPKp@ls zN-YQbYv0cqDYk5E{6*`T+4q0{uj~5neZRk)dtdj;At)C?A%X$=`LZ;6 z0d`u5Uckzg-V!o%tR2CCIPwaDf`T*xR8X*TbkJm)>7YlB(G#K-6hsiHpdeiD#=dty^x)JJtbXdj)eLj~F-Aei(L>&Sv4?^L0~Hjk zoPuy!g1#=J9^BA#=b$k`VheKq9pso}>7{P$m5ka|hFuki%kMyg` zQA41+lfGoe4#jRwRZASTUCPwz{BM={z<5YmwKom4tiuS?UCL40TjQ{Ut?m>l@4Gzj z770J99NTuWEzdfJvd)4RefLL|V_V%T-ur6ZBO8|n(^8dsR!7UF%27kCmdvOTo{qji z4>MGQK&rcxqqet3Ahtuws?~d_tmf?mmuEiSU30Zs8tx}pxkGbHt=qy;t8&!PsqTy# z3a(qH#8!98=DJN;-E!Tg9JReQX24aO^1jR7y3=HV#eBdu|4r^DSh;<3CDpJV0VJ1j zt%8CV9s>;JJD7LyqN(NBt=8Gw#AyB4E8RHe=+7ODT4p6?P6Jks)<5th`+pkMPFn%1 zBg%4_QwNP87@(gJ!GM)RPcB^+q8Ctp^{6l_qDsT6J7w=q)mpcf@LKEE=t$ih6PU^6 zkve&odTloW`!VVIA@$mJKI*pf(Mr{ohTV81SsK*#DM#&KjfUT_UCUA1$q@&;PdRG! z5U99{^Fa`y6UZ$lZ{9UoA`SNwtlXiw2u95rC_^o?l=@dktksfpN@zMVGCyzY(bJV% zWB$b|3@aTOa6iGy9h##fW3Ul|zOGa&vf*xml|xTIx>lbm6eAd*qc8p-7_f2(%B6!s z1Orw-^yJb(w>R_x^b;-!iXtdPFkp2=P%eUwk3uj&KOuSnD~FyOb37n=#ntuK5af*u z1qE>gDkxYv1>uGuP(guy5zDsW*`V5UuX2ij(F_$!Y&zlxhPN5F#d@^98St5 z^{j?pd}~I0h*@wu!R48tJ5M;vSi5JN_sc{pd1$Wo(}HUjZ&WYT?y#c2TGPl{-yd1) zbf5b2#;dP*Iyo|@;mk&rI#<8Tr+Z%u&sv0P-mMYWW6!oLy1?!xO9v}ebOx&JCyiiB z99AToeB%fYR13T@!eMJeo*3P*wX!BgH|$}NcBC7+0X8N?HDKk?luK8IC9)IIsPaJIRt?U3KGowe+mlHX`q6Fl~WHc^Xw2k_2Ae@tFJeQ-V&5XAQcp> zoPuzpgFpoZ@#BRjJ19saK=lMGrykt?zQx{v6%@pgU0Fdv8UZROSUCmZMhAiF3G{R9 zAbJ7K)$s>G$*dD0f&uLqLAh+@In?8Kjh@ge4_K@{7 z1YNZ_3epHrLBYx?2siX@AVaJmGXk`Og1Y}}yMmBegvKZcH|Chtt)L)+Km`RWryktU z1F9#8UpgAp;v;;9Z*5R$|(pp=J*+8h!w;SXa@x=r=F>D#yY{Spg>1o z|3xssUJDTnSUCjcGRH#n0^&!WK=lOGMh#UmSHFUg*=Xbxgi8nI6okthdx}@kC!p!H z4?byFuyX3b4L#tZAZQ4Z+(JP?f`N8WuyP8*4MCuKg7~rds3%xC^=@$fA9L)sN3R8| zqk?dwgABn@P!K<$f`XN^gK(pRK=lOpRjS$#x1b>F38lmn@KtI20KnDZt zj1awmm8VEL7;+y z)lWgVtP(%PprGqGO97~!VCB?<%Mx4+RuD1=nmmTDISDX0kKvokp;`{XfYlE@x#*eT zhYkifBf=f~U(iY-D3?_tL@*$JByLksV8f0yW{v|^PCfiG$C}lH z+*pD@^cFt}GJ43>l7fN+0~HjkoPuye5NHPl@#CPVo?zwFgWJ{fe`X%6pkQ@W5H1}w z;Sz!Y`Uw#XSUL3M(m^450s7So!gc;Z&{u_XBorbT;Gz~H7_f2(%H>Gt=Safnb)U+|UE62Uvz6*d7WJ%$Za{!OAHJHw1wS3RXY$;KnLuOoMuY_?fC!LC7Hp zR8SB{pn`&M+<7Y~$YKSmCs;Z4;KnKemR^0TR1`;cX$1ua*k5j#Rf|XMbZgdcc-eQ82Aqc6UAi*90Sqg%cQ_v&f%Gp7W zge#{W+?ZpwM>{CU^FP0Qr6AVie>YosCdVLAb0EIR)Xe&B!U}`Q26GM{3k_n@PiNDl{gTT)ltL z0nVKe!2mfw zUWQF2?sF+1z8F}1qCao zAY9h5qb=+pT(%j%aPBv;e_268^HBy*=97W2RG&z zsGcCP9Avlbe|}DGtP@BD1({=@f`XM(5N^yd*CNibAbvpg1S_XralZdcAFwMZh$Bx) z6%?e`Km`RWry$&zW1xD1_;HX`PhcC1Ns<(V%#lk@LAbGwfeH!?Hm?$b0V{`|T=azK z1+0Dt_CLuiLcgP@Acx0nG;#{UWgW{Y=%LlQlcR$H;kbTPPmqRz>H(I~L$HE^)losX z(LpW`6%@n|sGwlw)Pu_s^s|5J!3{mH{}kl9Zp<-IK|$sOsGwlw6okux#;*jb$7&jS zK=lNP1(x1&uMyd3gbHGfpo!BF3@})TV8F`JLAmG&(F=&5ckOV3T>ps`gv=_DQxGnT zP)uGuP(4BXIOeD) zSUL0-Kc1L57AlD25cGgW2L%b{>Ptbv%Gp7=*`6WgK19TK37_f2( z%B6!s1OxOl_ZNBru6H4N0nOF(KLjPS(FhR?h@;yIoFI(=6%?$TdT{BWA?m?p75nW- zJGgLFlHEj7LAV@f0gBmL^tkU8w`Wb;TzYP8yN-@io%YFt{f znjz45x1tN|ezMG8`;MV+rNtbMDBMAqBwF)P*AXPb`WlK5U8MF^|OO;>7d^nMK3@fjvf`T*xR8X*T2%5Mr9TcjEUsj1qA%X#NCi+7#z)lO%3s^by-dkLWOh5h@QPr7jR+A8SUCjcGRH#n z0`&8M>9D0u}oK`c(+7S#`9TAkvA~Yo-f&uyo5e!&4^yJb(A$kGa{}=WaZh{1R z3)fsasGow~%dLJ2dQ%sUnN)-@yPY4>RS@eC1S%+qAW%VpoY@%>3|Kkz{c>y~;~Q;C_OYJ2ZEw=f7Ydt4|O|pQblA1bv#`9P>6}6&#P%;W(&nCy1X1+UACy@1NU4 ztV0l}pdi7%=0mVx<(y-W>jmxLB3elW;f5ekLBZ;$AY24}#}L5) z{elBHN&K86eAcAN1qeI306))xO7mnf^b=aa_Yg24)!WGHEQkTNEcV*{|0Ko zUdK8PL04`24MA>jont`+f$9lX&JMy2J)j-*_m?5a`9?uOf`JMOR!%{cM4(9qGRyD5{e}Lj>hG z9t?<}9LIzK5tPeu!8`!!c?T3fZ+!3*tekrO{xSr;@oC2hK6qu!ae#9oq=Nw~hoD@R zppXs*#IH9(KRfP5Snr=fDkxYT6@(jtKm`Ty^9K_e5v-hga9Jgq)q@*)zytfA)~pi~ z<+2C^BIu9X%?&|++-`0N`r~$ULyw{U>ItF;w1Wj||656Wh;;}8onwK14{<4vV8F^D zC>K2;dI75+dUEOD;!uAC19TK37_f2(%B6!s1OxOl&mMXKD~Fz3Iw;(;{{^j%2+F00 zj-*F0KtCaZ0V_ub<R8X*T>cI^? zpn8J%nW|es$T7zp9u*YC5vZVGuGuP(i`!rygA9_(}STUVwfD z>;3~l$p{J&3~0x!61gGB(^mxr@dK(SSUL6JvWoN5F>Uz#H-i3di(r7kLIeX=4nesH z3Yp`8_<2r(6Rezia5cl6f3zzIIRt?U3KGoipB)sqmzd5$LAdNCa_Yh5^e(3!Tu$$# zVW|7B>VXLvaseVlFu+EH2nMVif^tKUV~%ppYGi*z!?#u7qD{Z$z>G_ z7X(Gw2u)m04+9JqA{el82+BoJh+cqxS$tKF8Zx~aYDqEwy~$;JtMb0fohE%d!OHEL zt999MKf%f!nu}g_p$I$T6q)Ks9sdO=*b%41rX$Z0@`7b~GTo&dwY{~Pf2W6(RofY7 zIchsOIt{x|S;s%YA!Qj~Gtlz#nYCTYQQKRqP0Z;b<>i^;ecMUjYj0|}n_%VkgY)^!9GVcL{P4lMML!DY84c$C7E*6km2|{swlV$oD%CQc=34yEz6VX zF6F50tuqV}S<`kJMS@$n%iDn7Gu}W&x zdM*EL^6V3u(e7i?^?mAuXPEn#RNc)q<*4nfu}ZP+Q;yof8V$ceS1q~z73@-u+TI#X z;(ADV-{qIw5mS!Z&RR9-bf0q64%VtczXh;OIchs=?Eiv&%27L5BajD;+mxfWvqpnH zl1w>jm9mlUR*jU9hBjEaT^rG;ROLGEo2+?mSm`cj4u^s_xtQ5bpr0Iq0V{`|T=azK z1?cx7HobcOM;=X-R}w)vdN?40auF0F7@%Kz*GjedmD!EZDFW`^P6=g6Etfy`ERCnU zFSVVuYTW5Q<)|I3)gu1b>&97*+D?u**nP@TJ6Nl@mu)X)UH=GnDTi@yt>)9|A!XHG zrooh>wzF0ZI^CxnwSzSp+#6?k`OMlbWz`nwz12EUI6b7S+HC_ZM{Orp2NV!+)5;T9bCO)}5J@i2khqdZ*L78hV6LIeX=4m~-#HXwR(>0rdz z{&cC$r0i9W8VZ>+ro>i>E$bkYu2(3uv49G8()J3cPP_-6lr>vbtLGn9S%(<+DM#&K zt=8d=4s277+Rj?VIo+omwSzU{*vhvlM{Q?~IBd(_NiBG%hm=)wyG}W3J8J~Gb*CJ) zgSBdK&zx>kj@r&z#W~%l9JRw@l|ZjmQeHlDx=T4~duz2uogPwF&CN4qsZgtQyOg80 zw^lRg^pLV@p04&@|J62Nb(eC~_SR_7^Hs{K)!PzmwA%?*Zr5CG3k~-ZtXxt7m&l=h z|1Y~k_z(=J-VKPL9CI@uf^y8$fC$R5B{E64LS4z2Iu{Mlg#U|Bbe7(>7& z^pLV@^=8MTHCNjSN^5YHLmeace=E6fYxTWhQmwP#vISTU^X>fPO;s0_ys=F@lm26e1XqV2=lI;|SPLLAWeI&FaAoJ)n95 z_4tvcp8u7KKeyCA&=5hn2nrDlSp5)`%N*Z$yc$6-pthF*(UW6a8xXx4k9+=GNd$Fd zV=L$nnG@KsoPz$MxpE4^Wsdbz&tEq6^D9ntFrc`){}Uq^usR|r7eOI{0s8r&CjthiH=qN-mVC4{$O9zGM1?ZRck=>Q+U1WMYVRxzYJJ++TQ)H?mb^k9w!Hzg3HXY&f3`Es883M|G zz}*BZw{MP)48Ow!a()vFJps=@8Y3uK>#QMyauF0F7!W@ot16D@_NL znOV%LgeBBf>BqpfDKF>KV7tR}?EiI8klv>pwS%=f4jg>Km-4>LPIoCsZEvkskkdoT zs(CAxa@2O6?AYnP$~ylL=>2KRs(CAxa@2O#YAHG0ryR9|wOTTMD5U0RJ3)2+mGLIW z1S_{MZs~VA+J2Zo$7{ESV8F`Nhe__0W9bcupj@548=@!2lB`DnUiD5;*Z++X)XH2A zh@c$HXg~xz+eN|5Zk5W{%9Kl|YS~&#cWNuJMn^tz?+q_Z4ojnIV8Go3E4OcsjtsxU z1af{TZChNx%AqI6j17pM9Pj@(Mo^M<`3dLD(*81M)R;4-C{#IWgx363tsgE=P+I+L zd4iQ&_D#}}ez-h=oR7%!$hr&{YIj(7M^@(cqBjk#v1#r4v&*ZMt&iiw;mFJq817k| z$F)1Gv*EdzFUHP>wRXeRF7?WPaU^}AVYaNHQ*dpPH8J@MbICfH{Do#N%v(Wq{1P7O zH_ZPNaY>{DR!3yzA}XZU0s75_6tHsW$)%%0^aASouQ7s>>0xHR=R31prNWufDo2gb z%o!gTD!BDaLxA!a+-`!E+aF$E5`olkJWL>G)-?nJRt`ZqIyGPk&eUxU(d*Q01=G9k z8u$N_5n;}7Nq1Or)L0^XF3H#iE4OPS8pFJwVC4?YMK4FfrN|&1g$M?$9D;ImZ9oL& zBIqE;ZE*qm3DFB!IrQMX|8F~jI!2I1^(kiBFt!0|nM;-wwRDFiMXlmq9p66OPOx&j z=1OD0YQM>nYIvAH?uM6O#RXhGYlxs+9Tgj*Cs)VDhUm%F5wc(&&j~pH*QujP1pE11 zLj>g_n5S~hMX)rAXr)?4*3zk3M%EHt%g7pWJX>0}PUr&FUCL40TO$zLA!XHGq^Xp( ze5mbGj@sTD%M;rnW!1bONjYk}#VYFq<38o69jviV>m}8!f(F%z0e2Iu+`c)wKKu?7 z$T0&PHMss2pyS5n6a_afr^LE(z2+RGyzjEpUCL40TdO&DdPrHd+x~DkCSBiE-Rr+5 z_HiKGfWw$ny*|D4SoL8$!R51ty9riq-&`$$hKC8{BF68pql!)~fl))DmcXbHTT7s7 zZ`upX>K4;o%2C@}V-~O-Qr>s@buVI5j@piz?|(|~Qx4<7TCICO^hTQ#tlX}-+I|}D zCs?^dbJ2@fmF2N`7gl!Hy;Bri_fCm*-S?N~?8|G<^PM@K@gTC+b{FxHWq-j^OQ&nB z|3g>ShUj9c*5vY8fXc3()YWqZrLKsp$PeKWI>BYW1e*MW6AX^__f6K?Zg`j=Tzyop zB?r97@9+*^+s|52IsDL0MRj3a{}_-bCKcI_P8uTJk4_pQD;H5Ak^$=F5L;Zp%AqHh zjxJ&Z16D@_<Wos9R6Z~u(GI9C-SVt!>`<5tY7%5{RMh_-ae5ZfAN*u z&wu3Q`mB^5cT`Jvsiy0DQT4%lzHnb-^gsT};iXUe?f46tHNxE*-MZrw@fUko!@8$i z4bk@F@mH_Z_topu>h((b1wp^F&ezqKy#MUy)i2oVrQQqQSwC6~FTGlyS${TSuibv> z#b<9{_~`oWwFC8<-76QHJg$9=H~gx>TK*9!_jmY4`x~`X>h+1+uhiQP7rHmUyZ&l@ zJN-33jy`7nxtBirYJDjE<%`~aHvJQaPu7D;9yyOmJnD-ltbIR+exZSS(r1kYe)v>< z4t@Rp`30|h2)!N-$UVP*%&gmk6fd8nBpC8Pu@Fmzjxw(@5KFqhYz2sdy@A~++Xt( z_ZK~9d&*vJf8Ea6p0bxZ@qeAp*`7|9uirV_Q}*H`=Qm%h6Ee?Q`@CE~PJGF8_B-Ec zf0;Mh%h&0I-RJA&>vY2I^YypAcfx+)ks3bxz%x}mc!lBv6pv9nLGi&V-g}bx-ktV8 zvlDjR?|3Itzp`u^oHaOK3dQj0dtpC)@AHdfG9{`gK zCSP15N`OhA=^3MVk_plVz;=zHIfMx?2{d(M*8rLTnx5<^fF=N@hk0WbK>;Q~nU;GW z{$Sex+5m(-^7t?bFbOc3%?3pQCV{4>N7SPLO#n>S?;j=srbVQUU++=84!|bBWM&#n z0!#u;J=3N=(bO~j_>Cbbz$75*iJQap!~()5h3$j9=|y7#Oae?d+a$mw(8QZJMEM~C zO)#2zDc%HV0%*F)Z>xrNKTLA3t(S*sDGYgR=&hh^K_a+0I#>S)SKD`W2UdgR=&huHC%%^=IozR^8Dfb;rCD4e*h{L1n2nQ0Go^^kJc~%O#n?UCrkiM08Q-h8@}z} z*Uhy-X>(l?2G9i1bGUJ^_*Sh?NA#7XaX4KA(=J+ngE#m7BW!+Oae{4efZ5|O}%|Q@TkXs zyt|JA!@7@teb}(>qMr?>F##q4QNBfrNq|Y9iQ%%E0Gb~0_>WUBD}lCNRv+ko2sCv+ z{8lcC0!#uL^YgcwI52DSe2A%+2VjY<7lsjEcQ4J=oi+KvQ?o55?Np>|*aP z5BB~dFp71z*jTSlKk|yr7oji-P!wQt2sI`FCV{5j%^vREOrXhQInDs10Ox#L$M2Y` zNUybZ_j6Xnhts1bfQnvLVDhq}0Vcl$ilBfffu>$mkEE?S{)n*oo-Sbm!UURn;rs1P zO)#41kWt;mN4txD5>rzzU>TzPqGg%Jw=GeW(&m{L2A~O`sSi8X`>-RxF(OhA-C zQy(O5^j0Only5s4){D_M#0(>!geHKB08QNwKdz{$4}UlM;4RS92X9mEDcBq1jRmA4 zgCey73M( z{S2RBy$#D4)vN5WUS$GJy$AXQJq3H7>x*`E{i$M~5B$g-VSb$slYlURre2+DAQ7u_t;v6=*7ENmzH^FTZK)E$)e4_ySFRHhZ#70!#w>;b+q5y#SLyQ=iOC zgXg!UMcTZI#k-H10!@8v@oQx&j#GHvK(sN|l%`%~emG21Z}~C`x=Z_QuRfsqSukyV zwDPlGDtfe{XT9YMH1(G65Oy2uGpCG#ed71CSw_K}&u?21=0~ou2?)E@t5cw-)5qVjy%95pfNwUq^S?yGMM~c z5+(sr0!_V_Eg$N2CeYT$T0duGSRZT6s;6QfKxByW(?Xa8L{xXv#65m?c-Vg>pqZK5RmmquL1*5a!29G_gHv%7#6@HiF5|j$jfH z<#$6c2`~vT-T01Ne~yoVVDqaW*!-FYVFFA7On#UHlK_)IQw~$bThYk;W)x6gY1wQ}y~4>p$dMT0UIv(wAT z_{za~JwMM4eS`VA15IhKd;pp}v*YY>rGX~DEnt}U2H3neXrRe+0Sv$}&jrQgmaPG% z+_C{YlQq!f^*>B^F}d}%bsw*KT*S%4Vgvf&4c?}KQJ(c-dd>uzJYr((w^bor~iUJe`nDTJ80j4~hEs)1E4b1H-MOYrQHXtmISpgof8ffx(1=BMv(9|=X zOLBaAZRV03W^7d>x3*!uS>5QF7HER8iykM4_aNOslLu)S57G@VnN*rZB|uPsDY>P! zdBDQvbyWc~J!29OCLrvI-mCZ5?!dFj0XlK?>z z?P3yO5@4$Bm+e1Cm1@2++UAi$gR=&h%G(kW=jUW7&&22Er`=edS)QADc4L`bwC9zv zHP09ZsN;WA*j)FU2AHxn6sYDcA**>)xNa=ZEYB-@O)^c!DPNoOae>-Otk@JU0jC z#`4Va+~k%U%QMS!(^hUQ&n(Z)N4c>~hRSm@O>QiciSoSO?Y!#N-Jo(c*0=rKyo)mP z|<(cLAVp7D7<(cKV z*$y|BXO`#YG2B=tv*EcJ3OAO?Zg_6$!Hs2-5uVrU-1j@X4Q9GQOnHp8uWx(hIaX8H z9MZOFF!TLkswH6wSxdrfd>hL%%kxT$XI{QdgR=&hD%=vX!f!uczdzc?N@ABsTn);m zHzj%U@E(clHVw`iV9G^N15Ejq1K{f|+IqQ=Tz5I6Kt#pNGsvRc9>;ptB=~&8ERjiG#^JAeaQ01enmFOcI*& zdb2*~*akBJO_}QXbG>}OI;=Ik)*33-M!Oel({uD-*yzTD(}x+WYgDc=F)t#l0&KGd z0j8;&80L(NgYy~>U@ntQgR=&h@>5U)O!+CO06LVl{F$5|MoN%PgR=&h#yT4S(^zFA z69-XwFxG%5+MvY0ny8Et*L|ATfP5C${rwA@kF7TiW>O4HIU6;YX)rKV8_%q@Iw284}evL~FI+F)aOW_eyY2D~}nG&pO3sT3_CYnB-AUoU`q z7Px@T6EJvvIQ}I!z(^r*&YPpIrv`Jlzov}M-;DQ5tEmek`Ekhcy8*@=zYAnNHo#O2 z%fC={-x3zaBbViw<@s0T?bLU8IkoOTpRD&^9%r!06E~~);FmKF^j!`o>92fXlqZ=8Fb zx4!n{-1|k;`D*Is&{VySx{M!hCs)V8@iQ(|9W_&?lJ{57Qo*y1q=yS{vzGL}xV+R_ zZdCHNYq{O|ifcL7)rl`T@v30JbmN`DyZ{At{06L%QUnG}F9P#=EL5%`Czuq0*y`!b zcyT*^gV)aXyS(G2UcJ2I#a%Ednv_Z) zMw*ne$wwZii$w_*J1ZnI05K7f0Z@79Ji(+0)Mk@;5Z#nb`kF9J1{ml)BX1|?9pcH+ zq?D1zuM^wX`C@SZf$JN_ynUQNADdzm=mny$Qug(_UtXt4IV1Coyr=*oMMMTnia@CZ zy6&L_db#eKvPq2wx)Ch69lyuyTpqvCY~*~mIe@_RE$5r8)Jf4-Z3fO4p9Apnh38Fy zC1`R)N*O7lvjKP=zaedt$L~rTxV|;bovVw-_Z(<4K&=OSI@w$z{Sm3DMD`YYQ(z>B zzDgw!ioQxE(4_)37f;@4XsYgx8XAdPB75`6<9D0v%_nbbPs%`S2IiT3i3Z>$qOSpy zBJkGY?8^C+3{Eg9BcYs2jYdM*{`({$})%UYW@m{*_=TOIQx^LdjTyR>(OY?7k zLtgb`DVn*1LF)KDdOIpL->6q=zFQwaV7+Z#^G^r!Ci>uNku+swUP^DuSxgj*y6E^< znMz*LFSm$j+}q8ptp6A_s;Jpt75Km_}G zJS3qO%*DY8z7`<$6pIj>U{Z6yf`LnZ8*eu|o3OE9YC9++8<&%eawxa_Qa2`}Iz~8@ ztJaKvsDpG3P&gj(u$ff79XO0Sdu1UFzjMrm;TMKqEa%_u!mX%vD8@jDdJD3d zU{bU_?pPYp^0;FG`3v|220|GqkXnF73gmp(gh;`pjD&J-H5!;L4*#2F+)Q8~l!1at z83?twlrpr3NMHHT4P>NXQbs~0&~2N*Y~+m$gi2pQ1`5#Xp(OR|G}p|omg7++My}&a za%D}V54I;z42mYl!J`pPj)MoNmurB55BJ;2a%s}#+5XO@gHtw8zLa4k$R-7ovPq}} z_8M^vOJ=gyh}2o-;VD@-BL!02pKP!RCS{Y}yG+VJs5HrasDT2#Kn4mXMc^161DAKL zCYThF+FU$&|EejQyy~kvPyz!%1_~xclX@laA&>uX-ude04R2Fv63R${UOqs^OE4*$ zgi0XDK!IMaK>`H_-cA-8IoG9>k#k)hkJ@ovN*U;HwCaNl6i9J#zM&n#qzqi((piv^ z0^E*={Mh92;2#6mVL;(kc}Wu2AfJCMgs+E^%}{jvq{<4xkfZP zIwMFoo+)h57fi}Xs5A+(Nr7HIm?1DvXc`&lqnhbuppR;x9`VZ~8_--n$3xm|avj_@ z7ms7w40LuCbLP;Rb19<90KD>BcY}KV*^tg^w8{Jc-jwUok#RH`U|{bf88tU4nv_bQ zixWzq7pPjS=bxg<83~oh-qbVDPA|7`#O9_x$J_W@w&n zKD~qzOv*^8M1l+y=+zfn*9DU@5GsLQVj(Z^`YUe{7Z*UJh{%9R(WI1%=LScz4U|By zKHf>$q{u)Ie0uc;J4Odi&L-y;n_srkpIht=U*(1p80bsgO{GaFn-u8fQo&0wD4zc` zm&jf|x@9iCDZoo$q&EdkCD5CKrV{8O7D`}XA7S$jwbx%lJL|DlH{So+5Gk0HeT7Ps zAOi(@^>woY7~Q!<2F~l|b#!CijyAG4^}HxDDI@2mJ_kPr;B`C<(k71wMB3MNXk?y9 z6IgGt*ZuE6Im9|jk|qPx_ht8{T(FLuG>}ton(XVljH)!y^D=?+1bsZfvI6V+OU@=a zgr~E8RL}5B#oQYa6HLlLs06w+E=NC%_rkNjqDxT5k!gX9Xf-Grk8}OA_Lt&4eZ?# zTit)j^>&xxw39N@4RBI6Ik!6p)NW{!D}gN7z9x{$;_-61z0xAPxY!S<@`R53JhO|c#Tp$v zPwfsSw#j*(Jsw}2zV>-mTblGFiN3%lLH1QJDQ6NYfzNpVOXOUz$77SzWG`54M*21; zeGNcAFKA9MDVo%lKrfe}1YUm$>1=?JSdA1w9S?SPgFPPhY+u&_&>j*DbZ1Axwpt%2SHzSWnSv!}<d6DY<~XZdJLWUrA7-`1;72aexPlTZc3-`kDQ z3>hc#c*wYs$AiahREod=13ec*jg#^~bT?znr zk>WX$H<%QG8K6EXn#@pjsXFp-{aGBxAm>tKZW9@R+wm}do16ps1MqSjKY;<0qDd(O=V<=`ye^LW&-Le> zDFBR|$p8X~JWdPXMsS=Uz`!+Cz_`__z+BQbP|GVh_WvXjm>kPT%8ej50#II}>E#7k zQ)$u*vZfN)`$%7&mqhO)0cb6`EGKgAB&H$QKzU1Qliq+fl_sI=s{pU#Yy~c!7uR7%n}2ECunB@)Ue1$ub`#7i(Kn}kXr$Up&J^;;&#pP&MR%Uq71 zJljZQe*4tRdrGL>TsERfDVr1-n0rf%y<7thj7}FCITx&yk#oUb_2L!xT*Wpr5GqZA z3>4T`4_vtC2-e8J#beoYGty(3RIkgVznGMfb6sjQ5Xx0~oD(C<)hkNc7N}LIjz7&A znfJAe3cyW7lL3<=P%435H9_U_sgZ%Xd}_)-ucHpzdjHVm8k;f(pf4gaU{XX%*`zr* z40NUH6mu1b-Cb5&kQ)#35SNFMOmq+n7;LZwL``l2t;3uK^RQU*dL(6_V+%0eIzg?FlAD;CNko%lp^fS2Sm5^Tt$Bfq@|VT7cgl+iGVq8hM;j#bd~Eauow-nw0>wl3`M3eG^Jzq1$1|Tsv?x`h;|m_=wz0FvS#Dgg zGv7@BfybF|3_Q+(W8ln&6F^|rW0kg_-Ekb_YBA_=e(2OVB*hsopER0jbIzvM%&lW! zNt;AxgT0hfrjk@U<@TJBxd-5tL&^k`n#q0)tQjbmDzVq{NO^cM z*Zp11%LV}`Fe8aXaQ%M4&^Fw7S;`%?yi>OuaQl^r$M&$-tQ>DFChG zj7bJU)%mxLM$R-!0YozExsi5~yh=JVPu@-+Oste7OPf@ZYw0YN^CVt6T*$msZm*a0 zk_=4cM=hzDDnE$3O3GA{mZ`FZU`f8Fa(kK1rA#Hcn#v7Jk~o#yUN_5BlEA6l_U!*j z?NolZ^|MSRX`RXsF8Q9y?am}mn@N$q=_+T23K?kTgfj3r15^c)oy6<*@(&c}32YZgi-hh&plGD$62rOK0iCF!OrGmEXJOeLwNDo=~8 zraW9oJXLvI*ea5Ls@&W}QXWh|m1I=q_B#7tl2VnQTR_Ugg%nkrRFbLcES03NIzL>< zTUBnH4U)d9+&nd;JX}a-Rc@@zX5zTRnbfEt9A! zU{W&!R`p;QXPPVn^Cn-}0v6oz2Hymenpw1r%#X_DHGW-gG_z;fq{f+I%fNKBN`#qp z%T$)6+$ulTdGc(Pp9jm7sU#2A`Jq#7&E#8~NzF7|XOo(_x(qD+BsCeRD&Y-z3*!*DY)cV0yuXAiTl$9cpIoEgOe&^pd7X5ev_ zF$0%1;n?0Gb=w2J1J8+OG2|$lH;uMJ-NrGOeN{fD#H_& zc)HYxKq&+BaH**T`u-hFCYu}jA|taaHbs-k@P=0&4NWj90>?9=Mg-Nt6vEp{5YdjIQIFW8w;E`TPFbITcd zoMq0w&U|wL1e#`UGr^?jt2P5~UHAH@IadkWCueLMt5t4A@7kTLR0^>>lhswCu|dtW zb(KMEP&2okfu&pq^qJK5Z%yxNz)Jbunc%Lx2SuW}`#5h+y*=0dhT zDVt1`i;+7Y{MkR9Bro6j;X*RLO)51sR5vFziZstZ&COT01Z65eZv{u`q;5RQQgU8h zj`BkGzOz{B`ZI1h1*E)?(y!d$lIyS3Oz`LVcc%Ia$9xQ`Vg{NC0gXJ)|5u&9GZUbJ zdd+l!2IlSiavxx6NR#93`$jXHpph(_Y`0F5LeOTICoJW5XF|d1*`Q`>!3r4^nFN;` zoJWitS@~w-!LpA0d=oS^(-Rs<&puHqHparEVlyaHDYw3jUxCQ;Z*vthZ{H^}I`IzLqVV>XJTqbZ#bU?;tju~PGQ zXGX?a>w}uP8EX*@Y9??rFazewHb9|e43rz3kr^@9Zbs5GZqICtK2gz`1?P?1;c#b) z$8sLjO!inIgBlqjYyYnX?#uvb!;a5m(TN!0{qtA!erX zZ#hrYl10)E(kH0%LuGwD&MCQ>RI*DN*j_UQrGbtRm)EOBSb-WWy}C`r(%uCDY{?2P^XU(_F<2>&%#GWWt;+_lWYc z0rv|l=La)oCTr#!L`92e^&r)K$D9SJwfyAoGa(QlTl3u(b^9yX=escPB1CcgK`o*o77A(n!vYZ6y4}4UDuyT#w7~D z7jluRlG}#z4M0Iec)+A)mQn+=50}^2nHv#!Ghas_QY(=GP_=kcrb57&%UUB=ssSH} zr%Wa3OlwwrAfA$heGWj{HrNfY+`DfoS^e<%^u3rrt?ASECXL_LWPUr#y(KA}%y+SE zuxqeyfRD-e@Dbo6z~}yl*#2?yMGl+%$pKAAmjpe9XOuj{u)Vgp&ZLbU6}w5P zdCs;UsrOGYQ0^_s;bkhbZG&BdeFJ=s^N`_u%n)_V5Pr;*&XVk9#G99lFah2I8Z$2y zJ_39M_z3W+)n z8sL*9)&L*!crEuLWJWKX1PBRqdWt0r1JDPc4*;Jy#2OR4KV)w+*=4fNgd|s91aF^T z2HG{XZ-CR9L2G+d!+W(6veuG83;*hnCHCr24X#OItnEZQ#{Y)gPZGyuz``-8K5jn{ ze&O)T`M&{PGB^qJ;nfP5_+%Isx>#***Y$0Qvy*;c2Rk0rUar z1JK7+N{62E{2w%&T&KmDc?ux`LIQ*Y2xVh!pwG3@STPtTZ4`7HOE5=kLuNT;4N zO$eFu2p<7H0y-qX$EP;<2=Ec$BhZJtJ$(T9Eao=S2}-B#wKR!v})2qYOMf!B7 zW$^KpF&Y!#BcLsTKIWg*i3=;806KZ8g?Ar71p4%%mQkoTG~eygr#CbieR@|k$B;he zf3q=cI`y2HPe`YpGZ}sQ0AfxcePGn-L-$!zcupd-LXfR5R1>ij>DPTm@|O{YHQ zUkB*Z$9x%ld_kPX1o#MOOrTHi9X9~_jJ>0tKZ11Xb>`y?onRE|b!O5T8{?%VeE|9Z z)ag!}K}MhMw2VG|nqF@7d0L=QpQn9g+{W0c6zbhflS2F~tq(vSfI0wu`mAW$7kyy# z@%Lw)dcFEQRwo#pdcEcmN-bCcJ_39M=$N;K4hi((HF13a`t$+qF|U7g>LZ%DT6AJJ zRjBt@ld&iSqmQ@OGzL(ow;>sQSZ3<sQdUcpqM4w(AGUop=O^7|>h^Ui?M&=BlP;ZRp1JMV@ zx$lk9Bp~Ya!AeFS7Pk3TxG?da4Sky$WwVk6L}w-57N zDAe1BgBkScgN%$mFzA@70v`cw72y8ADV)qtfs+6y0X_nKdhX3cp-<1fj6S_fn{+~- z-tT4bVXo@@oB2*Z=xL{r*iN*YXg?8t=8M2hfR_L#0Y0XNz(;_O03QJNe}!@+8$hQV z$p)iO_KIS%@eH643_j*|pfLeD0(=DeuzBkPsQbUNWs4s`r|ysm9`xxB$>@_4OGRa! z4`5?i=Ob$ajhT-D9RWT9^KV?cGI}JyNr01p#!Rk&kU*atS_gxV@0Z~tz(;_NK%eZ& z#Q^I0$3Qw|Umn>J5Hd{yLIQjQ2$=@~9|1lBd<6KI@&F$JJ_39M$z8XzRVM}Ut&pPVy_0qB!+#$fcx`Jfm8K8r{pD4lxUkJD;-_qrda)nanBG=Poe zYH2X&m}h`a3-A%pmH?lpmRu|r%1L7YoMd$B#>^gokU*bYnvL`TG-jRvd<6Ii(8*?> z`DS;Asq_D<8Yp+-y=4*rY)u2Ov)o(GQU2TQND8oPuy24*HD-ym8net`{;fm$cb5Cb z$r!p{mL1*5WS8pn*a# z`uL0hlh*?c^znM285+woqz3rp8Bzm%a{mwIi%4K&F#7Zo@`)3RIuD~7=);+a#`1$^19b9(W&?em@%|sm zV|oLGWN>=WqhkYvWbhH_(@V&~=KAy!lF`R2YN7!86sY_ErgY*ajyZFDxlZ2SHKUKe zXu|l5W&?F#@Og+sDLMjl1o#N>@mI?Qr~IPX0H^$-S-_|6_~h|>1AJr<^4CUu1T-eV zN1%_F<}d(#JbyU;8l+P%M#pmN)QeF@pWe`NyNx1@jrE4+xNJMbPN7ck9h%h1Qmo$} z9c8T(ltR52{f$&1j==hOD5h0_LY{_UJPkKc$J1~z4vt1gfR3Pi>ipAl??6VKJb~ck z38VpyIrbSJ0YU;A6X@gR84N%lFVC96#{tdgEFzo)2nlrZ8V&}akJoU`;NyK2Lb>H? zfKG0?0KMf4&=KgfU{lDG3^ssH0G&L^lqo;MHozx8!!|(2!Nqh)pigffGWv9fZav2F zM<*y6^JiR~a!K6)pIlNmz{iomG$z1DfQ|qk2mRtBz(;^jfx7<2DLZSrl71B5gMGIR zb`ACoGN2cSinYW<>~m;Z?kx9~8L_){-0qIDjz3m1MtAE--JRv$lHsyGz}+_3HP|

k!)A3im6-x8~Lok6MD3b6D)Q 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

QV^Mt19; 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ⅇ&u8ysA7q~cblPq9b2jE4 z-hP~DPCTa-r#ojL2t}uJmT=Z_c5}{fu5uobvl3h$OmQD;x*=(3YUbbLJtxVPD(ZWD z{Lq0(DZ>X^`=q2LXOb_IT-iSx?`s0*pco^J_h(>X5yNlKAIIOqKgloWs|8I3{REQ) z%LOEWab5`oFort`2Mgy4j|p!J-w0)*U{QC`NYQN3e$hRV29L&D;sfzXASKMh3-I@N zh`5V*vUru)DLyB@EB2HaKoZzYGDI>BB!I^xHzgk>Mxr*+naCtI5{HSqWEYe30*~h^ za1~A{I8bOS$VKnZa6tBPNF3;t zqNN9Og`{_=)4=*kAFhKwJUs;Zkj4~1FGd2~usbt}IUK--J3(D@4WNe4nD3bqmJT+k zdaRbL-mFoq8L&C6W$j>{WL;sEu^zJ+Y%K`6qhOJ0#qI_|?hNB||yvAMUTt(<+FUpY59&&h{rt|y-Sq@$ zgJDl?%<0M*#+e0!&%rsuDd61TJcUJ(!^+|Jwq085;~UB=zQJ;1%f{f+w; zaP2#b^AbqqLYMhJHr~ni^LnAV%UxcYI%x^RtuS`eQw*K0g9lstBqmc;9SLhSYWjO# zEO}?S>&VaQyGJnxJy;9%wJ~QfSAk91yy_&s^oez`mmYbrGZ~_1j+L1E|>OGD#118=E&Mr)ulDpNjMGI`s3~r?B(J2079_CC6s|$8xka%rRP+o-ll1LR!j5YpS(xawZD-n5!To!X5j6 zR>Nb}=AgQdDFO?Bfk(kIGG2UC{2Qnfx=Pkd$|V9KlxRn!!m@FKct?au8%w81Pf6cO z<+7TxF0w_k1G1|!y1cG@hJ2U2K>kv$Q`Asc6=M_!6n7PRWpCvaO}QCwNw2-?W1X|83VQq;eR@O$Z1a;Lvil9@(GK|C#)%-und*o zpQ~RGN#gT+I+^6BPmTx-i9R}UKMSM^URRId6 z72X#g1KOZ<_+I=1eh2hGQgIb=ZE+LO0`(CmgBED9TMKktd`-vLi8g>6AOsV#9rbkafNtH(4=~4v=ng( z*D&(_I&w81ucYk0(D9RZlWT(hla7bl*yzx;$8`akG~Nv+Drsp?z~ zmSt1y1kKE!3cfN%*+n^7xl#F48Lyh4I;*;`5~*ve`>N-t*Q-hOUA31cMl(ROT600e z1WU38+Hu-E?K`bm*I74Iw?ubT_fp5yhw5AF$LLq-FX&(D&0dYY=6Dr&z4B5Uq72Op z$%c7`V}>#V(^$pW+L&qFU_4+ft!{i|-F4J9;);rX@z4s*VZQeJ% zKY0iEH1Zkdv)!l2hi?usw=mnx%gw)-Uzx*v(N(GA1S!zvCd<)kZ--K_v|wlDMqM}^ zt&Gf-k+UOnW3hl0usbg=*bh7S6!_d}DInp(q=Ewldkgj#E-)1AtJty&7c9`_0&+s1 zE7JZrPk{@C#^(B=GX_T}8W@`^A-PexHo?C-jiv;Zk)_qCX(Z$OgZXKDAoer)v-qp| zdHmx*?n?v_g7$)B!9>9_!4APOK>C8;W^<$p%ApNwSegF zEgB}8DOxDn1rw`O^icFkMBssVExZSA!)M})@bxgU4&fK^-|@G&Q0ya)5H|w$F)2R@cv14%xg?K8~%{$Fl45T?4M{DW*w4|_~T6J8NgJ7>>B}%q9J=WAyq%2 z!Tt~Cn7v&Yd2UTE^wE6KU_MYPR8>^HRMS+uRd-Y$RCRziIinV9f;Am9(=^*PrJ9!- zK5!?awR5#cwI8%0xB&oA8%`5pIr=@;scx|*Cv$Y$$vcPhS1 z2MbB&f!r>bn-J!IO$ei&2Xocvz@}UQYPBg>{Li!{2XlSU%0szAwCrH6fJ{D^J4X1g z&Y!85@pfetpG=EBLs1F+?Ii6TUeVP<5A?4o(1<-ijE z3UlM5$P16bTjO2uVfY+;2Yw#^4gUyp!T=ji6LC8+7&wd9!gRPO{#}eoWRe<^-jWp9 zU+gdyb^*wsMDkdI5jvO(Er~wFh&WwIEnBMBrX3{~@@zTZ8 zZPFvs0_i;|O(v5C%c{y6%DT#uWfNpeWuy!V*Sm(0*T*>J0>MATNhqiOt0dH6y0fpo zlFTd5pZOJ?SMJI?JFYx$4Jw%KRD1tCkDD4qt0p?-q-(k}{okEOIit(S;jf*rR~QT$1u0$!KYee^mo*P{l{U|!T>e#Bwsuoq% z#&n5U6;m2xsMe_3oNB*S3y2*SyD;`#EVFvO>eH(iRS&7*rr8kCY`4+5w@wqn|8l5M z$EOZC`VdYAk>p$FY40Bqa|nnzIa=XIn%tz_K|Q(a3u*UEMgl!Zk#_E4KNEa^U!@2> z5(vHk*}powP>(V)A(>kHCTR9(Zfj)P7;RVWOzmOqORZFwpmXXt`fmDB`rZ29^(vqf z26#>JI_8BLw6LZvG+Z7~ifyR-KQ?=9Zfy+uCNefs+> z^SS8r$tT`C$-LHl+Wfnj?;GUX)pxpYp6@+hm0u0NWWVKp2jl!o{a*W-{oDEv^IznD z!vCc|9?&FUV!(!gk^oU){lHOyy8`b83WFL1jSbozbT^0*>=)c6ct-Gn;J3li7WB;K zP?6PBbL(mZJ1+0IoO@+F1l#2;$hss~LynxDn}Y?~E<4-_`!8>~y#Mm5E4C|BF7Lg3 z5U#Ge>@-}SczNBGiIA}6@(zS$=lc2nZ~4*ewA?_{eP(VHS!-r)Q~Y1;z7@5~$kavD zl&dZ2B^fVSB-tuCC@GWBfh=xG*a;`BQw*uEw7E1vIzhTnx(U{&4tuadK?<@aO5~=`Itg4+V zO|@9%1h(gaimMJ$*HyQvuI{6rsotVKqQ0trrp7foO)X7=W`t%T_&6v9X`WVFL)%t6 zT)P5B);;Z8Ev^gKwb5nhmg-LEP_IFbvE-H|&KY8&c#~UV3GzzAwS6F?JOGl&kZsOa z=As>jO0lH@B$xJ*8AUrvW)zPD31;!8qCF6WJCS~mQ|a?RFx%6q|`Vi3b6dxKO-Pd<^)n z`(m0zB?*-@khGClB_kv=B&&c%yh8qQIkz3}8)qPRNmk};=kwf1>PWYZw6%eJi#t#;M;=t z0+ldC*ihI(*ax&TO8_c)RCr7HUZ@uNifW3wh_XZ*MTdaIcqF2OJzN#M4j>&<@M-um zAihrnSNje(iJOZP#OdNm;(6jVVwd=`o5&DLjFKpjV|J|}83080bje!aWRFO$0HyIx zA_h*j8cf?nVhk~#*g_m9E)!1(fz&LmBW)?|E*&nNE?px%D7{Jk^)z=P|9e7{(I0XT zR{y8a#4zaCl8od;10`Y^44SI+q(s9vIX)BMAYmav!gA@~r5ebPGP3?;ibLuv7%rG0 zSS8pl$QPUv{3c)tCBg>6k8gujDHq)HS798gohp(cx_0t$5xC_WyFJaHlLKMn9c z05V;K@5Ybdmq2Cq5oe2a050t)PJ%92BHjbA(uZQPL@x;dL2?^O4@nx(c}pbQB_|{| zByS{q!Wak59}R+p9>Dcv64Quf#0KIZagDe`ydy^-1nAc`O7}}INpFIV zjUkiEf@Jk%J>B%(Skh)EFY>=f-#O=#-3^tr+mFO}=EX-4M@Eb7-f~r9ejN!@7mJ6`mw0Jo{;XrsmA5eq%8D zaLO?oEx70;(9R8cngxgQ;=k2*DiO9T&kHTq-}qV!szj}i!qu6F@}f}ox;!}vF>lGg zy2l)cMXo-d>aAydra%i4hPrM6-Wb7rm|bT;bf*^961E3r%w*wW;SS+G;Rz7jF-2n7 zI~s}li6((kX0zxQ*f)5%Hy#Iyncnyad^Wxql+ovahkA{3#oppLacfxK7Qo`RL%bi9 zG}k~m?E#SOAW0obOG$spXvx$XfY)9raZ1j?6ng*~8woH|F(7>HMGON@YCUlX7Ptq* zTY`{Ur1hlTqywaxFuT@C_eqPSFG)<8SH`cDHdy;w(=*XEb)K_jy@bJuY5lDuGm|X+ zkM2wEZOyQzRjN4*87YH@rYB_@hFkk*Wd1bg7pcQGO`Z|L)>fR1-uG zjVN;9qgXGgE$IjX&M_d~JR~UuL3INngIEFDtvduy>J6Jf2WfxlENQ;;OQ74AD$I&{ioOcFVkN-2ixh7ZGG$8;7%o@tP@YoW1hFAk z<*#b1N>PnhtyLXW-BZ0+@v5t}>INV~9Hq`yZ&sgDUs1nN>owt;)|#Q3`I9hnA|2UCPr+A&|+lRHs#s zR0?$y^8AOn zU8X|QTNCbG%X_$Yw)al&Yu=tdu|C~>ruuC3x#Yt!hnjnsN1E4|&zYZ^wZ4sf$M~Wq zHrHjcc^7h~+;jGn@>vd)lSrD-yHrwuoTEuO2{A9sJbOd=?7ii)R~yP_Z!4cYAM%yY z+G;4DwX%Hn`108^%V$r92+HX}8jSxnCpw%&no;T$mnUgRBX9k?BH5cDP}=yKS{@I8 zam;f8N2m~*K|9@4*jJb=oF{Y%i-m86T%h#gfznG8*+es7IXo%4Bzg|091GqAZ;z+q z)A4PfzxV_91j^Y0BGx|Q6d*KKi1Wl3K-u(BtdLkF?O`s@gB5TW@MNbYcVGpS5y3<( z(HJm0>BP7=ATkiKi#S6R6ZeTvAWSkzYrrJ$4Yn!sq+6xOq@~gqKw=mG#?wibE*k?z zCTnCaSs@sbh~#K?f-8YM{a0Q?!FQDTILnn+`x|9GMF}2Xk}{B@073mv!Ir4#x4FKe z2KTdEzx}%l;>|u~WXppTL#xy^*JbJ!>h|ic=-B!=eOvt!kU>-dsp?*@hhBb$&W2fr zora?zf#_x&YIGVu7^|DIObboLCNCKJzj~{D`uNQEISt~|y5`R28Ri4#Kh0jgEqo{Y z9`=3eTi0)?U%uZZzxRH6|5*P-|0N)D_{(1z&^Evx;0m}F@UnVb5_BM_I7k%SFnCsQQ83F=&C@N5Re%JP$W)%#c7igkT%Wi?Bb)S8_u_4N7>o~Wovi1kywDo z+Tn&+X^^qRsr^oxnDt1H8hv;uK!>h6C5V^zM2cqJbZW>RH=S3+|0ea}acc%vMjmQQ z&97SlH3DY_k|4KHfS7N2(Cr{1I5W63xT%G-lv`?s%n2zAF^6^y%?d3IjSTA&wk6Cn zyhHeg@H64si2f0!5lthvMAEA?tTLg>^(rAzZK5_rVbSfQS4Q88u2FSb)%{g1G0S3( z#XO7AS8G~rRJC8L8DbM+*TxpcdQ`7lJ*)cGxay_VnKc^Km{wzdje9j>;wHpxj^oy} z*37B-tY-aM8*9;Px2?Ur_VwBg>a4HBjPD*lGoFlR*6mZ5tZS$@rrtMzq748j!VrIl zgGV0fkO!6!{~cN+Cp%~5h0#HN;ZXhrQB-Kn^Fyhf^LYO)V_7E9lp#AoF$~b7IFwB~ zJW;_02Z8wA^MvjM5)$eu|IO}Z+-_Y)1|Fg|!O@EKit`E&rCC`|nV`%8RZ5Xkt?H&) z0uV2ex~4iwJy)HlE>i!g4%ApRqd}EYrXfH**;6|e2#Pd7J3$S@406ghZOOW~Bl=>>ZLLuCIOL$u-u z>FrKdelNKo7j%*3C8Pp9y$(A0vSQK)^*ByyNQlM%yXuttVP)i>Mbzv(1B#Rn0vYUm zQNU(fg`K@ra=CW1<}S?NvY%y5Nz-WH7HS95Ce!TB8S)t`jZmkK@H*! zU$q+a%C4~k7e&+&-0VlmUjjt zNkr}_&f6#WPmO+``*{}{{RA7-F5Q}G&CEARfRmZD4$ow~Z9q@YY36}XBJzf3 zE*N=K%65MUNku8*T(2)8;2&i}Vs@?(*_BQusbuAb{JVtZ>xO0Ipa5!W1xcF%5PO+) zFQ8#xOI5PEK)+?n3S>;^!k+S}@_hMC=tLhy9CYIf#cst-g%mi4WaT8~c3{v{s;;Uj zs(jTo)hCrv-4wX8nd%+tYwEw$MooRq5Y24OFB(s+L0ethO50yMUb`Bc^gh(eb+vU} zb>noKbVuTJ*L2TyxIP+)rd0hL{XTu6{vAMO8-e;J8*nO5t`^rZ)TEv>9JQ)@6GzE?Zt{>Y1hQPWxa8<*El^UDeVtw; z#D;nyTNA*IO=ZuKp|C106DnwYlZi&f0RbDA0ww%?x-`Wpr%pZ5ia1v(+TY7L8ttg# z6acR6nZ+eTJ+*V>sHN4(bQk`Eq!XQ;;18hzNSxXT%50l-J|d+7*mKGQl`t1ADI< zJ^crO78x7?z&C3HXf=g15n!u}IBUTS+j7dol9hCCEks7EMq>(HXJcP_aS7x~UO#fYQfLf1pDUm*nRHLkJB=~q6=i;DN z8KegpGSj8|2?NTwqMw7}k{o@|?D~#+SuNOB0HQ7iK<**-W$@K1;e>J802Fr&XAXE~ zJ<7SndBG8Kb=+#)-rP|DJ6^@z%`N2K=f380;WqA%uz+jIc|xb~ zSI`QwL^VKXGC{OmbVBr}C<^Ze(%iZDCQ$vo0mWaKxF?7$4uI2*Phz7aPSOD|FtfnC z=%XYIbWusf6k;WD5=4+62wWNq(utwc^`L{gBbCV-0C8fM9h2RaDZte?@VxI)ufxF3 zURd~qK&Rh4ffTd!no0Veu|5#ZSmE#y%>j)?2at;0>^j^`roFW{R-$P48);q?I* zmMeLCz}@X@o{%5H@5vv}M=*>}@t^Z~0uzjwu7asBVh#!}K?RsX9T=na5{?qC0&Vgw z;a@_D$WK%cOwuSLjh&)1zy^F0Y4K_>elh`1<-~8}uW$_}CV^hq#rYvwaM8WVYraDQ z6u5vd$}8B9X4TFMMISpkm}GKq$F!dSY#I0c=AgX<)H;sW37qOI0hc;uP&*ZL(+vJb z{xSY_{xd#Z;0x2UuV6ChJMv*_vcUHZ#p0xZzGDT5_in(9u!vfV2Em+I2Ws3xcfIQ2 zY4}up11QDMfLQMn_&PO;QrF)fpW0rdVh2o?f_ z@57m(`0NQt>P_O4Vgf+Z6F|**PV!PBg$bPu!1pVl(ewjN=2mcY^9m+%3)wK)91tvi zly#F&k#CY0%HPZNin@vf#Wc_$yato4Udk-xD&pvo;sNF`1*`-p<4P8|6zTwq;4Q$cz9&?`r?!@6NRgYSeM+K?5BJ%85#}NK^Fi8k7Nn+Gwy5sHtiZdEF77C_tM(9wM zLr6}Z;gJ1oRs0k@2Q9wr*pxLFCj1|Kjvx>$vIYQca1oqA1q&O48>k7O{5vgtCe(lUm1@QalAkU`2 zkpsZr(pC^-KL)Tun6#sGBs9|r=^GeXHDv{8Tp5U{K(ew0g&rX#6Xl6Zc=X2gP$8#;K>Mg_R7)t~U})KWcp1TK>LV21tydsCH=$oi zUj~ZvhUnGAk_lpF-ORK>ZQ7HaN0l64p;j-8d8B4S$s#%(-ekw5l1VId%&#<bT5E*_SZXk20}Fn%-!o4SMk?1br|sj7FT_eF0XpWZ&3eSY`xGj}sjF>f-zHOKo} zeRui3@wNCR_>J@X)lcm|%KwVLZ@}PyEdgb~;dKL);{L!lftH}2LDPdy1$_*P4qg)c zI@oNfXBlc)X*pu~U|Ro88U zJiE-fxQv|U{+w6loK)t_FC*umoyS~eH1?QFgz`M{{gLgs%Y(e}#+8L}NL!iHRp!hy zpl)wn8Z_~l%b)%AB{iCR&1FC<-?*6Qz*|=kIs2_Ehd_t@N<+WfmyA!<=Awe#u1#47 z@p4=)t|v|daKLe}G|+;D!4lAbJ&~x18lcmjOPnU26G9ODJ$D%&ZCf#I+VaEQ1nRMk{92@J2!s%UDHx|Mp0 z`T!WdNHi_Le3;sUD%(W@E6ZM&gbZ=GT8VzvL?kqk-#2ZdMjm&;c&f4ig3-Vu(2SgS zuI}j6K9>X)ymJv`;xDdfPDP_U&2^|SI=G6cbY+m!F8>b0DW8iH@%#;0N~W1vgGo6< zOk|D#w+5@3xo$Jd2f(v3SSt8=ifAykY|Bbujb+VdEn;n8?Pi?;v%@=J^~(XSm9iLZ z&F%?SzaxRl&0((sI`nLOHRVW}Hr(1kO;-c(55=4aSH2!T7M8 z^O(WGxa2HxK9?!zoSd2dmEEr*KV8`{rT?IWzI_cX(*_Mn816Qv?2`^&4akp*{CSM; z*N?;!t~bD_*?Z@IusTCvb=C`=`{NNC*$VDJvtGE)Mp@_o#8LC=`E2ym1X6~DFP(f& z5zOR@*&KfDH^TE4oE{@#Ml-MKBP?9-iX-}9iY$R0jN7zk|kLn*#>k*32dTtLP5B16RU|l zq7XJyw5&sZZ?b=_{GB{r68N7m)BzC8v0X^mG=EdX@#Tqwt%EGBPb~s7-!Jf&&elvr z2kVH8W4rz{9a(GV6C~L*e+29QNB#LR;1*eKr26wR?=J5hPr^6w!}zuMZNMkyP+({F z^RMz>0)o>(dA}5N112~_FiWr%tj?)^{RlWtZ!o25D@+9sOzVXQ!IbJIP&_P=1i*xG z(6_xsLxJ*HA##YW0!)^ND{((O3Xca0XdpfkfU?W*onTIN0l!%Xe~B~1La|yLD6T1P zA?_+3C>|rWi zg26v)Yo?W%%pAj<4&GJvGS4$_F`qL(G5O$MBLqCFGzY`=!L0GDrC{%y2ZrmHz^BT4 z7K<%s2eVs(qXXGdGgXZAdQdkgs9>V^DAJmY**B&q14*WJNoh=4;5P?)gUCBkPm^8&$~08g zT{d2JL?)GYluwfH0Z*ASMQufI#au;!!cW-=&{DgUrAiObr?gfLQ>{{!tNhgM)S2of z>Z9rpYJY(6q-b_%u4+DMqP0D=bF`O1ml6kX_QSd-x(G~7m6YRAwypL8L!R{$~(lQ?BRa@Pos3vLROz^|nV=YuKe1%N`!VPEPX z$`s{*@Zz~hiHCtz^+0?naQ>IT9kwPOKyL6TcCd)Js}dY6a_%O&|@pC&j_XZ6}`0CM(DW6J1+@16eyiBG8HXCD)K} zL5Y^!aH`}e&j`l3hJxjV+qAk6f==B;U4^EZuU+1jftY)JkZ1o zPjDn$eY+Q$k?CZ^kH?1BdZ|F!f0hEv1(x!jr-1G_gP!)ku0V!iL{|kw-vd>*|;oK4tmX)h32LyF}eS&_1evSUV-pi|+ z*L<(rpl3}0bkjb=3qvGWSB*C=HC{5xO<|@?(_R4gMR+&yUJ6q(%4f9CHkgn+bBwu< zd7*i?`HtDox0^5WJ?8tw*T=7|-zL9jej@)M|K|RK{ipix_W#Y_E1+pWDk!A(gG!27 zC$L&z$H4T!*@5Q+KLvUR)d}hsG(TutP+5>JxLWYY;BA1_ky&b5hFIoWj#)lg>V=F7 zK_Q1i{s>xpapwN{7|p8C8B^IW%-XGL?-Pm*^OhD$ebBBTYb%Zx~JQC)qG?ryRjwf z3OH_g54#`bwxt&Aegk0dTglqPI>IVs-C(@{#Sy{w2d^KEz}Lwz@F_J9i0^#Z_b4Ir zN4DPW=b{C84;#!G%bCqt%Gm)*qsyF!ZeJ%#kj=(&8*@8=FYyuF$=vzeja-s@l3N1q zpx%K)C~sZ_1Ac7?EpB?fFX<}3(VHo<8X6B8M0q#)?1@Tt|AvhKpPeb-Z7jjiH^I>S z=qAeL7&-I)4VlM}YBxhMAN~0lYA0Z7!BtH>c-86)KDEX(moXjS;o}DLA^1|`u=K1T zsB>#p5BOEQS>S4DliQ!xC15+AftL@d`zP^QvU{;p*kj-a@fLx?%n6Qa?t+&W9!JhG zgNVH$rvo^t$>7*I2*m8UoRgd)&I71Bm#gB2a%<4Jtx3((8=pKypY2{L&>>}wf|Rdr zjOC|;r%%em$AFZ+$>fw*H(oG)RH0SD^U<#PrCYM*@ectdPZ#LHG^#WBrEq}wS}F_| zb`+)yr-HWYi133@CyEqx11P&obQf%NlpwZlgD2o)@wIpn2>t}(P;pbS6-2vx#HT>6 z#gl{s_BvHES+W%Ti(isFk}wE4QJrW_3?ya%L4E+dtC_&kx05tWx`iPvlcK^!rE2oU zf>JBrbK1so`(`wDX{i#mSyNhKw(l&rFD;)ow|x3EL%Drf`LuQA_ATX8*BQ#ELrzH9 zR&L*ocC9Tnp)2QasL7^lOS{6V_Wd!sHgZ1d)C?+J1PRo#tbk%ItC6k>cOvK zEdYy`OCYcI0=8oiI8fgvE*1MpI)Q_@ML>Pt0vB-<%RLf&Vo*zc0N@h(!&1Lgx*Kpd ze*%cS50F6=<9!i?zdA4lY%CumUj!hN-{qbP3zz~<1)p7K6f9*hL)l$Pj%!}JU5MJ- z=itZ?bVK`MVx?uM~?_C1W% z!SFi)+2D_7C-{832tJ4~n3Iv<467xp8>>Gs##4bN-3^}pF0g)Qz5RcvdkgR=()5dW zW9{yp?sRutJ8mRMSR}Z+2M7=n2<{Nv9g+_xKya5(xclHRSgax4X(Wa-402C3GB87C zX7AqpKX;zp%tF%4QuS4R^1kQ%4$~2#jV`Q8crTmb%^V5}(IQqBYZvPT>oO|`D5YmC z7M{^S5P6!gJAgtn7!;ZX>=nodIE1J47Tb!3paVz7amRBShc~plJ!cs8;(dMx#`nI1 zI*tXyw14I%Ahd~qy6sr7w8E$Nm^?lG9>(!kCniI~@J2*r9HSAVElL4%8JW1=gJ@u1 zWfa=DJ5r{W>BEd;Het2}ho~QOG9X~f@Mi7--!KnX{u&S|Auv*OE4vD-9;>U3Z#aXM zfq7{cC_Lv_*8u}7WxZvwu`73HhvN=30+lG4Jw$-lwA%>Kc1|xHklH<&G_}u_68jH% zt*K%5xoJ*}&r?nrGqVpY!oIjT4B+H-XdNfis0e`Wf?TQ!RzLMNaZBD@dX-ad>YK zHcoEslhlG-jmb+O%jLNmmlpIl3@5kJq??mFhG2OaJirKDIE@k5P3@ECKB@SU4e6x2 z3B&sY$T z*Z_zhxN@E!e6MYIeR-338Gs<3TqMLS~~_;3tR!Ms5J4 ziYo>CA=EE-5b%W5tou26EavCsg(O&V6r|VVoDC#1JJ*T(4WpB?dy=!j=?9Y#Dp?kL z?{spKQ-I?mw*>vtAYKEsen;}A;BWMv=Z(KjPjnRL;_q@D{Ux2i3+1Aow!g_eBAu}U-qg1hyajdrM{owmOO{r* z)o(7K(e+634hJWlG*ntk+Dw{?!xNEigN(I6`U}d~Zn7|0Bbe95$rj7j%l6q2An#?) z@<=)L^WEG69wT`OFnztr)7iO7a;PYGQ}uR9iG31J9RgBz(BM9Uzf|uA0+cf_VF2dl zl!T<@ffapd1pYA9$(oC}=O6DG{{K0wnL$O=djC8OYioWA4z^49>yb}G15TL&Df;tm z1(O83ZOVi90x^^a;lem!XP9rM3)f+{b6@x`5l7@9iW4P>CZZ^}4-({j9CmqPg^h63 z1zyCd=+hs86Y+)E1un!W@WNV4x`XF3A06F8lB<#;$y*6as*RHdVrN$yKEz(qG0-Eg zljkaaD_8&YHZ%G|Ey8n99BeKt4oh6g?L~CPMg6O zKM|Ixt?*^jC@P867qLE_(KK2C71AA)@!m*T7^YAlEqY-myF|7VKB=3s$8j<{xfmm5 zl-I&Y^+y4N$hXN4+Q`RDg+WnC(MmB8P`|ZsN#0PDqr2pzj8w*Bi#rrb>g~$Q)Z+Si z8w7tid&RWNyT@a6a4oW+P;r#u%7p7aXb1Z&<9u*nwjcVObZ3oL>gs7r~E6lMTeeNJ#+P%iiY`9Lgu z!`-p+(d*t>p+(5R#M@BR1Jmw8=**7WAQP`K?fQwU!I9q=Z|XAf3Goy0ujq-W0U?Wo z(2#ZvFO+OT$@V&qU-ofmig;tftuAd1SmQ+W3^z(o;4P&!23kzG4RMf}h=U9TrNILb zm(=nI$gG>mdqFRsF5e};PK}$HC+7d*%sF>)-pQD6&zwmH1Ti3S;nSJ4VPdbO)2nQV zgx`*(0497QSm1gmX zS^4wB8>OZW=#H%(wy!o~x3POd&y*yXz`Ot9C!Yp0VL*3Ewl+VV@sCy81C0N%}w@|l9 zSFWq1pP;{_XB+Ap#u)Y(@<4jjx>j-R<~j{h_w%l0u5!0nx5NUY@vDZ;Aiq?r^HP2zJDpP-^=^&R9)x~&&$papfWD!*B1VxM*bAG#hQ2Y%$$H%aSj>-+xdr(@0Smu zw-W?iZ3ROCs@V)s&3(Ltd~Anl8&@Y`FX2S&ppHTN_6QA5r6>xb@4;wXX8{v`RrCa1 zO)iLdv4H<{6AuF*as_7QVl>d4BvP!%{(us+m!v>Kn2t4hzvPT0zk%ebRcvT6pTpie$`^lz(m2wm|W~;1R<|tRngOQ)tTmJj{1BLoS z1Rtr-`OL3}xF~M^=9nK89SlCUKW&?_eJid_ka6~K*P=k7q@Ho}$N#f7Rj0#?ko`pu z^%cMkSs2J*P=^```=FjVAL$U!F^rW(4d6nbg-pd$qHCf&Oqw1LGd4mIa0;f&ZIB$i z7yC*Y0dq44Ot2GZLVtu0PYM18eSnz+w#^9$8y&IVts<)@YlqF$5UBQc$}V6veIpaf zU17j$R9)T^($MkpIr4P?ON=BN5gBI!RUPeWFX$lLDQuv@Q$z6pjA35LHY z#gK6W^Q5{zFV|3YN9M8F|EPERLUIw6qo!x`XZlD(SHlvp9ey^vHblE7xQ>SD$mllA z?Jxvs;qKkt*ScSJFL!5nbn#g2anQpauJ&=B`#rCCioF_o9rluYC!^c**jwt;(Pxg& zTAw@U^fdCF=DWrBxv!gF7r#S(68~`jCjP_xm-z4Xf9-Dw=oBy!4W6q3WdUMi-Rj11 z(5D?T-Z44``USQO92B@K@J`^9z?wmmf>s4tg2cf+gV&-*D-4MWnFW8gCbW6zfY9{N zYoVI3hGA>M?uXH}VS78mI`N5^<4<&J_OnQ8qg>fum;4mn?~2A0%+H`~wcQ$n7B-T81h3ilkh^ zL3hGvbJR|fE=3>rj`XS284R7~IJ@?fjgid+i}Wno6mNhvH_EF4Y@S*jYGnd`#18pc z$n45d@7F5A6x9^n72_3ikkN5Y@kGH?dP4@$1aI;@m`g7!bCge&ER~O{x~eB65%jxU zm9%%s^&)toIm9p^k405!liyAtO->q^gu*Fa=ijHpwt&-?@btPOV&hWz0WddPK%X=#Cy)F^C$>9f&ooQ))4H75ce zvI6b;d#GRtz$x=XGb%+q9D1?+P!hcWc25tAQZG#1+aTPxV7XOGBVfL22XNASFwRe) z^7Ibaq^j^u4+p<~5$aBRW!F)xcrTNoSW!dX62*!sacJ$(XW;X4ll(0*XElm&MMFh9 z#R#OtZcv<5v{~ODoi8Il zP02qis)IdvPvrXGK|89tc7-+@+xtANgG-=GJRID^T$aH2d&%V&c(?;}Ep&r)%X9~H zCb)hL`iA;c{Zjo7{YCv#J>L)paLP0Tsg{(RNrGjgfxNqXB<9tF^2>52QX>W`)+x?l zCRHgzm93P0@LFwD-cyzm(;)y>eY zhQ~W!SEBRKH`RC76a6v$bG_COW9Whd-WtPI@G^9+k*>{Lhq^9s-S1l9Ds-ctfTmR5 zjl9mXZwEhQP_8pQ_;3He9&!ppp2+)0jrQwKMbze(bR*(~_7^a^8o8vp%mk(Dfr~=t zrW>L=3nFqaBqvLgWFuUyC4#^bDxhB^dp?0 z+rU;Z9y_?F9$ubpJm+|x^vw74@=Ep^?lsA4wbu@>Ltf9lyuI6bPxn3w_0C6cgHNUE zI6aT_nc$NNh22N^4Z?jBeHZvH_09El_Ve=V=r<4g9hQF;|5X3E{#X3D0ks3h1WXTD z8L&T~G{9g?01?sFPs-pG93UNacn9@wpgkzdfFC8!9#zYnH@O1x+9l7Oy89-#nMVGN z!`~+9+D!|zNK;*2Ak|5h=W8ebVGD{hew(W!^H)8Uf9gT)Xi>s}tZL=4QWui>A(!>d zk8JI!4m$e(P@dQGpLGuZSha}a@1mayx?bG@!n*mAjgsT2Ry&|%83%)1x@~=aFExPX z)dk0@>9QRVojwM3IT}FtG@I4#fm{I3SSRegRw@oD@)REw5@m?8nQ|bC(Oa?eda4v+ z*ELKvN0o&+<2p6S0a1M8_>zwF35fjL6=WEVo&JG$c zO?@mHV>N4miFl-8YXia9>8YKe-KxE!HETa;^)8hWsFvok)`g&NI+M&fo6Dej?a3V_ z<`}>BH0UO(ocqJCPa)eaLncRJx3f_5# zyV7*hXe65ERh{ZOb#O{@8tgRQX^zukr>#zBY+T~$3oyZ2}PUb=8X{eVAm=Doxw_|ZX z3k#~S8bDgmjWqy9iun$#H7qh~aeg!E{HWY}j$gd{pUaD6`MBK9qTkmLsW;2EEk)=vn45*Fa-^7U_l0AqL>H)Q~+? zgQBAyGL_~-&9Mz?j*HM&KLVb>9+^r(kOFjsrF|HClFigk+dIy)bJ>qzZDs*Z;DubR zI-HiAWO#w++HT8J zNp6o2I}ap>HHV6PEOS1zblaiXp|y0+m`;E&c*Dxl5@!AWtT7Ngtb@4YB(Cuns8y6~ zA7}|`+Z1K}*%Kh_$cBC8)Ac!Vl(@byNF7?hz~7%Ufis`8hO?J*mh&@A{2w_22Z-s& z&SCi-N!_?yF4d@7ZmmBgYvoVL??N8c%GFoAN!?pJmsz2zt^{K{dKI;k0 zJ1n*ol9p(My(L0NHx_|?1cH|R?9+&Qv$CHa*Y)g`yGCDO(%+8xB+z`e! z(Lx)-&p@O53a|{XZ333+2=?hF7>gvtErQeV8NIgojB3GXKUuicUU-12nw0Cu{`P{u z?8td!;DFo(f8$ySuC)@u{@RmdM&u5lE)C2LcCx!;(@j%zw-q@5UJ6Ss%q(nVM^dho zsYqGXXRM6pxgruwn=ZqzV1vM03tMK@)rBmlZ4fcX*q9wqjz zwByCVht#hg1yY2Hxea#PS!=zaan%zVUMf0D6u8QU5po^#djcoCT*hLlSNtbHC?JS$NkWbFEDG2}Vx0Io7K_{Ezool~t z$8TsH2NQnRC;)#F(46moM9S(HxKg0v43_=bAUfnnXOMac1zXb^A@PO+shMNzvG)}3 z1o^y*qz6PTn^3`blp3TRrL$22z9H3sET1e}Aloi`h*D7ym}}|s964LzqiC)esUV8| zu*SSqsFB_}S$SMph`O$ysxA^-7pwNEE~}i>9_qU4q3V_DOX^?M?#`8wuc^6(i|ZksZ+P$BuN}tASB843Zy@{ zFZj31XsSRnQr-=p?^M5_F4cHYK@i*S4y~qb{zo_FCg%*|cony(HMDqRvHY&J1sdMQ z@+*M3DUQ|1W^WqDn#0P3wdoin-p^rel0k(RiYUVl$Wu*YFS3c8Y5C|Ywv&wr6oN3z z)|^z%Q~)@#IJ-H=u_T!}PdOhrQm!jE959YH+}@yz%;qlR?&6;3-f@7&j9gt8A}YujV+oq}n!)VsTp7JGlv7i>V~gnD_WAlR|O%etn{AUD!*SvssjHL(Zkfj!U+ zWLNHEUT2nL=fg*8r9Wb0n_|@+W`pGHVV!}F`#!4#tF9Vhqm_V48HioQ0-$nsK}>lC ztL{U{3wiKtRRQj(EvE-(AZHS10oH>poMW6zoC3}ZjssW5_2NcyYjE3h`*YLmx$~*_ zV+%M;d16Y+gRzV24ov+<$eu{-rxuJ?HB1~xTT5vb1unB&x1{cgeFr6^BvPZM7RV~R zMlwofwu#lckaF0<$=EApj{nhIMKD(lp&npnv#ZGZ^zB@=H}WCnuz;ScN}y*jh+{9#$a9k;**)B>yWnE)Sm zmTC$Q+5;g6zJV`}f{)0yzmJ@aMAb7VxPRYYPst zDtZ$CuSe-}$V2Ml9(t5o3VI_AWwBrr_7|4~R>V+>P;Boa94E{|%j}-;1$0PSQ6R># zooEzDFPSK}--Z>y9^KD4SOF%AmqTT6L7b0ZJb}d7rb+1m_1Ik7ocRC^&p@2+8rk9@ zMnX2W5oPxqXm|c96=FoAtIL|;u$O{z>@0+IWCLV*OZEiycPV7x>@3K16uDKqEkP z^_y3U#8CxmYWe8`2`CP6=JgJw+k)H@65QX~{`ZOW_t%%FWXQ~;9APkxrh(z3Ajc%{-|VmNPGuG}4f4Ir0B z6s{!vk%a>4enMe)Hs|hyFQGsrGP&?4@s}#&r|5FSo%N*iyuuL5Ii)a}VV6r17UY)w z>DA&ht0w2YBs*GH{Q_WDFQr%Ut)s@uU)U(u?>T7Cy=P+R8vAp7p@2QE`^wBKPY_NGy zy#bQ%shy*xv^TVT)PuXY3eI%dSyfwjdjS6Swzkj;KB75HAhCp zE))?*>l&;NP zhal;j!4`7U;_Eo&ymqQ0(d9H5B&X*Iw@4UA;GZ zGklVLvV5#Q65n{=;lA0vg}%P%NU!oc@As=;2(apFuzPPEFeBh#Kt623(Z&?xGUGL) zD6o3qs6Z<4U0_VDpw2;AL8c&w;Gp1+!PA0|20sqgg|rV@9db2<6WSg55xH3bpXRW6V8sP6p;vK_^k+GWCP>?QW@(C2N`}iI0S>EBZs#Zx=?Xv3(wl| zt{%OX0goy&XRl2mrtO9D--e6a+fby{ba=d*75sg?!||` zn~gebmy<4U!B6*- zre0<_$qX?&lF|^ffVvuDX4#47;{)R^XVZV?@}4V`u1qBNL(QQ-{sAJi2RiO9P;kM;YLwVt6Ta`rvL5aN;8n8UuE{HZm zHi7DLCM*#rAUOB{!9kF?of9PVOT@N2mz`-I!O{Nd-I8$4J1V#hc5{fG&a5Q32O<(4 zX7-?%JhO&D9b0SOsvu)$6(oE!k=u;8>8iz~c@OjMw0W???S&EUXSl@P!8zr@tP1O2 z0?LS^P|{t>+<-FTB`|S{0i$q60DUB@2CD;zIKx;|!F@ae+g~mYG_*C<8HL0MSX0}; z^fuOJ%B92Se`4RWnf~ZfsV6521+^CNh|#v(49<4W5eDZvr!kt?y&IU@QxAHY0~nmo zrXu>guI{}q z?CglTs0|Ta$>Wif#3B_vaf6NLWYv-#cPy33SYLo~b_@rb!8{jq-cp)|wd1OpmBGNA z|01wlMzjo+V`?;eC9|`V{F$$mcDeAM4<%i$=4JhGBZ5HpZ%;O+&{AqpJ+mG;tpLN) zFcc-iPAUT_&<~)SHE=GKK&9X=stUwSKhYF;OxFN`k_VS56C$%XxR!c}CnGuc9CU&# zSY|3gW|joi{ydv{I~VdmhExxsSwrbithYN6qIMN}g;Jb+bg~G9<#q*naJp=%Y+D?5 z$=7iJD#1dm!BSjB-dvu99nu1bRF25+$e+piC`805+9-OUQL+NI)az)cmn+yxS7jx% zR|irJ8kqMAxHPWl%gN?lJM*R3ACC{l@T-v9K)d{KuKvRylFp6Ia_UNZvpdV~;g3fW zu#r!DkB!bt$ht*%Y+EwB!Pqwk#=f1*Q#f%yW%5`$)O+J>z@{OrNr2Ms#N&AroD@g4 z$i_*j!)^=5+z>pPTi6Ft!_R?V`~yzgT8=+z_;oq$P)>lGkF(ZRPPoB&z$t@YT*TFJ z1G#bB#@w#lA>1k4bW~!GIf7SertT~@S7R!XwB3^y;UxDTJQR+`;fCtNQxX#T68S3g zBxUP_{;8?MlZ^2RDP0p2yMHzwrBd@(nFAgF>~e`=!_#r(W4EIIq>H2VAu0C(Z|VDo zm%_svZuch-A-$!@iYcd@hSQIp4u<8Hkav$0&x2m^I6TvBD zi&Pz1In$+!r3a-?m7SHNmFYMwUxdcxO)Vu`rNe=_Hn3t{RijbQ+6=VVb*yr~ zs8onJt%Gg%IQ0tk5nwJ~s#(t7pWAHnu>n7fEqFOHPQx_RgAr!bbicpbvh9kg{(&Wk?9SX?!1-p*{IYX!*rQ!a;Ox#$I|e`L zLm+wtXstwnZrz=g2CM!y)?wBq7@=t!unWjwby2;V0Qcw~_6Z<)3fXLqGsl-x740Ir zrLc)}2=#3f=NXnD5!VAJzee0dJY*BO3%IMfd%0(@7!@L@lnv^1B)HRUcqzOwyjfsR zA9Cbf;+d(}GRtv&HB}xXuXMYTmyb=i;m7bQ;uF?t8br3?ANX0;5Hk5ToL0}=oc!y<$2y~zY z(DCvfhy-7PNQYA70k}buRR2`qr79Ul+{oMfWO)TM56v+u&z>OD^Dx) zlrL~PbW_D4@1d`1x@s#5un$%LQcKjq>IUjwNOPM9BHl4|j{1dKj=nIt&$CF0$id=B zv0s~Oilm8#KaR~jwnawTGA$QkDjXo&3J2C0$P^8S{YO&YT-LV^LULStoduE0S4fx{Dm0!_~^iNx-;SwkBCZ03@<*XBXQD947X_%-hA3b~~c$$n$jQZC2L z_Zk21rY;Io*BD2;ViMB95=xq!&N)uko>G2y(kpgYWLQcXcvyC)T}09SBTEDoTYR}}H0Gph_RdNHk)wo@_LopLChG{+zek~^aTGau) z?g_Wn99|}GJJ8o=5K)x4=t{7vcSBtCG+1nR0|%djj5{ZRvmjc~7mX(?U6(XWpsUM2e=$2Vk$m{f{&9d2vW`A$j&?r zaJ{2kD-S`zeW-l3d?zdob_!o$PuhWeeL!JR=#@>ePgsTHZ=TXY<*EvYaBs9~Inu1I zsa~t(>M(V0^e@lHnXWsiyQ^dBje0W9(=v)` zRK?O#LfF0*-uGi<*Uq9Qdm3Aw#8os74Yh!8yMlQWDa_;!G`X zVp+h&yNyS<+jl)P>@?EIv6|UQ4l}}+lEs~{)u1^vzDQy24J!Oj*zDN|zwQS=(RPT} zOTeLt{mh{$Lz+MrBnhlX43tHpLhNC4&@DGf%cT-oh^#H<(RH%ZvindhYvoO0*UgsS zmOqEP#2-f8VTy%{O^O^_=P@2_j)lsj2%~4KLRHC5Sor{iuHMZeC){zC`$RX+!lSNs zv8>~Z(oGxi-)z&yDW(lOOdH3N`vWX)q*W%C!B(jj0hy9%*@Dy(izi7 zFZQyC*AO#^SJ<6othM$d#UP9Qy%QZ>x9JEr!zvNkF&cmExzq9(zlZhV(QifiwWjJg zOzT}rxujWK?9w{wGxVqRuk~sq{B%TG+D`_vp%RL@w_J5@4c(^0PVyXb#OjDg+vxtp zo$1lYV~)pFAjDjO5Zmlo;#u1(-7C*awO*k z+JbYu#P5Wkqkm=p1pl!xlsxd41;hn(4p>+t;AFs?0B2*Qv88dMaVrcZZ;io$9Rddg z9!5Z-OHeBqN-hO)gX4pT2WJI83ib#|3dssN8B!eL8QLjyR_OImewb}=DGaVk|0hFA z1!r0Y0q^@CjO5N`pNHQ4Zwx&V&#|~sS*t7)B~$u1J;c`|d_Bh36RM9>@#`UE#~zF6 z+iCvmx$vH58M$!AvXNA!EP}LYVEJxko@CwtMDt&0#Cl^@Y|ZKefaYBEnD?U%n+wv* zN0tz2B@M71q}n>nn{7Sj0!V)(kp2V#rP7Ae3zdqgoJHu(9O2x-aquJXVOp*)R)=Ob zNAFbbBJO7HDFi;0V3)23!sOq)u8wHd%-eg;VrG!-M=a@_tm31Go~+waJaG$&J!(m3 zKiNC3c;d1rd&g2ok6KnZl5s^PQ!BnS)2B(k5Ea zbrhnt2dtMYC*X%7k-^dxoSsPlFYRO>Vbg&XERKrf!->F3*NM{?Ssb%DE1<49#mVQq z)%jsj)j%J0 z$A>?sn&wzM5i={eBY0xN33~!3+yV8tG*nrR3C)Prqy3n4Ep7s4&^4knq6Y}#5n_j0 z3w&8RxqcDo!B#O}5`eyPTgebmWoba!b##_LVxjW|+pD>BES9;w(lc1-o+G2h-DZyO zfJW(zKMjh|jzqk-DAFzn%vjC1AXbE6 zMeBv?*9;idFAB?nj&T>&uqB<$5v|19c3o5|`c_+GRRxQ*p7;+al zv|`|kO~6rN3J!)l(Ivi&N`_S7j@nl%oFmfF`9BA@jw6hgHIyB2f=IKub*>}ZTWK4a z{eu5$IM(B(M~iJ%LHBRxGc5ka4yzT*$b`tE?6eC`x1GwI-aBy_&J15h1fv0?3uB;7 zjk}Do9%|gvjN6Qd(0&S`aIFS%Svyb^2cl?8n5&@vJj1kLqWFjcg$v6MC5l?8jM0VL z@z{cHV;y3hW!+#EU>bY}v5f>E^I&!rc0+K4lh`BJ)38Ba%K)+sLRXToth^GbrZ4iP zG|@%voPJHo9!Tf7wNlM~2pDg!@7E%A?+#7de%=rtOt zT^W+ql0Cqv+=ln}SER|Sq>(na_8{q0X%^~fHxWAhS}KzTfOylN2`Z6?>u&!ERDd&VbO{zXe-{&2rrCnOIVY-o~{Ff<{_P{B`| zC&4#H3aS*zsb&F1r>_y?#bNbo$ylSqSg|oXo{WXTKIE*Q3NO_f^2wFc^@T}XjN;~ zMAb^w6$oYwP}om~p6#$YA635q=XmE1&eKryyY0-;1ZsL~7HamP<^Dz!=A>;3YYtVd zR#8hax!tx%YdAR0bYPX~z)sVFaSz7MFddj^I&jc*V7=|vYfJ~SsoAZHyzI#RHbshv z1B*=uwwewsqVjo2&t>r>X}ttORLPQ~{sLWL#MtH4X=-4RZe4c-@l zco+f1L!nP&-%J3Yb?ES}@q36Uj5_`U{15os2lxgg!jR%3mO!3 zGe{fUJ$PR*Bg7-5e#m0TF?~b(BKJZRRwHb8m~VK)@LrH&9u0qk2lq8>&`q9db|qL)SQjD8gzTd8fO^_5;$ii?>Mb2i2ywp#4C z*psmzV`D3~t30yu-pa+5-KzAdvabs1pHk#S)$3hU)$8vKBK-$Esr7SA4A0^8_#fx3G_5U-gmgjJ;wj&LHflwm^k=i}Q%X#U3#hvh7}I7VPBSLj{+| zb49XQMGpoX zEo@~Nn5-g!chKh34I6sH3* zIE_Xpt&(K}UF_vlJYP?*@r>$zf_{dZ+7j+)1lS!!k|^NVX^NBSZp<4FSeR=ffp~^| zv;3HikLs_e47c=b#ZDCT9w`*cSd?cMBAldL>5NW73+P1VtJa_>TZ(`Z9|ZCCQqM)V zJQrmGx?A4dc_>Uedz{ZZTOi=kXd*OCHL3BMX)t%5(A?8J)iAVbZIrgPc7S$@c8&J9 z_Bt$o2A2qzw(#n#aXIf|MJ_h&_e;^uMic$4u0ZFYcSSsJPyHOd?bcJc^&!%4?>d!s zyNF}=+il-|2kpkpJ4K@JzU4^2{YXm4{5E?nVK%#Y& zIm`Zy#OE8bUr`hPH;^FnVN||^B*BkzKSeU6jz8nc$N93|OJ=Q(LV?I-xk-v~`wg>rT_wDW)wuOk2kqNYmFvu7n?8m9Ec! zRbI?=}NE{EKeyK^5=qKFz(*y^6;|k0%~3p2?ouJ*}P!uUM}ZUTeKdkdfZO zdzbff?{J@bwt3#icRtt>aefoQmU!Y9zH~Kxk}e+-uYa#s_u_ zoE>;E&@RXqv9=R}_61pk_`#KfljDOkf^TEmuO8APWK_takP9L2LcBw3hmH%~9eOcT z3MKyVusvZ#VIJX$;UxTG_|x#35wj!iM5rUHM)r(c6KRgrMx{pWkD`1y)-2aQ8-A+5 z-@3Y@@mCW!zExkr!=WZO1Ff#)p3xdf`beyL>W1zq+a=3JBmF2a*m0uh4@s_bZ1T~;S#YRqP>h2Wgt z2bY5?u4en<=p4`P4Cw1j_EJpnhk<;}MUnU|n+dYEFMLq#kohH_&>R>Q~!pZ%>_WZ8bWIq0CMih=2UHdCnhU9rqjT zE^ZOpY4RLvELFdu^{O4!d9?M49jVpDI`S`Xz`{>AfJ$?K6U@l03|~}F=1}G&oCS`+ z5mgSan+k~E%DD1&Ad8P=t+Y|a&A4(qwvg?IW+g4%=*OPLUT(Yc%Qo2Ud!T()96z9a zo1#rH0{!s}^vCzX0d|vfpYw|2$dy8k9s$HzTW&IUICmCJcKJmf!ry=|aDGcre?tJe0 zuWy1sT0UUy|KS}_k85krS}jC%7?h3J(RH{WHV5kKMq1XT{x1-1F#Qj zFCP#upD165&CqH2P5FHQWY}n4M*s!YUNJzC26fJ1xD(8Z_aKVU)uSZk2qjVOL7q-A zh#~@&r>ZKBSwmFmDzfU5RZ1-{wobF7;x=2m@r6b8GEKLH# zYqxFCSSkg)Ue#6YR0FZE-TB+F1=&;eFIg}1Djv3O^kmxWKOeh$c4W#2tC-yFkjEum zURWKe$ser8?8xFiR#|qthX)o}fPHqKpBEtsi0=XobN|98GvCXH}r87BV8D&jAPYRK}INgQ3?4CznIk7zG1RBBo9{?2^{~lq>i!bqXmZ_}vorb@ue5m$3!^`D=KaW%q^_o+baW?q}_NNJ025<_R zVyzwptK?eNX^0x0u^900`?0HI=aOVIQEz5nz>d|<=3bBBG~o2+j7DK1lXCzz>JJqE&nh&z@$kGqC@92TQrxqO}*nmn~ppcn%Beg1sdF(UJSVbC{AwyG_bWI5p$tyKR*(s=iub3jMmD4s6%aSn zKa$YM*@KBdhbWdVfE_P_T&zeW$MV}8w!rP;r(!qA#JV92aEs(Js;~^q-(&F99Fm%0 z8Vi9utOxRB7J*@0EOV7Nl&1jEmIWOn-IXz*T-j4GL9rT$#bR`1>OhpgLV1u0GKdsQ zDRty^@fd+~x@qk!Q&zTV?G#hi4%1q+u(P(4x-bSXrh(Cs2}%bABE&uP1zsWa!wuPj(KrP)V|GMu zU=ZjR)6p7Thv1KMXpZJGi)`ltHiSbuoQfjgR&9daaWdjgry%Y$lL0^OQPx$ed$aO* zr>}OL`Of4oo&sc5TXQI8^u?FN<73G>b5t=aFjf3_W~H(P;E} zvPHj$Lcl?thTP7xn7t+7pLUhZLXx8uJEl(P=WKu)tPpjgXb}JVVWxf!^+E)InyUR=y3<~=0}YS@zEfdZ0xvxLLUHf=g$+O$36bcvR9*^Cc7 z+E83fW-l$~lcyI-)5*Zn|saQ<<-7E>995)ret$0}1`mG@Qn&MTi$Lj_a=LX(! z@Sa~INizaz3u*l2$VR=wH}iP{S3wjQ&%Fg}(W-a@XhQ&EE;_(ckR_zB48B5}B3KkJ z>V@6R3f#cUC;`2N1*;}DpbHU4bPUajQZz8V(3xlib?;PUhaZE)?v+G|EogmdGE=%7 z-AiP}%}gsss9WW)({)PM4DNj0&HMEzT>*{*HLk<6t`I!5jjL$4cf7oLK25 zx1)_66OI!~8&)E+hGs$2y&v-0hqmxt+Cg2-7D>|w!MmC49kw%@iCvD8tPpWDew@mX z*`|P5vHrp;giAVnPRyg%b99W%Hm=vx}C{vTNRd{f&^eAULR(P#Ds;nRbv+)KT73YXW zk|0St;3d~%Xw(3%&ygOG-j#lkDxuczE8B*a(66!v^6Bz3*g<-N43MhW25I#pg;?1D zlhPURUiGS)puJ84t>`eSb8l3&)MM4za0eBc?q&O?!?K@yb#+D&}0VogGO_Efw00 z!xfh5NbLDyea8M`9)({3F;vldwJUlB=_|oQ9wusx+=w-zpRn1Mi=&`X+JxzucKLQf zR@N5DX{7LmAsBHs1Y=I90=GwXa+&OB*$Wv5=b!HK{c)pxc$RR; zC~X-Vm%DqGY1eks?s2AFJ50N$l1}brT57p_iK88+?OjLcpY6*2^dSSOesjLkZaOIu zmsKZLml7dqB`HfF`=lj2qLP-0sIHzR0(OP>BDdNKQYQ)bq9J(B>ybVDUJ?SC+(;}( z@1<2`z0ohDU|JTS9X-%Cn{mOjn~lbEi9&;ICB6I{P(D=p;u;UB@>M*j{b#HMrAH5 zPLVhNW_R)>%Q=;5QeJ$-;ZL?C_XAgurekn@OW*;s@EtKxR~B>@3W77^6f20TGlE1#vxN8(VMkd%vmQf3 zpaIRIJ;r7NJA=L6W_EaloHr>@K8-kifbv-gv%^71sERr7ATahvbtnOI?^Nz`G{{bI z?{FVu7b@ZzAvEg78wz3ZV%}z?K;6O~^bJqK_uxmteb<#g6hYHj{Js23O#VZJQ^^F> zn%l+ASbvFS;8v#>dPIoAQ4auF;qFAy$JPt%XD*`>+T zD7B^?b4)uB$2g>1P6U-o)zqQB<=olZ!#m5n)Vrq7c%O?t4}83RQ;>Je^Xu)m z2WrSjz=+oQoBjWV^wi{lH31g`ehmmVCL7lnodO32t_VC3_%P5tsC&?~ptV6oLB`>s1m%=`VMTL(HKaD1c zdqn++`4I&X1~fsYMQ)FL8yOna4@e?rG_|CpG|la=Ru^O+<29^YUaI}{tH=B@H)^D) zEbLEK9Fw?AUGaKkS6=E#ei4^NkSKYXg2JyZODlS3|NCKEJo+Uy-jyD<)t=W7IZ@TC zi`P`I{a#PJe7$RVuk(KIo#1oO=ao;8?;zhbzWI==YW=$V?en|sSH*v^|8xHu0UZKX z1(*T?jgyU!jUk{!?G1bp7!))lC>>e^MsUsGKEbPl^Po&^8)6FS5;{9{Yv>)cquPay z4_g&>BTO7#C46u=6y?I6Tk4gzmH+RCXMNa9YGFVbhQ|RB6d}lfX(jB2>gNu>rc|#(nnIB%nP!|YAB)gf$T908=1@THX##j5e$V- zr!LNDgCVb5h@H(50I@+eQ1DT}jW~?aiknc=}^tCa1m`NI`Fz(A)|^iDJk8GyLA3O@2RStykfRxPW~s(8Ww z?_Zsb-Y=;oigJui0jC@;KPmEnjNBM3e_cSQn#9e-x#Jdib|qXpo){XD0F+@GU49mVn}lMPZ8v%5d;Zp;I!DomczRb8<8uJ zx4lFx7#HmOAXMQRgL5~qws1V$1DWs-91@;J7o$K}2E7|gF@6a`*CK6`l?M`t)wpT>ahJ zx%ETVj?g{HeUEz)2*K?=W_cX)$oF{RQO$FTXQt;<&oHkM;A#BiW#?Vjd%X8O?;xKc zJ_me?eKfwUd}sS+`QG*w`qlL7=eOAJnjgbI!ha3)Rvr-K_X^k-@G?MY3^R5$&Nf~& zzBYyic8U)ijdTYqGQ2~B8U^*nT68VwU63ZYUhojGHEskuheU=Xhb#}t57CA;4V?hC z#>>!3VPnHig*^&mgjWt96uvS1akx(em7Gy#G5l2tit0#}wV;=lAJm{+$mGRkzCZYx zm|3DDb>0`N33q>45M|m|c2)8}nX<~2uh!T{y`;Pw(gRZq(KvKS-73)z(Gh@TUZW=L zCvG7gf`@Id_&OZQOo^)`7%07tlCdZYZ9+}B2*N{&Gz5K_0XFgBT?EweWu8Fuw?jhN zIF$4E%6@|R^PNnEgB8vHnFN3Pdiez$D4$~zQbT>+0+N?;iY2w-c{u}v`!5QXQVywa z3=E*XfcToFTnF3o6=j+7J*vUMs`@x-k5es!VemW*p!To}8r5-Na}0*wXq);9#cx~| zERv@FcF;gs6mt*~C}TR8o%p^O`t`UGKr)+_$wdg_rfZ~MzfBTal>sGEwMQ|W<_U@@qN^snJ4A#FlHcM;lQu97+k=*~bF7``M6ng=!i?COQdZrt=!&I;BiO9`!HdQ~CYN3PiSW!EZ`A#BOAn zoSYqB_K>b%{;r%uj!h_&OaJ)&RQlhlA_rELNU2j3%AQwv;Q#NZtP3MvQlqca1Cr*k z*~8?a@vP$6-*c^Jo~O>Miq|}^hhCMv7kJE*VAts z@SHOL4*u)?U-~x**n_=redB0IKc5?;0Ow2(JR4{qAXi-pZkW28KV8@W?kai(c zu?cb~%h4UOhZ1d`9@D@Pcq)2 z;mG$z1~qZCl(~IZchC}!xy+3mLfwH{^0qAOf4FpTsH6mUe4u8L<}6f!Jguj; zGU#5*v^%v=wXrV!U3P-~&(Ou_I_jqD)*|JhR2KjhU|;=O{WU$);B07Vm|{3-cxR{v zdd?cx6RuBOC2lp`+PF<}+v1joKsQhK2JWNXSGnJH*LZ|`)b~j7*yxdi#fIw{?Ah5f z!}Ek^p{F3;%iF7s*C?+IUdO#|Vs9<*js=r`p!Y&h>92d+`Na4%#TvBM=cuub5Gz~aE*pq4>>gH8wO zgR2KG!I7{w#L)Xf*r8o;5Ol@iBPT38d`$RH;hKoP5z`~iNAM$CM9zu46se5r9AJx6?>r66|ai0hfCXnF4f){ryS_%b9 zNgAoA%oJMWK5Ic0vd_Keo^wC<@CV(L zgzl_aKY8W*KHo7_4pdRTQ@g=CUEf(px)E8mQPq#D4y*cYRdcmE)e@>LsCKPdK=qHS zAFO`6da)XPYaFjpqGq3(V$CZxtJj)aE2~zW+DWzN*1li6Qk^k%?$l{iSD;86DyaHo z77>3<$h=zA^2TzlK)Kc;-uWtX@9Q;N!Pl2$@voI!#Z*>ODP%&e!8(b_V=GTpMANvx~%f-LxSmmn>Xym_Y5 z&^uv$NXaD(Z{gOqf5G_NVmpmDwB*hc3mftSdn)CZdG<~AQ}+811B%ci>cqT{R5M$L zp5`RXfIxcs5(A=ReP2jIp9lohq4|*E_&rXDYnm@{oE2Zf^LLy$h1vU6UoA z!$0nwWTC~vhYpeZWoE9;qyPJ>bLq98 zaXyr9{P$dATnk(oAU3nP?<24~Y6jbHJoo*!kaHnFH{i33gsIUAi>pBhT~>$gr?~A8 zwS`4uw-6VW27PgP*nzOyVG7(9%7?cKPo>bkih1t~;lBYsi^NBP8e zVzU-lk-J$zz;b<&oIad&hNcW-lFaQoc(BHKicjGV#kQMJIZ0tX5_E>H#&x@nl0 zTr2oX!P-$HfpuStijMv;dMb0nkD^N!>RsqtJU`nJ5$vZzRJp_83{NhxtHkvNC7zdviAlhSZdc4Daus*U zMi|jeEV--Xt&+c$tXFDOsX0_=0;tfWmtI}^Oz9`3YnB;OW>=YC%DBqb0N}m3?9XLO z#rBRBwHqEj$)9`6loI&qhaW2b(KroUFkkZ zbS*HufLI{AK#_t87z92jm=AlIPokDaosJ4-qPrC^_4UzbqXP@Y6#BT(xIzaC844FI zT(@u{WwP^yeT6MWDi`ThWI>S=MRJN1tS^sue^c~$(R)P|)XeG>ODiT&*1UkVOg@|g zKQF$t_^IN`60s$EadjOkahECYHZj9vw#J-^c^p%+WZRPCcvH{CBbzc)^8K4Gz*1eN zMQnOHvz0<_1LXhrz1tE(^0v&v|Iw9x?LcNBarT+ahL+rJj-cPdW($pK)BUQVX@nX# zg}%?1sF7ErKRt`?ATZbhA-e=PxfaYNCZlSZ39;)KTI`qp3GH-8i?2GxDINd3mlZBgTg16+SQMgC0DXcXlUD>iv(P%55^ z!=8DPU)Sx737sQM9plMJ&#x}2u7i_GclAK^c(tHjqdusyc5K7=~=HY46jyekT5%Yia!2VQC*W8PD= z%j8ezG+-AGeHW*$FrAkscuDk9Ll&N++bEtI>oKTJ+ZNqgxaIEX33qlbytQGokiVX% zwqVH4DJFPZd-7<1UbO4h1mW(8r9!PdIibRudY&fCW9LK&L*Mg!A~v4nLEn+v^N!Cy zN7&Jx=WQjUu%>{Y*%^E;I1><7l(s5L;zaEbwB;+%)L)}RZ$eF9m!YYzfVWF(>btx( zBSYvlW%ThR`aF>YvHOQ&kP^4Jn3^Xp6FfoSQKjchKb-?o ztgEN7D%S`5GJKAZcOVaz7sH#HnwFZ*2x&%XCTljbjBjBzqGF`LEeq>Ego^kXO|h+D z=k7q>2|`$33UQXi80aI96fDJmU%gztLw!Ph8==t)#x0zhV%P!IVCdO7#*7X@`$(Ac$($DIA?{;5!UaM zo{a6cPkjz~Q~(x65&Xo>WN`mz-sc=-Q3rUZH#wa23{BUD8Ij0OIXZYg2h$FYrkmKt zgfk>kUfUSKU4nLib`*os3$cy)4z^piERE0mstMhWw&!5ocL%#EF~}$H8C`?K zYZ>1Noz>Yb#71X5mDQGgldf$erP%PErv}egzw21;me|@Yo;n z-gNpr1`KFwG16J9OG><37@Flx5<7~XdTPh-rSQ~2iQVJV65VNgm-P~+@9>Ov>|K^R zD6VJHz<%B1(qbf?T+h851jBQ$TdcCv^CI_0UKBD%xaQ%J%c!e!Z+xuos~)AEil^09 z^?7v`3)iR#*FH4^PQ3`W%X3b(P*X^%(f-*b3S_dW4!&;9Y1a-P0>oyW5wPn1}* zonXzTvr;#}nZ1Htl>)n}{K!G1!IU1HsGkSVVo2+Iq#&6H4eVSevD!LVa`n?r;>TXE z-K#yLy^n2%P9~hxXGCj&Je0CjzF4H8lpMyCUJ_2K%NWlzFrHtl^_%p2_2`!rMjg8ZgT&>j%SrwmsLzyd(YE8U+PQ6$liHUv3Bnc6s-s!d-5CUZ;HD6o=&-M z)uol@2w!Z;%1HkZ)8QR55w8T-sLd5G^vkF>-lY6q1!i(Km!H%uNu5OukbqoI- zd3DQ@t6NrG-LgPf+t(W+8jfUH6++pgSP-S`&kECxy}D)7)h+X{ZW%3Hd(R7D>u6R> zZLSyS%7{6FTHu9mBZ84)JH56r+p!K&-M2(5N}UA)Yav}7CW_)YQpRv#?#27+8M9SR z;<$S3x)1fq3=%CQCfKPzroTeF?1^4OyR5jOg`u-ypkbC_xnVtH)kh3h4VfrL0*s+( zM#>uNBdqLZ9E@sYhH<{xxYKw9PpFdYY(^jo>)WV9xR;7tQlv9Y1>^;@4)l-;B}DVhitL3;(QrrnT}D zmtfYgHP=gTe9*>o5KGQC$7E}trwrDIFlSj#-&Bh0rCGyu`u+MV!rg_wd}7K}-(|J_ z)p5Lc%fL9H#w_0$*hm!|}gy2%qs zL0%cI&O|PkaXigZ8Mt(j>B*Q2kL+aM0%2((}C=v(U(^@H_O)Q}~Q>2Hg? zzZb8)x^}|Mu|A_veWkD9pD&R+_SH)ic!~K0-&mhTSQ!f%<=0uhG$HSJpFtS-jW1IS zT$rq7kFu)@>GQL~ZLc=z zUjQ%Sk_kQyAO3w*WD?^n9l@*osl+Bb~l>?NYDQ7BID!)}8 zRsNv7u6&>jP~}sVQB_qnP_2AH0&7?I3skPke4qh)F|m0V}PoseMn|S(_qSullYBiA_DeHVVOV-WUJJ zUXZ47lDY~>=Y8EV4oyi*7$8xPU+wmj=Y6Ac|A2P=fAIm$C6?WyIZz$v)N4$s|H^q| zW+tONh_?>G3AmQ&^E2Z-{$ zYCTbYl%@;^QwvHX-8B7xTFnP(wM}!VI#KQoqTHuU@>{uMDsn@9NSK%getR4M?nUT^ zx4znX(%CHCHAUorv8_}|5?Df!;=qT#{qj26s`#&1LI-|dLW7>L$NWLT%5Y`0va+&) zvW2pna-edw@*Cx90q3KG&>_j=q< zk5*@N;x^~f3kKdD40u<;c(*g`!vTQ5q)2)x*00;;RNy8RiWk8~IOWB`9@M8sk)RzU z&imDO^FOu6x+_F;pg%hIC3K2g=j2ij-71>KGL>PZW(q%b1_@jiv*>P;(;D&*n@nNg zYvP^lq}*9d=NmaZOKR)kP}_~8a}u?#{oJp2D4}R^OfAg~+mu=K-nwBj(r^z2l&cg_ z0`w*@JS7lYHP*KUWjsJXjyraZez%4z_C9(m(YQP-O7m|b|6!qj5BgXA5%dd9?Y=Pa zz`CsFxnHuEc--6PcwEW%a4?ehnOGHkOMsW9iIz9^d$QPlEa%=fS?C^wDPTDu46RwH z$=Y$+>13&!8RxsL^-LgBa4XQ;Q$x;XE#_HxVbiZGBN*-!~zN`<^M^jvAsDB>> z#lNsJnWbNay6iih{x0BVgF*7sI3J(gR}{`?omSf$r6%<2)hm%xGOlMoYzw3>|CW~B zLHPDcR%F=AduHznYDj42`^2a0U6Ir^v3GKcVC|D_7av{8s-63j-yrl|ew!yBPRKh? zy_Q}56Cv-fSTR>5+-)ll0rX=OZ3;D_?SxoT6-^3w3lMVDm8%m;q^PW-JoAwB2}f8!E>H7=6nr zUL1f-@tejL97(meA_;Ft`6T;-i*Pr=!&MUAXPohN9=oUk=lvc85EsX;~06A zxcNuzBbiU)Vu8f!Y7=cz6<5oTK0_?!&+4qU*5i>S#}1zO)dP=zn)RifCyeW!8rMD9 zO~}F1$D}1d?#>DqTEzNv!jM=(&PLC&zR2}y(;vF#0K*9g&;~>Xlnd6*0~$-OMpsYQxs8@SG=p}qZp-_rC6)TP#jkLptzyPQan>= z0>c6e1(pr08rU$fU0_n+(7?|FX9q3`To-sK@M7SDz{i1xpopNVLCu1?1oa6T9<(%Q zbI_6WpbJ3{g94OxWf5f+BAO4lj`}LcD+T2eLXQeCMn0M zW~dgbHmi;^N14SGrBxlNE~9RsZl~@-hBkq@l{G{%hpDtY!VbqsBvXR<$q$H*hp}L1 zFgv-MGQ~NT>=R8BJ7X*5D73Vop4pQyc?3g~bHHdwyzwlqXDRNBVlYD5(;u^Dhman8 z!;9OA!S)Y09S7))yfmq#6eB$P-utoeU?Z7T_h6Xa)35p}3QacoDm#+m-0vm!j<1!{ zJ3c-sHQt?;*rTgZO6xBuY~SQtCY-TE^kGK?dstNb^bQviwK|13Er{?%Wp*QH-v*W{F!|7k(= zbrP0k`K;R1E(x!Gkhof)3qvn?V}xI`h`r;t_)4p!|FB!A?(sznd$-``SMF2d&)FVd zuxQ%q8}i4M`@7xyh;6<>uSIPFQCmf!Y=*DXtLK`U;rmE1oB3#z+kH(<@=w$vKBeEw zA1z_KFDfvtq;PG7-!3${=rf3GxBEINUj5zBoxY`lO6hkBXLtI-bB&cV$qHHoQK$%4 z7b3uIq;9K@C%_%TMqWnRev_tbV{%-v{Q>S6uee`F@^r0`gLI!kf_GC<+Kw z$#~iTVI30u6(_|qdA|BPKDnD5^)EOtT;w}txfHaH5eEeOzxq!F zW(f*`*#2Gdn%RFU_s{AHH`l#to*n6VSscIjD?d|z!*d=d=FH^!R?8%zsw|B5gyf?& zD>w_kf7LFLtihygMVcKSOTTgzm-kT~>~8R5(zv`_+jXc^b*18y4q<75b`6K1#MZh` z)yKfK9mCD_p00~-q!Pf?iX}Tsd(^?(7XQ3t?460D1hIl=^q<$1i9Y_ndJuuO z>pQ~3MmbjT;=7)1N}+x0oF?MeJKNS*+QM_t3 zIqAIMb>g+&p6Y)B0>V>09bT4f_AY#NNXdi%Vf*BqaB)JaCnVQ{immKf=IyNzno4#; zea>xF3Ks8WrEs#kPbqXPaK$aO7`WLWti)7Pu|}9wGRy9iNPiOLFN0q)>5u>2Eu2!_ z2@zJU$EmI)z@auQrUlzar z9s1|v_kQW?n@@P7F|&TNHNgC!F{S+Cg)hB`tW?HuV<}@@`KKRNM0Uwygj#ZrC2{}q0Uk62Oa1?ViM8Pe&!_9GQMAdZ<*}UFa2yD4zpgcj`Gs){S z{H;^DX14c0%pXdDTMNIunX&M;u=%dA@z(sM(w|RW^v73k&mDbh^eBN18!8T&<9({h z?Wq2r{@%yM_=R;9{6D16)Qa*<@@-N!H@JN%GfiUtRdSWfppJc=W|W##t)#v-iJDXe zod_=OB07gagNu$vB|}R(8a-g`e`Q!^*v0_Rb=cNAV}4AZD;paaI~r4sLyWVG+knO9 zF#Zz(DxwiCl!>NP(*)C!bkjPR5x4Mv4l%pURhR@zF;6nDM{arDoNZQGLb2nn1mHRj z(dB4lmupy6m$0LZw3Y;;)BvNwRBJkuu5+OA90XI4X;s)Pw!#<=z6-9Pt1TIUpkP~U zTW{NoNboAl%Vy7q4Ocx-qp9{$_OO}E2JD7pkjYHH#o>l;*1*xx(a$ka7#8k7CnTl$ zs|(et_)X%j%Kpy5;>3^qmlWc&nf_G8@4bna0jV&lo_~JDf7bHcb>jy1O6;DHCP#g* zeYJHO!ERhiS1EYJ&~2#S9wc3T4q;_CzgcK8&z~kfY~-K))}B7&OZbJP_gIAm2_afi zMo6OBy%fdG8SPEFx(2ExG4$Eqg99YNT^^qIxO$Tg`#vA7wGeDQzFg^lcY%|5AvnX52 ze&pY9#_i^Wd(3*Q#3WjR`CsO7=GmNYJI$xfcaQ)ZE%_NhlUC#ifO_j`j~+z`EX@H~ ztWj`%nnH)_%kbG`#!|LhPlAZK!|=S)7RsuOwbezm8VBuXvTe3)o$Y|_GG+i8dstq3 zG1%noK}L<@yp#07hwMK}=r(B;RuQQG)e}2U@?Tbno7(x;sHJ7tIOxB60SYX@H1Tm4 z|0(s`dhAVS!Ex+G3dj5UH>VdT(ft7P*ZvSA*HWXC$f?h@4kROz?MO#m3g_T7SdlyN ze7&uELdnXluge(Z5CnRQY2_W#U(;vlf7R=uQB^iHhDMcQ_za7&HFS0l8-6f6fbnnQ z9BgdtZXC%N0Xsg|h&kpC}COH6A`M@+X(fl#w+n?K+T9B!Us zUdoy~1Pjn()?fls)KbIJmYLYkIRm$I3Z8{Wrm&i<;hcnZ0X6i%u5Kdd;ASRcFI(?& z$36#6RLEAITUN54NCw<64~)q!&<*!MnKR(=dZOdEbEwZJZ$;F@_;zbV$EwUZLM!@$FM`X^(*T<>=lkc zn7D8CSp#eiSd+p*ux|wUQ2rq;<;(EJRbz zjr@PO*8Y0CirlXDToac6>EYe%7{3@%EQheZtf7ISBjed43{wqD47)g$Zr}}`-&l>C zFqNBdfpH7IBNw27r#USl2TkdlOI<2wzj z1wv*sK&6YeN4DoSyS)gbG3^NUhjS1uV`Be;J%^>?#7?sr{JUX}S>lXFIlHw&%~qZ! z;-{fG;emf9qZ7mV${zu|FzkH{e*gH_+e@e4-Zo7{9jp$1Tko|0m+yL+pcdcMmy$<>JmB82F%<8Q+)VA&hWSPXkT2!FFyH9I@%>L>WBJ1 z`cG*TPs8MWCj$sKNP!{^bqvi7@ucJv4ByB?!=pIAW*f95KoyKljjd%a$3()>W2g%s zla#wm#ju>JPc+)sG@7G-g=v@RwCM`*XtcSsxw5&Qxh-{`H2Bn$U^;Fve~X>V?bh4` z3X8>303t^@OEpU~%SV|NY> z2Ad{=eOqhFz@I0}^wi{p(plHs!5mMnF~^)?z6}1&WhsS8ODkaU>6URYE;d_^P;I$y z2`00x$?KZ{vU)RigBKWb{S9n&K3f@EJu=!Jw!sZ-Q#j~%FztFBr}M`)9S(P;?bXOH zIurGcBIa9V--Dr8rd`8fUlI;OTj;2xfgvw7KEt~aUF6Wv0^ zXWq?K-=tGdbSDjzKxyv8uKOpw`5Ol9-H;R~n4WthM9p*W%D471SXI$4wp=YOi9_aV z=I6kaih@&W27YyvMIfBN3;#csFh0&Y6z|B5)&pRyv#k!Bg;8? zs0TvNSjjd06AEvgBMN9qM@O1NvVl9`xWue-jI$;H>u!Mm7dtmIsQi=jHynCnT=iUW z^EdCN|Xau?p&-UhS^`gb{+t@Uu7Rj{O=D7>Z} zrc)-rDL;ovMXrfhZ`>osiRtU9pn{*b@Pf^fHRxS4NS z@xP5%xl{Y~kB^J*{*OO=RcRGFHutT6Yj1;$etz*^&7`frq~MPQ?NP0w`bW)(+7qRY zu8nu|vFJ*LMie?#NLjdk;W34`6#ltzK#_qI*dg|~mDAMl&!J!{03S+2!Cn4?GJw>m z-Yqg8T;;93`}dUdi*3J^l7lY@_O=kj|7?0@vYBIO)pjO)pJm==K96^?iI}~X<$cP@ zgE)w{!~6AF@>;8L{PxC|WU+N0zNy7*O`!b_gQvBcM#Wi(S|)oTD#nfRB^g9ia7f0O z<%MBV)6ov7;&6xHSn1da!{oXnr-4K3jKa3BJ&pxKoS!>q;R>|ZdDeNy`NXMrm2kZa z!z9f$0i4+(d`YrhnviIWE#hQ?$(oSkA-4dTMTFJ}6>ILx8DI)|Z7lls^x=1>O%^tM zpL3+@9?_%)Fc?)>Csd4XoyJkUtfBp|zGXmd7~LMDqSS1J|4g#pW0d z1OsK9#_ZLBVC)tV*&l|n5+pZIU+&Y<_7(OM#Dx!GgS&99t_}<7EX^E5}k?bx+pE zpeEa4a26n_ua0v?H)lFjyLkkON1Z=F8n!YRSkcwO)x$LsGsm^AJ+8~HUtE@uqIg3! zf@L!>WMaswkRxKr?m41K_vh5OaIs&`o_{klE{spe2@Cz_B)NP4&cyiMOq?@OUO?R3 zKd0AQTMs|nA`=*BSt=XaWLg4gA4StZde_>= zI*71q3swxj(n2pt!1WPRo(mv-|G=E*Gg~N%jE1n_``X9RE80SLQtgOv)S+ZIfSvyZ zK-Ha&!{l-I94coRuN*(H2AFn^!dQ7VY-!1_{2JC5765yatUiDk`nSo$U-1{2f^;#4+#hj!_Kp1sHk6%6K#H@4mxIi&grmRb=f~etoZ=!`$CI z6i@MOa8_?}vF9OFe+TO^$x(b0u2uJuTiC1-)=Kae-?Jv;d@_@3eV6r?HOLl1p|=a1 zs}Z(Ia%=Gj_ryb1iyZ`SC3}PV_BdMF!!fa1K>9C&iNC~gA<-|x9R-2Kw{?7sq16Oj z$+pQ>a@h`})9tJbqUk+n7w14HfCuMx=PB`2ly8F3xp!!9_v^~=56k+lm3@OUykA^e zLhXJ@F?;9smZb5o3CB;B^Et%ovA)@FZ7^`Eyk9J%kd{F=^QY$N=1n-uod)y&n^{uT zw6c6+nL#<=B3HlDTAYHw`$TO+Fa%r$PV*i;n`rb3jclE5gRz46!Is7K@8%F_31Dl8 zeJbzsi2WKzzY`<%ri^}niPgG04EqjpWPq$>yFUqn!r3&mu9MgakB%bcz zcg;m@I^LjKw>Qv3x7BK!|>kWW5d_uJ?;swkw?gLJx|5F-SU2w_fX!4 zdGqEglCNyOgnUc$tPy5Ror``~36rpUkgy7XvFg#J$d);lAe9 zMOKUK6gf6>W8~e)XOR^Pd{SU+fvE)+6gW{pRnT5Af5G|%2Y?qnTkvtg==xEOquNKM zMoo)a7_~BLbJUMfrs$~Xve9j#hewZ#&WL70uTarK?F)@Aw6oByLaxF^3b!phv2aG= ztin$VD~q^`)GLw(Wb~?7TH!qu=KKp6;pD87YfoV+eZ!rn^g}f?n z$iMxT2;p6ow}6aO5b~%fDa7RUhQ0i=P4hgp#GJg|X2!R*j&QNKUyS)oS~Bk%K0;D3 z(lEs^pNhghT)}Ptw{pUKZ%svEsBxNc8S3Ff#_x>}2s}beF%aL|nR=W0qjg$j+HSgt zzRqGULT9Nlh|NJLh`;6tIb{CHoI|a!5Q@+CgdbmWKP^LF_r2wc9E4cGyT@AVSX-0%$1&Kj*95p|(C)r2J z73~A|tK!22+09g9mkHUA{=mh^Ln|@uh0<%YrC*q{_2o;YsWd3Fi{m+LilQdm%+wJ90-fZrMALtK;Tg#Dubf#c5mN&Xwwu5_! zpvYz`Ms(THHqy4tCL-TGPY9y1JLJf+nY|0X#1kadT3z%aC%6qBgZnh&bX(ie%JGpS z)iKI3iE_jN$M>|Bo?%5)l9c2F8T~mE*!L0VIcgEloeo@5>$y6MHPqSnjIR@*3OcfP zyheZ$_NcS%hQBePPCBv!-`a5DCwhz(2T9>VVPjQX9X>WDgQ)o$SBISd?6Qq^bd1TS z;k^3wy!wlHUuK(BIC~T{SEFy-+dKj-<6`QD=gm(rQjE1UCAsK>)!Nr`@!wNZ_#BIl$@_Mz__-G4{ykW#?!_hjlUSBwoxSxh%Ok94MSb9 z)^v~qBFLPF36i>a?)JuU_e;EAGRRKunqQEeNaC`1^!_tYSZ}l(viykF)o;jvX62;Jj$v& z`~b#tZfqckyd&KJ4gUxn|0~Y^Qg7}1zriW6te|xJmo(NfwlgLg`x{3X=NQ);_Z!b6 zX%06viH{j(?wk}0I|2?)2Ppwum(RXYunH2hrS&n7(d8-F_s}F2XY4OlRlx5q0 zz;c*k+=Q0&hViM@7R7gT)D9B6VnDt71>mym)F4ZThQ=;yGwtcwm zOWPc#e0HKZ_E3N_+oQM~THE7kRY)$}+lUeFpelV%sk$gGQ0*LPq9WEi)L?$4G@2QPu^y}VE)x?wG>0{(iELRf1KN=a=PqdIbCBxnOROPaOxRB zj3IR<4`FM0jl@AO>zJBg^V*wWa~d)xkuc*JVa9JXCSCTztfab>zWdvU$@=77_=(-K zhrOVT8AX)Q%8`g7W+XN~s~q2=iSdg}PkgEsn9&&`nDY7c!rA9QzkaOk|52P9?GFrm z!`#m6IDvT)erH&-f1sh|Dg3^!6@0>X@0eq?@M)EiCChA4OPl z*Boex$ym59Yr*Y+TYF&j~ z6^5fMqt@9f>+1y1;;pm4`$rw~FYr#uF76e>^Y3-cSKlMFte@=^pR~??^46v! zztZ12+(pVfTbX;ZASU6@Ax$RSrTQ3w#Au{tFDms1h!*q8^9U{ISxlm=eVBy8VJk;Y z*_~~^kQm}V##{yL<*^w5*ghCR`U3k_7Q=0d+71+owQ-IZ&#qpIJ=bx^9fzOJRvx&f zjhu1LG@Pzy)|GqO=gA|zU``#b;;!nhrmoJe0j?4Bwzj*DV$}5*1MHZPx*?rHl0(LZ zET*@0A>>ZTub6Zd#}50W&`-s6@!7@Aws@vU|IRlO&ZlJ`GXH~XBv@MqfAi_S58wdQ|?xNr@ROcO$nnXPr9ls zke>Fco~ohX&=x{6I07y1vC0ISu^eQ^ZZaiqp?Wn4w4c;ZV8n$0KzkQ(WxD1Y7}qDD zlW1W`mBCOT5!%^!_!=Szidzs@@_?Id3N;RIjSKhXR2!GwOH?vwpRg`Q`wYL3HdH~D+uKW`J;uw7rc3e(jWTEMf?YFq5BuSyM?bm@mCP8T=W(a zy43R9{>~K&75ZNCmKSzU=Ua#cs{Fwgswa-S?A;$|{axey2lGg1bj7>&|7wO@3JDqt zt*?5w3C1~m9%Zk2=L^pp_$vvz>)tuS2P6D-g=g2jwO*lz3P>~NMFr49WyGB~yox|! z%mPN;zrW>euMlo8^p_Pn-SIYjTkF{`Xp^_PDqSQ~Sr z4YhoxNlml7FdDd448jdG3lua@n(rg0Fe0OBVClu`U(Tg-fhx7in#Wp*R(UHbSwoQ0 zyewx1GW{maMz)e8%t_3CZ$V;x+xDx?N}f`#9yd^DIUJcN2PB7>jl6~4+Dw~V3Q9Vm zv6#R;wA*pPkx4N#92cWz&UknipCPQ;L^1QOQ|S`cxo_LWX4kXZ7;UeUS%p(KfzGaZ zl)b#{>rB?RaXr)e{T^I`A(g?(e`U5d$jUDEwq7JbF9;}Z?v&!o1p4ID=tS(sh4?zU zWg8J@T|{!@EURRtveFuYSf&OsW}vT$!d+1pD>?TKf;3a(>>C~o|I`$G}UI&(Llkp`joeA}wBM>}f;7fJGnMHBh z<|^i@Mo96It3Um6DWo{!xduugiZb%>6byOqh2Bam2lo z*^_d8=|NBbYxqkEvR}+sm}>mhFD4AcXH9K$7_oh{gJD9<)vnZLfR?-fb^BM@)}a_^ zNy*eRS`H2tk^WFpVXvJcquI_y(7Q=A< znX6lmY07EcE!|`C<05b@8o>KZ(5LIa1cA9+zej&ke_j6zy!dcKY5Df = phf::phf_map! { // https://github.com/tc39/proposal-iterator-helpers "iterator-helpers" => SpecEdition::ESNext, + // Promise.try + // https://github.com/tc39/proposal-promise-try + "promise-try" => SpecEdition::ESNext, + // Set methods // https://github.com/tc39/proposal-set-methods "set-methods" => SpecEdition::ESNext, diff --git a/tools/gen-icu4x-data/Cargo.toml b/tools/gen-icu4x-data/Cargo.toml index d54c2f153e5..f9de0dc3212 100644 --- a/tools/gen-icu4x-data/Cargo.toml +++ b/tools/gen-icu4x-data/Cargo.toml @@ -10,9 +10,15 @@ license.workspace = true description.workspace = true [dependencies] -icu_provider = { workspace = true, features = ["datagen"] } -icu_provider_blob = { workspace = true, features = ["export"] } -icu_datagen = { workspace = true, features = ["networking", "use_wasm"] } +icu_provider.workspace = true +icu_datagen = { workspace = true, features = [ + "networking", + "use_wasm", + "provider", + "blob_exporter", + "experimental_components", + "rayon", +] } log.workspace = true simple_logger.workspace = true @@ -28,12 +34,6 @@ icu_normalizer = { workspace = true, features = ["datagen"] } icu_plurals = { workspace = true, features = ["datagen", "experimental"] } icu_segmenter = { workspace = true, features = ["datagen"] } -[target.'cfg(windows)'.dependencies] -# wasmer-wasi apparently has a wrong deps config... -# This dep patches that. -winapi = { workspace = true, features = ["sysinfoapi"] } - - [lints] workspace = true diff --git a/tools/gen-icu4x-data/src/main.rs b/tools/gen-icu4x-data/src/main.rs index 1ce90601463..904491f8136 100644 --- a/tools/gen-icu4x-data/src/main.rs +++ b/tools/gen-icu4x-data/src/main.rs @@ -2,96 +2,50 @@ use std::{error::Error, fs::File, path::Path}; -use icu_datagen::{CoverageLevel, DatagenDriver, DatagenProvider}; -use icu_plurals::provider::{PluralRangesV1, PluralRangesV1Marker}; -use icu_provider::{ - datagen::{ExportMarker, IterableDynamicDataProvider}, - dynutil::UpcastDataPayload, - prelude::*, -}; -use icu_provider_blob::export::BlobExporter; - -#[cfg(target_os = "windows")] // wasmer-wasi is a really fun dependency to maintain :) -use winapi as _; - -/// Hack that associates the `und` locale with an empty plural ranges data. -/// This enables the default behaviour for all locales without data. -#[derive(Debug)] -struct PluralRangesFallbackHack(DatagenProvider); - -// We definitely don't want to import dependencies just to do `T::default`. -#[allow(clippy::default_trait_access)] -impl DynamicDataProvider for PluralRangesFallbackHack { - fn load_data( - &self, - key: DataKey, - req: DataRequest<'_>, - ) -> Result, DataError> { - if req.locale.is_und() && key.hashed() == PluralRangesV1Marker::KEY.hashed() { - let payload = >::upcast( - DataPayload::from_owned(PluralRangesV1 { - ranges: Default::default(), - }), - ); - Ok(DataResponse { - metadata: DataResponseMetadata::default(), - payload: Some(payload), - }) - } else { - self.0.load_data(key, req) - } - } -} - -#[allow(clippy::default_trait_access)] -impl DynamicDataProvider for PluralRangesFallbackHack { - fn load_data( - &self, - key: DataKey, - req: DataRequest<'_>, - ) -> Result, DataError> { - if req.locale.is_und() && key.hashed() == PluralRangesV1Marker::KEY.hashed() { - let payload = >::upcast( - DataPayload::from_owned(PluralRangesV1 { - ranges: Default::default(), - }), - ); - Ok(DataResponse { - metadata: DataResponseMetadata::default(), - payload: Some(payload), - }) - } else { - self.0.load_data(key, req) - } - } -} +use icu_datagen::blob_exporter::BlobExporter; +use icu_datagen::prelude::*; +use icu_provider::data_key; -impl IterableDynamicDataProvider for PluralRangesFallbackHack { - fn supported_locales_for_key(&self, key: DataKey) -> Result, DataError> { - if key.hashed() == PluralRangesV1Marker::KEY.hashed() { - let mut locales = self.0.supported_locales_for_key(key)?; - locales.push(DataLocale::default()); - Ok(locales) - } else { - self.0.supported_locales_for_key(key) - } - } -} +const KEYS_LEN: usize = 129; /// List of keys used by `Intl` components. /// /// This must be kept in sync with the list of implemented components of `Intl`. -const KEYS: [&[DataKey]; 9] = [ - icu_casemap::provider::KEYS, - icu_collator::provider::KEYS, - icu_datetime::provider::KEYS, - icu_decimal::provider::KEYS, - icu_list::provider::KEYS, - icu_locid_transform::provider::KEYS, - icu_normalizer::provider::KEYS, - icu_plurals::provider::KEYS, - icu_segmenter::provider::KEYS, -]; +const KEYS: [DataKey; KEYS_LEN] = { + const CENTINEL_KEY: DataKey = data_key!("centinel@1"); + const SERVICES: [&[DataKey]; 9] = [ + icu_casemap::provider::KEYS, + icu_collator::provider::KEYS, + icu_datetime::provider::KEYS, + icu_decimal::provider::KEYS, + icu_list::provider::KEYS, + icu_locid_transform::provider::KEYS, + icu_normalizer::provider::KEYS, + icu_plurals::provider::KEYS, + icu_segmenter::provider::KEYS, + ]; + + let mut array = [CENTINEL_KEY; KEYS_LEN]; + + let mut offset = 0; + let mut service_idx = 0; + + while service_idx < SERVICES.len() { + let service = SERVICES[service_idx]; + let mut idx = 0; + while idx < service.len() { + array[offset + idx] = service[idx]; + idx += 1; + } + + offset += service.len(); + service_idx += 1; + } + + assert!(offset == array.len()); + + array +}; fn main() -> Result<(), Box> { simple_logger::SimpleLogger::new() @@ -106,18 +60,26 @@ fn main() -> Result<(), Box> { let _unused = std::fs::remove_dir_all(path); std::fs::create_dir_all(path)?; - log::info!("Generating ICU4X data for keys: {:?}", KEYS); + log::info!("Generating ICU4X data for keys: {:#?}", KEYS); let provider = DatagenProvider::new_latest_tested(); + let locales = provider + .locales_for_coverage_levels([CoverageLevel::Modern])? + .into_iter() + .chain([langid!("en-US")]); DatagenDriver::new() - .with_keys(KEYS.into_iter().flatten().copied()) - .with_locales(provider.locales_for_coverage_levels([CoverageLevel::Modern])?) + .with_keys(KEYS) + .with_locales_and_fallback(locales.map(LocaleFamily::with_descendants), { + let mut options = FallbackOptions::default(); + options.deduplication_strategy = Some(DeduplicationStrategy::None); + options + }) .with_additional_collations([String::from("search*")]) .with_recommended_segmenter_models() .export( - &PluralRangesFallbackHack(provider), - BlobExporter::new_with_sink(Box::new(File::create(path.join("icudata.postcard"))?)), + &provider, + BlobExporter::new_v2_with_sink(Box::new(File::create(path.join("icudata.postcard"))?)), )?; Ok(()) From c7683e3951567d9ab66b8a2a7e3e8fb0d28e0438 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Thu, 30 May 2024 01:58:53 +0100 Subject: [PATCH 035/212] Update regress to v0.10.0 (#3869) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 099f5867102..b770f0938e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2752,9 +2752,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "regress" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eae2a1ebfecc58aff952ef8ccd364329abe627762f5bf09ff42eb9d98522479" +checksum = "16fe0a24af5daaae947294213d2fd2646fbf5e1fbacc1d4ba3e84b2393854842" dependencies = [ "hashbrown", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 022a139bae8..1c66baab4b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ once_cell = { version = "1.19.0", default-features = false } phf = { version = "0.11.2", default-features = false } pollster = "0.3.0" regex = "1.10.4" -regress = { version="0.9.1", features = ["utf16"]} +regress = { version="0.10.0", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } serde_json = "1.0.116" serde = "1.0.203" From c7e3ec49087a67a9d3a6e2cfb620c0afd97c172f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 20:53:34 +0200 Subject: [PATCH 036/212] Bump proc-macro2 from 1.0.84 to 1.0.85 in the rust-dependencies group (#3871) Bumps the rust-dependencies group with 1 update: [proc-macro2](https://github.com/dtolnay/proc-macro2). Updates `proc-macro2` from 1.0.84 to 1.0.85 - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.84...1.0.85) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b770f0938e8..b284463848d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2624,9 +2624,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] From 4419e6df04b91ae14e150b3b2f2f17c030eec7ad Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Tue, 4 Jun 2024 04:16:09 +0100 Subject: [PATCH 037/212] Fix base objects in `with` statements (#3870) --- core/engine/src/builtins/eval/mod.rs | 3 ++ core/engine/src/builtins/function/mod.rs | 2 + core/engine/src/builtins/json/mod.rs | 2 + core/engine/src/bytecompiler/class.rs | 5 ++ core/engine/src/bytecompiler/declarations.rs | 2 + core/engine/src/bytecompiler/function.rs | 9 ++++ core/engine/src/bytecompiler/mod.rs | 21 +++++++- .../engine/src/bytecompiler/statement/with.rs | 3 ++ core/engine/src/environments/runtime/mod.rs | 51 +++++++++++++++++++ core/engine/src/module/source.rs | 1 + core/engine/src/module/synthetic.rs | 1 + core/engine/src/script.rs | 1 + core/engine/src/vm/code_block.rs | 4 +- core/engine/src/vm/flowgraph/mod.rs | 4 +- core/engine/src/vm/opcode/environment/mod.rs | 39 ++++++++++++++ core/engine/src/vm/opcode/mod.rs | 9 +++- 16 files changed, 150 insertions(+), 7 deletions(-) diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 8982c716c07..1555473f6f4 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -246,6 +246,8 @@ impl Eval { context, )?; + let in_with = context.vm.environments.has_object_environment(); + let mut compiler = ByteCompiler::new( js_string!("

"), body.strict(), @@ -253,6 +255,7 @@ impl Eval { var_env.clone(), lex_env.clone(), context.interner_mut(), + in_with, ); compiler.current_open_environments_count += 1; diff --git a/core/engine/src/builtins/function/mod.rs b/core/engine/src/builtins/function/mod.rs index 1bf76b5e82c..8970df508a2 100644 --- a/core/engine/src/builtins/function/mod.rs +++ b/core/engine/src/builtins/function/mod.rs @@ -626,10 +626,12 @@ impl BuiltInFunctionObject { body }; + let in_with = context.vm.environments.has_object_environment(); let code = FunctionCompiler::new() .name(js_string!("anonymous")) .generator(generator) .r#async(r#async) + .in_with(in_with) .compile( ¶meters, &body, diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index 52e8debde26..06e047ea7c8 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -113,6 +113,7 @@ impl Json { parser.set_json_parse(); let script = parser.parse_script(context.interner_mut())?; let code_block = { + let in_with = context.vm.environments.has_object_environment(); let mut compiler = ByteCompiler::new( js_string!("
"), script.strict(), @@ -120,6 +121,7 @@ impl Json { context.realm().environment().compile_env(), context.realm().environment().compile_env(), context.interner_mut(), + in_with, ); compiler.compile_statement_list(script.statements(), true, false); Gc::new(compiler.finish()) diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index ca43318bd6f..f18b9f6f9e0 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -55,6 +55,7 @@ impl ByteCompiler<'_> { self.variable_environment.clone(), self.lexical_environment.clone(), self.interner, + self.in_with, ); compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR; @@ -288,6 +289,7 @@ impl ByteCompiler<'_> { self.variable_environment.clone(), self.lexical_environment.clone(), self.interner, + self.in_with, ); // Function environment @@ -316,6 +318,7 @@ impl ByteCompiler<'_> { self.variable_environment.clone(), self.lexical_environment.clone(), self.interner, + self.in_with, ); let _ = field_compiler.push_compile_environment(true); if let Some(node) = field { @@ -354,6 +357,7 @@ impl ByteCompiler<'_> { self.variable_environment.clone(), self.lexical_environment.clone(), self.interner, + self.in_with, ); let _ = field_compiler.push_compile_environment(true); if let Some(node) = field { @@ -388,6 +392,7 @@ impl ByteCompiler<'_> { self.variable_environment.clone(), self.lexical_environment.clone(), self.interner, + self.in_with, ); let _ = compiler.push_compile_environment(true); diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index b8b4fde7a04..793f8a25d43 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -586,6 +586,7 @@ impl ByteCompiler<'_> { .generator(generator) .r#async(r#async) .strict(self.strict()) + .in_with(self.in_with) .binding_identifier(Some(name.sym().to_js_string(self.interner()))) .compile( parameters, @@ -954,6 +955,7 @@ impl ByteCompiler<'_> { .generator(generator) .r#async(r#async) .strict(self.strict()) + .in_with(self.in_with) .binding_identifier(Some(name.sym().to_js_string(self.interner()))) .compile( parameters, diff --git a/core/engine/src/bytecompiler/function.rs b/core/engine/src/bytecompiler/function.rs index 2a09a497b96..fceb90ce131 100644 --- a/core/engine/src/bytecompiler/function.rs +++ b/core/engine/src/bytecompiler/function.rs @@ -22,6 +22,7 @@ pub(crate) struct FunctionCompiler { strict: bool, arrow: bool, method: bool, + in_with: bool, binding_identifier: Option, } @@ -35,6 +36,7 @@ impl FunctionCompiler { strict: false, arrow: false, method: false, + in_with: false, binding_identifier: None, } } @@ -85,6 +87,12 @@ impl FunctionCompiler { self } + /// Indicate if the function is in a `with` statement. + pub(crate) const fn in_with(mut self, in_with: bool) -> Self { + self.in_with = in_with; + self + } + /// Compile a function statement list and it's parameters into bytecode. pub(crate) fn compile( mut self, @@ -105,6 +113,7 @@ impl FunctionCompiler { variable_environment, lexical_environment, interner, + self.in_with, ); compiler.length = length; compiler diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index c1bbe380f99..bad92dad8e8 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -304,6 +304,9 @@ pub struct ByteCompiler<'ctx> { pub(crate) async_handler: Option, json_parse: bool, + /// Whether the function is in a `with` statement. + pub(crate) in_with: bool, + pub(crate) interner: &'ctx mut Interner, #[cfg(feature = "annex-b")] @@ -324,6 +327,7 @@ impl<'ctx> ByteCompiler<'ctx> { variable_environment: Rc, lexical_environment: Rc, interner: &'ctx mut Interner, + in_with: bool, ) -> ByteCompiler<'ctx> { let mut code_block_flags = CodeBlockFlags::empty(); code_block_flags.set(CodeBlockFlags::STRICT, strict); @@ -356,6 +360,7 @@ impl<'ctx> ByteCompiler<'ctx> { #[cfg(feature = "annex-b")] annex_b_function_names: Vec::new(), + in_with, } } @@ -1320,6 +1325,7 @@ impl<'ctx> ByteCompiler<'ctx> { .r#async(r#async) .strict(self.strict()) .arrow(arrow) + .in_with(self.in_with) .binding_identifier(binding_identifier) .compile( parameters, @@ -1395,6 +1401,7 @@ impl<'ctx> ByteCompiler<'ctx> { .strict(self.strict()) .arrow(arrow) .method(true) + .in_with(self.in_with) .binding_identifier(binding_identifier) .compile( parameters, @@ -1442,6 +1449,7 @@ impl<'ctx> ByteCompiler<'ctx> { .strict(true) .arrow(arrow) .method(true) + .in_with(self.in_with) .binding_identifier(binding_identifier) .compile( parameters, @@ -1481,8 +1489,19 @@ impl<'ctx> ByteCompiler<'ctx> { if *ident == Sym::EVAL { kind = CallKind::CallEval; } + + if self.in_with { + let name = self.resolve_identifier_expect(*ident); + let binding = self.lexical_environment.get_identifier_reference(name); + let index = self.get_or_insert_binding(binding.locator()); + self.emit_with_varying_operand(Opcode::ThisForObjectEnvironmentName, index); + } else { + self.emit_opcode(Opcode::PushUndefined); + } + } else { + self.emit_opcode(Opcode::PushUndefined); } - self.emit_opcode(Opcode::PushUndefined); + self.compile_expr(expr, true); } expr => { diff --git a/core/engine/src/bytecompiler/statement/with.rs b/core/engine/src/bytecompiler/statement/with.rs index ec90b75b17d..db05b3d4dd8 100644 --- a/core/engine/src/bytecompiler/statement/with.rs +++ b/core/engine/src/bytecompiler/statement/with.rs @@ -10,7 +10,10 @@ impl ByteCompiler<'_> { let _ = self.push_compile_environment(false); self.emit_opcode(Opcode::PushObjectEnvironment); + let in_with = self.in_with; + self.in_with = true; self.compile_stmt(with.statement(), use_expr, true); + self.in_with = in_with; self.pop_compile_environment(); self.lexical_environment = old_lex_env; diff --git a/core/engine/src/environments/runtime/mod.rs b/core/engine/src/environments/runtime/mod.rs index 05bdb0e9a56..59cc5a0aeb2 100644 --- a/core/engine/src/environments/runtime/mod.rs +++ b/core/engine/src/environments/runtime/mod.rs @@ -411,6 +411,13 @@ impl EnvironmentStack { } names } + + /// Indicate if the current environment stack has an object environment. + pub(crate) fn has_object_environment(&self) -> bool { + self.stack + .iter() + .any(|env| matches!(env, Environment::Object(_))) + } } /// A binding locator contains all information about a binding that is needed to resolve it at runtime. @@ -541,6 +548,50 @@ impl Context { Ok(()) } + /// Finds the object environment that contains the binding and returns the `this` value of the object environment. + pub(crate) fn this_from_object_environment_binding( + &mut self, + locator: &BindingLocator, + ) -> JsResult> { + let current = self.vm.environments.current(); + if let Some(env) = current.as_declarative() { + if !env.with() { + return Ok(None); + } + } + + for env_index in (locator.environment_index..self.vm.environments.stack.len() as u32).rev() + { + match self.environment_expect(env_index) { + Environment::Declarative(env) => { + if env.poisoned() { + let compile = env.compile_env(); + if compile.is_function() && compile.get_binding(locator.name()).is_some() { + break; + } + } else if !env.with() { + break; + } + } + Environment::Object(o) => { + let o = o.clone(); + let key = locator.name().clone(); + if o.has_property(key.clone(), self)? { + if let Some(unscopables) = o.get(JsSymbol::unscopables(), self)?.as_object() + { + if unscopables.get(key.clone(), self)?.to_boolean() { + continue; + } + } + return Ok(Some(o)); + } + } + } + } + + Ok(None) + } + /// Checks if the binding pointed by `locator` is initialized. /// /// # Panics diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index 2062028e095..f136b3c47c7 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1433,6 +1433,7 @@ impl SourceTextModule { env.clone(), env.clone(), context.interner_mut(), + false, ); compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index 734b1e9dd9e..81446866bf8 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -287,6 +287,7 @@ impl SyntheticModule { module_compile_env.clone(), module_compile_env.clone(), context.interner_mut(), + false, ); // 4. For each String exportName in module.[[ExportNames]], do diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index 457b731704b..31152ee8894 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -135,6 +135,7 @@ impl Script { self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(), context.interner_mut(), + false, ); #[cfg(feature = "annex-b")] diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index a1c26992192..66f8682690c 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -611,6 +611,7 @@ impl CodeBlock { | Instruction::Exception | Instruction::MaybeException | Instruction::This + | Instruction::ThisForObjectEnvironmentName { .. } | Instruction::Super | Instruction::CheckReturn | Instruction::Return @@ -719,8 +720,7 @@ impl CodeBlock { | Instruction::Reserved50 | Instruction::Reserved51 | Instruction::Reserved52 - | Instruction::Reserved53 - | Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved53 => unreachable!("Reserved opcodes are unrechable"), } } } diff --git a/core/engine/src/vm/flowgraph/mod.rs b/core/engine/src/vm/flowgraph/mod.rs index 0c931d5afed..5ed0326f591 100644 --- a/core/engine/src/vm/flowgraph/mod.rs +++ b/core/engine/src/vm/flowgraph/mod.rs @@ -400,6 +400,7 @@ impl CodeBlock { | Instruction::ToPropertyKey | Instruction::ToBoolean | Instruction::This + | Instruction::ThisForObjectEnvironmentName { .. } | Instruction::Super | Instruction::IncrementLoopIteration | Instruction::CreateForInIterator @@ -515,8 +516,7 @@ impl CodeBlock { | Instruction::Reserved50 | Instruction::Reserved51 | Instruction::Reserved52 - | Instruction::Reserved53 - | Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved53 => unreachable!("Reserved opcodes are unrechable"), } } diff --git a/core/engine/src/vm/opcode/environment/mod.rs b/core/engine/src/vm/opcode/environment/mod.rs index 2d04092ec78..31f61ce8f1b 100644 --- a/core/engine/src/vm/opcode/environment/mod.rs +++ b/core/engine/src/vm/opcode/environment/mod.rs @@ -35,6 +35,45 @@ impl Operation for This { } } +/// `ThisForObjectEnvironmentName` implements the Opcode Operation for `Opcode::ThisForObjectEnvironmentName` +/// +/// Operation: +/// - Pushes `this` value that is related to the object environment of the given binding. +#[derive(Debug, Clone, Copy)] +pub(crate) struct ThisForObjectEnvironmentName; + +impl ThisForObjectEnvironmentName { + fn operation(context: &mut Context, index: usize) -> JsResult { + let binding_locator = context.vm.frame().code_block.bindings[index].clone(); + let this = context + .this_from_object_environment_binding(&binding_locator)? + .map_or(JsValue::undefined(), Into::into); + context.vm.push(this); + Ok(CompletionType::Normal) + } +} + +impl Operation for ThisForObjectEnvironmentName { + const NAME: &'static str = "ThisForObjectEnvironmentName"; + const INSTRUCTION: &'static str = "INST - ThisForObjectEnvironmentName"; + const COST: u8 = 1; + + fn execute(context: &mut Context) -> JsResult { + let index = context.vm.read::(); + Self::operation(context, index as usize) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::() as usize; + Self::operation(context, index) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let index = context.vm.read::(); + Self::operation(context, index as usize) + } +} + /// `Super` implements the Opcode Operation for `Opcode::Super` /// /// Operation: diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index 3f54a0080f8..15a362159dc 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -1614,6 +1614,13 @@ generate_opcodes! { /// Stack: **=>** this This, + /// Pushes `this` value that is related to the object environment of the given binding + /// + /// Operands: index: `VaryingOperand` + /// + /// Stack: **=>** value + ThisForObjectEnvironmentName { index: VaryingOperand }, + /// Pushes the current `super` value to the stack. /// /// Operands: @@ -2252,8 +2259,6 @@ generate_opcodes! { Reserved52 => Reserved, /// Reserved [`Opcode`]. Reserved53 => Reserved, - /// Reserved [`Opcode`]. - Reserved54 => Reserved, } /// Specific opcodes for bindings. From 109dd3d395859fcf0d8ecdc01b146b517437716e Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Wed, 5 Jun 2024 18:11:52 -0700 Subject: [PATCH 038/212] Adding TryFromJs implementations for BTreeMap and HashMap (#3844) --- .../src/value/conversions/try_from_js.rs | 47 ++++++++++++++ .../conversions/try_from_js/collections.rs | 64 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 core/engine/src/value/conversions/try_from_js/collections.rs diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index a4b5a465606..dc860a33496 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -4,6 +4,8 @@ use num_bigint::BigInt; use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; +mod collections; + /// This trait adds a fallible and efficient conversions from a [`JsValue`] to Rust types. pub trait TryFromJs: Sized { /// This function tries to convert a JavaScript value into `Self`. @@ -364,3 +366,48 @@ fn value_into_vec() { ), ]); } + +#[test] +fn value_into_map() { + use boa_engine::{run_test_actions, TestAction}; + use indoc::indoc; + + run_test_actions([ + TestAction::assert_with_op(indoc! {r#" ({ a: 1, b: 2, c: 3 }) "#}, |value, context| { + let value = std::collections::BTreeMap::::try_from_js(&value, context); + + match value { + Ok(value) => { + value + == vec![ + ("a".to_string(), 1), + ("b".to_string(), 2), + ("c".to_string(), 3), + ] + .into_iter() + .collect::>() + } + _ => false, + } + }), + TestAction::assert_with_op(indoc! {r#" ({ a: 1, b: 2, c: 3 }) "#}, |value, context| { + let value = std::collections::HashMap::::try_from_js(&value, context); + + match value { + Ok(value) => { + value + == std::collections::HashMap::from_iter( + vec![ + ("a".to_string(), 1), + ("b".to_string(), 2), + ("c".to_string(), 3), + ] + .into_iter() + .collect::>(), + ) + } + _ => false, + } + }), + ]); +} diff --git a/core/engine/src/value/conversions/try_from_js/collections.rs b/core/engine/src/value/conversions/try_from_js/collections.rs new file mode 100644 index 00000000000..37d649e9630 --- /dev/null +++ b/core/engine/src/value/conversions/try_from_js/collections.rs @@ -0,0 +1,64 @@ +//! [`JsValue`] conversions for std collections. + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +use crate::value::TryFromJs; +use crate::{Context, JsNativeError, JsResult, JsValue}; + +impl TryFromJs for BTreeMap +where + K: TryFromJs + Ord, + V: TryFromJs, +{ + fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { + let JsValue::Object(object) = value else { + return Err(JsNativeError::typ() + .with_message("cannot convert value to a BTreeMap") + .into()); + }; + + let keys = object.__own_property_keys__(context)?; + + keys.into_iter() + .map(|key| { + let js_value = object.get(key.clone(), context)?; + let js_key: JsValue = key.into(); + + let key = K::try_from_js(&js_key, context)?; + let value = V::try_from_js(&js_value, context)?; + + Ok((key, value)) + }) + .collect() + } +} + +impl TryFromJs for HashMap +where + K: TryFromJs + Ord + Hash, + V: TryFromJs, + S: std::hash::BuildHasher + Default, +{ + fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { + let JsValue::Object(object) = value else { + return Err(JsNativeError::typ() + .with_message("cannot convert value to a BTreeMap") + .into()); + }; + + let keys = object.__own_property_keys__(context)?; + + keys.into_iter() + .map(|key| { + let js_value = object.get(key.clone(), context)?; + let js_key: JsValue = key.into(); + + let key = K::try_from_js(&js_key, context)?; + let value = V::try_from_js(&js_value, context)?; + + Ok((key, value)) + }) + .collect() + } +} From 95ba66094364680b53da403bf63549a38d99c565 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:03:05 +0200 Subject: [PATCH 039/212] Bump the rust-dependencies group with 5 updates (#3873) Bumps the rust-dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [clap](https://github.com/clap-rs/clap) | `4.5.4` | `4.5.6` | | [regex](https://github.com/rust-lang/regex) | `1.10.4` | `1.10.5` | | [toml](https://github.com/toml-rs/toml) | `0.8.13` | `0.8.14` | | [icu_datetime](https://github.com/unicode-org/icu4x) | `1.5.0` | `1.5.1` | | [icu_calendar](https://github.com/unicode-org/icu4x) | `1.5.0` | `1.5.1` | Updates `clap` from 4.5.4 to 4.5.6 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.4...v4.5.6) Updates `regex` from 1.10.4 to 1.10.5 - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.10.4...1.10.5) Updates `toml` from 0.8.13 to 0.8.14 - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.13...toml-v0.8.14) Updates `icu_datetime` from 1.5.0 to 1.5.1 - [Release notes](https://github.com/unicode-org/icu4x/releases) - [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md) - [Commits](https://github.com/unicode-org/icu4x/commits) Updates `icu_calendar` from 1.5.0 to 1.5.1 - [Release notes](https://github.com/unicode-org/icu4x/releases) - [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md) - [Commits](https://github.com/unicode-org/icu4x/commits) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: regex dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: icu_datetime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: icu_calendar dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 38 +++++++++++++++++++------------------- Cargo.toml | 10 +++++----- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b284463848d..994b7b86864 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -563,7 +563,7 @@ dependencies = [ "serde_repr", "serde_yaml", "time", - "toml 0.8.13", + "toml 0.8.14", ] [[package]] @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" dependencies = [ "clap_builder", "clap_derive", @@ -737,9 +737,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" dependencies = [ "anstream", "anstyle", @@ -749,9 +749,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1481,9 +1481,9 @@ dependencies = [ [[package]] name = "icu_calendar" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4231cddf3dcbfc21e04bb52768f18e11a077d2935774eabdf7b239bb83bf3882" +checksum = "7defdcb785245edd773a38b56f0ccef39065cdc4509a7d85435f6edea0fd58c5" dependencies = [ "calendrical_calculations", "databake", @@ -1622,9 +1622,9 @@ dependencies = [ [[package]] name = "icu_datetime" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57d14f11efa0425216b4e183db61dd23e1bb1357a86009308de18812bb860e" +checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780" dependencies = [ "databake", "displaydoc", @@ -2707,9 +2707,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -3382,14 +3382,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.14", ] [[package]] @@ -3414,9 +3414,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -3500,7 +3500,7 @@ dependencies = [ "serde_derive", "serde_json", "termcolor", - "toml 0.8.13", + "toml 0.8.14", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1c66baab4b8..f108d83ce19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ boa_string = { version = "~0.18.0", path = "core/string" } # Shared deps arbitrary = "1" bitflags = "2.5.0" -clap = "4.5.4" +clap = "4.5.6" colored = "2.1.0" fast-float = "0.2.0" hashbrown = { version = "0.14.5", default-features = false } @@ -64,7 +64,7 @@ num-traits = "0.2.19" once_cell = { version = "1.19.0", default-features = false } phf = { version = "0.11.2", default-features = false } pollster = "0.3.0" -regex = "1.10.4" +regex = "1.10.5" regress = { version="0.10.0", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } serde_json = "1.0.116" @@ -79,7 +79,7 @@ simple_logger = "5.0.0" cargo_metadata = "0.18.1" trybuild = "1.0.95" rayon = "1.10.0" -toml = "0.8.12" +toml = "0.8.14" color-eyre = "0.6.3" comfy-table = "7.1.1" serde_repr = "0.1.19" @@ -128,8 +128,8 @@ winapi = { version = "0.3.9", default-features = false } icu_provider = { version = "~1.5.0", default-features = false } icu_locid = { version = "~1.5.0", default-features = false } icu_locid_transform = { version = "~1.5.0", default-features = false } -icu_datetime = { version = "~1.5.0", default-features = false } -icu_calendar = { version = "~1.5.0", default-features = false } +icu_datetime = { version = "~1.5.1", default-features = false } +icu_calendar = { version = "~1.5.1", default-features = false } icu_collator = { version = "~1.5.0", default-features = false } icu_plurals = { version = "~1.5.0", default-features = false } icu_list = { version = "~1.5.0", default-features = false } From 32e75620b4bb71042b211b1a90b31d527bcd2812 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Fri, 14 Jun 2024 22:56:18 +0100 Subject: [PATCH 040/212] Fix boa cli history (#3875) * Fix boa cli history * Ignore dead code warning in fuzzer --- Cargo.lock | 12 ++++++++++++ cli/Cargo.toml | 2 +- tests/fuzz/fuzz_targets/common.rs | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 994b7b86864..a27742458ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1245,6 +1245,17 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "fixed_decimal" version = "0.5.6" @@ -2846,6 +2857,7 @@ dependencies = [ "bitflags 2.5.0", "cfg-if", "clipboard-win", + "fd-lock", "libc", "log", "memchr", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e9310bf6d9f..883a3882f27 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -16,7 +16,7 @@ boa_engine = { workspace = true, features = ["deser", "flowgraph", "trace"] } boa_parser.workspace = true boa_gc.workspace = true boa_runtime.workspace = true -rustyline = { workspace = true, features = ["derive"]} +rustyline = { workspace = true, features = ["derive", "with-file-history"] } clap = { workspace = true, features = ["derive"] } serde_json.workspace = true colored.workspace = true diff --git a/tests/fuzz/fuzz_targets/common.rs b/tests/fuzz/fuzz_targets/common.rs index 2903a6ad464..afb81621312 100644 --- a/tests/fuzz/fuzz_targets/common.rs +++ b/tests/fuzz/fuzz_targets/common.rs @@ -74,6 +74,7 @@ impl Debug for FuzzData { } } +#[allow(dead_code)] pub struct FuzzSource { pub interner: Interner, pub source: String, From 1eaf9230ae52440a8785d2befb4e380983190f24 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Sat, 15 Jun 2024 04:03:02 +0100 Subject: [PATCH 041/212] Fix hashbang comments by using proper goal symbols (#3876) --- core/parser/src/lexer/mod.rs | 49 ++++++++++++++++++++--------------- core/parser/src/parser/mod.rs | 5 +++- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/core/parser/src/lexer/mod.rs b/core/parser/src/lexer/mod.rs index 01650f899b6..d0c41b0da81 100644 --- a/core/parser/src/lexer/mod.rs +++ b/core/parser/src/lexer/mod.rs @@ -161,7 +161,7 @@ impl Lexer { )) } } - InputElement::RegExp => { + InputElement::RegExp | InputElement::HashbangOrRegExp => { // Can be a regular expression. RegexLiteral.lex(&mut self.cursor, start, interner) } @@ -214,28 +214,34 @@ impl Lexer { { let _timer = Profiler::global().start_event("next()", "Lexing"); - let (start, next_ch) = loop { - let start = self.cursor.pos(); - if let Some(next_ch) = self.cursor.next_char()? { - // Ignore whitespace - if !is_whitespace(next_ch) { - break (start, next_ch); - } - } else { - return Ok(None); - } + let mut start = self.cursor.pos(); + let Some(mut next_ch) = self.cursor.next_char()? else { + return Ok(None); }; - //handle hashbang here so the below match block still throws error on - //# if position isn't (1, 1) - if start.column_number() == 1 - && start.line_number() == 1 - && next_ch == 0x23 - && self.cursor.peek_char()? == Some(0x21) - { - let _token = HashbangComment.lex(&mut self.cursor, start, interner); - return self.next(interner); - }; + // If the goal symbol is HashbangOrRegExp, then we need to check if the next token is a hashbang comment. + // Since the goal symbol is only valid for the first token, we need to change it to RegExp after the first token. + if self.get_goal() == InputElement::HashbangOrRegExp { + self.set_goal(InputElement::RegExp); + if next_ch == 0x23 && self.cursor.peek_char()? == Some(0x21) { + let _token = HashbangComment.lex(&mut self.cursor, start, interner); + return self.next(interner); + }; + } + + // Ignore whitespace + if is_whitespace(next_ch) { + loop { + start = self.cursor.pos(); + let Some(next) = self.cursor.next_char()? else { + return Ok(None); + }; + if !is_whitespace(next) { + next_ch = next; + break; + } + } + } if let Ok(c) = char::try_from(next_ch) { let token = match c { @@ -392,6 +398,7 @@ pub(crate) enum InputElement { Div, RegExp, TemplateTail, + HashbangOrRegExp, } impl Default for InputElement { diff --git a/core/parser/src/parser/mod.rs b/core/parser/src/parser/mod.rs index 1f2bd364070..ada983b6e24 100644 --- a/core/parser/src/parser/mod.rs +++ b/core/parser/src/parser/mod.rs @@ -11,7 +11,7 @@ mod tests; use crate::{ error::ParseResult, - lexer::Error as LexError, + lexer::{Error as LexError, InputElement}, parser::{ cursor::Cursor, function::{FormalParameters, FunctionStatementList}, @@ -140,6 +140,7 @@ impl<'a, R: ReadChar> Parser<'a, R> { /// /// [spec]: https://tc39.es/ecma262/#prod-Script pub fn parse_script(&mut self, interner: &mut Interner) -> ParseResult { + self.cursor.set_goal(InputElement::HashbangOrRegExp); ScriptParser::new(false).parse(&mut self.cursor, interner) } @@ -155,6 +156,7 @@ impl<'a, R: ReadChar> Parser<'a, R> { where R: ReadChar, { + self.cursor.set_goal(InputElement::HashbangOrRegExp); ModuleParser.parse(&mut self.cursor, interner) } @@ -172,6 +174,7 @@ impl<'a, R: ReadChar> Parser<'a, R> { direct: bool, interner: &mut Interner, ) -> ParseResult { + self.cursor.set_goal(InputElement::HashbangOrRegExp); ScriptParser::new(direct).parse(&mut self.cursor, interner) } From 1f446a8710fa4e78926f60aac80729f5eb3f461a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 02:33:42 +0000 Subject: [PATCH 042/212] Bump clap from 4.5.6 to 4.5.7 in the rust-dependencies group (#3878) Bumps the rust-dependencies group with 1 update: [clap](https://github.com/clap-rs/clap). Updates `clap` from 4.5.6 to 4.5.7 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.5.6...v4.5.7) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a27742458ad..46a9f2aceb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -737,9 +737,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", diff --git a/Cargo.toml b/Cargo.toml index f108d83ce19..1ac83d16600 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ boa_string = { version = "~0.18.0", path = "core/string" } # Shared deps arbitrary = "1" bitflags = "2.5.0" -clap = "4.5.6" +clap = "4.5.7" colored = "2.1.0" fast-float = "0.2.0" hashbrown = { version = "0.14.5", default-features = false } From 69ea2f52ed976934bff588d6b566bae01be313f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Wed, 19 Jun 2024 23:29:12 +0000 Subject: [PATCH 043/212] Fix AsyncGenerator to correctly handle `return` inside `then` (#3879) * Fix invalid assumptions for `AsyncGenerator` * Patch AsyncGenerator functions * Progress * Progress 2 * Finish implementation * cargo fmt * cargo clippy * Fix await resumptions * Assert count in queue test --- .../src/builtins/async_generator/mod.rs | 402 ++++++++---------- core/engine/src/tests/async_generator.rs | 122 ++++++ core/engine/src/tests/mod.rs | 3 + core/engine/src/vm/call_frame/mod.rs | 1 + core/engine/src/vm/completion_record.rs | 4 - core/engine/src/vm/opcode/await/mod.rs | 20 +- core/engine/src/vm/opcode/generator/mod.rs | 34 +- .../src/vm/opcode/generator/yield_stm.rs | 29 +- 8 files changed, 364 insertions(+), 251 deletions(-) create mode 100644 core/engine/src/tests/async_generator.rs diff --git a/core/engine/src/builtins/async_generator/mod.rs b/core/engine/src/builtins/async_generator/mod.rs index 6fa423ddbdc..71796006b2e 100644 --- a/core/engine/src/builtins/async_generator/mod.rs +++ b/core/engine/src/builtins/async_generator/mod.rs @@ -138,21 +138,19 @@ impl AsyncGenerator { .with_message("generator resumed on non generator object") .into() }); - let generator_object = if_abrupt_reject_promise!(result, promise_capability, context); - let result: JsResult<_> = generator_object.downcast_mut::().ok_or_else(|| { + let generator = if_abrupt_reject_promise!(result, promise_capability, context); + let result: JsResult<_> = generator.clone().downcast::().map_err(|_| { JsNativeError::typ() .with_message("generator resumed on non generator object") .into() }); - let mut generator = if_abrupt_reject_promise!(result, promise_capability, context); + let generator = if_abrupt_reject_promise!(result, promise_capability, context); // 5. Let state be generator.[[AsyncGeneratorState]]. - let state = generator.state; + let state = generator.borrow().data.state; // 6. If state is completed, then if state == AsyncGeneratorState::Completed { - drop(generator); - // a. Let iteratorResult be CreateIterResultObject(undefined, true). let iterator_result = create_iter_result_object(JsValue::undefined(), true, context); @@ -170,28 +168,7 @@ impl AsyncGenerator { let completion = CompletionRecord::Normal(args.get_or_undefined(0).clone()); // 8. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). - generator.enqueue(completion.clone(), promise_capability.clone()); - - // 9. If state is either suspendedStart or suspendedYield, then - if state == AsyncGeneratorState::SuspendedStart - || state == AsyncGeneratorState::SuspendedYield - { - // a. Perform AsyncGeneratorResume(generator, completion). - let generator_context = generator - .context - .take() - .expect("generator context cannot be empty here"); - - drop(generator); - - Self::resume( - generator_object, - state, - generator_context, - completion, - context, - ); - } + Self::enqueue(&generator, completion, promise_capability.clone(), context); // 11. Return promiseCapability.[[Promise]]. Ok(promise_capability.promise().clone().into()) @@ -226,49 +203,19 @@ impl AsyncGenerator { .into() }); let generator_object = if_abrupt_reject_promise!(result, promise_capability, context); - let result: JsResult<_> = generator_object.downcast_mut::().ok_or_else(|| { + let result: JsResult<_> = generator_object.clone().downcast::().map_err(|_| { JsNativeError::typ() .with_message("generator resumed on non generator object") .into() }); - let mut generator = if_abrupt_reject_promise!(result, promise_capability, context); + let generator = if_abrupt_reject_promise!(result, promise_capability, context); // 5. Let completion be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. let return_value = args.get_or_undefined(0).clone(); - let completion = CompletionRecord::Return(return_value.clone()); + let completion = CompletionRecord::Return(return_value); // 6. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). - generator.enqueue(completion.clone(), promise_capability.clone()); - - // 7. Let state be generator.[[AsyncGeneratorState]]. - let state = generator.state; - - // 8. If state is either suspendedStart or completed, then - if state == AsyncGeneratorState::SuspendedStart || state == AsyncGeneratorState::Completed { - // a. Set generator.[[AsyncGeneratorState]] to awaiting-return. - generator.state = AsyncGeneratorState::AwaitingReturn; - - // b. Perform ! AsyncGeneratorAwaitReturn(generator). - drop(generator); - Self::await_return(generator_object.clone(), return_value, context); - } - // 9. Else if state is suspendedYield, then - else if state == AsyncGeneratorState::SuspendedYield { - // a. Perform AsyncGeneratorResume(generator, completion). - let generator_context = generator - .context - .take() - .expect("generator context cannot be empty here"); - - drop(generator); - Self::resume( - generator_object, - state, - generator_context, - completion, - context, - ); - } + Self::enqueue(&generator, completion, promise_capability.clone(), context); // 11. Return promiseCapability.[[Promise]]. Ok(promise_capability.promise().clone().into()) @@ -303,30 +250,31 @@ impl AsyncGenerator { .into() }); let generator_object = if_abrupt_reject_promise!(result, promise_capability, context); - let result: JsResult<_> = generator_object.downcast_mut::().ok_or_else(|| { + let result: JsResult<_> = generator_object.clone().downcast::().map_err(|_| { JsNativeError::typ() .with_message("generator resumed on non generator object") .into() }); - let mut generator = if_abrupt_reject_promise!(result, promise_capability, context); + let generator = if_abrupt_reject_promise!(result, promise_capability, context); + let mut gen = generator.borrow_mut(); // 5. Let state be generator.[[AsyncGeneratorState]]. - let mut state = generator.state; + let mut state = gen.data.state; // 6. If state is suspendedStart, then if state == AsyncGeneratorState::SuspendedStart { // a. Set generator.[[AsyncGeneratorState]] to completed. - generator.state = AsyncGeneratorState::Completed; - generator.context = None; + gen.data.state = AsyncGeneratorState::Completed; + gen.data.context = None; // b. Set state to completed. state = AsyncGeneratorState::Completed; } + drop(gen); + // 7. If state is completed, then if state == AsyncGeneratorState::Completed { - drop(generator); - // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »). promise_capability .reject() @@ -346,25 +294,12 @@ impl AsyncGenerator { CompletionRecord::Throw(JsError::from_opaque(args.get_or_undefined(0).clone())); // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). - generator.enqueue(completion.clone(), promise_capability.clone()); - - // 10. If state is suspendedYield, then - if state == AsyncGeneratorState::SuspendedYield { - let generator_context = generator - .context - .take() - .expect("generator context cannot be empty here"); - drop(generator); - - // a. Perform AsyncGeneratorResume(generator, completion). - Self::resume( - generator_object, - state, - generator_context, - completion, - context, - ); - } + Self::enqueue( + &generator, + completion.clone(), + promise_capability.clone(), + context, + ); // 12. Return promiseCapability.[[Promise]]. Ok(promise_capability.promise().clone().into()) @@ -377,10 +312,12 @@ impl AsyncGenerator { /// /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorenqueue pub(crate) fn enqueue( - &mut self, + generator: &JsObject, completion: CompletionRecord, promise_capability: PromiseCapability, + context: &mut Context, ) { + let mut gen = generator.borrow_mut(); // 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }. let request = AsyncGeneratorRequest { completion, @@ -388,7 +325,14 @@ impl AsyncGenerator { }; // 2. Append request to the end of generator.[[AsyncGeneratorQueue]]. - self.queue.push_back(request); + gen.data.queue.push_back(request); + + // Patch that mirrors https://262.ecma-international.org/12.0/#sec-asyncgeneratorenqueue + // This resolves the return bug. + if gen.data.state != AsyncGeneratorState::Executing { + drop(gen); + AsyncGenerator::resume_next(generator, context); + } } /// `AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )` @@ -453,67 +397,17 @@ impl AsyncGenerator { } } - /// `AsyncGeneratorResume ( generator, completion )` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorresume - pub(crate) fn resume( - generator: &JsObject, - state: AsyncGeneratorState, - mut generator_context: GeneratorContext, - completion: CompletionRecord, - context: &mut Context, - ) { - // 1. Assert: generator.[[AsyncGeneratorState]] is either suspendedStart or suspendedYield. - assert!( - state == AsyncGeneratorState::SuspendedStart - || state == AsyncGeneratorState::SuspendedYield - ); - - // 2. Let genContext be generator.[[AsyncGeneratorContext]]. - - // 3. Let callerContext be the running execution context. - // 4. Suspend callerContext. - - // 5. Set generator.[[AsyncGeneratorState]] to executing. - generator - .downcast_mut::() - .expect("already checked before") - .state = AsyncGeneratorState::Executing; - - let (value, resume_kind) = match completion { - CompletionRecord::Normal(val) => (val, GeneratorResumeKind::Normal), - CompletionRecord::Return(val) => (val, GeneratorResumeKind::Return), - CompletionRecord::Throw(err) => (err.to_opaque(context), GeneratorResumeKind::Throw), - }; - // 6. Push genContext onto the execution context stack; genContext is now the running execution context. - - let result = generator_context.resume(Some(value), resume_kind, context); - - // 7. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended it. Let result be the Completion Record returned by the resumed computation. - - generator - .downcast_mut::() - .expect("already checked before") - .context = Some(generator_context); - - // 8. Assert: result is never an abrupt completion. - assert!(!result.is_throw_completion()); - - // 9. Assert: When we return here, genContext has already been removed from the execution context stack and - // callerContext is the currently running execution context. - // 10. Return unused. - } - /// `AsyncGeneratorAwaitReturn ( generator )` /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn - pub(crate) fn await_return(generator: JsObject, value: JsValue, context: &mut Context) { + pub(crate) fn await_return( + generator: JsObject, + value: JsValue, + context: &mut Context, + ) { // 1. Let queue be generator.[[AsyncGeneratorQueue]]. // 2. Assert: queue is not empty. // 3. Let next be the first element of queue. @@ -532,15 +426,14 @@ impl AsyncGenerator { let promise = match promise_completion { Ok(value) => value, Err(value) => { - let mut gen = generator - .downcast_mut::() - .expect("already checked before"); - gen.state = AsyncGeneratorState::Completed; - gen.context = None; - let next = gen.queue.pop_front().expect("queue must not be empty"); - drop(gen); + let next = { + let mut gen = generator.borrow_mut(); + gen.data.state = AsyncGeneratorState::Completed; + gen.data.context = None; + gen.data.queue.pop_front().expect("queue must not be empty") + }; Self::complete_step(&next, Err(value), true, None, context); - Self::drain_queue(&generator, context); + Self::resume_next(&generator, context); return; } }; @@ -552,15 +445,13 @@ impl AsyncGenerator { NativeFunction::from_copy_closure_with_captures( |_this, args, generator, context| { let next = { - let mut gen = generator - .downcast_mut::() - .expect("already checked before"); + let mut gen = generator.borrow_mut(); // a. Set generator.[[AsyncGeneratorState]] to completed. - gen.state = AsyncGeneratorState::Completed; - gen.context = None; + gen.data.state = AsyncGeneratorState::Completed; + gen.data.context = None; - gen.queue.pop_front().expect("must have one entry") + gen.data.queue.pop_front().expect("must have one entry") }; // b. Let result be NormalCompletion(value). @@ -570,7 +461,7 @@ impl AsyncGenerator { Self::complete_step(&next, result, true, None, context); // d. Perform AsyncGeneratorDrainQueue(generator). - Self::drain_queue(generator, context); + Self::resume_next(generator, context); // e. Return undefined. Ok(JsValue::undefined()) @@ -588,24 +479,24 @@ impl AsyncGenerator { context.realm(), NativeFunction::from_copy_closure_with_captures( |_this, args, generator, context| { - let mut gen = generator - .downcast_mut::() - .expect("already checked before"); + let next = { + let mut gen = generator.borrow_mut(); - // a. Set generator.[[AsyncGeneratorState]] to completed. - gen.state = AsyncGeneratorState::Completed; - gen.context = None; + // a. Set generator.[[AsyncGeneratorState]] to completed. + gen.data.state = AsyncGeneratorState::Completed; + gen.data.context = None; + + gen.data.queue.pop_front().expect("must have one entry") + }; // b. Let result be ThrowCompletion(reason). let result = Err(JsError::from_opaque(args.get_or_undefined(0).clone())); // c. Perform AsyncGeneratorCompleteStep(generator, result, true). - let next = gen.queue.pop_front().expect("must have one entry"); - drop(gen); Self::complete_step(&next, result, true, None, context); // d. Perform AsyncGeneratorDrainQueue(generator). - Self::drain_queue(generator, context); + Self::resume_next(generator, context); // e. Return undefined. Ok(JsValue::undefined()) @@ -627,64 +518,135 @@ impl AsyncGenerator { ); } - /// `AsyncGeneratorDrainQueue ( generator )` + /// [`AsyncGeneratorResumeNext ( generator )`][spec] /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue - pub(crate) fn drain_queue(generator: &JsObject, context: &mut Context) { - let mut gen = generator - .downcast_mut::() - .expect("already checked before"); - - // 1. Assert: generator.[[AsyncGeneratorState]] is completed. - assert_eq!(gen.state, AsyncGeneratorState::Completed); - - // 2. Let queue be generator.[[AsyncGeneratorQueue]]. - let queue = &mut gen.queue; - - // 3. If queue is empty, return unused. - if queue.is_empty() { - return; + /// [spec]: https://262.ecma-international.org/12.0/#sec-asyncgeneratorresumenext + pub(crate) fn resume_next(generator: &JsObject, context: &mut Context) { + // 1. Assert: generator is an AsyncGenerator instance. + let mut gen = generator.borrow_mut(); + // 2. Let state be generator.[[AsyncGeneratorState]]. + match gen.data.state { + // 3. Assert: state is not executing. + AsyncGeneratorState::Executing => panic!("3. Assert: state is not executing."), + // 4. If state is awaiting-return, return undefined. + AsyncGeneratorState::AwaitingReturn => return, + _ => {} } - // 4. Let done be false. - // 5. Repeat, while done is false, - loop { - // a. Let next be the first element of queue. - let next = queue.front().expect("must have entry"); - - // b. Let completion be Completion(next.[[Completion]]). - match next.completion.clone() { - // c. If completion.[[Type]] is return, then - CompletionRecord::Return(val) => { - // i. Set generator.[[AsyncGeneratorState]] to awaiting-return. - gen.state = AsyncGeneratorState::AwaitingReturn; + // 5. Let queue be generator.[[AsyncGeneratorQueue]]. + // 6. If queue is an empty List, return undefined. + // 7. Let next be the value of the first element of queue. + // 8. Assert: next is an AsyncGeneratorRequest record. + let Some(next) = gen.data.queue.front() else { + return; + }; + // 9. Let completion be next.[[Completion]]. + let completion = &next.completion; + + match (completion, gen.data.state) { + // 11. Else if state is completed, return ! AsyncGeneratorResolve(generator, undefined, true). + (CompletionRecord::Normal(_), s) => { + if s == AsyncGeneratorState::Completed { + let next = gen + .data + .queue + .pop_front() + .expect("already have a reference to the front"); drop(gen); + AsyncGenerator::complete_step( + &next, + Ok(JsValue::undefined()), + true, + None, + context, + ); + return AsyncGenerator::resume_next(generator, context); + } + } + // b. If state is completed, then + // i. If completion.[[Type]] is return, then + ( + CompletionRecord::Return(val), + AsyncGeneratorState::SuspendedStart | AsyncGeneratorState::Completed, + ) => { + let val = val.clone(); + // 1. Set generator.[[AsyncGeneratorState]] to awaiting-return. + gen.data.state = AsyncGeneratorState::AwaitingReturn; + drop(gen); - // ii. Perform ! AsyncGeneratorAwaitReturn(generator). - Self::await_return(generator.clone(), val, context); + // Steps 2-11 are superseeded by `AsyncGeneratorAwaitReturn` + AsyncGenerator::await_return(generator.clone(), val, context); - // iii. Set done to true. - break; - } - // d. Else, - completion => { - // i. If completion.[[Type]] is normal, then - // 1. Set completion to NormalCompletion(undefined). - let completion = completion.consume().map(|_| JsValue::undefined()); - - // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true). - let next = queue.pop_front().expect("must have entry"); - Self::complete_step(&next, completion, true, None, context); - - // iii. If queue is empty, set done to true. - if queue.is_empty() { - break; - } - } + // 12. Return undefined. + return; } + // ii. Else, + ( + CompletionRecord::Throw(e), + AsyncGeneratorState::SuspendedStart | AsyncGeneratorState::Completed, + ) => { + let e = e.clone(); + // 1. Assert: completion.[[Type]] is throw. + // 2. Perform ! AsyncGeneratorReject(generator, completion.[[Value]]). + gen.data.state = AsyncGeneratorState::Completed; + + let next = gen + .data + .queue + .pop_front() + .expect("already have a reference to the front"); + drop(gen); + AsyncGenerator::complete_step(&next, Err(e), true, None, context); + // 3. Return undefined. + return AsyncGenerator::resume_next(generator, context); + } + _ => {} } + + // 12. Assert: state is either suspendedStart or suspendedYield. + assert!(matches!( + gen.data.state, + AsyncGeneratorState::SuspendedStart | AsyncGeneratorState::SuspendedYield + )); + + let completion = completion.clone(); + + // 16. Set generator.[[AsyncGeneratorState]] to executing. + gen.data.state = AsyncGeneratorState::Executing; + + // 13. Let genContext be generator.[[AsyncGeneratorContext]]. + let mut generator_context = gen + .data + .context + .take() + .expect("generator context cannot be empty here"); + + drop(gen); + + let (value, resume_kind) = match completion { + CompletionRecord::Normal(val) => (val, GeneratorResumeKind::Normal), + CompletionRecord::Return(val) => (val, GeneratorResumeKind::Return), + CompletionRecord::Throw(err) => (err.to_opaque(context), GeneratorResumeKind::Throw), + }; + + // 14. Let callerContext be the running execution context. + // 15. Suspend callerContext. + // 17. Push genContext onto the execution context stack; genContext is now the running execution context. + // 18. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended it. + // Let result be the completion record returned by the resumed computation. + let result = generator_context.resume(Some(value), resume_kind, context); + + // 19. Assert: result is never an abrupt completion. + assert!(!matches!(result, CompletionRecord::Throw(_))); + + generator + .borrow_mut() + .data + .context + .get_or_insert(generator_context); + + // 20. Assert: When we return here, genContext has already been removed from the execution context stack and + // callerContext is the currently running execution context. + // 21. Return undefined. } } diff --git a/core/engine/src/tests/async_generator.rs b/core/engine/src/tests/async_generator.rs new file mode 100644 index 00000000000..82889b1e264 --- /dev/null +++ b/core/engine/src/tests/async_generator.rs @@ -0,0 +1,122 @@ +use crate::{ + builtins::promise::PromiseState, object::JsPromise, run_test_actions, Context, JsValue, + TestAction, +}; +use boa_macros::js_str; +use indoc::indoc; + +#[track_caller] +fn assert_promise_iter_value( + promise: &JsValue, + target: &JsValue, + done: bool, + context: &mut Context, +) { + let promise = JsPromise::from_object(promise.as_object().unwrap().clone()).unwrap(); + let PromiseState::Fulfilled(v) = promise.state() else { + panic!("promise was not fulfilled"); + }; + let o = v.as_object().unwrap(); + let value = o.get(js_str!("value"), context).unwrap(); + let d = o + .get(js_str!("done"), context) + .unwrap() + .as_boolean() + .unwrap(); + assert_eq!(&value, target); + assert_eq!(d, done); +} + +#[test] +fn return_on_then_infinite_loop() { + // Checks that calling `return` inside `then` only enters an infinite loop without + // crashing the engine. + run_test_actions([ + TestAction::run(indoc! {r#" + async function* f() {} + const g = f(); + let count = 0; + Object.defineProperty(Object.prototype, "then", { + get: function() { + if (count < 100) { + count++; + g.return(); + } + return; + }, + }); + g.return(); + "#}), + TestAction::inspect_context(Context::run_jobs), + TestAction::assert_eq("count", 100), + ]); +} + +#[test] +fn return_on_then_single() { + // Checks that calling `return` inside `then` once runs without panicking. + run_test_actions([ + TestAction::run(indoc! {r#" + async function* f() {} + const g = f(); + let first = true; + Object.defineProperty(Object.prototype, "then", { + get: function() { + if (first) { + first = false; + g.return(); + } + return; + }, + }); + let ret = g.return() + "#}), + TestAction::inspect_context(Context::run_jobs), + TestAction::assert_eq("first", false), + TestAction::assert_with_op("ret", |ret, context| { + assert_promise_iter_value(&ret, &JsValue::undefined(), true, context); + true + }), + ]); +} + +#[test] +fn return_on_then_queue() { + // Checks that calling `return` inside `then` doesn't mess with the request queue. + run_test_actions([ + TestAction::run(indoc! {r#" + async function* f() { + yield 1; + yield 2; + } + const g = f(); + let count = 0; + Object.defineProperty(Object.prototype, "then", { + get: function() { + if (count < 2) { + count++; + g.return(); + } + return; + }, + }); + let first = g.next(); + let second = g.next(); + let ret = g.return(); + "#}), + TestAction::inspect_context(Context::run_jobs), + TestAction::assert_with_op("first", |first, context| { + assert_promise_iter_value(&first, &JsValue::from(1), false, context); + true + }), + TestAction::assert_with_op("second", |second, context| { + assert_promise_iter_value(&second, &JsValue::from(2), false, context); + true + }), + TestAction::assert_with_op("ret", |ret, context| { + assert_promise_iter_value(&ret, &JsValue::undefined(), true, context); + true + }), + TestAction::assert_eq("count", JsValue::from(2)), + ]); +} diff --git a/core/engine/src/tests/mod.rs b/core/engine/src/tests/mod.rs index 705375cd778..1bc7712fab8 100644 --- a/core/engine/src/tests/mod.rs +++ b/core/engine/src/tests/mod.rs @@ -1,6 +1,9 @@ +#![cfg(any(not(feature = "intl"), feature = "intl_bundled"))] + use boa_macros::js_str; use indoc::indoc; +mod async_generator; mod control_flow; mod env; mod function; diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index a48f73b543a..92ee7f13c07 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -292,6 +292,7 @@ impl CallFrame { /// # Panics /// /// If the index is out of bounds. + #[track_caller] pub(crate) fn register<'stack>(&self, index: u32, stack: &'stack [JsValue]) -> &'stack JsValue { debug_assert!(index < self.code_block().register_count); let at = self.rp + index; diff --git a/core/engine/src/vm/completion_record.rs b/core/engine/src/vm/completion_record.rs index 18b45d1213f..181347487d1 100644 --- a/core/engine/src/vm/completion_record.rs +++ b/core/engine/src/vm/completion_record.rs @@ -29,10 +29,6 @@ unsafe impl Trace for CompletionRecord { // ---- `CompletionRecord` methods ---- impl CompletionRecord { - pub(crate) const fn is_throw_completion(&self) -> bool { - matches!(self, Self::Throw(_)) - } - /// This function will consume the current `CompletionRecord` and return a `JsResult` // NOTE: rustc bug around evaluating destructors that prevents this from being a const function. // Related issue(s): diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index b78908ab966..d9571669780 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -1,4 +1,6 @@ -use boa_gc::{Gc, GcRefCell}; +use std::cell::Cell; + +use boa_gc::Gc; use boa_macros::js_str; use crate::{ @@ -46,7 +48,17 @@ impl Operation for Await { let gen = GeneratorContext::from_current(context); - let captures = Gc::new(GcRefCell::new(Some(gen))); + // Even though it would be great to avoid cloning, we need to ensure + // the original async generator has a copy of the context in case it is resumed + // by a `return` or `throw` call instead of a continuation. + if let Some(async_generator) = gen.async_generator_object() { + async_generator + .downcast_mut::() + .expect("must be async generator") + .context = Some(gen.clone()); + } + + let captures = Gc::new(Cell::new(Some(gen))); // 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called: // 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »). @@ -58,7 +70,7 @@ impl Operation for Await { // b. Suspend prevContext. // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. // d. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it. - let mut gen = captures.borrow_mut().take().expect("should only run once"); + let mut gen = captures.take().expect("should only run once"); // NOTE: We need to get the object before resuming, since it could clear the stack. let async_generator = gen.async_generator_object(); @@ -100,7 +112,7 @@ impl Operation for Await { // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context. // f. Return undefined. - let mut gen = captures.borrow_mut().take().expect("should only run once"); + let mut gen = captures.take().expect("should only run once"); // NOTE: We need to get the object before resuming, since it could clear the stack. let async_generator = gen.async_generator_object(); diff --git a/core/engine/src/vm/opcode/generator/mod.rs b/core/engine/src/vm/opcode/generator/mod.rs index fb6f98fa90e..d5a47e22a05 100644 --- a/core/engine/src/vm/opcode/generator/mod.rs +++ b/core/engine/src/vm/opcode/generator/mod.rs @@ -118,31 +118,35 @@ impl Operation for AsyncGeneratorClose { fn execute(context: &mut Context) -> JsResult { // Step 3.e-g in [AsyncGeneratorStart](https://tc39.es/ecma262/#sec-asyncgeneratorstart) - let async_generator_object = context + let generator = context .vm .frame() .async_generator_object(&context.vm.stack) - .expect("There should be a object"); - - let mut gen = async_generator_object - .downcast_mut::() + .expect("There should be a object") + .downcast::() .expect("must be async generator"); - gen.state = AsyncGeneratorState::Completed; - gen.context = None; + let mut gen = generator.borrow_mut(); - let next = gen.queue.pop_front().expect("must have item in queue"); - drop(gen); + gen.data.state = AsyncGeneratorState::Completed; + gen.data.context = None; + + let next = gen.data.queue.pop_front().expect("must have item in queue"); let return_value = context.vm.get_return_value(); context.vm.set_return_value(JsValue::undefined()); - if let Some(error) = context.vm.pending_exception.take() { - AsyncGenerator::complete_step(&next, Err(error), true, None, context); - } else { - AsyncGenerator::complete_step(&next, Ok(return_value), true, None, context); - } - AsyncGenerator::drain_queue(&async_generator_object, context); + let completion = context + .vm + .pending_exception + .take() + .map_or(Ok(return_value), Err); + + drop(gen); + + AsyncGenerator::complete_step(&next, completion, true, None, context); + // TODO: Upgrade to the latest spec when the problem is fixed. + AsyncGenerator::resume_next(&generator, context); Ok(CompletionType::Normal) } diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index 1cc692061a6..27cc18335f0 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -44,9 +44,12 @@ impl Operation for AsyncGeneratorYield { .async_generator_object(&context.vm.stack) .expect("`AsyncGeneratorYield` must only be called inside async generators"); let completion = Ok(value); + let async_generator_object = async_generator_object + .downcast::() + .expect("must be async generator object"); let next = async_generator_object - .downcast_mut::() - .expect("must be async generator object") + .borrow_mut() + .data .queue .pop_front() .expect("must have item in queue"); @@ -54,12 +57,15 @@ impl Operation for AsyncGeneratorYield { // TODO: 7. Let previousContext be the second to top element of the execution context stack. AsyncGenerator::complete_step(&next, completion, false, None, context); - let mut generator_object_mut = async_generator_object.borrow_mut(); - let gen = generator_object_mut - .downcast_mut::() - .expect("must be async generator object"); + // TODO: Upgrade to the latest spec when the problem is fixed. + let mut gen = async_generator_object.borrow_mut(); + if gen.data.state == AsyncGeneratorState::Executing { + let Some(next) = gen.data.queue.front() else { + gen.data.state = AsyncGeneratorState::SuspendedYield; + context.vm.set_return_value(JsValue::undefined()); + return Ok(CompletionType::Yield); + }; - if let Some(next) = gen.queue.front() { let resume_kind = match next.completion.clone() { CompletionRecord::Normal(val) => { context.vm.push(val); @@ -81,7 +87,14 @@ impl Operation for AsyncGeneratorYield { return Ok(CompletionType::Normal); } - gen.state = AsyncGeneratorState::SuspendedYield; + assert!(matches!( + gen.data.state, + AsyncGeneratorState::AwaitingReturn | AsyncGeneratorState::Completed + )); + + AsyncGenerator::resume_next(&async_generator_object, context); + + async_generator_object.borrow_mut().data.state = AsyncGeneratorState::SuspendedYield; context.vm.set_return_value(JsValue::undefined()); Ok(CompletionType::Yield) } From 855c1bbde8c3b9a9876c0f63d7d26e1daf69d6e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:48:44 +0200 Subject: [PATCH 044/212] Bump the rust-dependencies group with 3 updates (#3881) Bumps the rust-dependencies group with 3 updates: [syn](https://github.com/dtolnay/syn), [proc-macro2](https://github.com/dtolnay/proc-macro2) and [bytemuck](https://github.com/Lokathor/bytemuck). Updates `syn` from 2.0.66 to 2.0.68 - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.66...2.0.68) Updates `proc-macro2` from 1.0.85 to 1.0.86 - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.85...1.0.86) Updates `bytemuck` from 1.16.0 to 1.16.1 - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.16.0...v1.16.1) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46a9f2aceb8..375a4d937c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" dependencies = [ "bytemuck_derive", ] @@ -2635,9 +2635,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3169,9 +3169,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 1ac83d16600..f061ff91c86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,7 @@ isahc = "1.7.2" rustyline = { version = "14.0.0", default-features = false } dhat = "0.3.3" quote = "1.0.36" -syn = { version = "2.0.66", default-features = false } +syn = { version = "2.0.68", default-features = false } proc-macro2 = "1.0" synstructure = "0.13" measureme = "11.0.1" @@ -109,7 +109,7 @@ dashmap = "5.5.3" num_enum = "0.7.2" itertools = { version = "0.13.0", default-features = false } portable-atomic = "1.6.0" -bytemuck = { version = "1.15.0", default-features = false } +bytemuck = { version = "1.16.1", default-features = false } arrayvec = "0.7.4" intrusive-collections = "0.9.6" cfg-if = "1.0.0" From 149693ae8bba1a69b3ee3f6c25c055f50ad4ce69 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 24 Jun 2024 15:31:26 -0700 Subject: [PATCH 045/212] Adding TryFromJs implementations for tuples (#3843) * Adding TryFromJs implementations for tuples * rustfmt and clippies * rustfmt * Remove unit type * Clippies --- .../src/value/conversions/try_from_js.rs | 51 +++++++++++++++++++ .../value/conversions/try_from_js/tuples.rs | 41 +++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 core/engine/src/value/conversions/try_from_js/tuples.rs diff --git a/core/engine/src/value/conversions/try_from_js.rs b/core/engine/src/value/conversions/try_from_js.rs index dc860a33496..955b57f7239 100644 --- a/core/engine/src/value/conversions/try_from_js.rs +++ b/core/engine/src/value/conversions/try_from_js.rs @@ -5,6 +5,7 @@ use num_bigint::BigInt; use crate::{js_string, Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue}; mod collections; +mod tuples; /// This trait adds a fallible and efficient conversions from a [`JsValue`] to Rust types. pub trait TryFromJs: Sized { @@ -367,6 +368,56 @@ fn value_into_vec() { ]); } +#[test] +fn value_into_tuple() { + use boa_engine::{run_test_actions, TestAction}; + use indoc::indoc; + + run_test_actions([ + TestAction::assert_with_op(indoc! {r#" [42, "hello", true] "#}, |value, context| { + type TestType = (i32, String, bool); + TestType::try_from_js(&value, context).unwrap() == (42, "hello".to_string(), true) + }), + TestAction::assert_with_op(indoc! {r#" [42, "hello", true] "#}, |value, context| { + type TestType = (i32, String, Option, Option); + TestType::try_from_js(&value, context).unwrap() + == (42, "hello".to_string(), Some(true), None) + }), + TestAction::assert_with_op(indoc! {r#" [] "#}, |value, context| { + type TestType = ( + Option, + Option, + Option, + Option, + Option, + Option, + Option, + Option, + Option, + Option, + ); + TestType::try_from_js(&value, context).unwrap() + == (None, None, None, None, None, None, None, None, None, None) + }), + TestAction::assert_with_op(indoc!(r#"[42, "hello", {}]"#), |value, context| { + type TestType = (i32, String, bool); + let Err(value) = TestType::try_from_js(&value, context) else { + return false; + }; + assert!(value.to_string().contains("TypeError")); + true + }), + TestAction::assert_with_op(indoc!(r#"[42, "hello"]"#), |value, context| { + type TestType = (i32, String, bool); + let Err(value) = TestType::try_from_js(&value, context) else { + return false; + }; + assert!(value.to_string().contains("TypeError")); + true + }), + ]); +} + #[test] fn value_into_map() { use boa_engine::{run_test_actions, TestAction}; diff --git a/core/engine/src/value/conversions/try_from_js/tuples.rs b/core/engine/src/value/conversions/try_from_js/tuples.rs new file mode 100644 index 00000000000..babb54a36c6 --- /dev/null +++ b/core/engine/src/value/conversions/try_from_js/tuples.rs @@ -0,0 +1,41 @@ +//! Implementation of [`TryFromJs`] for tuples. +//! +//! Tuples are converted from a JavaScript array, using similar semantics to `TypeScript` tuples: +//! - If the tuple is shorter than the array, the extra elements are ignored. +//! - If the tuple is longer than the array, the extra elements are `undefined`. +//! - If the array is empty, all elements are `undefined`. +//! +//! A tuple of size 0 (unit type) does not implement [`TryFromJs`]. + +use crate::value::JsValue; +use crate::{Context, JsResult}; + +use super::TryFromJs; + +macro_rules! impl_try_from_js_for_tuples { + ($($name:ident),*) => { + impl<$($name: TryFromJs),*> TryFromJs for ($($name,)*) { + fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult { + let vec: Vec = value.try_js_into(context)?; + let mut iter = vec.into_iter(); + + Ok(( + $( + $name::try_from_js(&iter.next().unwrap_or_else(JsValue::undefined), context)?, + )* + )) + } + } + }; +} + +impl_try_from_js_for_tuples!(A); +impl_try_from_js_for_tuples!(A, B); +impl_try_from_js_for_tuples!(A, B, C); +impl_try_from_js_for_tuples!(A, B, C, D); +impl_try_from_js_for_tuples!(A, B, C, D, E); +impl_try_from_js_for_tuples!(A, B, C, D, E, F); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H, I); +impl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H, I, J); From 2458d736690d114cc73dd6d020501a728c02aa1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Tue, 25 Jun 2024 19:21:36 +0000 Subject: [PATCH 046/212] Bump temporal_rs to latest commit (#3880) * Bump temporal_rs to latest commit * Fix build * update test262 * bump temporal pt. 2 * bump temporal pt. 3 * bump temporal (last time) --- Cargo.lock | 257 +++++++++--------- Cargo.toml | 2 +- core/engine/src/builtins/options.rs | 2 +- .../src/builtins/temporal/duration/mod.rs | 7 +- core/engine/src/builtins/temporal/error.rs | 1 + .../src/builtins/temporal/instant/mod.rs | 8 +- core/engine/src/builtins/temporal/options.rs | 50 +--- .../src/builtins/temporal/time_zone/mod.rs | 64 ----- test262_config.toml | 2 +- 9 files changed, 150 insertions(+), 243 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 375a4d937c2..df825ab7405 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -132,7 +132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener-strategy 0.5.2", + "event-listener-strategy", "futures-core", "pin-project-lite", ] @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ "async-lock", "cfg-if", @@ -173,7 +173,7 @@ dependencies = [ "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.7.0", + "polling 3.7.2", "rustix", "slab", "tracing", @@ -182,12 +182,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener 5.3.1", + "event-listener-strategy", "pin-project-lite", ] @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53fc6301894e04a92cb2584fedde80cb25ba8e02d9dc39d4a87d036e22f397d" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" dependencies = [ "async-channel 2.3.1", "async-io", @@ -215,7 +215,7 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.3.0", + "event-listener 5.3.1", "futures-lite 2.3.0", "rustix", "tracing", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ "async-io", "async-lock", @@ -287,9 +287,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -312,12 +312,12 @@ name = "boa_ast" version = "0.18.0" dependencies = [ "arbitrary", - "bitflags 2.5.0", + "bitflags 2.6.0", "boa_interner", "boa_macros", "indexmap", "num-bigint", - "rustc-hash", + "rustc-hash 1.1.0", "serde", ] @@ -345,7 +345,7 @@ name = "boa_engine" version = "0.18.0" dependencies = [ "arrayvec", - "bitflags 2.5.0", + "bitflags 2.6.0", "boa_ast", "boa_gc", "boa_icu_provider", @@ -390,7 +390,7 @@ dependencies = [ "portable-atomic", "rand", "regress", - "rustc-hash", + "rustc-hash 1.1.0", "ryu-js", "serde", "serde_json", @@ -460,7 +460,7 @@ dependencies = [ "indexmap", "once_cell", "phf", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "static_assertions", ] @@ -472,7 +472,7 @@ dependencies = [ "boa_engine", "boa_gc", "boa_macros", - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] @@ -497,7 +497,7 @@ dependencies = [ name = "boa_parser" version = "0.18.0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "boa_ast", "boa_interner", "boa_macros", @@ -507,7 +507,7 @@ dependencies = [ "num-bigint", "num-traits", "regress", - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] @@ -516,7 +516,7 @@ version = "0.18.0" dependencies = [ "measureme", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] @@ -526,7 +526,7 @@ dependencies = [ "boa_engine", "boa_gc", "indoc", - "rustc-hash", + "rustc-hash 1.1.0", "textwrap", ] @@ -537,7 +537,7 @@ dependencies = [ "boa_macros", "fast-float", "paste", - "rustc-hash", + "rustc-hash 1.1.0", "sptr", "static_assertions", ] @@ -546,7 +546,7 @@ dependencies = [ name = "boa_tester" version = "0.18.0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "boa_engine", "boa_gc", "boa_runtime", @@ -557,7 +557,7 @@ dependencies = [ "comfy-table", "phf", "rayon", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "serde_repr", @@ -682,9 +682,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" [[package]] name = "cfg-if" @@ -753,7 +753,7 @@ version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -761,9 +761,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clipboard-win" @@ -954,7 +954,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", "parking_lot", @@ -1082,7 +1082,7 @@ dependencies = [ "lazy_static", "mintex", "parking_lot", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "thousands", @@ -1090,9 +1090,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", @@ -1174,43 +1174,22 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.0", + "event-listener 5.3.1", "pin-project-lite", ] @@ -1437,12 +1416,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1455,6 +1428,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "http" version = "0.2.12" @@ -1492,9 +1471,9 @@ dependencies = [ [[package]] name = "icu_calendar" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7defdcb785245edd773a38b56f0ccef39065cdc4509a7d85435f6edea0fd58c5" +checksum = "7265b2137f9a36f7634a308d91f984574bbdba8cfd95ceffe1c345552275a8ff" dependencies = [ "calendrical_calculations", "databake", @@ -1517,9 +1496,9 @@ checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0" [[package]] name = "icu_casemap" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7701c9ff229dae365623cfb1b24c86552fe3a8e82877f9a75400b95452a9c02" +checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f" dependencies = [ "databake", "displaydoc", @@ -1810,9 +1789,9 @@ dependencies = [ [[package]] name = "icu_properties" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "databake", "displaydoc", @@ -1988,7 +1967,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] @@ -2050,6 +2029,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "ixdtf" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2123305f927452a9502fc05c33800280d90127c95c50eb45ec6b3c50346afbf3" +dependencies = [ + "displaydoc", +] + [[package]] name = "jemalloc-sys" version = "0.5.4+5.3.0-patched" @@ -2081,9 +2069,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -2170,15 +2158,15 @@ dependencies = [ "memmap2", "parking_lot", "perf-event-open-sys", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", ] [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2206,9 +2194,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -2238,7 +2226,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", @@ -2306,7 +2294,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -2515,9 +2503,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", "fastrand 2.1.0", @@ -2576,13 +2564,13 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.4.0", "pin-project-lite", "rustix", "tracing", @@ -2709,11 +2697,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -2724,8 +2712,8 @@ checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2740,13 +2728,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -2757,9 +2745,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "regress" @@ -2798,13 +2786,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2854,7 +2848,7 @@ version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "clipboard-win", "fd-lock", @@ -2972,9 +2966,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "itoa", "ryu", @@ -3144,17 +3138,17 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -3163,9 +3157,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -3207,13 +3201,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "temporal_rs" version = "0.0.2" -source = "git+https://github.com/boa-dev/temporal.git?rev=61a05fbb7c72353deda72a3df0e6887d65b840d2#61a05fbb7c72353deda72a3df0e6887d65b840d2" +source = "git+https://github.com/boa-dev/temporal.git?rev=ec2f2d00294ee641285ec1570df6683ecd0d1a8e#ec2f2d00294ee641285ec1570df6683ecd0d1a8e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "icu_calendar", + "ixdtf", "num-bigint", "num-traits", - "rustc-hash", + "rustc-hash 2.0.0", "tinystr", ] @@ -3370,9 +3365,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -3434,7 +3429,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.9", + "winnow 0.6.13", ] [[package]] @@ -3561,9 +3556,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe-libyaml" @@ -3596,9 +3591,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -3619,9 +3614,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -3816,9 +3811,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] @@ -4004,9 +3999,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -4114,9 +4109,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +checksum = "5d7fce6acea41ceb5b97f7aee91a5d876d5fbca1f847cb60e13afecd7093cad0" dependencies = [ "databake", "serde", @@ -4127,9 +4122,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index f061ff91c86..9602ca3e89c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,7 +115,7 @@ intrusive-collections = "0.9.6" cfg-if = "1.0.0" either = "1.12.0" sys-locale = "0.3.1" -temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "61a05fbb7c72353deda72a3df0e6887d65b840d2" } +temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "ec2f2d00294ee641285ec1570df6683ecd0d1a8e" } web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.9.0" diff --git a/core/engine/src/builtins/options.rs b/core/engine/src/builtins/options.rs index 368566e0686..4c939c91649 100644 --- a/core/engine/src/builtins/options.rs +++ b/core/engine/src/builtins/options.rs @@ -116,7 +116,7 @@ impl OptionType for f64 { if !value.is_finite() { return Err(JsNativeError::range() - .with_message("roundingIncrement must be finite.") + .with_message("numeric option must be finite.") .into()); } diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index f22ef7599c1..7c1c91adcd2 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -18,11 +18,11 @@ use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::Duration as InnerDuration, - options::{RelativeTo, TemporalRoundingMode, TemporalUnit}, + options::{RelativeTo, RoundingIncrement, TemporalRoundingMode, TemporalUnit}, }; use super::{ - options::{get_temporal_rounding_increment, get_temporal_unit, TemporalUnitGroup}, + options::{get_temporal_unit, TemporalUnitGroup}, to_integer_if_integral, DateTimeValues, }; @@ -658,7 +658,8 @@ impl Duration { super::to_relative_temporal_object(&round_to, context)?; // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo). - let rounding_increment = get_temporal_rounding_increment(&round_to, context)?; + let rounding_increment = + get_option::(&round_to, js_str!("roundingIncrement"), context)?; // 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). let rounding_mode = diff --git a/core/engine/src/builtins/temporal/error.rs b/core/engine/src/builtins/temporal/error.rs index 22757acdf9b..5755434b4ef 100644 --- a/core/engine/src/builtins/temporal/error.rs +++ b/core/engine/src/builtins/temporal/error.rs @@ -9,6 +9,7 @@ impl From for JsNativeError { ErrorKind::Type => JsNativeError::typ().with_message(value.message()), ErrorKind::Generic => JsNativeError::error().with_message(value.message()), ErrorKind::Syntax => JsNativeError::syntax().with_message(value.message()), + ErrorKind::Assert => JsNativeError::error().with_message("internal engine error"), } } } diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index d4c927e4ca4..3c02f38be55 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -23,7 +23,7 @@ use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::Instant as InnerInstant, - options::{TemporalRoundingMode, TemporalUnit}, + options::{RoundingIncrement, TemporalRoundingMode, TemporalUnit}, }; /// The `Temporal.Instant` object. @@ -287,7 +287,8 @@ impl Instant { // Fetch the necessary options. let options = get_options_object(args.get_or_undefined(1))?; let mode = get_option::(&options, js_str!("roundingMode"), context)?; - let increment = get_option::(&options, js_str!("roundingIncrement"), context)?; + let increment = + get_option::(&options, js_str!("roundingIncrement"), context)?; let smallest_unit = get_option::(&options, js_str!("smallestUnit"), context)?; let largest_unit = get_option::(&options, js_str!("largestUnit"), context)?; let result = instant @@ -315,7 +316,8 @@ impl Instant { let other = to_temporal_instant(args.get_or_undefined(0))?; let options = get_options_object(args.get_or_undefined(1))?; let mode = get_option::(&options, js_str!("roundingMode"), context)?; - let increment = get_option::(&options, js_str!("roundingIncrement"), context)?; + let increment = + get_option::(&options, js_str!("roundingIncrement"), context)?; let smallest_unit = get_option::(&options, js_str!("smallestUnit"), context)?; let largest_unit = get_option::(&options, js_str!("largestUnit"), context)?; let result = instant diff --git a/core/engine/src/builtins/temporal/options.rs b/core/engine/src/builtins/temporal/options.rs index a13fe2865de..146c875ccd9 100644 --- a/core/engine/src/builtins/temporal/options.rs +++ b/core/engine/src/builtins/temporal/options.rs @@ -9,53 +9,17 @@ // https://github.com/tc39/proposal-temporal/blob/main/polyfill/index.d.ts use crate::{ - builtins::options::{get_option, ParsableOptionType}, - js_string, + builtins::options::{get_option, OptionType, ParsableOptionType}, string::JsStr, - Context, JsNativeError, JsObject, JsResult, + Context, JsNativeError, JsObject, JsResult, JsValue, }; use temporal_rs::options::{ ArithmeticOverflow, DurationOverflow, InstantDisambiguation, OffsetDisambiguation, - TemporalRoundingMode, TemporalUnit, + RoundingIncrement, TemporalRoundingMode, TemporalUnit, }; // TODO: Expand docs on the below options. -// TODO: Remove and refactor: migrate to `boa_temporal` -#[inline] -pub(crate) fn get_temporal_rounding_increment( - options: &JsObject, - context: &mut Context, -) -> JsResult> { - // 1. Let increment be ? GetOption(normalizedOptions, "roundingIncrement", "number", undefined, 1𝔽). - let value = options.get(js_string!("roundingIncrement"), context)?; - - if value.is_undefined() { - return Ok(None); - } - let increment = value.to_number(context)?; - - // 2. If increment is not finite, throw a RangeError exception. - if !increment.is_finite() { - return Err(JsNativeError::range() - .with_message("rounding increment was out of range.") - .into()); - } - - // 3. Let integerIncrement be truncate(ℝ(increment)). - let integer_increment = increment.trunc(); - - // 4. If integerIncrement < 1 or integerIncrement > 10^9, throw a RangeError exception. - if !(1.0..=1_000_000_000.0).contains(&integer_increment) { - return Err(JsNativeError::range() - .with_message("rounding increment was out of range.") - .into()); - } - - // 5. Return integerIncrement. - Ok(Some(integer_increment)) -} - /// Gets the `TemporalUnit` from an options object. #[inline] pub(crate) fn get_temporal_unit( @@ -135,3 +99,11 @@ impl ParsableOptionType for DurationOverflow {} impl ParsableOptionType for InstantDisambiguation {} impl ParsableOptionType for OffsetDisambiguation {} impl ParsableOptionType for TemporalRoundingMode {} + +impl OptionType for RoundingIncrement { + fn from_value(value: JsValue, context: &mut Context) -> JsResult { + let value = value.to_number(context)?; + + Ok(RoundingIncrement::try_from(value)?) + } +} diff --git a/core/engine/src/builtins/temporal/time_zone/mod.rs b/core/engine/src/builtins/temporal/time_zone/mod.rs index a1e35e93e6a..652d76d5240 100644 --- a/core/engine/src/builtins/temporal/time_zone/mod.rs +++ b/core/engine/src/builtins/temporal/time_zone/mod.rs @@ -336,70 +336,6 @@ pub(super) fn create_temporal_time_zone( .into()) } -/// Abstract operation `ParseTimeZoneOffsetString ( offsetString )` -/// -/// The abstract operation `ParseTimeZoneOffsetString` takes argument `offsetString` (a String). It -/// parses the argument as a numeric UTC offset string and returns a signed integer representing -/// that offset as a number of nanoseconds. -/// -/// More information: -/// - [ECMAScript specififcation][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-parsetimezoneoffsetstring -#[allow(clippy::unnecessary_wraps, unused)] -fn parse_timezone_offset_string(offset_string: &str, context: &mut Context) -> JsResult { - use temporal_rs::parser::{Cursor, TemporalTimeZoneString}; - - // 1. Let parseResult be ParseText(StringToCodePoints(offsetString), UTCOffset). - let parse_result = TemporalTimeZoneString::parse(&mut Cursor::new(offset_string))?; - - // 2. Assert: parseResult is not a List of errors. - // 3. Assert: parseResult contains a TemporalSign Parse Node. - let Some(utc_offset) = parse_result.offset else { - return Err(JsNativeError::typ() - .with_message("Offset string was not a valid offset") - .into()); - }; - - // 4. Let parsedSign be the source text matched by the TemporalSign Parse Node contained within - // parseResult. - // 5. If parsedSign is the single code point U+002D (HYPHEN-MINUS) or U+2212 (MINUS SIGN), then - let sign = utc_offset.sign; - // a. Let sign be -1. - // 6. Else, - // a. Let sign be 1. - - // 7. NOTE: Applications of StringToNumber below do not lose precision, since each of the parsed - // values is guaranteed to be a sufficiently short string of decimal digits. - // 8. Assert: parseResult contains an Hour Parse Node. - // 9. Let parsedHours be the source text matched by the Hour Parse Node contained within parseResult. - let parsed_hours = utc_offset.hour; - - // 10. Let hours be ℝ(StringToNumber(CodePointsToString(parsedHours))). - // 11. If parseResult does not contain a MinuteSecond Parse Node, then - // a. Let minutes be 0. - // 12. Else, - // a. Let parsedMinutes be the source text matched by the first MinuteSecond Parse Node contained within parseResult. - // b. Let minutes be ℝ(StringToNumber(CodePointsToString(parsedMinutes))). - // 13. If parseResult does not contain two MinuteSecond Parse Nodes, then - // a. Let seconds be 0. - // 14. Else, - // a. Let parsedSeconds be the source text matched by the second MinuteSecond Parse Node contained within parseResult. - // b. Let seconds be ℝ(StringToNumber(CodePointsToString(parsedSeconds))). - // 15. If parseResult does not contain a TemporalDecimalFraction Parse Node, then - // a. Let nanoseconds be 0. - // 16. Else, - // a. Let parsedFraction be the source text matched by the TemporalDecimalFraction Parse Node contained within parseResult. - // b. Let fraction be the string-concatenation of CodePointsToString(parsedFraction) and "000000000". - // c. Let nanosecondsString be the substring of fraction from 1 to 10. - // d. Let nanoseconds be ℝ(StringToNumber(nanosecondsString)). - // 17. Return sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + nanoseconds). - - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) -} - /// Abstract operation `FormatTimeZoneOffsetString ( offsetNanoseconds )` fn format_time_zone_offset_string(offset_nanoseconds: i64) -> String { // 1. Assert: offsetNanoseconds is an integer. diff --git a/test262_config.toml b/test262_config.toml index ba75e1590d6..d5a1842e242 100644 --- a/test262_config.toml +++ b/test262_config.toml @@ -1,4 +1,4 @@ -commit = "c00830acef42bdb0e917b5fdec76ed9d399c0eea" +commit = "c47b716e8d6bea0c4510d449fd22b7ed5f8b0151" [ignored] # Not implemented yet: From 2d91cd1e2cf4494a7c7c204c3faa21c6ee314d12 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Sat, 29 Jun 2024 04:09:03 +0200 Subject: [PATCH 047/212] Remove `FormalParameterList` from CodeBlock (#3882) It is not used in strict mode and in non-strict mode it's only used to construct the binding indices for the `MappedArguments`. --- .../engine/src/builtins/function/arguments.rs | 72 ++++++++++--------- core/engine/src/bytecompiler/declarations.rs | 1 + core/engine/src/bytecompiler/mod.rs | 15 +++- core/engine/src/vm/code_block.rs | 10 +-- core/engine/src/vm/opcode/arguments.rs | 2 +- .../src/vm/opcode/rest_parameter/mod.rs | 6 +- 6 files changed, 61 insertions(+), 45 deletions(-) diff --git a/core/engine/src/builtins/function/arguments.rs b/core/engine/src/builtins/function/arguments.rs index 001185c24b0..2112aeaf62e 100644 --- a/core/engine/src/builtins/function/arguments.rs +++ b/core/engine/src/builtins/function/arguments.rs @@ -13,6 +13,7 @@ use crate::{ use boa_ast::{function::FormalParameterList, operations::bound_names}; use boa_gc::{Finalize, Gc, Trace}; use rustc_hash::FxHashMap; +use thin_vec::{thin_vec, ThinVec}; #[derive(Debug, Copy, Clone, Trace, Finalize, JsData)] #[boa_gc(empty_trace)] @@ -137,30 +138,7 @@ impl MappedArguments { } impl MappedArguments { - /// Creates a new mapped Arguments exotic object. - /// - /// - #[allow(clippy::new_ret_no_self)] - pub(crate) fn new( - func: &JsObject, - formals: &FormalParameterList, - arguments_list: &[JsValue], - env: &Gc, - context: &mut Context, - ) -> JsObject { - // 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers. - // It may contain duplicate identifiers. - // 2. Let len be the number of elements in argumentsList. - let len = arguments_list.len(); - - // 3. Let obj be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »). - // 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1. - // 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2. - // 6. Set obj.[[Get]] as specified in 10.4.4.3. - // 7. Set obj.[[Set]] as specified in 10.4.4.4. - // 8. Set obj.[[Delete]] as specified in 10.4.4.5. - // 9. Set obj.[[Prototype]] to %Object.prototype%. - + pub(crate) fn binding_indices(formals: &FormalParameterList) -> ThinVec> { // Section 17-19 are done first, for easier object creation in 11. // // The section 17-19 differs from the spec, due to the way the runtime environments work. @@ -196,14 +174,9 @@ impl MappedArguments { // In the case of duplicate parameter names, the last one is bound as the environment binding. // // The following logic implements the steps 17-19 adjusted for our environment structure. - let mut bindings = FxHashMap::default(); let mut property_index = 0; for name in bound_names(formals) { - if property_index >= len { - break; - } - // NOTE(HalidOdat): Offset by +1 to account for the first binding ("argument"). let binding_index = bindings.len() as u32 + 1; @@ -215,15 +188,44 @@ impl MappedArguments { property_index += 1; } - let mut map = MappedArguments { - binding_indices: vec![None; property_index], - environment: env.clone(), - }; - + let mut binding_indices = thin_vec![None; property_index]; for (binding_index, property_index) in bindings.values() { - map.binding_indices[*property_index] = Some(*binding_index); + binding_indices[*property_index] = Some(*binding_index); } + binding_indices + } + + /// Creates a new mapped Arguments exotic object. + /// + /// + #[allow(clippy::new_ret_no_self)] + pub(crate) fn new( + func: &JsObject, + binding_indices: &[Option], + arguments_list: &[JsValue], + env: &Gc, + context: &mut Context, + ) -> JsObject { + // 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers. + // It may contain duplicate identifiers. + // 2. Let len be the number of elements in argumentsList. + let len = arguments_list.len(); + + // 3. Let obj be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »). + // 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1. + // 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2. + // 6. Set obj.[[Get]] as specified in 10.4.4.3. + // 7. Set obj.[[Set]] as specified in 10.4.4.4. + // 8. Set obj.[[Delete]] as specified in 10.4.4.5. + // 9. Set obj.[[Prototype]] to %Object.prototype%. + + let range = binding_indices.len().min(len); + let map = MappedArguments { + binding_indices: binding_indices[..range].to_vec(), + environment: env.clone(), + }; + // %Array.prototype.values% let values_function = context.intrinsics().objects().array_prototype_values(); diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index 793f8a25d43..941e349f44f 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -1182,6 +1182,7 @@ impl ByteCompiler<'_> { // default value initializers, or any destructured parameters. // ii. Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, env). self.emit_opcode(Opcode::CreateMappedArgumentsObject); + self.emitted_mapped_arguments_object_opcode = true; } // c. If strict is true, then diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index bad92dad8e8..94082e90e0b 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -14,7 +14,7 @@ mod utils; use std::{cell::Cell, rc::Rc}; use crate::{ - builtins::function::ThisMode, + builtins::function::{arguments::MappedArguments, ThisMode}, environments::{BindingLocator, BindingLocatorError, CompileTimeEnvironment}, js_string, vm::{ @@ -307,6 +307,9 @@ pub struct ByteCompiler<'ctx> { /// Whether the function is in a `with` statement. pub(crate) in_with: bool, + /// Used to determine if a we emited a `CreateUnmappedArgumentsObject` opcode + pub(crate) emitted_mapped_arguments_object_opcode: bool, + pub(crate) interner: &'ctx mut Interner, #[cfg(feature = "annex-b")] @@ -361,6 +364,7 @@ impl<'ctx> ByteCompiler<'ctx> { #[cfg(feature = "annex-b")] annex_b_function_names: Vec::new(), in_with, + emitted_mapped_arguments_object_opcode: false, } } @@ -1575,12 +1579,19 @@ impl<'ctx> ByteCompiler<'ctx> { handler.stack_count += self.register_count; } + let mapped_arguments_binding_indices = if self.emitted_mapped_arguments_object_opcode { + MappedArguments::binding_indices(&self.params) + } else { + ThinVec::new() + }; + CodeBlock { name: self.function_name, length: self.length, register_count: self.register_count, this_mode: self.this_mode, - params: self.params, + parameter_length: self.params.as_ref().len() as u32, + mapped_arguments_binding_indices, bytecode: self.bytecode.into_boxed_slice(), constants: self.constants, bindings: self.bindings.into_boxed_slice(), diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index 66f8682690c..b3fbb40a78b 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -12,7 +12,6 @@ use crate::{ Context, JsBigInt, JsString, JsValue, }; use bitflags::bitflags; -use boa_ast::function::FormalParameterList; use boa_gc::{empty_trace, Finalize, Gc, Trace}; use boa_profiler::Profiler; use std::{cell::Cell, fmt::Display, mem::size_of, rc::Rc}; @@ -137,14 +136,16 @@ pub struct CodeBlock { /// The number of arguments expected. pub(crate) length: u32, + pub(crate) parameter_length: u32, + pub(crate) register_count: u32, /// `[[ThisMode]]` pub(crate) this_mode: ThisMode, - /// Parameters passed to this function. + /// Used for constructing a `MappedArguments` object. #[unsafe_ignore_trace] - pub(crate) params: FormalParameterList, + pub(crate) mapped_arguments_binding_indices: ThinVec>, /// Bytecode #[unsafe_ignore_trace] @@ -180,7 +181,8 @@ impl CodeBlock { length, register_count: 0, this_mode: ThisMode::Global, - params: FormalParameterList::default(), + mapped_arguments_binding_indices: ThinVec::new(), + parameter_length: 0, handlers: ThinVec::default(), ic: Box::default(), } diff --git a/core/engine/src/vm/opcode/arguments.rs b/core/engine/src/vm/opcode/arguments.rs index 7daf372ee31..92a86560528 100644 --- a/core/engine/src/vm/opcode/arguments.rs +++ b/core/engine/src/vm/opcode/arguments.rs @@ -31,7 +31,7 @@ impl Operation for CreateMappedArgumentsObject { let env = context.vm.environments.current(); let arguments = MappedArguments::new( &function_object, - &code.params, + &code.mapped_arguments_binding_indices, &args, env.declarative_expect(), context, diff --git a/core/engine/src/vm/opcode/rest_parameter/mod.rs b/core/engine/src/vm/opcode/rest_parameter/mod.rs index eda0764c610..daabd434f5d 100644 --- a/core/engine/src/vm/opcode/rest_parameter/mod.rs +++ b/core/engine/src/vm/opcode/rest_parameter/mod.rs @@ -17,12 +17,12 @@ impl Operation for RestParameterInit { const COST: u8 = 6; fn execute(context: &mut Context) -> JsResult { - let argument_count = context.vm.frame().argument_count as usize; - let param_count = context.vm.frame().code_block().params.as_ref().len(); + let argument_count = context.vm.frame().argument_count; + let param_count = context.vm.frame().code_block().parameter_length; let array = if argument_count >= param_count { let rest_count = argument_count - param_count + 1; - let args = context.vm.pop_n_values(rest_count); + let args = context.vm.pop_n_values(rest_count as usize); Array::create_array_from_list(args, context) } else { Array::array_create(0, None, context).expect("could not create an empty array") From 9738d44749304d2212f95016d8c383aa9025f908 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Sun, 30 Jun 2024 10:24:07 +0100 Subject: [PATCH 048/212] Combine `HasProperty` and `Get` operations when possible (#3883) --- core/engine/src/builtins/array/mod.rs | 157 +++++++----------- core/engine/src/builtins/error/mod.rs | 6 +- .../engine/src/builtins/function/arguments.rs | 39 ++++- core/engine/src/builtins/intl/locale/utils.rs | 7 +- core/engine/src/builtins/proxy/mod.rs | 25 +++ .../src/builtins/typed_array/builtin.rs | 20 +-- .../engine/src/builtins/typed_array/object.rs | 41 ++++- core/engine/src/environments/runtime/mod.rs | 6 +- core/engine/src/error.rs | 8 +- core/engine/src/module/namespace.rs | 88 +++++++++- .../engine/src/object/internal_methods/mod.rs | 81 +++++++++ core/engine/src/object/jsobject.rs | 26 ++- core/engine/src/object/operations.rs | 22 +++ 13 files changed, 371 insertions(+), 155 deletions(-) diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index 31472c605fc..6fb6fa8118a 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -811,11 +811,9 @@ impl Array { for k in 0..len { // 1. Let P be ! ToString(𝔽(k)). // 2. Let exists be ? HasProperty(E, P). - let exists = item.has_property(k, context)?; // 3. If exists is true, then - if exists { - // a. Let subElement be ? Get(E, P). - let sub_element = item.get(k, context)?; + // 3.a. Let subElement be ? Get(E, P). + if let Some(sub_element) = item.try_get(k, context)? { // b. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), subElement). arr.create_data_property_or_throw(n, sub_element, context)?; } @@ -960,11 +958,9 @@ impl Array { // a. Let Pk be ! ToString(𝔽(k)). let pk = k; // b. Let kPresent be ? HasProperty(O, Pk). - let present = o.has_property(pk, context)?; // c. If kPresent is true, then - if present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(pk, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(pk, context)? { // ii. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »). let this_arg = args.get_or_undefined(1); callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?; @@ -1093,47 +1089,37 @@ impl Array { // Skipped: b. Let upperP be ! ToString(𝔽(upper)). // Skipped: c. Let lowerP be ! ToString(𝔽(lower)). // d. Let lowerExists be ? HasProperty(O, lowerP). - let lower_exists = o.has_property(lower, context)?; // e. If lowerExists is true, then - let lower_value = if lower_exists { - // i. Let lowerValue be ? Get(O, lowerP). - o.get(lower, context)? - } else { - JsValue::undefined() - }; + // e.i. Let lowerValue be ? Get(O, lowerP). + let lower_value = o.try_get(lower, context)?; // f. Let upperExists be ? HasProperty(O, upperP). - let upper_exists = o.has_property(upper, context)?; // g. If upperExists is true, then - let upper_value = if upper_exists { - // i. Let upperValue be ? Get(O, upperP). - o.get(upper, context)? - } else { - JsValue::undefined() - }; - match (lower_exists, upper_exists) { + // g.i. Let upperValue be ? Get(O, upperP). + let upper_value = o.try_get(upper, context)?; + match (lower_value, upper_value) { // h. If lowerExists is true and upperExists is true, then - (true, true) => { + (Some(lower_value), Some(upper_value)) => { // i. Perform ? Set(O, lowerP, upperValue, true). o.set(lower, upper_value, true, context)?; // ii. Perform ? Set(O, upperP, lowerValue, true). o.set(upper, lower_value, true, context)?; } // i. Else if lowerExists is false and upperExists is true, then - (false, true) => { + (None, Some(upper_value)) => { // i. Perform ? Set(O, lowerP, upperValue, true). o.set(lower, upper_value, true, context)?; // ii. Perform ? DeletePropertyOrThrow(O, upperP). o.delete_property_or_throw(upper, context)?; } // j. Else if lowerExists is true and upperExists is false, then - (true, false) => { + (Some(lower_value), None) => { // i. Perform ? DeletePropertyOrThrow(O, lowerP). o.delete_property_or_throw(lower, context)?; // ii. Perform ? Set(O, upperP, lowerValue, true). o.set(upper, lower_value, true, context)?; } // k. Else, - (false, false) => { + (None, None) => { // i. Assert: lowerExists and upperExists are both false. // ii. No action is required. } @@ -1255,11 +1241,9 @@ impl Array { // b. Let to be ! ToString(𝔽(k - 1)). let to = k - 1; // c. Let fromPresent be ? HasProperty(O, from). - let from_present = o.has_property(from, context)?; // d. If fromPresent is true, then - if from_present { - // i. Let fromVal be ? Get(O, from). - let from_val = o.get(from, context)?; + // d.i. Let fromVal be ? Get(O, from). + if let Some(from_val) = o.try_get(from, context)? { // ii. Perform ? Set(O, to, fromVal, true). o.set(to, from_val, true, context)?; // e. Else, @@ -1318,11 +1302,9 @@ impl Array { // ii. Let to be ! ToString(𝔽(k + argCount - 1)). let to = k + arg_count - 1; // iii. Let fromPresent be ? HasProperty(O, from). - let from_present = o.has_property(from, context)?; // iv. If fromPresent is true, then - if from_present { - // 1. Let fromValue be ? Get(O, from). - let from_value = o.get(from, context)?; + // iv.1. Let fromValue be ? Get(O, from). + if let Some(from_value) = o.try_get(from, context)? { // 2. Perform ? Set(O, to, fromValue, true). o.set(to, from_value, true, context)?; // v. Else, @@ -1383,11 +1365,9 @@ impl Array { for k in 0..len { // a. Let Pk be ! ToString(𝔽(k)). // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(k, context)?; // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(k, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(k, context)? { // ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). let test_result = callback .call(this_arg, &[k_value, k.into(), o.clone().into()], context)? @@ -1438,11 +1418,9 @@ impl Array { for k in 0..len { // a. Let Pk be ! ToString(𝔽(k)). // b. Let k_present be ? HasProperty(O, Pk). - let k_present = o.has_property(k, context)?; // c. If k_present is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(k, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(k, context)? { // ii. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »). let mapped_value = callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?; @@ -1514,11 +1492,9 @@ impl Array { // 10. Repeat, while k < len, while k < len { // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))). - let k_present = o.has_property(k, context)?; // b. If kPresent is true, then - if k_present { - // i. Let elementK be ? Get(O, ! ToString(𝔽(k))). - let element_k = o.get(k, context)?; + // b.i. Let elementK be ? Get(O, ! ToString(𝔽(k))). + if let Some(element_k) = o.try_get(k, context)? { // ii. Let same be IsStrictlyEqual(searchElement, elementK). // iii. If same is true, return 𝔽(k). if search_element.strict_equals(&element_k) { @@ -1590,11 +1566,9 @@ impl Array { // 8. Repeat, while k ≥ 0, while k >= 0 { // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))). - let k_present = o.has_property(k, context)?; // b. If kPresent is true, then - if k_present { - // i. Let elementK be ? Get(O, ! ToString(𝔽(k))). - let element_k = o.get(k, context)?; + // b.i. Let elementK be ? Get(O, ! ToString(𝔽(k))). + if let Some(element_k) = o.try_get(k, context)? { // ii. Let same be IsStrictlyEqual(searchElement, elementK). // iii. If same is true, return 𝔽(k). if JsValue::strict_equals(search_element, &element_k) { @@ -1907,12 +1881,9 @@ impl Array { let p = source_index; // b. Let exists be ? HasProperty(source, P). - let exists = source.has_property(p, context)?; // c. If exists is true, then - if exists { - // i. Let element be Get(source, P) - let mut element = source.get(p, context)?; - + // c.i. Let element be Get(source, P) + if let Some(mut element) = source.try_get(p, context)? { // ii. If mapperFunction is present, then if let Some(mapper_function) = mapper_function { // 1. Set element to ? Call(mapperFunction, thisArg, <>) @@ -2156,11 +2127,9 @@ impl Array { // a. Let Pk be ! ToString(𝔽(k)). let pk = k; // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(pk, context)?; // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(pk, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(pk, context)? { // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), kValue). a.create_data_property_or_throw(n, k_value, context)?; } @@ -2327,10 +2296,8 @@ impl Array { for k in 0..actual_delete_count { // a. Let from be ! ToString(𝔽(actualStart + k)). // b. If ? HasProperty(O, from) is true, then - if o.has_property(actual_start + k, context)? { - // i. Let fromValue be ? Get(O, from). - let from_value = o.get(actual_start + k, context)?; - + // b.i. Let fromValue be ? Get(O, from). + if let Some(from_value) = o.try_get(actual_start + k, context)? { // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue). arr.create_data_property_or_throw(k, from_value, context)?; } @@ -2356,10 +2323,8 @@ impl Array { let to = k + item_count; // iii. If ? HasProperty(O, from) is true, then - if o.has_property(from, context)? { - // 1. Let fromValue be ? Get(O, from). - let from_value = o.get(from, context)?; - + // iii.1. Let fromValue be ? Get(O, from). + if let Some(from_value) = o.try_get(from, context)? { // 2. Perform ? Set(O, to, fromValue, true). o.set(to, from_value, true, context)?; } else { @@ -2391,10 +2356,8 @@ impl Array { let to = k + item_count; // iii. If ? HasProperty(O, from) is true, then - if o.has_property(from, context)? { - // 1. Let fromValue be ? Get(O, from). - let from_value = o.get(from, context)?; - + // iii.1. Let fromValue be ? Get(O, from). + if let Some(from_value) = o.try_get(from, context)? { // 2. Perform ? Set(O, to, fromValue, true). o.set(to, from_value, true, context)?; } @@ -2555,10 +2518,8 @@ impl Array { // a. Let Pk be ! ToString(𝔽(k)). // b. Let kPresent be ? HasProperty(O, Pk). // c. If kPresent is true, then - if o.has_property(idx, context)? { - // i. Let kValue be ? Get(O, Pk). - let element = o.get(idx, context)?; - + // c.i. Let kValue be ? Get(O, Pk). + if let Some(element) = o.try_get(idx, context)? { let args = [element.clone(), JsValue::new(idx), JsValue::new(o.clone())]; // ii. Let selected be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). @@ -2612,11 +2573,9 @@ impl Array { for k in 0..len { // a. Let Pk be ! ToString(𝔽(k)). // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(k, context)?; // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(k, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(k, context)? { // ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). let this_arg = args.get_or_undefined(1); let test_result = callback @@ -2868,11 +2827,13 @@ impl Array { // i. Let Pk be ! ToString(𝔽(k)). let pk = k; // ii. Set kPresent to ? HasProperty(O, Pk). - k_present = o.has_property(pk, context)?; // iii. If kPresent is true, then - if k_present { - // 1. Set accumulator to ? Get(O, Pk). - accumulator = o.get(pk, context)?; + // iii.1. Set accumulator to ? Get(O, Pk). + if let Some(v) = o.try_get(pk, context)? { + accumulator = v; + k_present = true; + } else { + k_present = false; } // iv. Set k to k + 1. k += 1; @@ -2890,11 +2851,9 @@ impl Array { // a. Let Pk be ! ToString(𝔽(k)). let pk = k; // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(pk, context)?; // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(pk, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(pk, context)? { // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »). accumulator = callback.call( &JsValue::undefined(), @@ -2962,11 +2921,13 @@ impl Array { // i. Let Pk be ! ToString(𝔽(k)). let pk = k; // ii. Set kPresent to ? HasProperty(O, Pk). - k_present = o.has_property(pk, context)?; // iii. If kPresent is true, then - if k_present { - // 1. Set accumulator to ? Get(O, Pk). - accumulator = o.get(pk, context)?; + // iii.1. Set accumulator to ? Get(O, Pk). + if let Some(v) = o.try_get(pk, context)? { + k_present = true; + accumulator = v; + } else { + k_present = false; } // iv. Set k to k - 1. k -= 1; @@ -2984,11 +2945,9 @@ impl Array { // a. Let Pk be ! ToString(𝔽(k)). let pk = k; // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(pk, context)?; // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(pk, context)?; + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(pk, context)? { // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »). accumulator = callback.call( &JsValue::undefined(), @@ -3071,11 +3030,9 @@ impl Array { let to_key = to; // c. Let fromPresent be ? HasProperty(O, fromKey). - let from_present = o.has_property(from_key, context)?; // d. If fromPresent is true, then - if from_present { - // i. Let fromVal be ? Get(O, fromKey). - let from_val = o.get(from_key, context)?; + // d.i. Let fromVal be ? Get(O, fromKey). + if let Some(from_val) = o.try_get(from_key, context)? { // ii. Perform ? Set(O, toKey, fromVal, true). o.set(to_key, from_val, true, context)?; // e. Else, diff --git a/core/engine/src/builtins/error/mod.rs b/core/engine/src/builtins/error/mod.rs index 39911f71156..343080137bd 100644 --- a/core/engine/src/builtins/error/mod.rs +++ b/core/engine/src/builtins/error/mod.rs @@ -210,11 +210,9 @@ impl Error { context: &mut Context, ) -> JsResult<()> { // 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then + // 1.a. Let cause be ? Get(options, "cause"). if let Some(options) = options.as_object() { - if options.has_property(js_str!("cause"), context)? { - // a. Let cause be ? Get(options, "cause"). - let cause = options.get(js_str!("cause"), context)?; - + if let Some(cause) = options.try_get(js_str!("cause"), context)? { // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). o.create_non_enumerable_data_property_or_throw(js_str!("cause"), cause, context); } diff --git a/core/engine/src/builtins/function/arguments.rs b/core/engine/src/builtins/function/arguments.rs index 2112aeaf62e..c21b544a3c4 100644 --- a/core/engine/src/builtins/function/arguments.rs +++ b/core/engine/src/builtins/function/arguments.rs @@ -3,7 +3,8 @@ use crate::{ object::{ internal_methods::{ ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property, - ordinary_set, InternalMethodContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS, + ordinary_set, ordinary_try_get, InternalMethodContext, InternalObjectMethods, + ORDINARY_INTERNAL_METHODS, }, JsObject, }, @@ -87,6 +88,7 @@ impl JsData for MappedArguments { static METHODS: InternalObjectMethods = InternalObjectMethods { __get_own_property__: arguments_exotic_get_own_property, __define_own_property__: arguments_exotic_define_own_property, + __try_get__: arguments_exotic_try_get, __get__: arguments_exotic_get, __set__: arguments_exotic_set, __delete__: arguments_exotic_delete, @@ -394,6 +396,41 @@ pub(crate) fn arguments_exotic_define_own_property( Ok(true) } +/// Internal optimization method for `Arguments` exotic objects. +/// +/// This method combines the internal methods `OrdinaryHasProperty` and `[[Get]]`. +/// +/// More information: +/// - [ECMAScript reference OrdinaryHasProperty][spec0] +/// - [ECMAScript reference Get][spec1] +/// +/// [spec0]: https://tc39.es/ecma262/#sec-ordinaryhasproperty +/// [spec1]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver +pub(crate) fn arguments_exotic_try_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + if let PropertyKey::Index(index) = key { + // 1. Let map be args.[[ParameterMap]]. + // 2. Let isMapped be ! HasOwnProperty(map, P). + if let Some(value) = obj + .downcast_ref::() + .expect("arguments exotic method must only be callable from arguments objects") + .get(index.get()) + { + // a. Assert: map contains a formal parameter mapping for P. + // b. Return Get(map, P). + return Ok(Some(value)); + } + } + + // 3. If isMapped is false, then + // a. Return ? OrdinaryGet(args, P, Receiver). + ordinary_try_get(obj, key, receiver, context) +} + /// `[[Get]]` for arguments exotic objects. /// /// More information: diff --git a/core/engine/src/builtins/intl/locale/utils.rs b/core/engine/src/builtins/intl/locale/utils.rs index 3e496804e03..d2b536889e9 100644 --- a/core/engine/src/builtins/intl/locale/utils.rs +++ b/core/engine/src/builtins/intl/locale/utils.rs @@ -93,12 +93,9 @@ pub(crate) fn canonicalize_locale_list( for k in 0..len { // a. Let Pk be ToString(k). // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(k, context)?; // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(k, context)?; - + // c.i. Let kValue be ? Get(O, Pk). + if let Some(k_value) = o.try_get(k, context)? { // ii. If Type(kValue) is not String or Object, throw a TypeError exception. if !(k_value.is_object() || k_value.is_string()) { return Err(JsNativeError::typ() diff --git a/core/engine/src/builtins/proxy/mod.rs b/core/engine/src/builtins/proxy/mod.rs index ff6327c63c5..880838fdc94 100644 --- a/core/engine/src/builtins/proxy/mod.rs +++ b/core/engine/src/builtins/proxy/mod.rs @@ -53,6 +53,7 @@ impl JsData for Proxy { __get_own_property__: proxy_exotic_get_own_property, __define_own_property__: proxy_exotic_define_own_property, __has_property__: proxy_exotic_has_property, + __try_get__: proxy_exotic_try_get, __get__: proxy_exotic_get, __set__: proxy_exotic_set, __delete__: proxy_exotic_delete, @@ -759,6 +760,30 @@ pub(crate) fn proxy_exotic_has_property( Ok(boolean_trap_result) } +/// Internal optimization method for `Proxy` exotic objects. +/// +/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`. +/// +/// More information: +/// - [ECMAScript reference HasProperty][spec0] +/// - [ECMAScript reference Get][spec1] +/// +/// [spec0]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p +/// [spec1]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver +pub(crate) fn proxy_exotic_try_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + // Note: For now, this just calls the normal methods. Could be optimized further. + if proxy_exotic_has_property(obj, key, context)? { + Ok(Some(proxy_exotic_get(obj, key, receiver, context)?)) + } else { + Ok(None) + } +} + /// `10.5.8 [[Get]] ( P, Receiver )` /// /// More information: diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index faa4961e6e1..c419132b2e1 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -1155,15 +1155,9 @@ impl BuiltinTypedArray { let ta = ta.upcast(); for k in k..len { // a. Let kPresent be ! HasProperty(O, ! ToString(𝔽(k))). - let k_present = ta - .has_property(k, context) - .expect("HasProperty cannot fail here"); - // b. If kPresent is true, then - if k_present { - // i. Let elementK be ! Get(O, ! ToString(𝔽(k))). - let element_k = ta.get(k, context).expect("Get cannot fail here"); - + // b.i. Let elementK be ! Get(O, ! ToString(𝔽(k))). + if let Some(element_k) = ta.try_get(k, context).expect("Get cannot fail here") { // ii. Let same be IsStrictlyEqual(searchElement, elementK). // iii. If same is true, return 𝔽(k). if args.get_or_undefined(0).strict_equals(&element_k) { @@ -1296,15 +1290,9 @@ impl BuiltinTypedArray { let ta = ta.upcast(); for k in (0..k).rev() { // a. Let kPresent be ! HasProperty(O, ! ToString(𝔽(k))). - let k_present = ta - .has_property(k, context) - .expect("HasProperty cannot fail here"); - // b. If kPresent is true, then - if k_present { - // i. Let elementK be ! Get(O, ! ToString(𝔽(k))). - let element_k = ta.get(k, context).expect("Get cannot fail here"); - + // b.i. Let elementK be ! Get(O, ! ToString(𝔽(k))). + if let Some(element_k) = ta.try_get(k, context).expect("Get cannot fail here") { // ii. Let same be IsStrictlyEqual(searchElement, elementK). // iii. If same is true, return 𝔽(k). if args.get_or_undefined(0).strict_equals(&element_k) { diff --git a/core/engine/src/builtins/typed_array/object.rs b/core/engine/src/builtins/typed_array/object.rs index dc59a60427a..0389956c5e4 100644 --- a/core/engine/src/builtins/typed_array/object.rs +++ b/core/engine/src/builtins/typed_array/object.rs @@ -7,8 +7,8 @@ use crate::{ object::{ internal_methods::{ ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property, - ordinary_has_property, ordinary_set, InternalMethodContext, InternalObjectMethods, - ORDINARY_INTERNAL_METHODS, + ordinary_has_property, ordinary_set, ordinary_try_get, InternalMethodContext, + InternalObjectMethods, ORDINARY_INTERNAL_METHODS, }, JsData, JsObject, }, @@ -42,6 +42,7 @@ impl JsData for TypedArray { __get_own_property__: typed_array_exotic_get_own_property, __has_property__: typed_array_exotic_has_property, __define_own_property__: typed_array_exotic_define_own_property, + __try_get__: typed_array_exotic_try_get, __get__: typed_array_exotic_get, __set__: typed_array_exotic_set, __delete__: typed_array_exotic_delete, @@ -413,6 +414,42 @@ pub(crate) fn typed_array_exotic_define_own_property( ordinary_define_own_property(obj, key, desc, context) } +/// Internal optimization method for `TypedArray` exotic objects. +/// +/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`. +/// +/// More information: +/// - [ECMAScript reference HasProperty][spec0] +/// - [ECMAScript reference Get][spec1] +/// +/// [spec0]: https://tc39.es/ecma262/#sec-typedarray-hasproperty +/// [spec1]: https://tc39.es/ecma262/#sec-typedarray-get +pub(crate) fn typed_array_exotic_try_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + let p = match key { + PropertyKey::String(key) => { + // 1.a. Let numericIndex be CanonicalNumericIndexString(P). + canonical_numeric_index_string(key) + } + PropertyKey::Index(index) => Some(index.get().into()), + PropertyKey::Symbol(_) => None, + }; + + // 1. If P is a String, then + // 1.b. If numericIndex is not undefined, then + if let Some(numeric_index) = p { + // i. Return IntegerIndexedElementGet(O, numericIndex). + return Ok(typed_array_get_element(obj, numeric_index)); + } + + // 2. Return ? OrdinaryGet(O, P, Receiver). + ordinary_try_get(obj, key, receiver, context) +} + /// Internal method `[[Get]]` for `TypedArray` exotic objects. /// /// More information: diff --git a/core/engine/src/environments/runtime/mod.rs b/core/engine/src/environments/runtime/mod.rs index 59cc5a0aeb2..b72512a5892 100644 --- a/core/engine/src/environments/runtime/mod.rs +++ b/core/engine/src/environments/runtime/mod.rs @@ -621,11 +621,7 @@ impl Context { if locator.global { let global = self.global_object(); let key = locator.name().clone(); - if global.has_property(key.clone(), self)? { - global.get(key, self).map(Some) - } else { - Ok(None) - } + global.try_get(key, self) } else { match self.environment_expect(locator.environment_index) { Environment::Declarative(env) => Ok(env.get(locator.binding_index)), diff --git a/core/engine/src/error.rs b/core/engine/src/error.rs index 4bf3637d623..44adccd7f8f 100644 --- a/core/engine/src/error.rs +++ b/core/engine/src/error.rs @@ -233,13 +233,7 @@ impl JsError { .ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?; let try_get_property = |key: JsString, name, context: &mut Context| { - obj.has_property(key.clone(), context) - .map_err(|e| TryNativeError::InaccessibleProperty { - property: name, - source: e, - })? - .then(|| obj.get(key, context)) - .transpose() + obj.try_get(key, context) .map_err(|e| TryNativeError::InaccessibleProperty { property: name, source: e, diff --git a/core/engine/src/module/namespace.rs b/core/engine/src/module/namespace.rs index fff27d75743..1874d481bb7 100644 --- a/core/engine/src/module/namespace.rs +++ b/core/engine/src/module/namespace.rs @@ -8,7 +8,7 @@ use boa_gc::{Finalize, Trace}; use crate::object::internal_methods::immutable_prototype::immutable_prototype_exotic_set_prototype_of; use crate::object::internal_methods::{ ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property, - ordinary_has_property, ordinary_own_property_keys, InternalMethodContext, + ordinary_has_property, ordinary_own_property_keys, ordinary_try_get, InternalMethodContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS, }; use crate::object::{JsData, JsPrototype}; @@ -38,6 +38,7 @@ impl JsData for ModuleNamespace { __get_own_property__: module_namespace_exotic_get_own_property, __define_own_property__: module_namespace_exotic_define_own_property, __has_property__: module_namespace_exotic_has_property, + __try_get__: module_namespace_exotic_try_get, __get__: module_namespace_exotic_get, __set__: module_namespace_exotic_set, __delete__: module_namespace_exotic_delete, @@ -242,6 +243,91 @@ fn module_namespace_exotic_has_property( Ok(exports.contains(&key)) } +/// Internal optimization method for `Module Namespace` exotic objects. +/// +/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`. +/// +/// More information: +/// - [ECMAScript reference HasProperty][spec0] +/// - [ECMAScript reference Get][spec1] +/// +/// [spec0]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p +/// [spec1]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver +fn module_namespace_exotic_try_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + // 1. If P is a Symbol, then + // a. Return ! OrdinaryGet(O, P, Receiver). + let key = match key { + PropertyKey::Symbol(_) => return ordinary_try_get(obj, key, receiver, context), + PropertyKey::Index(idx) => js_string!(format!("{}", idx.get())), + PropertyKey::String(s) => s.clone(), + }; + + let obj = obj + .downcast_ref::() + .expect("internal method can only be called on module namespace objects"); + + // 2. Let exports be O.[[Exports]]. + let exports = obj.exports(); + + // 3. If exports does not contain P, return undefined. + let Some(export_name) = exports.get(&key).cloned() else { + return Ok(None); + }; + + // 4. Let m be O.[[Module]]. + let m = obj.module(); + + // 5. Let binding be m.ResolveExport(P). + let binding = m + .resolve_export( + export_name.clone(), + &mut HashSet::default(), + context.interner(), + ) + .expect("6. Assert: binding is a ResolvedBinding Record."); + + // 7. Let targetModule be binding.[[Module]]. + // 8. Assert: targetModule is not undefined. + let target_module = binding.module(); + + // TODO: cache binding resolution instead of doing the whole process on every access. + if let BindingName::Name(name) = binding.binding_name() { + // 10. Let targetEnv be targetModule.[[Environment]]. + let Some(env) = target_module.environment() else { + // 11. If targetEnv is empty, throw a ReferenceError exception. + let import = export_name.to_std_string_escaped(); + return Err(JsNativeError::reference() + .with_message(format!( + "cannot get import `{import}` from an uninitialized module" + )) + .into()); + }; + + let locator = env + .compile_env() + .get_binding(&name) + .expect("checked before that the name was reachable"); + + // 12. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true). + env.get(locator.binding_index()).map(Some).ok_or_else(|| { + let import = export_name.to_std_string_escaped(); + + JsNativeError::reference() + .with_message(format!("cannot get uninitialized import `{import}`")) + .into() + }) + } else { + // 9. If binding.[[BindingName]] is namespace, then + // a. Return GetModuleNamespace(targetModule). + Ok(Some(target_module.namespace(context).into())) + } +} + /// [`[[Get]] ( P, Receiver )`][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver diff --git a/core/engine/src/object/internal_methods/mod.rs b/core/engine/src/object/internal_methods/mod.rs index 4e1efa75821..ac671752f3c 100644 --- a/core/engine/src/object/internal_methods/mod.rs +++ b/core/engine/src/object/internal_methods/mod.rs @@ -179,6 +179,26 @@ impl JsObject { (self.vtable().__has_property__)(self, key, context) } + /// Internal optimization method. + /// + /// This method combines the internal methods `[[hasProperty]]` and `[[Get]]`. + /// + /// More information: + /// - [ECMAScript reference hasProperty][spec0] + /// - [ECMAScript reference get][spec1] + /// + /// [spec0]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p + /// [spec1]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver + pub(crate) fn __try_get__( + &self, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, + ) -> JsResult> { + let _timer = Profiler::global().start_event("Object::__try_get__", "object"); + (self.vtable().__try_get__)(self, key, receiver, context) + } + /// Internal method `[[Get]]` /// /// Get the specified property of this object or its prototype. @@ -308,6 +328,7 @@ pub(crate) static ORDINARY_INTERNAL_METHODS: InternalObjectMethods = InternalObj __get_own_property__: ordinary_get_own_property, __define_own_property__: ordinary_define_own_property, __has_property__: ordinary_has_property, + __try_get__: ordinary_try_get, __get__: ordinary_get, __set__: ordinary_set, __delete__: ordinary_delete, @@ -345,6 +366,12 @@ pub struct InternalObjectMethods { fn(&JsObject, &PropertyKey, &mut InternalMethodContext<'_>) -> JsResult, pub(crate) __get__: fn(&JsObject, &PropertyKey, JsValue, &mut InternalMethodContext<'_>) -> JsResult, + pub(crate) __try_get__: fn( + &JsObject, + &PropertyKey, + JsValue, + &mut InternalMethodContext<'_>, + ) -> JsResult>, pub(crate) __set__: fn( &JsObject, PropertyKey, @@ -643,6 +670,60 @@ pub(crate) fn ordinary_get( } } +/// Abstract optimization operation. +/// +/// This operation combines the abstract operations `OrdinaryHasProperty` and `OrdinaryGet`. +/// +/// More information: +/// - [ECMAScript reference OrdinaryHasProperty][spec0] +/// - [ECMAScript reference OrdinaryGet][spec1] +/// +/// [spec0]: https://tc39.es/ecma262/#sec-ordinaryhasproperty +/// [spec1]: https://tc39.es/ecma262/#sec-ordinaryget +pub(crate) fn ordinary_try_get( + obj: &JsObject, + key: &PropertyKey, + receiver: JsValue, + context: &mut InternalMethodContext<'_>, +) -> JsResult> { + let _timer = Profiler::global().start_event("Object::ordinary_try_get", "object"); + // 1. Assert: IsPropertyKey(P) is true. + // 2. Let desc be ? O.[[GetOwnProperty]](P). + match obj.__get_own_property__(key, context)? { + // If desc is undefined, then + None => { + // a. Let parent be ? O.[[GetPrototypeOf]](). + if let Some(parent) = obj.__get_prototype_of__(context)? { + context.slot().set_not_cachable_if_already_prototype(); + context.slot().attributes |= SlotAttributes::PROTOTYPE; + + // c. Return ? parent.[[Get]](P, Receiver). + parent.__try_get__(key, receiver, context) + } + // b. If parent is null, return undefined. + else { + Ok(None) + } + } + Some(ref desc) => { + match desc.kind() { + // 4. If IsDataDescriptor(desc) is true, return desc.[[Value]]. + DescriptorKind::Data { + value: Some(value), .. + } => Ok(Some(value.clone())), + // 5. Assert: IsAccessorDescriptor(desc) is true. + // 6. Let getter be desc.[[Get]]. + DescriptorKind::Accessor { get: Some(get), .. } if !get.is_undefined() => { + // 8. Return ? Call(getter, Receiver). + get.call(&receiver, &[], context).map(Some) + } + // 7. If getter is undefined, return undefined. + _ => Ok(Some(JsValue::undefined())), + } + } + } +} + /// Abstract operation `OrdinarySet`. /// /// More information: diff --git a/core/engine/src/object/jsobject.rs b/core/engine/src/object/jsobject.rs index c7d1718f329..a920db35aff 100644 --- a/core/engine/src/object/jsobject.rs +++ b/core/engine/src/object/jsobject.rs @@ -379,41 +379,40 @@ impl JsObject { // 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable"). // 4. If hasEnumerable is true, then ... - if self.has_property(js_str!("enumerable"), context)? { + if let Some(enumerable) = self.try_get(js_str!("enumerable"), context)? { // a. Let enumerable be ! ToBoolean(? Get(Obj, "enumerable")). // b. Set desc.[[Enumerable]] to enumerable. - desc = desc.enumerable(self.get(js_str!("enumerable"), context)?.to_boolean()); + desc = desc.enumerable(enumerable.to_boolean()); } // 5. Let hasConfigurable be ? HasProperty(Obj, "configurable"). // 6. If hasConfigurable is true, then ... - if self.has_property(js_str!("configurable"), context)? { + if let Some(configurable) = self.try_get(js_str!("configurable"), context)? { // a. Let configurable be ! ToBoolean(? Get(Obj, "configurable")). // b. Set desc.[[Configurable]] to configurable. - desc = desc.configurable(self.get(js_str!("configurable"), context)?.to_boolean()); + desc = desc.configurable(configurable.to_boolean()); } // 7. Let hasValue be ? HasProperty(Obj, "value"). // 8. If hasValue is true, then ... - if self.has_property(js_str!("value"), context)? { + if let Some(value) = self.try_get(js_str!("value"), context)? { // a. Let value be ? Get(Obj, "value"). // b. Set desc.[[Value]] to value. - desc = desc.value(self.get(js_str!("value"), context)?); + desc = desc.value(value); } // 9. Let hasWritable be ? HasProperty(Obj, ). // 10. If hasWritable is true, then ... - if self.has_property(js_str!("writable"), context)? { + if let Some(writable) = self.try_get(js_str!("writable"), context)? { // a. Let writable be ! ToBoolean(? Get(Obj, "writable")). // b. Set desc.[[Writable]] to writable. - desc = desc.writable(self.get(js_str!("writable"), context)?.to_boolean()); + desc = desc.writable(writable.to_boolean()); } // 11. Let hasGet be ? HasProperty(Obj, "get"). // 12. If hasGet is true, then - let get = if self.has_property(js_str!("get"), context)? { - // a. Let getter be ? Get(Obj, "get"). - let getter = self.get(js_str!("get"), context)?; + // 12.a. Let getter be ? Get(Obj, "get"). + let get = if let Some(getter) = self.try_get(js_str!("get"), context)? { // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception. // todo: extract IsCallable to be callable from Value if !getter.is_undefined() && getter.as_object().map_or(true, |o| !o.is_callable()) { @@ -429,9 +428,8 @@ impl JsObject { // 13. Let hasSet be ? HasProperty(Obj, "set"). // 14. If hasSet is true, then - let set = if self.has_property(js_str!("set"), context)? { - // 14.a. Let setter be ? Get(Obj, "set"). - let setter = self.get(js_str!("set"), context)?; + // 14.a. Let setter be ? Get(Obj, "set"). + let set = if let Some(setter) = self.try_get(js_str!("set"), context)? { // 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception. // todo: extract IsCallable to be callable from Value if !setter.is_undefined() && setter.as_object().map_or(true, |o| !o.is_callable()) { diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index 0301d6c10b6..79e7a26d995 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -323,6 +323,28 @@ impl JsObject { self.__has_property__(&key.into(), &mut InternalMethodContext::new(context)) } + /// Abstract optimization operation. + /// + /// Check if an object has a property and get it if it exists. + /// This operation combines the abstract operations `HasProperty` and `Get`. + /// + /// More information: + /// - [ECMAScript reference HasProperty][spec0] + /// - [ECMAScript reference Get][spec1] + /// + /// [spec0]: https://tc39.es/ecma262/#sec-hasproperty + /// [spec1]: https://tc39.es/ecma262/#sec-get-o-p + pub(crate) fn try_get(&self, key: K, context: &mut Context) -> JsResult> + where + K: Into, + { + self.__try_get__( + &key.into(), + self.clone().into(), + &mut InternalMethodContext::new(context), + ) + } + /// Check if object has an own property. /// /// More information: From eb439557ed0ad6ab2299ff1eb42e97bbf4d837c9 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Sun, 30 Jun 2024 22:02:06 +0100 Subject: [PATCH 049/212] Remove some environment clones (#3884) --- core/engine/src/builtins/eval/mod.rs | 2 +- core/engine/src/builtins/function/arguments.rs | 2 +- core/engine/src/environments/runtime/mod.rs | 17 ++++++++--------- core/engine/src/module/source.rs | 4 ++-- core/engine/src/module/synthetic.rs | 2 +- core/engine/src/vm/opcode/arguments.rs | 2 +- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 1555473f6f4..7c592d6cfb6 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -229,7 +229,7 @@ impl Eval { } }); - let var_environment = context.vm.environments.outer_function_environment(); + let var_environment = context.vm.environments.outer_function_environment().clone(); let mut var_env = var_environment.compile_env(); let lex_env = context.vm.environments.current_compile_environment(); diff --git a/core/engine/src/builtins/function/arguments.rs b/core/engine/src/builtins/function/arguments.rs index c21b544a3c4..e8630582235 100644 --- a/core/engine/src/builtins/function/arguments.rs +++ b/core/engine/src/builtins/function/arguments.rs @@ -207,7 +207,7 @@ impl MappedArguments { binding_indices: &[Option], arguments_list: &[JsValue], env: &Gc, - context: &mut Context, + context: &Context, ) -> JsObject { // 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers. // It may contain duplicate identifiers. diff --git a/core/engine/src/environments/runtime/mod.rs b/core/engine/src/environments/runtime/mod.rs index b72512a5892..ed67f32f7a6 100644 --- a/core/engine/src/environments/runtime/mod.rs +++ b/core/engine/src/environments/runtime/mod.rs @@ -77,11 +77,11 @@ impl EnvironmentStack { } /// Gets the current global environment. - pub(crate) fn global(&self) -> Gc { - let env = self.stack[0].clone(); + pub(crate) fn global(&self) -> &Gc { + let env = &self.stack[0]; match env { - Environment::Declarative(ref env) => env.clone(), + Environment::Declarative(ref env) => env, Environment::Object(_) => { unreachable!("first environment should be the global environment") } @@ -89,7 +89,7 @@ impl EnvironmentStack { } /// Gets the next outer function environment. - pub(crate) fn outer_function_environment(&self) -> Gc { + pub(crate) fn outer_function_environment(&self) -> &Gc { for env in self .stack .iter() @@ -97,7 +97,7 @@ impl EnvironmentStack { .rev() { if let DeclarativeEnvironmentKind::Function(_) = &env.kind() { - return env.clone(); + return env; } } self.global() @@ -288,11 +288,10 @@ impl EnvironmentStack { /// /// Panics if no environment exists on the stack. #[track_caller] - pub(crate) fn current(&self) -> Environment { + pub(crate) fn current_ref(&self) -> &Environment { self.stack .last() .expect("global environment must always exist") - .clone() } /// Get the compile environment for the current runtime environment. @@ -502,7 +501,7 @@ impl Context { /// are completely removed of runtime checks because the specification guarantees that runtime /// semantics cannot add or remove lexical bindings. pub(crate) fn find_runtime_binding(&mut self, locator: &mut BindingLocator) -> JsResult<()> { - let current = self.vm.environments.current(); + let current = self.vm.environments.current_ref(); if let Some(env) = current.as_declarative() { if !env.with() && !env.poisoned() { return Ok(()); @@ -553,7 +552,7 @@ impl Context { &mut self, locator: &BindingLocator, ) -> JsResult> { - let current = self.vm.environments.current(); + let current = self.vm.environments.current_ref(); if let Some(env) = current.as_declarative() { if !env.with() { return Ok(None); diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index f136b3c47c7..81c204ec071 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1665,7 +1665,7 @@ impl SourceTextModule { BindingName::Name(name) => context .vm .environments - .current() + .current_ref() .declarative_expect() .kind() .as_module() @@ -1704,7 +1704,7 @@ impl SourceTextModule { let env = frame .environments - .current() + .current_ref() .as_declarative() .cloned() .expect("frame must have a declarative environment"); diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index 81446866bf8..330b57995c5 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -315,7 +315,7 @@ impl SyntheticModule { } let env = envs - .current() + .current_ref() .as_declarative() .cloned() .expect("should have the module environment"); diff --git a/core/engine/src/vm/opcode/arguments.rs b/core/engine/src/vm/opcode/arguments.rs index 92a86560528..8149c84e1d5 100644 --- a/core/engine/src/vm/opcode/arguments.rs +++ b/core/engine/src/vm/opcode/arguments.rs @@ -28,7 +28,7 @@ impl Operation for CreateMappedArgumentsObject { let code = context.vm.frame().code_block().clone(); let args = context.vm.frame().arguments(&context.vm).to_vec(); - let env = context.vm.environments.current(); + let env = context.vm.environments.current_ref(); let arguments = MappedArguments::new( &function_object, &code.mapped_arguments_binding_indices, From 4cc47b6dfe6c31c86d31c02a3171668222753e62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:57:18 +0000 Subject: [PATCH 050/212] Bump baptiste0928/cargo-install in the ci-dependencies group (#3885) Bumps the ci-dependencies group with 1 update: [baptiste0928/cargo-install](https://github.com/baptiste0928/cargo-install). Updates `baptiste0928/cargo-install` from 3.1.0 to 3.1.1 - [Release notes](https://github.com/baptiste0928/cargo-install/releases) - [Changelog](https://github.com/baptiste0928/cargo-install/blob/main/CHANGELOG.md) - [Commits](https://github.com/baptiste0928/cargo-install/compare/v3.1.0...v3.1.1) --- updated-dependencies: - dependency-name: baptiste0928/cargo-install dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/rust.yml | 2 +- .github/workflows/webassembly.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 10bc3589d7a..0da5ff0e49a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,7 +31,7 @@ jobs: with: key: tarpaulin - name: Install cargo-tarpaulin - uses: baptiste0928/cargo-install@v3.1.0 + uses: baptiste0928/cargo-install@v3.1.1 with: crate: cargo-tarpaulin diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index 2ac89d3abf6..9ff6e514c4f 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -39,7 +39,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Install wasm-pack - uses: baptiste0928/cargo-install@v3.1.0 + uses: baptiste0928/cargo-install@v3.1.1 with: crate: wasm-pack - name: Build Playground From 1a01d1fe1e8e0e93f02ec1e77d535000725145d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 19:14:04 +0200 Subject: [PATCH 051/212] Bump the rust-dependencies group with 5 updates (#3886) Bumps the rust-dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [clap](https://github.com/clap-rs/clap) | `4.5.7` | `4.5.8` | | [num-bigint](https://github.com/rust-num/num-bigint) | `0.4.5` | `0.4.6` | | [serde_json](https://github.com/serde-rs/json) | `1.0.118` | `1.0.119` | | [log](https://github.com/rust-lang/log) | `0.4.21` | `0.4.22` | | [either](https://github.com/rayon-rs/either) | `1.12.0` | `1.13.0` | Updates `clap` from 4.5.7 to 4.5.8 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.7...v4.5.8) Updates `num-bigint` from 0.4.5 to 0.4.6 - [Changelog](https://github.com/rust-num/num-bigint/blob/master/RELEASES.md) - [Commits](https://github.com/rust-num/num-bigint/compare/num-bigint-0.4.5...num-bigint-0.4.6) Updates `serde_json` from 1.0.118 to 1.0.119 - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.118...v1.0.119) Updates `log` from 0.4.21 to 0.4.22 - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.21...0.4.22) Updates `either` from 1.12.0 to 1.13.0 - [Commits](https://github.com/rayon-rs/either/compare/1.12.0...1.13.0) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: num-bigint dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: log dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: either dependency-type: direct:production update-type: version-update:semver-minor dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 28 ++++++++++++++-------------- Cargo.toml | 10 +++++----- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df825ab7405..7c76ea98c0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -737,9 +737,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -749,9 +749,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", @@ -1107,9 +1107,9 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elsa" @@ -2134,9 +2134,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matrixmultiply" @@ -2234,9 +2234,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "arbitrary", "num-integer", @@ -2966,9 +2966,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" +checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", diff --git a/Cargo.toml b/Cargo.toml index 9602ca3e89c..a83ae29d20f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,14 +52,14 @@ boa_string = { version = "~0.18.0", path = "core/string" } # Shared deps arbitrary = "1" bitflags = "2.5.0" -clap = "4.5.7" +clap = "4.5.8" colored = "2.1.0" fast-float = "0.2.0" hashbrown = { version = "0.14.5", default-features = false } indexmap = { version = "2.2.6", default-features = false } indoc = "2.0.5" jemallocator = "0.5.4" -num-bigint = "0.4.5" +num-bigint = "0.4.6" num-traits = "0.2.19" once_cell = { version = "1.19.0", default-features = false } phf = { version = "0.11.2", default-features = false } @@ -67,14 +67,14 @@ pollster = "0.3.0" regex = "1.10.5" regress = { version="0.10.0", features = ["utf16"]} rustc-hash = { version = "1.1.0", default-features = false } -serde_json = "1.0.116" +serde_json = "1.0.119" serde = "1.0.203" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" time = {version = "0.3.36", default-features = false, features = ["local-offset", "large-dates", "wasm-bindgen", "parsing", "formatting", "macros"]} tinystr = "0.7.5" -log = "0.4.21" +log = "0.4.22" simple_logger = "5.0.0" cargo_metadata = "0.18.1" trybuild = "1.0.95" @@ -113,7 +113,7 @@ bytemuck = { version = "1.16.1", default-features = false } arrayvec = "0.7.4" intrusive-collections = "0.9.6" cfg-if = "1.0.0" -either = "1.12.0" +either = "1.13.0" sys-locale = "0.3.1" temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "ec2f2d00294ee641285ec1570df6683ecd0d1a8e" } web-time = "1.1.0" From bb2d028faad6be434d80740b49fb1d48442a7ab3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:46:14 +0200 Subject: [PATCH 052/212] Bump rustc-hash from 1.1.0 to 2.0.0 (#3887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump rustc-hash from 1.1.0 to 2.0.0 Bumps [rustc-hash](https://github.com/rust-lang/rustc-hash) from 1.1.0 to 2.0.0. - [Changelog](https://github.com/rust-lang/rustc-hash/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/rustc-hash/commits) --- updated-dependencies: - dependency-name: rustc-hash dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * fix build * fix profiler build * cargo clippy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: jedel1043 Co-authored-by: José Julián Espina --- Cargo.lock | 18 +++++++++--------- Cargo.toml | 4 ++-- core/profiler/Cargo.toml | 2 +- core/string/src/common.rs | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c76ea98c0e..ae7d19fccbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -317,7 +317,7 @@ dependencies = [ "boa_macros", "indexmap", "num-bigint", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "serde", ] @@ -390,7 +390,7 @@ dependencies = [ "portable-atomic", "rand", "regress", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "ryu-js", "serde", "serde_json", @@ -460,7 +460,7 @@ dependencies = [ "indexmap", "once_cell", "phf", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "serde", "static_assertions", ] @@ -472,7 +472,7 @@ dependencies = [ "boa_engine", "boa_gc", "boa_macros", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", ] [[package]] @@ -507,7 +507,7 @@ dependencies = [ "num-bigint", "num-traits", "regress", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", ] [[package]] @@ -516,7 +516,7 @@ version = "0.18.0" dependencies = [ "measureme", "once_cell", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", ] [[package]] @@ -526,7 +526,7 @@ dependencies = [ "boa_engine", "boa_gc", "indoc", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "textwrap", ] @@ -537,7 +537,7 @@ dependencies = [ "boa_macros", "fast-float", "paste", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "sptr", "static_assertions", ] @@ -557,7 +557,7 @@ dependencies = [ "comfy-table", "phf", "rayon", - "rustc-hash 1.1.0", + "rustc-hash 2.0.0", "serde", "serde_json", "serde_repr", diff --git a/Cargo.toml b/Cargo.toml index a83ae29d20f..2a8478a18de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,8 +65,8 @@ once_cell = { version = "1.19.0", default-features = false } phf = { version = "0.11.2", default-features = false } pollster = "0.3.0" regex = "1.10.5" -regress = { version="0.10.0", features = ["utf16"]} -rustc-hash = { version = "1.1.0", default-features = false } +regress = { version = "0.10.0", features = ["utf16"] } +rustc-hash = { version = "2.0.0", default-features = false } serde_json = "1.0.119" serde = "1.0.203" static_assertions = "1.1.0" diff --git a/core/profiler/Cargo.toml b/core/profiler/Cargo.toml index 4f94ca8c05f..77120b47912 100644 --- a/core/profiler/Cargo.toml +++ b/core/profiler/Cargo.toml @@ -16,7 +16,7 @@ profiler = ["dep:measureme", "dep:once_cell", "dep:rustc-hash"] [dependencies] measureme = { workspace = true, optional = true } once_cell = { workspace = true, optional = true, features = ["std"] } -rustc-hash = { workspace = true, optional = true } +rustc-hash = { workspace = true, optional = true, features = ["std"] } [lints] workspace = true diff --git a/core/string/src/common.rs b/core/string/src/common.rs index 8b420f7c682..080a071e256 100644 --- a/core/string/src/common.rs +++ b/core/string/src/common.rs @@ -4,8 +4,8 @@ use crate::{tagged::Tagged, JsStr}; use super::JsString; use paste::paste; -use rustc_hash::{FxHashMap, FxHasher}; -use std::hash::BuildHasherDefault; +use rustc_hash::{FxBuildHasher, FxHashMap}; +use std::collections::HashMap; macro_rules! well_known_statics { ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => { @@ -216,9 +216,9 @@ const MAX_STATIC_LENGTH: usize = { thread_local! { /// Map from a string inside [`RAW_STATICS`] to its corresponding static index on `RAW_STATICS`. static RAW_STATICS_CACHE: FxHashMap, usize> = { - let mut constants = FxHashMap::with_capacity_and_hasher( + let mut constants = HashMap::with_capacity_and_hasher( RAW_STATICS.len(), - BuildHasherDefault::::default(), + FxBuildHasher ); for (idx, &s) in RAW_STATICS.iter().enumerate() { From 961d7b4d823cecac55556a32f4b81aa7a3be377f Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Wed, 3 Jul 2024 15:32:24 +0100 Subject: [PATCH 053/212] Refactor call frame access to avoid panic checks (#3888) --- core/engine/src/builtins/generator/mod.rs | 5 +- core/engine/src/context/mod.rs | 10 +- core/engine/src/module/source.rs | 4 +- core/engine/src/object/operations.rs | 12 +- core/engine/src/vm/mod.rs | 127 ++++++++++-------- core/engine/src/vm/opcode/arguments.rs | 9 +- core/engine/src/vm/opcode/await/mod.rs | 4 +- .../src/vm/opcode/control_flow/return.rs | 4 +- core/engine/src/vm/opcode/define/mod.rs | 10 +- core/engine/src/vm/opcode/delete/mod.rs | 11 +- .../src/vm/opcode/iteration/loop_ops.rs | 7 +- core/engine/src/vm/opcode/push/literal.rs | 9 +- .../src/vm/opcode/rest_parameter/mod.rs | 5 +- core/engine/src/vm/opcode/set/name.rs | 22 ++- 14 files changed, 125 insertions(+), 114 deletions(-) diff --git a/core/engine/src/builtins/generator/mod.rs b/core/engine/src/builtins/generator/mod.rs index da8e38d181d..5304ae56459 100644 --- a/core/engine/src/builtins/generator/mod.rs +++ b/core/engine/src/builtins/generator/mod.rs @@ -96,8 +96,9 @@ impl GeneratorContext { let rp = frame.rp; context.vm.push_frame(frame); - context.vm.frame_mut().rp = rp; - context.vm.frame_mut().set_exit_early(true); + let frame = context.vm.frame_mut(); + frame.rp = rp; + frame.set_exit_early(true); if let Some(value) = value { context.vm.push(value); diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index db2fa8b0fae..ff8c391a1a0 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -828,6 +828,10 @@ impl Context { // 1. If the execution context stack is empty, return null. // 2. Let ec be the topmost execution context on the execution context stack whose ScriptOrModule component is not null. // 3. If no such execution context exists, return null. Otherwise, return ec's ScriptOrModule. + if let Some(active_runnable) = &self.vm.frame.active_runnable { + return Some(active_runnable.clone()); + } + self.vm .frames .iter() @@ -846,11 +850,7 @@ impl Context { return self.vm.native_active_function.clone(); } - if let Some(frame) = self.vm.frames.last() { - return frame.function(&self.vm); - } - - None + self.vm.frame.function(&self.vm) } } diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index 81c204ec071..ceadec0c037 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -1772,9 +1772,7 @@ impl SourceTextModule { context .vm - .frames - .last() - .expect("there should be a frame") + .frame .set_promise_capability(&mut context.vm.stack, capability); // 9. If module.[[HasTLA]] is false, then diff --git a/core/engine/src/object/operations.rs b/core/engine/src/object/operations.rs index 79e7a26d995..5d4e7a7f57f 100644 --- a/core/engine/src/object/operations.rs +++ b/core/engine/src/object/operations.rs @@ -411,7 +411,11 @@ impl JsObject { return Ok(context.vm.pop()); } - context.vm.frames[frame_index].set_exit_early(true); + if frame_index + 1 == context.vm.frames.len() { + context.vm.frame.set_exit_early(true); + } else { + context.vm.frames[frame_index + 1].set_exit_early(true); + } let result = context.run().consume(); @@ -461,7 +465,11 @@ impl JsObject { .clone()); } - context.vm.frames[frame_index].set_exit_early(true); + if frame_index + 1 == context.vm.frames.len() { + context.vm.frame.set_exit_early(true); + } else { + context.vm.frames[frame_index + 1].set_exit_early(true); + } let result = context.run().consume(); diff --git a/core/engine/src/vm/mod.rs b/core/engine/src/vm/mod.rs index 46fcdfc297d..cf32142d4a4 100644 --- a/core/engine/src/vm/mod.rs +++ b/core/engine/src/vm/mod.rs @@ -9,8 +9,9 @@ use crate::{ Context, JsError, JsNativeError, JsObject, JsResult, JsValue, Module, }; -use boa_gc::{custom_trace, Finalize, Trace}; +use boa_gc::{custom_trace, Finalize, Gc, Trace}; use boa_profiler::Profiler; +use boa_string::JsString; use std::{future::Future, mem::size_of, ops::ControlFlow, pin::Pin, task}; #[cfg(feature = "trace")] @@ -52,7 +53,18 @@ mod tests; /// Virtual Machine. #[derive(Debug)] pub struct Vm { + /// The current call frame. + /// + /// Whenever a new frame is pushed, it will be swaped into this field. + /// Then the old frame will get pushed to the [`Self::frames`] stack. + /// Whenever the current frame gets poped, the last frame on the [`Self::frames`] stack will be swaped into this field. + /// + /// By default this is a dummy frame that gets pushed to [`Self::frames`] when the first real frame is pushed. + pub(crate) frame: CallFrame, + + /// The stack for call frames. pub(crate) frames: Vec, + pub(crate) stack: Vec, pub(crate) return_value: JsValue, @@ -100,6 +112,12 @@ impl Vm { pub(crate) fn new(realm: Realm) -> Self { Self { frames: Vec::with_capacity(16), + frame: CallFrame::new( + Gc::new(CodeBlock::new(JsString::default(), 0, true)), + None, + EnvironmentStack::new(realm.environment().clone()), + realm.clone(), + ), stack: Vec::with_capacity(1024), return_value: JsValue::undefined(), environments: EnvironmentStack::new(realm.environment().clone()), @@ -132,29 +150,22 @@ impl Vm { #[track_caller] pub(crate) fn read(&mut self) -> T { - let value = self.frame().code_block.read::(self.frame().pc as usize); - self.frame_mut().pc += size_of::() as u32; + let frame = self.frame_mut(); + let value = frame.code_block.read::(frame.pc as usize); + frame.pc += size_of::() as u32; value } - /// Retrieves the VM frame - /// - /// # Panics - /// - /// If there is no frame, then this will panic. + /// Retrieves the VM frame. #[track_caller] pub(crate) fn frame(&self) -> &CallFrame { - self.frames.last().expect("no frame found") + &self.frame } - /// Retrieves the VM frame mutably - /// - /// # Panics - /// - /// If there is no frame, then this will panic. + /// Retrieves the VM frame mutably. #[track_caller] pub(crate) fn frame_mut(&mut self) -> &mut CallFrame { - self.frames.last_mut().expect("no frame found") + &mut self.frame } pub(crate) fn push_frame(&mut self, mut frame: CallFrame) { @@ -177,9 +188,12 @@ impl Vm { // Keep carrying the last active runnable in case the current callframe // yields. if frame.active_runnable.is_none() { - frame.active_runnable = self.frames.last().and_then(|fr| fr.active_runnable.clone()); + frame + .active_runnable + .clone_from(&self.frame.active_runnable); } + std::mem::swap(&mut self.frame, &mut frame); self.frames.push(frame); } @@ -196,13 +210,14 @@ impl Vm { } pub(crate) fn pop_frame(&mut self) -> Option { - let mut frame = self.frames.pop(); - if let Some(frame) = &mut frame { + if let Some(mut frame) = self.frames.pop() { + std::mem::swap(&mut self.frame, &mut frame); std::mem::swap(&mut self.environments, &mut frame.environments); std::mem::swap(&mut self.realm, &mut frame.realm); + Some(frame) + } else { + None } - - frame } /// Handles an exception thrown at position `pc`. @@ -271,16 +286,17 @@ impl Context { const NUMBER_OF_COLUMNS: usize = 4; pub(crate) fn trace_call_frame(&self) { - let msg = if self.vm.frames.last().is_some() { + let frame = self.vm.frame(); + let msg = if self.vm.frames.is_empty() { + " VM Start ".to_string() + } else { format!( " Call Frame -- {} ", - self.vm.frame().code_block().name().to_std_string_escaped() + frame.code_block().name().to_std_string_escaped() ) - } else { - " VM Start ".to_string() }; - println!("{}", self.vm.frame().code_block); + println!("{}", frame.code_block); println!( "{msg:-^width$}", width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10 @@ -300,16 +316,13 @@ impl Context { where F: FnOnce(Opcode, &mut Context) -> JsResult, { - let bytecodes = &self.vm.frame().code_block.bytecode; - let pc = self.vm.frame().pc as usize; + let frame = self.vm.frame(); + let bytecodes = &frame.code_block.bytecode; + let pc = frame.pc as usize; let (_, varying_operand_kind, instruction) = InstructionIterator::with_pc(bytecodes, pc) .next() .expect("There should be an instruction left"); - let operands = self - .vm - .frame() - .code_block - .instruction_operands(&instruction); + let operands = frame.code_block.instruction_operands(&instruction); let opcode = instruction.opcode(); match opcode { @@ -332,12 +345,11 @@ impl Context { let result = self.execute_instruction(f); let duration = instant.elapsed(); - let fp = self - .vm - .frames - .last() - .map(CallFrame::fp) - .map(|fp| fp as usize); + let fp = if self.vm.frames.is_empty() { + None + } else { + Some(self.vm.frame.fp() as usize) + }; let stack = { let mut stack = String::from("[ "); @@ -434,14 +446,16 @@ impl Context { if !err.is_catchable() { let mut fp = self.vm.stack.len(); let mut env_fp = self.vm.environments.len(); - while let Some(frame) = self.vm.frames.last() { - if frame.exit_early() { + loop { + if self.vm.frame.exit_early() { break; } - fp = frame.fp() as usize; - env_fp = frame.env_fp as usize; - self.vm.pop_frame(); + fp = self.vm.frame.fp() as usize; + env_fp = self.vm.frame.env_fp as usize; + if self.vm.pop_frame().is_none() { + break; + } } self.vm.environments.truncate(env_fp); self.vm.stack.truncate(fp); @@ -466,11 +480,13 @@ impl Context { match result { CompletionType::Normal => {} CompletionType::Return => { - let fp = self.vm.frame().fp() as usize; + let frame = self.vm.frame(); + let fp = frame.fp() as usize; + let exit_early = frame.exit_early(); self.vm.stack.truncate(fp); let result = self.vm.take_return_value(); - if self.vm.frame().exit_early() { + if exit_early { return ControlFlow::Break(CompletionRecord::Normal(result)); } @@ -478,9 +494,10 @@ impl Context { self.vm.pop_frame(); } CompletionType::Throw => { - let mut fp = self.vm.frame().fp(); - let mut env_fp = self.vm.frame().env_fp; - if self.vm.frame().exit_early() { + let frame = self.vm.frame(); + let mut fp = frame.fp(); + let mut env_fp = frame.env_fp; + if frame.exit_early() { self.vm.environments.truncate(env_fp as usize); self.vm.stack.truncate(fp as usize); return ControlFlow::Break(CompletionRecord::Throw( @@ -493,11 +510,11 @@ impl Context { self.vm.pop_frame(); - while let Some(frame) = self.vm.frames.last_mut() { - fp = frame.fp(); - env_fp = frame.env_fp; - let pc = frame.pc; - let exit_early = frame.exit_early(); + loop { + fp = self.vm.frame.fp(); + env_fp = self.vm.frame.env_fp; + let pc = self.vm.frame.pc; + let exit_early = self.vm.frame.exit_early(); if self.vm.handle_exception_at(pc) { return ControlFlow::Continue(()); @@ -512,7 +529,9 @@ impl Context { )); } - self.vm.pop_frame(); + if self.vm.pop_frame().is_none() { + break; + } } self.vm.environments.truncate(env_fp as usize); self.vm.stack.truncate(fp as usize); diff --git a/core/engine/src/vm/opcode/arguments.rs b/core/engine/src/vm/opcode/arguments.rs index 8149c84e1d5..fb4fe7fb625 100644 --- a/core/engine/src/vm/opcode/arguments.rs +++ b/core/engine/src/vm/opcode/arguments.rs @@ -19,14 +19,13 @@ impl Operation for CreateMappedArgumentsObject { const COST: u8 = 8; fn execute(context: &mut Context) -> JsResult { - let function_object = context - .vm - .frame() + let frame = context.vm.frame(); + let function_object = frame .function(&context.vm) .clone() .expect("there should be a function object"); - let code = context.vm.frame().code_block().clone(); - let args = context.vm.frame().arguments(&context.vm).to_vec(); + let code = frame.code_block().clone(); + let args = frame.arguments(&context.vm).to_vec(); let env = context.vm.environments.current_ref(); let arguments = MappedArguments::new( diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index d9571669780..1211b35581d 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -183,9 +183,7 @@ impl Operation for CreatePromiseCapability { context .vm - .frames - .last() - .expect("there should be a frame") + .frame .set_promise_capability(&mut context.vm.stack, Some(&promise_capability)); Ok(CompletionType::Normal) } diff --git a/core/engine/src/vm/opcode/control_flow/return.rs b/core/engine/src/vm/opcode/control_flow/return.rs index 31e4294e0ff..93e502b8321 100644 --- a/core/engine/src/vm/opcode/control_flow/return.rs +++ b/core/engine/src/vm/opcode/control_flow/return.rs @@ -33,10 +33,10 @@ impl Operation for CheckReturn { const COST: u8 = 3; fn execute(context: &mut Context) -> JsResult { - if !context.vm.frame().construct() { + let frame = context.vm.frame(); + if !frame.construct() { return Ok(CompletionType::Normal); } - let frame = context.vm.frame(); let this = frame.this(&context.vm); let result = context.vm.take_return_value(); diff --git a/core/engine/src/vm/opcode/define/mod.rs b/core/engine/src/vm/opcode/define/mod.rs index 5c21d2b5cee..4af7365af40 100644 --- a/core/engine/src/vm/opcode/define/mod.rs +++ b/core/engine/src/vm/opcode/define/mod.rs @@ -62,13 +62,11 @@ pub(crate) struct DefInitVar; impl DefInitVar { fn operation(context: &mut Context, index: usize) -> JsResult { let value = context.vm.pop(); - let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); + let frame = context.vm.frame(); + let strict = frame.code_block.strict(); + let mut binding_locator = frame.code_block.bindings[index].clone(); context.find_runtime_binding(&mut binding_locator)?; - context.set_binding( - &binding_locator, - value, - context.vm.frame().code_block.strict(), - )?; + context.set_binding(&binding_locator, value, strict)?; Ok(CompletionType::Normal) } diff --git a/core/engine/src/vm/opcode/delete/mod.rs b/core/engine/src/vm/opcode/delete/mod.rs index 6150e610738..6940c861d15 100644 --- a/core/engine/src/vm/opcode/delete/mod.rs +++ b/core/engine/src/vm/opcode/delete/mod.rs @@ -16,15 +16,12 @@ impl DeletePropertyByName { fn operation(context: &mut Context, index: usize) -> JsResult { let value = context.vm.pop(); let object = value.to_object(context)?; - let key = context - .vm - .frame() - .code_block() - .constant_string(index) - .into(); + let code_block = context.vm.frame().code_block(); + let key = code_block.constant_string(index).into(); + let strict = code_block.strict(); let result = object.__delete__(&key, &mut InternalMethodContext::new(context))?; - if !result && context.vm.frame().code_block().strict() { + if !result && strict { return Err(JsNativeError::typ() .with_message("Cannot delete property") .into()); diff --git a/core/engine/src/vm/opcode/iteration/loop_ops.rs b/core/engine/src/vm/opcode/iteration/loop_ops.rs index 91d43c18e24..dc22333bef8 100644 --- a/core/engine/src/vm/opcode/iteration/loop_ops.rs +++ b/core/engine/src/vm/opcode/iteration/loop_ops.rs @@ -17,16 +17,17 @@ impl Operation for IncrementLoopIteration { const COST: u8 = 3; fn execute(context: &mut Context) -> JsResult { - let previous_iteration_count = context.vm.frame_mut().loop_iteration_count; - let max = context.vm.runtime_limits.loop_iteration_limit(); + let frame = context.vm.frame_mut(); + let previous_iteration_count = frame.loop_iteration_count; + if previous_iteration_count > max { return Err(JsNativeError::runtime_limit() .with_message(format!("Maximum loop iteration limit {max} exceeded")) .into()); } - context.vm.frame_mut().loop_iteration_count = previous_iteration_count.wrapping_add(1); + frame.loop_iteration_count = previous_iteration_count.wrapping_add(1); Ok(CompletionType::Normal) } } diff --git a/core/engine/src/vm/opcode/push/literal.rs b/core/engine/src/vm/opcode/push/literal.rs index 697e626c297..e7b66c25bdd 100644 --- a/core/engine/src/vm/opcode/push/literal.rs +++ b/core/engine/src/vm/opcode/push/literal.rs @@ -59,12 +59,9 @@ impl PushRegExp { pattern_index: usize, flags_index: usize, ) -> JsResult { - let pattern = context - .vm - .frame() - .code_block() - .constant_string(pattern_index); - let flags = context.vm.frame().code_block().constant_string(flags_index); + let code_block = context.vm.frame().code_block(); + let pattern = code_block.constant_string(pattern_index); + let flags = code_block.constant_string(flags_index); let regexp = JsRegExp::new(pattern, flags, context)?; context.vm.push(regexp); diff --git a/core/engine/src/vm/opcode/rest_parameter/mod.rs b/core/engine/src/vm/opcode/rest_parameter/mod.rs index daabd434f5d..59db486fb4b 100644 --- a/core/engine/src/vm/opcode/rest_parameter/mod.rs +++ b/core/engine/src/vm/opcode/rest_parameter/mod.rs @@ -17,8 +17,9 @@ impl Operation for RestParameterInit { const COST: u8 = 6; fn execute(context: &mut Context) -> JsResult { - let argument_count = context.vm.frame().argument_count; - let param_count = context.vm.frame().code_block().parameter_length; + let frame = context.vm.frame(); + let argument_count = frame.argument_count; + let param_count = frame.code_block().parameter_length; let array = if argument_count >= param_count { let rest_count = argument_count - param_count + 1; diff --git a/core/engine/src/vm/opcode/set/name.rs b/core/engine/src/vm/opcode/set/name.rs index a9646b53c0d..421412bbc68 100644 --- a/core/engine/src/vm/opcode/set/name.rs +++ b/core/engine/src/vm/opcode/set/name.rs @@ -54,18 +54,16 @@ pub(crate) struct SetName; impl SetName { fn operation(context: &mut Context, index: usize) -> JsResult { - let mut binding_locator = context.vm.frame().code_block.bindings[index].clone(); + let code_block = context.vm.frame().code_block(); + let mut binding_locator = code_block.bindings[index].clone(); + let strict = code_block.strict(); let value = context.vm.pop(); context.find_runtime_binding(&mut binding_locator)?; verify_initialized(&binding_locator, context)?; - context.set_binding( - &binding_locator, - value, - context.vm.frame().code_block.strict(), - )?; + context.set_binding(&binding_locator, value, strict)?; Ok(CompletionType::Normal) } @@ -105,9 +103,9 @@ impl Operation for SetNameByLocator { const COST: u8 = 4; fn execute(context: &mut Context) -> JsResult { - let binding_locator = context - .vm - .frame_mut() + let frame = context.vm.frame_mut(); + let strict = frame.code_block.strict(); + let binding_locator = frame .binding_stack .pop() .expect("locator should have been popped before"); @@ -115,11 +113,7 @@ impl Operation for SetNameByLocator { verify_initialized(&binding_locator, context)?; - context.set_binding( - &binding_locator, - value, - context.vm.frame().code_block.strict(), - )?; + context.set_binding(&binding_locator, value, strict)?; Ok(CompletionType::Normal) } From 8e76836c8c68fec857af9fe17b8b9b1a2880b4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Fri, 5 Jul 2024 17:04:04 +0000 Subject: [PATCH 054/212] Remove `Temporal.Calendar` and `Temporal.TimeZone` (#3890) * Remove `Temporal.Calendar` and `Temporal.TimeZone` * bump test262 * add new test262 features * fix bug --- Cargo.lock | 2 +- Cargo.toml | 4 +- core/engine/src/builtins/mod.rs | 2 - .../src/builtins/temporal/calendar/mod.rs | 1074 +---------------- .../src/builtins/temporal/calendar/object.rs | 860 ------------- .../src/builtins/temporal/calendar/tests.rs | 61 - .../src/builtins/temporal/duration/mod.rs | 21 - core/engine/src/builtins/temporal/fields.rs | 250 ---- core/engine/src/builtins/temporal/mod.rs | 41 +- core/engine/src/builtins/temporal/now.rs | 22 +- core/engine/src/builtins/temporal/options.rs | 3 +- .../src/builtins/temporal/plain_date/mod.rs | 91 +- .../builtins/temporal/plain_date_time/mod.rs | 74 +- .../temporal/plain_date_time/tests.rs | 3 +- .../builtins/temporal/plain_month_day/mod.rs | 14 +- .../builtins/temporal/plain_year_month/mod.rs | 18 +- .../src/builtins/temporal/time_zone/custom.rs | 63 - .../src/builtins/temporal/time_zone/mod.rs | 307 +---- .../builtins/temporal/zoned_date_time/mod.rs | 27 +- core/gc/src/cell.rs | 5 +- test262_config.toml | 2 +- tests/tester/src/edition.rs | 10 + 22 files changed, 169 insertions(+), 2785 deletions(-) delete mode 100644 core/engine/src/builtins/temporal/calendar/object.rs delete mode 100644 core/engine/src/builtins/temporal/calendar/tests.rs delete mode 100644 core/engine/src/builtins/temporal/fields.rs delete mode 100644 core/engine/src/builtins/temporal/time_zone/custom.rs diff --git a/Cargo.lock b/Cargo.lock index ae7d19fccbf..2a4d53881f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3201,7 +3201,7 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "temporal_rs" version = "0.0.2" -source = "git+https://github.com/boa-dev/temporal.git?rev=ec2f2d00294ee641285ec1570df6683ecd0d1a8e#ec2f2d00294ee641285ec1570df6683ecd0d1a8e" +source = "git+https://github.com/boa-dev/temporal.git?rev=c658ac7db4701822cc179d04b56bb9c8fb7e954c#c658ac7db4701822cc179d04b56bb9c8fb7e954c" dependencies = [ "bitflags 2.6.0", "icu_calendar", diff --git a/Cargo.toml b/Cargo.toml index 2a8478a18de..2e287af24d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ exclude = [ [workspace.package] edition = "2021" version = "0.18.0" -rust-version = "1.74.0" +rust-version = "1.79.0" authors = ["boa-dev"] repository = "https://github.com/boa-dev/boa" license = "Unlicense OR MIT" @@ -115,7 +115,7 @@ intrusive-collections = "0.9.6" cfg-if = "1.0.0" either = "1.13.0" sys-locale = "0.3.1" -temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "ec2f2d00294ee641285ec1570df6683ecd0d1a8e" } +temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "c658ac7db4701822cc179d04b56bb9c8fb7e954c" } web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.9.0" diff --git a/core/engine/src/builtins/mod.rs b/core/engine/src/builtins/mod.rs index 17e723ce5d3..2a40c0819a9 100644 --- a/core/engine/src/builtins/mod.rs +++ b/core/engine/src/builtins/mod.rs @@ -284,7 +284,6 @@ impl Realm { #[cfg(feature = "temporal")] { - temporal::TimeZone::init(self); temporal::Temporal::init(self); temporal::Now::init(self); temporal::Instant::init(self); @@ -295,7 +294,6 @@ impl Realm { temporal::PlainMonthDay::init(self); temporal::PlainYearMonth::init(self); temporal::ZonedDateTime::init(self); - temporal::Calendar::init(self); } } } diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index 489e8f0163d..11b5a4a049b 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -2,1024 +2,18 @@ use std::str::FromStr; -use super::{ - create_temporal_date, create_temporal_duration, create_temporal_month_day, - create_temporal_year_month, fields, options::TemporalUnitGroup, PlainDate, PlainDateTime, - PlainMonthDay, PlainYearMonth, ZonedDateTime, -}; -use crate::{ - builtins::{ - iterable::IteratorHint, - options::{get_option, get_options_object}, - temporal, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, - }, - context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, - js_string, - object::internal_methods::get_prototype_from_constructor, - property::Attribute, - realm::Realm, - string::StaticJsStrings, - Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, -}; -use boa_gc::{custom_trace, Finalize, Trace}; -use boa_macros::js_str; -use boa_profiler::Profiler; -use temporal_rs::{ - components::calendar::{ - CalendarDateLike, CalendarFieldsType, CalendarProtocol, CalendarSlot, - CALENDAR_PROTOCOL_METHODS, - }, - options::{ArithmeticOverflow, TemporalUnit}, -}; - -mod object; - -#[cfg(test)] -mod tests; -/// The `Temporal.Calendar` object. -#[derive(Debug, Finalize, JsData)] -pub struct Calendar { - slot: CalendarSlot, -} - -unsafe impl Trace for Calendar { - custom_trace!(this, mark, { - match &this.slot { - CalendarSlot::Protocol(custom) => mark(custom), - // SAFETY: CalendarSlot::Builtin does not contain any JsValues for the gc to trace. - CalendarSlot::Builtin(_) => {} - } - }); -} - -impl Calendar { - pub(crate) fn new(slot: CalendarSlot) -> Self { - Self { slot } - } -} - -impl BuiltInObject for Calendar { - const NAME: JsString = StaticJsStrings::CALENDAR; -} - -impl IntrinsicObject for Calendar { - fn init(realm: &Realm) { - let _timer = Profiler::global().start_event(std::any::type_name::(), "init"); - - let get_id = BuiltInBuilder::callable(realm, Self::get_id) - .name(js_string!("get Id")) - .build(); - - BuiltInBuilder::from_standard_constructor::(realm) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::CONFIGURABLE, - ) - .accessor( - js_string!("id"), - Some(get_id), - None, - Attribute::CONFIGURABLE, - ) - .static_method(Self::from, js_string!("from"), 1) - .method(Self::date_from_fields, js_string!("dateFromFields"), 2) - .method( - Self::year_month_from_fields, - js_string!("yearMonthFromFields"), - 2, - ) - .method( - Self::month_day_from_fields, - js_string!("monthDayFromFields"), - 2, - ) - .method(Self::date_add, js_string!("dateAdd"), 3) - .method(Self::date_until, js_string!("dateUntil"), 3) - .method(Self::era, js_string!("era"), 1) - .method(Self::era_year, js_string!("eraYear"), 1) - .method(Self::year, js_string!("year"), 1) - .method(Self::month, js_string!("month"), 1) - .method(Self::month_code, js_string!("monthCode"), 1) - .method(Self::day, js_string!("day"), 1) - .method(Self::day_of_week, js_string!("dayOfWeek"), 1) - .method(Self::day_of_year, js_string!("dayOfYear"), 1) - .method(Self::week_of_year, js_string!("weekOfYear"), 1) - .method(Self::year_of_week, js_string!("yearOfWeek"), 1) - .method(Self::days_in_week, js_string!("daysInWeek"), 1) - .method(Self::days_in_month, js_string!("daysInMonth"), 1) - .method(Self::days_in_year, js_string!("daysInYear"), 1) - .method(Self::months_in_year, js_string!("monthsInYear"), 1) - .method(Self::in_leap_year, js_string!("inLeapYear"), 1) - .method(Self::fields, js_string!("fields"), 1) - .method(Self::merge_fields, js_string!("mergeFields"), 2) - .method(Self::get_id, js_string!("toString"), 0) - .method(Self::get_id, js_string!("toJSON"), 0) - .build(); - } - - fn get(intrinsics: &Intrinsics) -> JsObject { - Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() - } -} - -impl BuiltInConstructor for Calendar { - const LENGTH: usize = 1; - - const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor = - StandardConstructors::calendar; - - fn constructor( - new_target: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. If NewTarget is undefined, then - if new_target.is_undefined() { - // a. Throw a TypeError exception. - return Err(JsNativeError::typ() - .with_message( - "newTarget cannot be undefined when constructing a Temporal.Calendar object.", - ) - .into()); - } - - let identifier = args.get_or_undefined(0); - - // 2. If id is not a String, throw a TypeError exception. - let JsValue::String(id) = identifier else { - return Err(JsNativeError::typ() - .with_message("Calendar id must be a string.") - .into()); - }; - - // 3. If IsBuiltinCalendar(id) is false, then - // a. Throw a RangeError exception. - - // 4. Return ? CreateTemporalCalendar(id, NewTarget). - create_temporal_calendar( - CalendarSlot::::from_str(&id.to_std_string_escaped())?, - Some(new_target.clone()), - context, - ) - } -} - -impl Calendar { - fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar_like = args.get_or_undefined(0); - let slot = to_temporal_calendar_slot_value(calendar_like, context)?; - create_temporal_calendar(slot, None, context) - } - - fn get_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message( - "the this value of Calendar.prototype.id must be a Calendar object.", - ) - })?; - - Ok(JsString::from(calendar.slot.identifier(context)?.as_str()).into()) - } - - /// 15.8.2.1 `Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] )` - Supercedes 12.5.4 - fn date_from_fields( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message( - "this value of Calendar dateFromFields must be a Calendar object.", - ) - })?; - - // 3. If Type(fields) is not Object, throw a TypeError exception. - let fields = args.get_or_undefined(0); - let fields_obj = fields.as_object().ok_or_else(|| { - JsNativeError::typ().with_message("fields parameter must be an object.") - })?; - - // 4. Set options to ? GetOptionsObject(options). - let options = get_options_object(args.get_or_undefined(1))?; - - // 5. Let relevantFieldNames be « "day", "month", "monthCode", "year" ». - let mut relevant_field_names = Vec::from([ - js_string!("day"), - js_string!("month"), - js_string!("monthCode"), - js_string!("year"), - ]); - - // 6. If calendar.[[Identifier]] is "iso8601", then - let mut fields = if calendar.slot.is_iso() { - // a. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « "year", "day" »). - let mut required_fields = Vec::from([js_string!("year"), js_string!("day")]); - fields::prepare_temporal_fields( - fields_obj, - &mut relevant_field_names, - &mut required_fields, - None, - false, - None, - context, - )? - // 7. Else, - } else { - // a. Let calendarRelevantFieldDescriptors be CalendarFieldDescriptors(calendar.[[Identifier]], date). - let calendar_relevant_fields = - calendar.slot.field_descriptors(CalendarFieldsType::Date)?; - // b. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « », calendarRelevantFieldDescriptors). - fields::prepare_temporal_fields( - fields_obj, - &mut relevant_field_names, - &mut Vec::new(), - Some(calendar_relevant_fields), - false, - None, - context, - )? - }; - - // 8. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option(&options, js_str!("overflow"), context)? - .unwrap_or(ArithmeticOverflow::Constrain); - - // NOTE: implement the below on the calenar itself - // 9. If calendar.[[Identifier]] is "iso8601", then - // a. Perform ? ISOResolveMonth(fields). - // b. Let result be ? ISODateFromFields(fields, overflow). - // 10. Else, - // a. Perform ? CalendarResolveFields(calendar.[[Identifier]], fields, date). - // b. Let result be ? CalendarDateToISO(calendar.[[Identifier]], fields, overflow). - - let result = calendar - .slot - .date_from_fields(&mut fields, overflow, context)?; - - create_temporal_date(result, None, context).map(Into::into) - } - - /// 15.8.2.2 `Temporal.Calendar.prototype.yearMonthFromFields ( fields [ , options ] )` - Supercedes 12.5.5 - fn year_month_from_fields( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message( - "this value of Calendar yearMonthFromFields must be a Calendar object.", - ) - })?; - - let fields = args.get_or_undefined(0); - let fields_obj = fields.as_object().ok_or_else(|| { - JsNativeError::typ().with_message("fields parameter must be an object.") - })?; - - // 5. Set options to ? GetOptionsObject(options). - let options = get_options_object(args.get_or_undefined(1))?; - - let mut relevant_field_names = Vec::from([ - js_string!("year"), - js_string!("month"), - js_string!("monthCode"), - ]); - - // 6. Set fields to ? PrepareTemporalFields(fields, « "month", "monthCode", "year" », « "year" »). - let mut fields = if calendar.slot.identifier(context)?.as_str() == "iso8601" { - // a. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « "year" »). - let mut required_fields = Vec::from([js_string!("year")]); - fields::prepare_temporal_fields( - fields_obj, - &mut relevant_field_names, - &mut required_fields, - None, - false, - None, - context, - )? - } else { - // a. Let calendarRelevantFieldDescriptors be CalendarFieldDescriptors(calendar.[[Identifier]], year-month). - // b. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « », calendarRelevantFieldDescriptors). - - let calendar_relevant_fields = calendar - .slot - .field_descriptors(CalendarFieldsType::YearMonth)?; - fields::prepare_temporal_fields( - fields_obj, - &mut relevant_field_names, - &mut Vec::new(), - Some(calendar_relevant_fields), - false, - None, - context, - )? - - // TODO: figure out the below. Maybe a method on fields? - // c. Let firstDayIndex be the 1-based index of the first day of the month described by fields (i.e., 1 unless the month's first day is skipped by this calendar.) - // d. Perform ! CreateDataPropertyOrThrow(fields, "day", 𝔽(firstDayIndex)). - }; - - // 7. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option::(&options, js_str!("overflow"), context)? - .unwrap_or(ArithmeticOverflow::Constrain); - - let result = calendar - .slot - .year_month_from_fields(&mut fields, overflow, context)?; - - create_temporal_year_month(result, None, context) - } - - /// 15.8.2.3 `Temporal.Calendar.prototype.monthDayFromFields ( fields [ , options ] )` - Supercedes 12.5.6 - fn month_day_from_fields( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message( - "this value of Calendar monthDayFromFields must be a Calendar object.", - ) - })?; - - // 3. If Type(fields) is not Object, throw a TypeError exception. - let fields = args.get_or_undefined(0); - let fields_obj = fields.as_object().ok_or_else(|| { - JsNativeError::typ().with_message("fields parameter must be an object.") - })?; - - // 4. Set options to ? GetOptionsObject(options). - let options = get_options_object(args.get_or_undefined(1))?; - - // 5. Let relevantFieldNames be « "day", "month", "monthCode", "year" ». - let mut relevant_field_names = Vec::from([ - js_string!("day"), - js_string!("month"), - js_string!("monthCode"), - js_string!("year"), - ]); - - // 6. If calendar.[[Identifier]] is "iso8601", then - let mut fields = if calendar.slot.identifier(context)?.as_str() == "iso8601" { - // a. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « "day" »). - let mut required_fields = Vec::from([js_string!("day")]); - fields::prepare_temporal_fields( - fields_obj, - &mut relevant_field_names, - &mut required_fields, - None, - false, - None, - context, - )? - // 7. Else, - } else { - // a. Let calendarRelevantFieldDescriptors be CalendarFieldDescriptors(calendar.[[Identifier]], month-day). - let calendar_relevant_fields = calendar - .slot - .field_descriptors(CalendarFieldsType::MonthDay)?; - // b. Set fields to ? PrepareTemporalFields(fields, relevantFieldNames, « », calendarRelevantFieldDescriptors). - fields::prepare_temporal_fields( - fields_obj, - &mut relevant_field_names, - &mut Vec::new(), - Some(calendar_relevant_fields), - false, - None, - context, - )? - }; - - // 8. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option(&options, js_str!("overflow"), context)? - .unwrap_or(ArithmeticOverflow::Constrain); - - let result = calendar - .slot - .month_day_from_fields(&mut fields, overflow, context)?; - - create_temporal_month_day(result, None, context) - } - - /// 15.8.2.4 `Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )` - supercedes 12.5.7 - fn date_add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - // 3. Assert: calendar.[[Identifier]] is "iso8601". - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar dateAdd must be a Calendar object.") - })?; - - // 4. Set date to ? ToTemporalDate(date). - let date_like = args.get_or_undefined(0); - let date = temporal::plain_date::to_temporal_date(date_like, None, context)?; - - // 5. Set duration to ? ToTemporalDuration(duration). - let duration_like = args.get_or_undefined(1); - let duration = temporal::duration::to_temporal_duration(duration_like, context)?; - - // 6. Set options to ? GetOptionsObject(options). - let options = args.get_or_undefined(2); - let options_obj = get_options_object(options)?; - - // 7. Let overflow be ? ToTemporalOverflow(options). - let overflow = get_option(&options_obj, js_str!("overflow"), context)? - .unwrap_or(ArithmeticOverflow::Constrain); - - // 8. Let balanceResult be ? BalanceTimeDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], - // duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day"). - let result = calendar - .slot - .date_add(&date.inner, &duration, overflow, context)?; - - create_temporal_date(result, None, context).map(Into::into) - } - - ///15.8.2.5 `Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] )` - Supercedes 12.5.8 - fn date_until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - // 3. Assert: calendar.[[Identifier]] is "iso8601". - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar dateUntil must be a Calendar object.") - })?; - - // 4. Set one to ? ToTemporalDate(one). - let one = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - // 5. Set two to ? ToTemporalDate(two). - let two = temporal::plain_date::to_temporal_date(args.get_or_undefined(1), None, context)?; - - // 6. Set options to ? GetOptionsObject(options). - let options = get_options_object(args.get_or_undefined(2))?; - - // 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto"). - // 8. If largestUnit is "auto", set largestUnit to "day". - let largest_unit = super::options::get_temporal_unit( - &options, - js_str!("largestUnit"), - TemporalUnitGroup::Date, - None, - context, - )? - .unwrap_or(TemporalUnit::Day); - - let result = calendar - .slot - .date_until(&one.inner, &two.inner, largest_unit, context)?; - - create_temporal_duration(result, None, context).map(Into::into) - } - - /// 15.8.2.6 `Temporal.Calendar.prototype.era ( temporalDateLike )` - fn era(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar era must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar - .slot - .era(&date_like, context)? - .map_or(JsValue::undefined(), |r| JsString::from(r.as_str()).into()); - - Ok(result) - } - - /// 15.8.2.7 `Temporal.Calendar.prototype.eraYear ( temporalDateLike )` - fn era_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar eraYear must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar - .slot - .era_year(&date_like, context)? - .map_or(JsValue::undefined(), JsValue::from); - - Ok(result) - } - - /// 15.8.2.8 `Temporal.Calendar.prototype.year ( temporalDateLike )` - fn year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar year must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar.slot.year(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.9 `Temporal.Calendar.prototype.month ( temporalDateLike )` - fn month(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar month must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - // 3. If Type(temporalDateLike) is Object and temporalDateLike has an [[InitializedTemporalMonthDay]] internal slot, then - // 3.a. Throw a TypeError exception. - // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then - // 4.a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). - - let result = calendar.slot.month(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.10 `Temporal.Calendar.prototype.monthCode ( temporalDateLike )` - fn month_code(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar monthCode must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar.slot.month_code(&date_like, context)?; - - Ok(JsString::from(result.as_str()).into()) - } - - /// 15.8.2.11 `Temporal.Calendar.prototype.day ( temporalDateLike )` - fn day(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar day must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar.slot.day(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.12 `Temporal.Calendar.prototype.dayOfWeek ( dateOrDateTime )` - fn day_of_week(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar dayOfWeek must be a Calendar object.") - })?; - - // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). - let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - - let result = calendar - .slot - .day_of_week(&CalendarDateLike::Date(date.inner.clone()), context)?; - - Ok(result.into()) - } - - /// 15.8.2.13 `Temporal.Calendar.prototype.dayOfYear ( temporalDateLike )` - fn day_of_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar dayOfYear must be a Calendar object.") - })?; - - // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). - let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - - let result = calendar - .slot - .day_of_year(&CalendarDateLike::Date(date.inner.clone()), context)?; - - Ok(result.into()) - } - - /// 15.8.2.14 `Temporal.Calendar.prototype.weekOfYear ( temporalDateLike )` - fn week_of_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar weekOfYear must be a Calendar object.") - })?; - - // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). - let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - - let result = calendar - .slot - .week_of_year(&CalendarDateLike::Date(date.inner.clone()), context)?; - - Ok(result.into()) - } - - /// 15.8.2.15 `Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike )` - fn year_of_week(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar yearOfWeek must be a Calendar object.") - })?; - - // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). - let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - - let result = calendar - .slot - .year_of_week(&CalendarDateLike::Date(date.inner.clone()), context)?; - - Ok(result.into()) - } - - /// 15.8.2.16 `Temporal.Calendar.prototype.daysInWeek ( temporalDateLike )` - fn days_in_week(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar daysInWeek must be a Calendar object.") - })?; - - // 3. Let temporalDate be ? ToTemporalDate(temporalDateLike). - let date = temporal::plain_date::to_temporal_date(args.get_or_undefined(0), None, context)?; - - let result = calendar - .slot - .days_in_week(&CalendarDateLike::Date(date.inner.clone()), context)?; - - Ok(result.into()) - } - - /// 15.8.2.17 `Temporal.Calendar.prototype.daysInMonth ( temporalDateLike )` - fn days_in_month(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar daysInMonth must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar.slot.days_in_month(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.18 `Temporal.Calendar.prototype.daysInYear ( temporalDateLike )` - fn days_in_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar daysInYear must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - let result = calendar.slot.days_in_year(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.19 `Temporal.Calendar.prototype.monthsInYear ( temporalDateLike )` - fn months_in_year( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar monthsInYear must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar.slot.months_in_year(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.20 `Temporal.Calendar.prototype.inLeapYear ( temporalDateLike )` - fn in_leap_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar inLeapYear must be a Calendar object.") - })?; - - let date_like = to_calendar_date_like(args.get_or_undefined(0), context)?; - - let result = calendar.slot.in_leap_year(&date_like, context)?; - - Ok(result.into()) - } - - /// 15.8.2.21 `Temporal.Calendar.prototype.fields ( fields )` - fn fields(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar Fields must be a Calendar object.") - })?; - - // Custom Calendars override the `fields` method. - if let CalendarSlot::Protocol(proto) = &calendar.slot { - // TODO: Is there a more efficient way to convert from iterable <-> Vec; - let mut iterator_record = - args.get_or_undefined(0) - .get_iterator(context, Some(IteratorHint::Sync), None)?; - let mut fields_list = Vec::default(); - - while iterator_record.step(context)? { - let next_val = iterator_record.value(context)?; - - if let JsValue::String(item) = next_val { - fields_list.push(item.to_std_string_escaped()); - } else { - // 1. Let completion be ThrowCompletion(a newly created TypeError object). - let completion = Err(JsNativeError::typ() - .with_message("field must be of type string") - .into()); - // 2. Return ? IteratorClose(iteratorRecord, completion). - return iterator_record.close(completion, context); - } - } - - let result = proto.fields(fields_list, context)?; - return Ok(Array::create_array_from_list( - result.iter().map(|s| JsString::from(s.clone()).into()), - context, - ) - .into()); - } - - // 3. Let iteratorRecord be ? GetIterator(fields, sync). - let mut iterator_record = - args.get_or_undefined(0) - .get_iterator(context, Some(IteratorHint::Sync), None)?; - - // 4. Let fieldNames be a new empty List. - let mut fields_names = Vec::new(); - - // 5. Let next be true. - // 6. Repeat, while next is not false, - while iterator_record.step(context)? { - // a. Set next to ? IteratorStep(iteratorRecord). - // b. If next is not false, then - // i. Let nextValue be ? IteratorValue(next). - let next_value = iterator_record.value(context)?; - - // ii. If Type(nextValue) is not String, then - if let JsValue::String(value) = next_value { - // iii. If fieldNames contains nextValue, then - // 1. Let completion be ThrowCompletion(a newly created RangeError object). - // 2. Return ? IteratorClose(iteratorRecord, completion). - // iv. If nextValue is not one of "year", "month", "monthCode", or "day", then - // 1. Let completion be ThrowCompletion(a newly created RangeError object). - // 2. Return ? IteratorClose(iteratorRecord, completion). - // v. Append nextValue to the end of the List fieldNames. - let this_field = value.to_std_string_escaped(); - match this_field.as_str() { - "year" | "month" | "monthCode" | "day" - if !fields_names.contains(&this_field) => - { - fields_names.push(this_field); - } - _ => { - let completion = Err(JsNativeError::range() - .with_message("Invalid field name string.") - .into()); - return iterator_record.close(completion, context); - } - } - } else { - // 1. Let completion be ThrowCompletion(a newly created TypeError object). - let completion = Err(JsNativeError::typ() - .with_message("field must be of type string") - .into()); - // 2. Return ? IteratorClose(iteratorRecord, completion). - return iterator_record.close(completion, context); - } - } - - // 7. Let result be fieldNames. - // 8. If calendar.[[Identifier]] is not "iso8601", then - if !calendar.slot.is_iso() { - // a. NOTE: Every built-in calendar preserves all input field names in output. - // b. Let extraFieldDescriptors be CalendarFieldDescriptors(calendar.[[Identifier]], fieldNames). - let extended_fields = calendar - .slot - .field_descriptors(CalendarFieldsType::from(&fields_names[..]))?; - // c. For each Calendar Field Descriptor Record desc of extraFieldDescriptors, do - for descriptor in extended_fields { - // i. Append desc.[[Property]] to result. - fields_names.push(descriptor.0); - } - } - - // 9. Return CreateArrayFromList(result). - Ok(Array::create_array_from_list( - fields_names - .iter() - .map(|s| JsString::from(s.clone()).into()), - context, - ) - .into()) - } - - /// 15.8.2.22 `Temporal.Calendar.prototype.mergeFields ( fields, additionalFields )` - fn merge_fields(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let calendar be the this value. - // 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]). - let calendar = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("this value of Calendar mergeFields must be a Calendar object.") - })?; - - let fields = args.get_or_undefined(0).to_object(context)?; - let additional_fields = args.get_or_undefined(1).to_object(context)?; - - // 3. Let fieldsCopy be ? SnapshotOwnProperties(? ToObject(fields), null, « », « undefined »). - let fields_copy = fields::object_to_temporal_fields(&fields, context)?; - - // 4. Let additionalFieldsCopy be ? SnapshotOwnProperties(? ToObject(additionalFields), null, « », « undefined »). - let additional_copy = fields::object_to_temporal_fields(&additional_fields, context)?; - - // Custom Calendars override the `fields` method. - if let CalendarSlot::Protocol(proto) = &calendar.slot { - let result = proto.merge_fields(&fields_copy, &additional_copy, context)?; // TBD - return JsObject::from_temporal_fields(&result, context).map(Into::into); - } - - // 5. NOTE: Every property of fieldsCopy and additionalFieldsCopy is an enumerable data property with non-undefined value, - // but some property keys may be Symbols. - // 6. Let additionalKeys be ! additionalFieldsCopy.[[OwnPropertyKeys]](). - // 7. If calendar.[[Identifier]] is "iso8601", then - // a. Let overriddenKeys be ISOFieldKeysToIgnore(additionalKeys). - // 8. Else, - // a. Let overriddenKeys be CalendarFieldKeysToIgnore(calendar, additionalKeys). - // 9. Let merged be OrdinaryObjectCreate(null). - // 10. NOTE: The following steps ensure that property iteration order of merged - // matches that of fields as modified by omitting overridden properties and - // appending non-overlapping properties from additionalFields in iteration order. - // 11. Let fieldsKeys be ! fieldsCopy.[[OwnPropertyKeys]](). - // 12. For each element key of fieldsKeys, do - // a. Let propValue be undefined. - // b. If overriddenKeys contains key, then - // i. Set propValue to ! Get(additionalFieldsCopy, key). - // c. Else, - // i. Set propValue to ! Get(fieldsCopy, key). - // d. If propValue is not undefined, perform ! CreateDataPropertyOrThrow(merged, key, propValue). - let merged = fields_copy.merge_fields(&additional_copy, &calendar.slot)?; - - // 13. Perform ! CopyDataProperties(merged, additionalFieldsCopy, « »). - // 14. Return merged. - JsObject::from_temporal_fields(&merged, context).map(Into::into) - } -} +use super::extract_from_temporal_type; +use crate::{js_str, Context, JsNativeError, JsObject, JsResult, JsValue}; +use temporal_rs::components::calendar::Calendar; // -- `Calendar` Abstract Operations -- -/// 12.2.1 `CreateTemporalCalendar ( identifier [ , newTarget ] )` -pub(crate) fn create_temporal_calendar( - identifier: CalendarSlot, - new_target: Option, - context: &mut Context, -) -> JsResult { - // 1. Assert: IsBuiltinCalendar(identifier) is true. - // 2. If newTarget is not provided, set newTarget to %Temporal.Calendar%. - let new_target = new_target.unwrap_or_else(|| { - context - .realm() - .intrinsics() - .constructors() - .calendar() - .constructor() - .into() - }); - - // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Calendar.prototype%", « [[InitializedTemporalCalendar]], [[Identifier]] »). - let proto = - get_prototype_from_constructor(&new_target, StandardConstructors::calendar, context)?; - - let obj = JsObject::from_proto_and_data(proto, Calendar::new(identifier)); - - // 4. Set object.[[Identifier]] to the ASCII-lowercase of identifier. - // 5. Return object. - Ok(obj.into()) -} - -fn extract_from_temporal_type( - object: &JsObject, - date_f: DF, - datetime_f: DTF, - year_month_f: YMF, - month_day_f: MDF, - zoned_datetime_f: ZDTF, -) -> JsResult> -where - DF: FnOnce(JsObject) -> JsResult>, - DTF: FnOnce(JsObject) -> JsResult>, - YMF: FnOnce(JsObject) -> JsResult>, - MDF: FnOnce(JsObject) -> JsResult>, - ZDTF: FnOnce(JsObject) -> JsResult>, -{ - if let Ok(date) = object.clone().downcast::() { - return date_f(date); - } else if let Ok(dt) = object.clone().downcast::() { - return datetime_f(dt); - } else if let Ok(ym) = object.clone().downcast::() { - return year_month_f(ym); - } else if let Ok(md) = object.clone().downcast::() { - return month_day_f(md); - } else if let Ok(dt) = object.clone().downcast::() { - return zoned_datetime_f(dt); - } - - Ok(None) -} - /// 12.2.21 `GetTemporalCalendarSlotValueWithISODefault ( item )` #[allow(unused)] pub(crate) fn get_temporal_calendar_slot_value_with_default( item: &JsObject, context: &mut Context, -) -> JsResult> { +) -> JsResult { // 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then // a. Return item.[[Calendar]]. if let Some(calendar) = extract_from_temporal_type( @@ -1041,19 +35,16 @@ pub(crate) fn get_temporal_calendar_slot_value_with_default( let calendar_like = item.get(js_str!("calendar"), context)?; // 3. Return ? ToTemporalCalendarSlotValue(calendarLike, "iso8601"). - to_temporal_calendar_slot_value(&calendar_like, context) + to_temporal_calendar_slot_value(&calendar_like) } /// `12.2.20 ToTemporalCalendarSlotValue ( temporalCalendarLike [ , default ] )` -pub(crate) fn to_temporal_calendar_slot_value( - calendar_like: &JsValue, - context: &mut Context, -) -> JsResult> { +pub(crate) fn to_temporal_calendar_slot_value(calendar_like: &JsValue) -> JsResult { // 1. If temporalCalendarLike is undefined and default is present, then // a. Assert: IsBuiltinCalendar(default) is true. // b. Return default. if calendar_like.is_undefined() { - return Ok(CalendarSlot::default()); + return Ok(Calendar::default()); // 2. If Type(temporalCalendarLike) is Object, then } else if let Some(calendar_like) = calendar_like.as_object() { // a. If temporalCalendarLike has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then @@ -1068,17 +59,6 @@ pub(crate) fn to_temporal_calendar_slot_value( )? { return Ok(calendar); } - - // TODO: implement ObjectImplementsTemporalCalendarProtocol - // b. If ? ObjectImplementsTemporalCalendarProtocol(temporalCalendarLike) is false, throw a TypeError exception. - if !object_implements_calendar_protocol(calendar_like, context) { - return Err(JsNativeError::typ() - .with_message("CalendarLike does not implement the CalendarProtocol.") - .into()); - } - - // c. Return temporalCalendarLike. - return Ok(CalendarSlot::Protocol(calendar_like.clone())); } // 3. If temporalCalendarLike is not a String, throw a TypeError exception. @@ -1091,43 +71,5 @@ pub(crate) fn to_temporal_calendar_slot_value( // 4. Let identifier be ? ParseTemporalCalendarString(temporalCalendarLike). // 5. If IsBuiltinCalendar(identifier) is false, throw a RangeError exception. // 6. Return the ASCII-lowercase of identifier. - Ok(CalendarSlot::::from_str( - &calendar_id.to_std_string_escaped(), - )?) -} - -fn object_implements_calendar_protocol(calendar_like: &JsObject, context: &mut Context) -> bool { - CALENDAR_PROTOCOL_METHODS.into_iter().all(|method| { - calendar_like - .__has_property__(&JsString::from(method).into(), &mut context.into()) - .unwrap_or(false) - }) -} - -/// Utility function for taking a `JsValue` and converting it to a temporal library `CalendarDateLike` enum. -fn to_calendar_date_like( - date_like: &JsValue, - context: &mut Context, -) -> JsResult> { - let Some(obj) = date_like.as_object() else { - let date = temporal::plain_date::to_temporal_date(date_like, None, context)?; - - return Ok(CalendarDateLike::Date(date.inner.clone())); - }; - - let Some(date_like) = extract_from_temporal_type( - obj, - |d| Ok(Some(CalendarDateLike::CustomDate(d))), - |dt| Ok(Some(CalendarDateLike::CustomDateTime(dt))), - |ym| Ok(Some(CalendarDateLike::CustomYearMonth(ym))), - |_| Ok(None), - |_| Ok(None), - )? - else { - let date = temporal::plain_date::to_temporal_date(date_like, None, context)?; - - return Ok(CalendarDateLike::Date(date.inner.clone())); - }; - - Ok(date_like) + Ok(Calendar::from_str(&calendar_id.to_std_string_escaped())?) } diff --git a/core/engine/src/builtins/temporal/calendar/object.rs b/core/engine/src/builtins/temporal/calendar/object.rs deleted file mode 100644 index c50e63b2309..00000000000 --- a/core/engine/src/builtins/temporal/calendar/object.rs +++ /dev/null @@ -1,860 +0,0 @@ -//! Boa's implementation of a user-defined Anonymous Calendar. - -use crate::{ - builtins::{ - iterable::IteratorHint, - temporal::{ - fields::object_to_temporal_fields, plain_date, plain_date_time, plain_month_day, - plain_year_month, - }, - Array, - }, - property::PropertyKey, - Context, JsObject, JsString, JsValue, -}; - -use boa_macros::js_str; -use num_traits::ToPrimitive; -use plain_date::PlainDate; -use plain_date_time::PlainDateTime; -use plain_month_day::PlainMonthDay; -use plain_year_month::PlainYearMonth; -use temporal_rs::{ - components::{ - calendar::{CalendarDateLike, CalendarProtocol}, - Date, Duration, MonthDay, YearMonth, - }, - options::ArithmeticOverflow, - TemporalError, TemporalFields, TemporalResult, TinyAsciiStr, -}; - -impl CalendarProtocol for JsObject { - type Date = JsObject; - type DateTime = JsObject; - type YearMonth = JsObject; - type MonthDay = JsObject; - type Context = Context; - fn date_from_fields( - &self, - fields: &mut TemporalFields, - overflow: ArithmeticOverflow, - context: &mut Context, - ) -> TemporalResult> { - let method = self - .get(js_str!("dateFromFields"), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let fields = JsObject::from_temporal_fields(fields, context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let overflow_obj = JsObject::with_null_proto(); - - overflow_obj - .create_data_property_or_throw( - js_str!("overflow"), - JsString::from(overflow.to_string()), - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let value = method - .as_callable() - .ok_or_else(|| { - TemporalError::general("dateFromFields must be implemented as a callable method.") - })? - .call( - &self.clone().into(), - &[fields.into(), overflow_obj.into()], - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let obj = value.as_object().map(JsObject::borrow).ok_or_else(|| { - TemporalError::r#type() - .with_message("datefromFields must return a valid PlainDate object.") - })?; - - let pd = obj.downcast_ref::().ok_or_else(|| { - TemporalError::r#type().with_message("Object returned was not a PlainDate") - })?; - - Ok(pd.inner.clone()) - } - - fn year_month_from_fields( - &self, - fields: &mut TemporalFields, - overflow: ArithmeticOverflow, - context: &mut Context, - ) -> TemporalResult> { - let method = self - .get(js_str!("yearMonthFromFields"), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let fields = JsObject::from_temporal_fields(fields, context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let overflow_obj = JsObject::with_null_proto(); - - overflow_obj - .create_data_property_or_throw( - js_str!("overflow"), - JsString::from(overflow.to_string()), - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let value = method - .as_callable() - .ok_or_else(|| { - TemporalError::general( - "yearMonthFromFields must be implemented as a callable method.", - ) - })? - .call( - &self.clone().into(), - &[fields.into(), overflow_obj.into()], - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let obj = value.as_object().map(JsObject::borrow).ok_or_else(|| { - TemporalError::r#type() - .with_message("yearMonthFromFields must return a valid PlainYearMonth object.") - })?; - - let ym = obj.downcast_ref::().ok_or_else(|| { - TemporalError::r#type().with_message("Object returned was not a PlainDate") - })?; - - Ok(ym.inner.clone()) - } - - fn month_day_from_fields( - &self, - fields: &mut TemporalFields, - overflow: ArithmeticOverflow, - context: &mut Context, - ) -> TemporalResult> { - let method = self - .get(js_str!("yearMonthFromFields"), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let fields = JsObject::from_temporal_fields(fields, context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let overflow_obj = JsObject::with_null_proto(); - - overflow_obj - .create_data_property_or_throw( - js_str!("overflow"), - JsString::from(overflow.to_string()), - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let value = method - .as_callable() - .ok_or_else(|| { - TemporalError::general( - "yearMonthFromFields must be implemented as a callable method.", - ) - })? - .call( - &self.clone().into(), - &[fields.into(), overflow_obj.into()], - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let obj = value.as_object().map(JsObject::borrow).ok_or_else(|| { - TemporalError::r#type() - .with_message("yearMonthFromFields must return a valid PlainYearMonth object.") - })?; - - let md = obj.downcast_ref::().ok_or_else(|| { - TemporalError::r#type().with_message("Object returned was not a PlainDate") - })?; - - Ok(md.inner.clone()) - } - - fn date_add( - &self, - _date: &Date, - _duration: &Duration, - _overflow: ArithmeticOverflow, - _context: &mut Context, - ) -> TemporalResult> { - // TODO - Err(TemporalError::general("Not yet implemented.")) - } - - fn date_until( - &self, - _one: &Date, - _two: &Date, - _largest_unit: temporal_rs::options::TemporalUnit, - _context: &mut Context, - ) -> TemporalResult { - // TODO - Err(TemporalError::general("Not yet implemented.")) - } - - fn era( - &self, - _: &CalendarDateLike, - _: &mut Context, - ) -> TemporalResult>> { - // Return undefined as custom calendars do not implement -> Currently. - Ok(None) - } - - fn era_year( - &self, - _: &CalendarDateLike, - _: &mut Context, - ) -> TemporalResult> { - // Return undefined as custom calendars do not implement -> Currently. - Ok(None) - } - - fn year( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("year")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("year must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("year return must be integral.")); - } - - if number < 1f64 { - return Err(TemporalError::r#type().with_message("year return must be larger than 1.")); - } - - let result = number - .to_i32() - .ok_or_else(|| TemporalError::range().with_message("year exceeded a valid range."))?; - - Ok(result) - } - - fn month( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("month")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("month must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("month return must be integral.")); - } - - if number < 1f64 { - return Err(TemporalError::r#type().with_message("month return must be larger than 1.")); - } - - let result = number - .to_u8() - .ok_or_else(|| TemporalError::range().with_message("month exceeded a valid range."))?; - - Ok(result) - } - - fn month_code( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult> { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("monthCode")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - let JsValue::String(result) = val else { - return Err(TemporalError::r#type().with_message("monthCode return must be a String.")); - }; - - let result = TinyAsciiStr::<4>::from_str(&result.to_std_string_escaped()) - .map_err(|_| TemporalError::general("Unexpected monthCode value."))?; - - Ok(result) - } - - fn day( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("day")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("day must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("day return must be integral.")); - } - - if number < 1f64 { - return Err(TemporalError::r#type().with_message("day return must be larger than 1.")); - } - - let result = number - .to_u8() - .ok_or_else(|| TemporalError::range().with_message("day exceeded a valid range."))?; - - Ok(result) - } - - fn day_of_week( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("dayOfWeek")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("DayOfWeek must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("DayOfWeek return must be integral.")); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("DayOfWeek return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("DayOfWeek exceeded valid range.") - })?; - - Ok(result) - } - - fn day_of_year( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("dayOfYear")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("dayOfYear must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("dayOfYear return must be integral.")); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("dayOfYear return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("dayOfYear exceeded valid range.") - })?; - - Ok(result) - } - - fn week_of_year( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("weekOfYear")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("weekOfYear must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("weekOfYear return must be integral.")); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("weekOfYear return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("weekOfYear exceeded valid range.") - })?; - - Ok(result) - } - - fn year_of_week( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("yearOfWeek")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("yearOfWeek must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("yearOfWeek return must be integral.")); - } - - let result = number.to_i32().ok_or_else(|| { - TemporalError::range().with_message("yearOfWeek exceeded valid range.") - })?; - - Ok(result) - } - - fn days_in_week( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("daysInWeek")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("daysInWeek must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("daysInWeek return must be integral.")); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("daysInWeek return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("daysInWeek exceeded valid range.") - })?; - - Ok(result) - } - - fn days_in_month( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("daysInMonth")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("daysInMonth must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err( - TemporalError::r#type().with_message("daysInMonth return must be integral.") - ); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("daysInMonth return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("daysInMonth exceeded valid range.") - })?; - - Ok(result) - } - - fn days_in_year( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("daysInYear")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("daysInYear must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err(TemporalError::r#type().with_message("daysInYear return must be integral.")); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("daysInYear return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("monthsInYear exceeded valid range.") - })?; - - Ok(result) - } - - fn months_in_year( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("monthsInYear")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - // Validate the return value. - // 3. If Type(result) is not Number, throw a TypeError exception. - // 4. If IsIntegralNumber(result) is false, throw a RangeError exception. - // 5. If result < 1𝔽, throw a RangeError exception. - // 6. Return ℝ(result). - - let Some(number) = val.as_number() else { - return Err(TemporalError::r#type().with_message("monthsInYear must return a number.")); - }; - - if !number.is_finite() || number.fract() != 0.0 { - return Err( - TemporalError::r#type().with_message("monthsInYear return must be integral.") - ); - } - - if number < 1f64 { - return Err( - TemporalError::r#type().with_message("monthsInYear return must be larger than 1.") - ); - } - - let result = number.to_u16().ok_or_else(|| { - TemporalError::range().with_message("monthsInYear exceeded valid range.") - })?; - - Ok(result) - } - - fn in_leap_year( - &self, - date_like: &CalendarDateLike, - context: &mut Context, - ) -> TemporalResult { - let date_like = date_like_to_object(date_like, context)?; - - let method = self - .get(PropertyKey::from(js_str!("inLeapYear")), context) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let val = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[date_like], context) - .map_err(|err| TemporalError::general(err.to_string()))?; - - let JsValue::Boolean(result) = val else { - return Err( - TemporalError::r#type().with_message("inLeapYear must return a valid boolean.") - ); - }; - - Ok(result) - } - - fn fields(&self, fields: Vec, context: &mut Context) -> TemporalResult> { - let fields_js = Array::create_array_from_list( - fields.iter().map(|s| JsString::from(s.clone()).into()), - context, - ); - - let method = self - .get(PropertyKey::from(js_str!("fields")), context) - .expect("method must exist on an object that implements the CalendarProtocol."); - - let result = method - .as_callable() - .expect("is method") - .call(&self.clone().into(), &[fields_js.into()], context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - // validate result and map to a `Vec` - let mut iterator = result - .get_iterator(context, Some(IteratorHint::Sync), None) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let mut result = Vec::default(); - while iterator - .step(context) - .map_err(|e| TemporalError::general(e.to_string()))? - { - let next_value = iterator - .value(context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let JsValue::String(s) = next_value else { - return Err(TemporalError::r#type() - .with_message("Invalid return type in fields method implementation.")); - }; - - result.push(s.to_std_string_escaped()); - } - - Ok(result) - } - - fn merge_fields( - &self, - fields: &TemporalFields, - additional_fields: &TemporalFields, - context: &mut Context, - ) -> TemporalResult { - let fields = JsObject::from_temporal_fields(fields, context) - .map_err(|e| TemporalError::general(e.to_string()))?; - let add_fields = JsObject::from_temporal_fields(additional_fields, context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let method = self - .get(PropertyKey::from(js_str!("mergeFields")), context) - .expect("method must exist on an object that implements the CalendarProtocol."); - - let value = method - .as_callable() - .expect("is method") - .call( - &self.clone().into(), - &[fields.into(), add_fields.into()], - context, - ) - .map_err(|e| TemporalError::general(e.to_string()))?; - - let JsValue::Object(o) = value else { - return Err( - TemporalError::r#type().with_message("mergeFields did not return an object.") - ); - }; - - object_to_temporal_fields(&o, context).map_err(|e| TemporalError::general(e.to_string())) - } - - fn identifier(&self, context: &mut Context) -> TemporalResult { - let identifier = self - .__get__( - &PropertyKey::from(js_str!("id")), - self.clone().into(), - &mut context.into(), - ) - .expect("method must exist on a object that implements the CalendarProtocol."); - - let JsValue::String(s) = identifier else { - return Err(TemporalError::range().with_message("Identifier was not a string")); - }; - - Ok(s.to_std_string_escaped()) - } -} - -/// Utility function for converting `Temporal`'s `CalendarDateLike` to it's `Boa` specific `JsObject`. -pub(crate) fn date_like_to_object( - date_like: &CalendarDateLike, - context: &mut Context, -) -> TemporalResult { - match date_like { - CalendarDateLike::Date(d) => plain_date::create_temporal_date(d.clone(), None, context) - .map_err(|e| TemporalError::general(e.to_string())) - .map(Into::into), - CalendarDateLike::DateTime(dt) => { - plain_date_time::create_temporal_datetime(dt.clone(), None, context) - .map_err(|e| TemporalError::general(e.to_string())) - .map(Into::into) - } - CalendarDateLike::CustomMonthDay(md) => Ok(md.clone().upcast().into()), - CalendarDateLike::CustomYearMonth(ym) => Ok(ym.clone().upcast().into()), - CalendarDateLike::CustomDate(pd) => Ok(pd.clone().upcast().into()), - CalendarDateLike::CustomDateTime(pdt) => Ok(pdt.clone().upcast().into()), - } -} diff --git a/core/engine/src/builtins/temporal/calendar/tests.rs b/core/engine/src/builtins/temporal/calendar/tests.rs deleted file mode 100644 index 7618d924f20..00000000000 --- a/core/engine/src/builtins/temporal/calendar/tests.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{js_string, run_test_actions, TestAction}; - -#[test] -fn calendar_constructor() { - // TODO: Add other BuiltinCalendars - run_test_actions([TestAction::assert_eq( - "new Temporal.Calendar('iso8601').id", - js_string!("iso8601"), - )]); -} - -#[test] -fn calendar_methods() { - run_test_actions([ - TestAction::run("let iso = new Temporal.Calendar('iso8601');"), - TestAction::assert_eq("iso.inLeapYear('2020-11-20')", true), - TestAction::assert_eq("iso.daysInYear('2020-11-20')", 366), - TestAction::assert_eq("iso.daysInYear('2021-11-20')", 365), - TestAction::assert_eq("iso.monthsInYear('2021-11-20')", 12), - TestAction::assert_eq("iso.daysInWeek('2021-11-20')", 7), - ]); -} - -#[test] -fn run_custom_calendar() { - run_test_actions([ - TestAction::run( - r#"const custom = { - dateAdd() {}, - dateFromFields() {}, - dateUntil() {}, - day() {}, - dayOfWeek() {}, - dayOfYear() {}, - daysInMonth() { return 14 }, - daysInWeek() {return 6}, - daysInYear() {return 360}, - fields() {}, - id: "custom-calendar", - inLeapYear() {}, - mergeFields() {}, - month() {}, - monthCode() {}, - monthDayFromFields() {}, - monthsInYear() {}, - weekOfYear() {}, - year() {}, - yearMonthFromFields() {}, - yearOfWeek() {}, - }; - - let cal = Temporal.Calendar.from(custom); - let date = "1972-05-01"; - "#, - ), - TestAction::assert_eq("cal.id", js_string!("custom-calendar")), - TestAction::assert_eq("cal.daysInMonth(date)", 14), - TestAction::assert_eq("cal.daysInWeek(date)", 6), - TestAction::assert_eq("cal.daysInYear(date)", 360), - ]); -} diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 7c1c91adcd2..100884b2b33 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -691,7 +691,6 @@ impl Duration { date: plain_relative_to.as_ref(), zdt: zoned_relative_to.as_ref(), }, - context, )?; create_temporal_duration(rounded_duration, None, context).map(Into::into) } @@ -781,26 +780,6 @@ impl Duration { // -- Duration Abstract Operations -- -/// 7.5.8 `ToTemporalDuration ( item )` -pub(crate) fn to_temporal_duration( - item: &JsValue, - context: &mut Context, -) -> JsResult { - // 1a. If Type(item) is Object - // 1b. and item has an [[InitializedTemporalDuration]] internal slot, then - if let Some(duration) = item - .as_object() - .and_then(JsObject::downcast_ref::) - { - return Ok(duration.inner); - } - - // 2. Let result be ? ToTemporalDurationRecord(item). - let result = to_temporal_duration_record(item, context)?; - // 3. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). - Ok(result) -} - /// 7.5.9 `ToTemporalDurationRecord ( temporalDurationLike )` pub(crate) fn to_temporal_duration_record( temporal_duration_like: &JsValue, diff --git a/core/engine/src/builtins/temporal/fields.rs b/core/engine/src/builtins/temporal/fields.rs deleted file mode 100644 index 08bc820161a..00000000000 --- a/core/engine/src/builtins/temporal/fields.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! A Rust native implementation of the `fields` object used in `Temporal`. - -use std::str::FromStr; - -use crate::{ - js_string, object::internal_methods::InternalMethodContext, property::PropertyKey, - value::PreferredType, Context, JsNativeError, JsObject, JsResult, JsString, JsValue, -}; - -use rustc_hash::FxHashSet; - -use temporal_rs::fields::{FieldConversion, FieldValue, TemporalFields}; - -use super::{to_integer_with_truncation, to_positive_integer_with_trunc}; - -// TODO: Move extended and required fields into the temporal library? -/// `PrepareTemporalFeilds` -pub(crate) fn prepare_temporal_fields( - fields: &JsObject, - field_names: &mut Vec, - required_fields: &mut Vec, - extended_fields: Option>, - partial: bool, - dup_behaviour: Option, - context: &mut Context, -) -> JsResult { - // 1. If duplicateBehaviour is not present, set duplicateBehaviour to throw. - let dup_option = dup_behaviour.unwrap_or_else(|| js_string!("throw")); - - // 2. Let result be OrdinaryObjectCreate(null). - let mut result = TemporalFields::default(); - - // 3. Let any be false. - let mut any = false; - // 4. If extraFieldDescriptors is present, then - if let Some(extra_fields) = extended_fields { - for (field_name, required) in extra_fields { - // a. For each Calendar Field Descriptor Record desc of extraFieldDescriptors, do - // i. Assert: fieldNames does not contain desc.[[Property]]. - // ii. Append desc.[[Property]] to fieldNames. - field_names.push(JsString::from(field_name.clone())); - - // iii. If desc.[[Required]] is true and requiredFields is a List, then - if required && !partial { - // 1. Append desc.[[Property]] to requiredFields. - required_fields.push(JsString::from(field_name)); - } - } - } - - // 5. Let sortedFieldNames be SortStringListByCodeUnit(fieldNames). - // 6. Let previousProperty be undefined. - let mut dups_map = FxHashSet::default(); - - // 7. For each property name property of sortedFieldNames, do - for field in &*field_names { - // a. If property is one of "constructor" or "__proto__", then - if field.to_std_string_escaped().as_str() == "constructor" - || field.to_std_string_escaped().as_str() == "__proto__" - { - // i. Throw a RangeError exception. - return Err(JsNativeError::range() - .with_message("constructor or proto is out of field range.") - .into()); - } - - let new_value = dups_map.insert(field); - - // b. If property is not equal to previousProperty, then - if new_value { - // i. Let value be ? Get(fields, property). - let value = fields.get(PropertyKey::from(field.clone()), context)?; - // ii. If value is not undefined, then - if !value.is_undefined() { - // 1. Set any to true. - any = true; - - // 2. If property is in the Property column of Table 17 and there is a Conversion value in the same row, then - // a. Let Conversion be the Conversion value of the same row. - - // TODO: Conversion from TemporalError -> JsError - let conversion = FieldConversion::from_str(field.to_std_string_escaped().as_str()) - .map_err(|_| JsNativeError::range().with_message("wrong field value"))?; - // b. If Conversion is ToIntegerWithTruncation, then - let converted_value = match conversion { - FieldConversion::ToIntegerWithTruncation => { - // i. Set value to ? ToIntegerWithTruncation(value). - let v = to_integer_with_truncation(&value, context)?; - // ii. Set value to 𝔽(value). - FieldValue::Integer(v) - } - // c. Else if Conversion is ToPositiveIntegerWithTruncation, then - FieldConversion::ToPositiveIntegerWithTruncation => { - // i. Set value to ? ToPositiveIntegerWithTruncation(value). - let v = to_positive_integer_with_trunc(&value, context)?; - // ii. Set value to 𝔽(value). - FieldValue::Integer(v) - } - // d. Else, - // i. Assert: Conversion is ToPrimitiveAndRequireString. - FieldConversion::ToPrimativeAndRequireString => { - // ii. NOTE: Non-primitive values are supported here for consistency with other fields, but such values must coerce to Strings. - // iii. Set value to ? ToPrimitive(value, string). - let primitive = value.to_primitive(context, PreferredType::String)?; - // iv. If value is not a String, throw a TypeError exception. - FieldValue::String(primitive.to_string(context)?.to_std_string_escaped()) - } - FieldConversion::None => { - unreachable!("todo need to implement conversion handling for tz.") - } - }; - - // 3. Perform ! CreateDataPropertyOrThrow(result, property, value). - result - .set_field_value(&field.to_std_string_escaped(), &converted_value) - .expect("FieldConversion enforces the appropriate type"); - // iii. Else if requiredFields is a List, then - } else if !partial { - // 1. If requiredFields contains property, then - if required_fields.contains(field) { - // a. Throw a TypeError exception. - return Err(JsNativeError::typ() - .with_message("A required TemporalField was not provided.") - .into()); - } - - // NOTE: flag that the value is active and the default should be used. - // 2. If property is in the Property column of Table 17, then - // a. Set value to the corresponding Default value of the same row. - // 3. Perform ! CreateDataPropertyOrThrow(result, property, value). - result.require_field(&field.to_std_string_escaped()); - } - // c. Else if duplicateBehaviour is throw, then - } else if dup_option.to_std_string_escaped() == "throw" { - // i. Throw a RangeError exception. - return Err(JsNativeError::range() - .with_message("Cannot have a duplicate field") - .into()); - } - // d. Set previousProperty to property. - } - - // 8. If requiredFields is partial and any is false, then - if partial && !any { - // a. Throw a TypeError exception. - return Err(JsNativeError::range() - .with_message("requiredFields cannot be partial when any is false") - .into()); - } - - // 9. Return result. - Ok(result) -} - -// NOTE(nekevss): The below serves as a replacement for `Snapshot` on `Calendar.prototype.mergeFields`. -// -// Some potential issues here: `Calendar.prototype.mergeFields` appears to allow extra fields that -// are not part of a `TemporalFields` record; however, the specification only calls `mergeFields` on an -// object returned by `PrepareTemporalFields`, so the translation should be fine sound. -// -// The restriction/trade-off would occur if someone wanted to include non-normative calendar fields (i.e. something -// not accounted for in the specification) in a Custom Calendar or use `Calendar.prototype.mergeFields` in -// general as a way to merge two objects. -pub(crate) fn object_to_temporal_fields( - source: &JsObject, - context: &mut Context, -) -> JsResult { - // Adapted from `CopyDataProperties` with ExcludedKeys -> << >> && ExcludedValues -> << Undefined >> - const VALID_FIELDS: [&str; 14] = [ - "year", - "month", - "monthCode", - "day", - "hour", - "minute", - "second", - "millisecond", - "microsecond", - "nanosecond", - "offset", - "timeZone", - "era", - "eraYear", - ]; - let mut copy = TemporalFields::default(); - - let keys = source.__own_property_keys__(context)?; - - for key in &keys { - let desc = source.__get_own_property__(key, &mut InternalMethodContext::new(context))?; - match desc { - // Enforce that the `PropertyKey` is a valid field here. - Some(desc) - if desc.expect_enumerable() && VALID_FIELDS.contains(&key.to_string().as_str()) => - { - let value = source.get(key.clone(), context)?; - // Below is repurposed from `PrepareTemporalFields`. - if !value.is_undefined() { - let conversion = FieldConversion::from_str(&key.to_string())?; - let converted_value = match conversion { - FieldConversion::ToIntegerWithTruncation => { - let v = to_integer_with_truncation(&value, context)?; - FieldValue::Integer(v) - } - FieldConversion::ToPositiveIntegerWithTruncation => { - let v = to_positive_integer_with_trunc(&value, context)?; - FieldValue::Integer(v) - } - FieldConversion::ToPrimativeAndRequireString => { - let primitive = value.to_primitive(context, PreferredType::String)?; - FieldValue::String( - primitive.to_string(context)?.to_std_string_escaped(), - ) - } - FieldConversion::None => { - unreachable!("todo need to implement conversion handling for tz.") - } - }; - // TODO: Test the below further and potentially expand handling. - copy.set_field_value(&key.to_string(), &converted_value) - .expect("FieldConversion enforces the appropriate type"); - } - } - _ => {} - }; - } - - Ok(copy) -} - -impl JsObject { - pub(crate) fn from_temporal_fields( - fields: &TemporalFields, - context: &mut Context, - ) -> JsResult { - let obj = JsObject::with_null_proto(); - - for (key, value) in fields.active_kvs() { - let js_value = match value { - FieldValue::Undefined => JsValue::undefined(), - FieldValue::Integer(x) => JsValue::Integer(x), - FieldValue::String(s) => JsValue::String(s.into()), - }; - - obj.create_data_property_or_throw(JsString::from(key), js_value, context)?; - } - - Ok(obj) - } -} diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index d8aa749aaaf..1988982929d 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -7,7 +7,6 @@ mod calendar; mod duration; mod error; -mod fields; mod instant; mod now; mod options; @@ -23,8 +22,8 @@ mod zoned_date_time; mod tests; pub use self::{ - calendar::*, duration::*, instant::*, now::*, plain_date::*, plain_date_time::*, - plain_month_day::*, plain_time::*, plain_year_month::*, time_zone::*, zoned_date_time::*, + duration::*, instant::*, now::*, plain_date::*, plain_date_time::*, plain_month_day::*, + plain_time::*, plain_year_month::*, zoned_date_time::*, }; use crate::{ @@ -242,10 +241,7 @@ pub(crate) fn _iterator_to_list_of_types( // 13.17 `ValidateTemporalRoundingIncrement ( increment, dividend, inclusive )` // Moved to temporal_rs -type RelativeTemporalObjectResult = JsResult<( - Option>, - Option>, -)>; +type RelativeTemporalObjectResult = JsResult<(Option, Option)>; /// 13.21 `ToRelativeTemporalObject ( options )` pub(crate) fn to_relative_temporal_object( @@ -285,6 +281,7 @@ pub(crate) fn to_relative_temporal_object( /// 13.43 `ToPositiveIntegerWithTruncation ( argument )` #[inline] +#[allow(unused)] pub(crate) fn to_positive_integer_with_trunc( value: &JsValue, context: &mut Context, @@ -345,3 +342,33 @@ pub(crate) fn to_integer_if_integral(arg: &JsValue, context: &mut Context) -> Js // Note: Deviates from Proposal spec -> proto appears to be always null across the specification. // 14.7 `SnapshotOwnProperties ( source, proto [ , excludedKeys [ , excludedValues ] ] )` // Migrated or repurposed to `temporal_rs`/`fields.rs` + +fn extract_from_temporal_type( + object: &JsObject, + date_f: DF, + datetime_f: DTF, + year_month_f: YMF, + month_day_f: MDF, + zoned_datetime_f: ZDTF, +) -> JsResult> +where + DF: FnOnce(JsObject) -> JsResult>, + DTF: FnOnce(JsObject) -> JsResult>, + YMF: FnOnce(JsObject) -> JsResult>, + MDF: FnOnce(JsObject) -> JsResult>, + ZDTF: FnOnce(JsObject) -> JsResult>, +{ + if let Ok(date) = object.clone().downcast::() { + return date_f(date); + } else if let Ok(dt) = object.clone().downcast::() { + return datetime_f(dt); + } else if let Ok(ym) = object.clone().downcast::() { + return year_month_f(ym); + } else if let Ok(md) = object.clone().downcast::() { + return month_day_f(md); + } else if let Ok(dt) = object.clone().downcast::() { + return zoned_datetime_f(dt); + } + + Ok(None) +} diff --git a/core/engine/src/builtins/temporal/now.rs b/core/engine/src/builtins/temporal/now.rs index 05782441442..8dc1e16919e 100644 --- a/core/engine/src/builtins/temporal/now.rs +++ b/core/engine/src/builtins/temporal/now.rs @@ -1,10 +1,7 @@ //! Boa's implementation of `Temporal.Now` ECMAScript Builtin object. use crate::{ - builtins::{ - temporal::{create_temporal_time_zone, default_time_zone}, - BuiltInBuilder, BuiltInObject, IntrinsicObject, - }, + builtins::{BuiltInBuilder, BuiltInObject, IntrinsicObject}, context::intrinsics::Intrinsics, js_string, property::Attribute, @@ -14,8 +11,9 @@ use crate::{ Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_profiler::Profiler; +use temporal_rs::components::tz::TimeZone; -use super::{ns_max_instant, ns_min_instant}; +use super::{ns_max_instant, ns_min_instant, time_zone::default_time_zone}; /// JavaScript `Temporal.Now` object. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -61,13 +59,16 @@ impl Now { /// `Temporal.Now.timeZoneId ( )` /// /// More information: - /// - [ECMAScript specififcation][spec] + /// - [ECMAScript specification][spec] /// /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.timezone #[allow(clippy::unnecessary_wraps)] fn time_zone_id(_: &JsValue, _args: &[JsValue], context: &mut Context) -> JsResult { // 1. Return ! SystemTimeZone(). - system_time_zone(context) + system_time_zone(context)? + .id() + .map(|s| JsValue::from(js_string!(s.as_str()))) + .map_err(Into::into) } /// `Temporal.Now.instant()` @@ -176,9 +177,12 @@ fn system_zoned_date_time() { /// /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-systemtimezone #[allow(unused)] -fn system_time_zone(context: &mut Context) -> JsResult { +fn system_time_zone(context: &mut Context) -> JsResult { // 1. Let identifier be ! DefaultTimeZone(). let identifier = default_time_zone(context); // 2. Return ! CreateTemporalTimeZone(identifier). - create_temporal_time_zone(identifier, None, context) + + Err(JsNativeError::error() + .with_message("not yet implemented.") + .into()) } diff --git a/core/engine/src/builtins/temporal/options.rs b/core/engine/src/builtins/temporal/options.rs index 146c875ccd9..4de73d7fd98 100644 --- a/core/engine/src/builtins/temporal/options.rs +++ b/core/engine/src/builtins/temporal/options.rs @@ -47,8 +47,9 @@ pub(crate) fn get_temporal_unit( } #[derive(Debug, Clone, Copy)] +#[allow(unused)] pub(crate) enum TemporalUnitGroup { - Date, + Date, // Need to assert if this is neede anymore with the removal of `Temporal.Calendar` Time, DateTime, } diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index 558d8121a67..a07fa7d87d6 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -21,24 +21,24 @@ use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::{ - calendar::{CalendarSlot, GetCalendarSlot}, + calendar::{Calendar, GetTemporalCalendar}, Date as InnerDate, DateTime, }, iso::IsoDateSlots, options::ArithmeticOverflow, }; -use super::{calendar, create_temporal_calendar, PlainDateTime, ZonedDateTime}; +use super::{calendar, PlainDateTime, ZonedDateTime}; /// The `Temporal.PlainDate` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDate` could contain `Trace` types. pub struct PlainDate { - pub(crate) inner: InnerDate, + pub(crate) inner: InnerDate, } impl PlainDate { - pub(crate) fn new(inner: InnerDate) -> Self { + pub(crate) fn new(inner: InnerDate) -> Self { Self { inner } } } @@ -49,8 +49,8 @@ impl IsoDateSlots for JsObject { } } -impl GetCalendarSlot for JsObject { - fn get_calendar(&self) -> CalendarSlot { +impl GetTemporalCalendar for JsObject { + fn get_calendar(&self) -> Calendar { self.borrow().data().inner.get_calendar() } } @@ -212,7 +212,6 @@ impl IntrinsicObject for PlainDate { .method(Self::to_plain_year_month, js_string!("toPlainYearMonth"), 0) .method(Self::to_plain_month_day, js_string!("toPlainMonthDay"), 0) .method(Self::get_iso_fields, js_string!("getISOFields"), 0) - .method(Self::get_calendar, js_string!("getCalendar"), 0) .method(Self::add, js_string!("add"), 2) .method(Self::subtract, js_string!("subtract"), 2) .method(Self::with, js_string!("with"), 2) @@ -248,8 +247,7 @@ impl BuiltInConstructor for PlainDate { let iso_year = super::to_integer_with_truncation(args.get_or_undefined(0), context)?; let iso_month = super::to_integer_with_truncation(args.get_or_undefined(1), context)?; let iso_day = super::to_integer_with_truncation(args.get_or_undefined(2), context)?; - let calendar_slot = - calendar::to_temporal_calendar_slot_value(args.get_or_undefined(3), context)?; + let calendar_slot = calendar::to_temporal_calendar_slot_value(args.get_or_undefined(3))?; let date = InnerDate::new( iso_year, @@ -275,7 +273,7 @@ impl PlainDate { JsNativeError::typ().with_message("the this object must be a PlainDate object.") })?; - Ok(JsString::from(date.inner.calendar().identifier(context)?).into()) + Ok(JsString::from(date.inner.calendar().identifier()?).into()) } /// 3.3.4 get `Temporal.PlainDate.prototype.year` @@ -284,13 +282,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_year(&date, context)?.into()) + Ok(date.inner.year()?.into()) } /// 3.3.5 get `Temporal.PlainDate.prototype.month` @@ -299,13 +297,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_month(&date, context)?.into()) + Ok(date.inner.month()?.into()) } /// 3.3.6 get Temporal.PlainDate.prototype.monthCode @@ -314,16 +312,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok( - JsString::from(InnerDate::::contextual_month_code(&date, context)?.as_str()) - .into(), - ) + Ok(JsString::from(date.inner.month_code()?.as_str()).into()) } /// 3.3.7 get `Temporal.PlainDate.prototype.day` @@ -332,13 +327,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_day(&date, context)?.into()) + Ok(date.inner.day()?.into()) } /// 3.3.8 get `Temporal.PlainDate.prototype.dayOfWeek` @@ -347,13 +342,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_day_of_week(&date, context)?.into()) + Ok(date.inner.day_of_week()?.into()) } /// 3.3.9 get `Temporal.PlainDate.prototype.dayOfYear` @@ -362,13 +357,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_day_of_year(&date, context)?.into()) + Ok(date.inner.day_of_year()?.into()) } /// 3.3.10 get `Temporal.PlainDate.prototype.weekOfYear` @@ -377,13 +372,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_week_of_year(&date, context)?.into()) + Ok(date.inner.week_of_year()?.into()) } /// 3.3.11 get `Temporal.PlainDate.prototype.yearOfWeek` @@ -392,13 +387,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_year_of_week(&date, context)?.into()) + Ok(date.inner.year_of_week()?.into()) } /// 3.3.12 get `Temporal.PlainDate.prototype.daysInWeek` @@ -407,13 +402,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_days_in_week(&date, context)?.into()) + Ok(date.inner.days_in_week()?.into()) } /// 3.3.13 get `Temporal.PlainDate.prototype.daysInMonth` @@ -426,13 +421,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_days_in_month(&date, context)?.into()) + Ok(date.inner.days_in_month()?.into()) } /// 3.3.14 get `Temporal.PlainDate.prototype.daysInYear` @@ -441,13 +436,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_days_in_year(&date, context)?.into()) + Ok(date.inner.days_in_year()?.into()) } /// 3.3.15 get `Temporal.PlainDate.prototype.monthsInYear` @@ -460,13 +455,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_months_in_year(&date, context)?.into()) + Ok(date.inner.months_in_year()?.into()) } /// 3.3.16 get `Temporal.PlainDate.prototype.inLeapYear` @@ -475,13 +470,13 @@ impl PlainDate { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDate object.") .into()); }; - Ok(InnerDate::::contextual_in_leap_year(&date, context)?.into()) + Ok(date.inner.in_leap_year()?.into()) } } @@ -506,18 +501,6 @@ impl PlainDate { .into()) } - /// 3.3.20 `Temporal.PlainDate.prototype.getCalendar ( )` - fn get_calendar(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; - - create_temporal_calendar(date.inner.calendar().clone(), None, context) - } - fn add(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { Err(JsNativeError::error() .with_message("not yet implemented.") @@ -575,7 +558,7 @@ impl PlainDate { /// 3.5.3 `CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] )` pub(crate) fn create_temporal_date( - inner: InnerDate, + inner: InnerDate, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { @@ -583,7 +566,7 @@ pub(crate) fn create_temporal_date( // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception. // 2. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception. - if !DateTime::::validate(&inner) { + if !DateTime::validate(&inner) { return Err(JsNativeError::range() .with_message("Date is not within ISO date time limits.") .into()); @@ -684,7 +667,7 @@ pub(crate) fn to_temporal_date( // 13. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). let result = date_like_string .to_std_string_escaped() - .parse::>() + .parse::() .map_err(|err| JsNativeError::range().with_message(err.to_string()))?; Ok(PlainDate::new(result)) diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 55e4d815621..28b312ab538 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -23,7 +23,7 @@ mod tests; use temporal_rs::{ components::{ - calendar::{CalendarSlot, GetCalendarSlot}, + calendar::{Calendar, GetTemporalCalendar}, DateTime as InnerDateTime, }, iso::{IsoDate, IsoDateSlots}, @@ -33,15 +33,15 @@ use temporal_rs::{ #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDateTime` could contain `Trace` types. pub struct PlainDateTime { - pub(crate) inner: InnerDateTime, + pub(crate) inner: InnerDateTime, } impl PlainDateTime { - fn new(inner: InnerDateTime) -> Self { + fn new(inner: InnerDateTime) -> Self { Self { inner } } - pub(crate) fn inner(&self) -> &InnerDateTime { + pub(crate) fn inner(&self) -> &InnerDateTime { &self.inner } } @@ -52,8 +52,8 @@ impl IsoDateSlots for JsObject { } } -impl GetCalendarSlot for JsObject { - fn get_calendar(&self) -> CalendarSlot { +impl GetTemporalCalendar for JsObject { + fn get_calendar(&self) -> Calendar { self.borrow().data().inner.get_calendar() } } @@ -325,8 +325,7 @@ impl BuiltInConstructor for PlainDateTime { .get(8) .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; // 11. Let calendar be ? ToTemporalCalendarSlotValue(calendarLike, "iso8601"). - let calendar_slot = - calendar::to_temporal_calendar_slot_value(args.get_or_undefined(9), context)?; + let calendar_slot = calendar::to_temporal_calendar_slot_value(args.get_or_undefined(9))?; let dt = InnerDateTime::new( iso_year, @@ -358,7 +357,7 @@ impl PlainDateTime { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - Ok(JsString::from(date.inner.calendar().identifier(context)?).into()) + Ok(JsString::from(date.inner.calendar().identifier()?).into()) } /// 5.3.4 get `Temporal.PlainDateTime.prototype.year` @@ -367,13 +366,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_year(&date, context)?.into()) + Ok(date.inner.year()?.into()) } /// 5.3.5 get `Temporal.PlainDateTime.prototype.month` @@ -382,13 +381,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_month(&date, context)?.into()) + Ok(date.inner.month()?.into()) } /// 5.3.6 get Temporal.PlainDateTime.prototype.monthCode @@ -397,16 +396,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(JsString::from( - InnerDateTime::::contextual_month_code(&date, context)?.as_str(), - ) - .into()) + Ok(JsString::from(date.inner.month_code()?.as_str()).into()) } /// 5.3.7 get `Temporal.PlainDateTime.prototype.day` @@ -415,13 +411,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_day(&date, context)?.into()) + Ok(date.inner.day()?.into()) } /// 5.3.8 get `Temporal.PlainDateTime.prototype.hour` @@ -520,13 +516,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_day_of_week(&date, context)?.into()) + Ok(date.inner.day_of_week()?.into()) } /// 5.3.15 get `Temporal.PlainDateTime.prototype.dayOfYear` @@ -535,13 +531,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_day_of_year(&date, context)?.into()) + Ok(date.inner.day_of_year()?.into()) } /// 5.3.16 get `Temporal.PlainDateTime.prototype.weekOfYear` @@ -550,13 +546,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_week_of_year(&date, context)?.into()) + Ok(date.inner.week_of_year()?.into()) } /// 5.3.17 get `Temporal.PlainDateTime.prototype.yearOfWeek` @@ -565,13 +561,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_year_of_week(&date, context)?.into()) + Ok(date.inner.year_of_week()?.into()) } /// 5.3.18 get `Temporal.PlainDateTime.prototype.daysInWeek` @@ -580,13 +576,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_days_in_week(&date, context)?.into()) + Ok(date.inner.days_in_week()?.into()) } /// 5.3.19 get `Temporal.PlainDateTime.prototype.daysInMonth` @@ -599,13 +595,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_days_in_month(&date, context)?.into()) + Ok(date.inner.days_in_month()?.into()) } /// 5.3.20 get `Temporal.PlainDateTime.prototype.daysInYear` @@ -614,13 +610,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_days_in_year(&date, context)?.into()) + Ok(date.inner.days_in_year()?.into()) } /// 5.3.21 get `Temporal.PlainDateTime.prototype.monthsInYear` @@ -633,13 +629,13 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_months_in_year(&date, context)?.into()) + Ok(date.inner.months_in_year()?.into()) } /// 5.3.22 get `Temporal.PlainDateTime.prototype.inLeapYear` @@ -648,20 +644,20 @@ impl PlainDateTime { .as_object() .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - let Ok(date) = obj.clone().downcast::() else { + let Some(date) = obj.downcast_ref::() else { return Err(JsNativeError::typ() .with_message("the this object must be a PlainDateTime object.") .into()); }; - Ok(InnerDateTime::::contextual_in_leap_year(&date, context)?.into()) + Ok(date.inner.in_leap_year()?.into()) } } // ==== `PlainDateTime` Abstract Operations` ==== pub(crate) fn create_temporal_datetime( - inner: InnerDateTime, + inner: InnerDateTime, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { diff --git a/core/engine/src/builtins/temporal/plain_date_time/tests.rs b/core/engine/src/builtins/temporal/plain_date_time/tests.rs index 4cf106bcfc0..0c5883291d8 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/tests.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/tests.rs @@ -3,8 +3,7 @@ use crate::{run_test_actions, TestAction}; #[test] fn pdt_year_of_week_basic() { run_test_actions([ - TestAction::run("let calendar = Temporal.Calendar.from('iso8601')"), - TestAction::run("let pdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar)"), + TestAction::run("let pdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, 'iso8601')"), TestAction::assert_eq("pdt.yearOfWeek", 1976), ]); } diff --git a/core/engine/src/builtins/temporal/plain_month_day/mod.rs b/core/engine/src/builtins/temporal/plain_month_day/mod.rs index 62e483ab2bd..1c57545d0cf 100644 --- a/core/engine/src/builtins/temporal/plain_month_day/mod.rs +++ b/core/engine/src/builtins/temporal/plain_month_day/mod.rs @@ -14,7 +14,7 @@ use boa_profiler::Profiler; use temporal_rs::{ components::{ - calendar::{CalendarSlot, GetCalendarSlot}, + calendar::{Calendar, GetTemporalCalendar}, DateTime, MonthDay as InnerMonthDay, }, iso::IsoDateSlots, @@ -24,11 +24,11 @@ use temporal_rs::{ #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerMonthDay` could contain `Trace` types. pub struct PlainMonthDay { - pub(crate) inner: InnerMonthDay, + pub(crate) inner: InnerMonthDay, } impl PlainMonthDay { - fn new(inner: InnerMonthDay) -> Self { + fn new(inner: InnerMonthDay) -> Self { Self { inner } } } @@ -39,8 +39,8 @@ impl IsoDateSlots for JsObject { } } -impl GetCalendarSlot for JsObject { - fn get_calendar(&self) -> CalendarSlot { +impl GetTemporalCalendar for JsObject { + fn get_calendar(&self) -> Calendar { self.borrow().data().inner.get_calendar() } } @@ -87,13 +87,13 @@ impl BuiltInConstructor for PlainMonthDay { // ==== `PlainMonthDay` Abstract Operations ==== pub(crate) fn create_temporal_month_day( - inner: InnerMonthDay, + inner: InnerMonthDay, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { // 1. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception. // 2. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception. - if DateTime::::validate(&inner) { + if DateTime::validate(&inner) { return Err(JsNativeError::range() .with_message("PlainMonthDay is not a valid ISO date time.") .into()); diff --git a/core/engine/src/builtins/temporal/plain_year_month/mod.rs b/core/engine/src/builtins/temporal/plain_year_month/mod.rs index 4b06c7ae46c..fb4b89fe950 100644 --- a/core/engine/src/builtins/temporal/plain_year_month/mod.rs +++ b/core/engine/src/builtins/temporal/plain_year_month/mod.rs @@ -13,28 +13,28 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; -use super::calendar::to_temporal_calendar_slot_value; - use temporal_rs::{ iso::IsoDateSlots, { components::{ - calendar::{CalendarSlot, GetCalendarSlot}, + calendar::{Calendar as InnerCalendar, GetTemporalCalendar}, YearMonth as InnerYearMonth, }, options::ArithmeticOverflow, }, }; +use super::calendar; + /// The `Temporal.PlainYearMonth` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerYearMonth` could contain `Trace` types. pub struct PlainYearMonth { - pub(crate) inner: InnerYearMonth, + pub(crate) inner: InnerYearMonth, } impl PlainYearMonth { - pub(crate) fn new(inner: InnerYearMonth) -> Self { + pub(crate) fn new(inner: InnerYearMonth) -> Self { Self { inner } } } @@ -45,8 +45,8 @@ impl IsoDateSlots for JsObject { } } -impl GetCalendarSlot for JsObject { - fn get_calendar(&self) -> CalendarSlot { +impl GetTemporalCalendar for JsObject { + fn get_calendar(&self) -> InnerCalendar { self.borrow().data().inner.get_calendar() } } @@ -193,7 +193,7 @@ impl BuiltInConstructor for PlainYearMonth { // 4. Let m be ? ToIntegerWithTruncation(isoMonth). let m = super::to_integer_with_truncation(args.get_or_undefined(1), context)?; // 5. Let calendar be ? ToTemporalCalendarSlotValue(calendarLike, "iso8601"). - let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(2), context)?; + let calendar = calendar::to_temporal_calendar_slot_value(args.get_or_undefined(2))?; // 7. Return ? CreateTemporalYearMonth(y, m, calendar, ref, NewTarget). let inner = InnerYearMonth::new(y, m, ref_day, calendar, ArithmeticOverflow::Reject)?; @@ -301,7 +301,7 @@ impl PlainYearMonth { // 9.5.5 `CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ , newTarget ] )` pub(crate) fn create_temporal_year_month( - ym: InnerYearMonth, + ym: InnerYearMonth, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { diff --git a/core/engine/src/builtins/temporal/time_zone/custom.rs b/core/engine/src/builtins/temporal/time_zone/custom.rs deleted file mode 100644 index 57aca912792..00000000000 --- a/core/engine/src/builtins/temporal/time_zone/custom.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! A custom `TimeZone` object. -use crate::{property::PropertyKey, Context, JsObject, JsValue}; - -use boa_gc::{Finalize, Trace}; -use boa_macros::js_str; -use num_bigint::BigInt; -use temporal_rs::{ - components::{tz::TzProtocol, Instant}, - TemporalError, TemporalResult, -}; - -#[derive(Debug, Clone, Trace, Finalize)] -pub(crate) struct JsCustomTimeZone { - tz: JsObject, -} - -impl TzProtocol for JsCustomTimeZone { - type Context = Context; - fn get_offset_nanos_for(&self, context: &mut Context) -> TemporalResult { - let method = self - .tz - .get(js_str!("getOffsetNanosFor"), context) - .expect("Method must exist for the custom calendar to be valid."); - - let result = method - .as_callable() - .expect("is method") - .call(&method, &[], context) - .map_err(|e| TemporalError::general(e.to_string()))?; - - // TODO (nekevss): Validate that the below conversion is fine vs. matching to JsValue::BigInt() - let Some(bigint) = result.as_bigint() else { - return Err(TemporalError::r#type() - .with_message("Expected BigInt return from getOffsetNanosFor")); - }; - - Ok(bigint.as_inner().clone()) - } - - fn get_possible_instant_for(&self, _context: &mut Context) -> TemporalResult> { - // TODO: Implement once Instant has been migrated to `boa_temporal`'s Instant. - Err(TemporalError::range().with_message("Not yet implemented.")) - } - - fn id(&self, context: &mut Context) -> TemporalResult { - let ident = self - .tz - .__get__( - &PropertyKey::from(js_str!("id")), - JsValue::undefined(), - &mut context.into(), - ) - .expect("Method must exist for the custom calendar to be valid."); - - let JsValue::String(id) = ident else { - return Err( - TemporalError::r#type().with_message("Invalid custom Time Zone identifier type.") - ); - }; - - Ok(id.to_std_string_escaped()) - } -} diff --git a/core/engine/src/builtins/temporal/time_zone/mod.rs b/core/engine/src/builtins/temporal/time_zone/mod.rs index 652d76d5240..c5cd5c6b6cb 100644 --- a/core/engine/src/builtins/temporal/time_zone/mod.rs +++ b/core/engine/src/builtins/temporal/time_zone/mod.rs @@ -1,279 +1,7 @@ //! Boa's implemetation of the `Temporal.TimeZone` builtin object. #![allow(dead_code)] -use crate::{ - builtins::{ - temporal::to_zero_padded_decimal_string, BuiltInBuilder, BuiltInConstructor, BuiltInObject, - IntrinsicObject, - }, - context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, - js_string, - object::{internal_methods::get_prototype_from_constructor, CONSTRUCTOR}, - property::Attribute, - realm::Realm, - string::StaticJsStrings, - Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, -}; -use boa_gc::{custom_trace, Finalize, Trace}; -use boa_profiler::Profiler; -use temporal_rs::components::tz::TimeZoneSlot; - -mod custom; - -#[doc(inline)] -pub(crate) use custom::JsCustomTimeZone; - -/// The `Temporal.TimeZone` object. -#[derive(Debug, Clone, Finalize, JsData)] -pub struct TimeZone { - slot: TimeZoneSlot, -} - -unsafe impl Trace for TimeZone { - custom_trace!(this, mark, { - match &this.slot { - TimeZoneSlot::Protocol(custom) => mark(custom), - // SAFETY: No values that are exposed to gc are in TZ - TimeZoneSlot::Tz(_) => {} - } - }); -} - -impl BuiltInObject for TimeZone { - const NAME: JsString = StaticJsStrings::TIMEZONE; -} - -impl IntrinsicObject for TimeZone { - fn init(realm: &Realm) { - let _timer = Profiler::global().start_event(std::any::type_name::(), "init"); - - let get_id = BuiltInBuilder::callable(realm, Self::get_id) - .name(js_string!("get Id")) - .build(); - - BuiltInBuilder::from_standard_constructor::(realm) - .method( - Self::get_offset_nanoseconds_for, - js_string!("getOffsetNanosecondsFor"), - 1, - ) - .method( - Self::get_offset_string_for, - js_string!("getOffsetStringFor"), - 1, - ) - .method( - Self::get_plain_date_time_for, - js_string!("getPlainDateTimeFor"), - 2, - ) - .method(Self::get_instant_for, js_string!("getInstantFor"), 2) - .method( - Self::get_possible_instants_for, - js_string!("getPossibleInstantFor"), - 1, - ) - .method( - Self::get_next_transition, - js_string!("getNextTransition"), - 1, - ) - .method( - Self::get_previous_transition, - js_string!("getPreviousTransition"), - 1, - ) - .method(Self::to_string, js_string!("toString"), 0) - .method(Self::to_string, js_string!("toJSON"), 0) - .property( - JsSymbol::to_string_tag(), - Self::NAME, - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, - ) - .static_property( - CONSTRUCTOR, - realm.intrinsics().constructors().time_zone().prototype(), - Attribute::default(), - ) - .accessor(js_string!("id"), Some(get_id), None, Attribute::default()) - .build(); - } - - fn get(intrinsics: &Intrinsics) -> JsObject { - Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor() - } -} - -impl BuiltInConstructor for TimeZone { - const LENGTH: usize = 1; - - const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor = - StandardConstructors::time_zone; - - fn constructor( - new_target: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. If NewTarget is undefined, then - // 1a. Throw a TypeError exception. - if new_target.is_undefined() { - return Err(JsNativeError::typ() - .with_message("newTarget cannot be undefined for Temporal.TimeZone constructor") - .into()); - }; - - // 2. Set identifier to ? ToString(identifier). - let identifier = args.get_or_undefined(0); - if identifier.is_undefined() { - return Err(JsNativeError::range() - .with_message("Temporal.TimeZone must be called with a valid initializer") - .into()); - } - - // 3. If IsTimeZoneOffsetString(identifier) is false, then - // a. If IsAvailableTimeZoneName(identifier) is false, then - // i. Throw a RangeError exception. - // b. Set identifier to ! CanonicalizeTimeZoneName(identifier). - // 4. Return ? CreateTemporalTimeZone(identifier, NewTarget). - create_temporal_time_zone( - identifier.to_string(context)?.to_std_string_escaped(), - Some(new_target.clone()), - context, - ) - } -} - -impl TimeZone { - // NOTE: id, toJSON, toString currently share the exact same implementation -> Consolidate into one function and define multiple accesors? - pub(crate) fn get_id( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - let tz = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("this value must be a Temporal.TimeZone") - })?; - Ok(JsString::from(tz.slot.id(context)?).into()) - } - - pub(crate) fn get_offset_nanoseconds_for( - this: &JsValue, - args: &[JsValue], - _: &mut Context, - ) -> JsResult { - // 1. Let timeZone be the this value. - // 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]). - let _tz = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("this value must be a Temporal.TimeZone") - })?; - // 3. Set instant to ? ToTemporalInstant(instant). - let _i = args.get_or_undefined(0); - - // 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return 𝔽(timeZone.[[OffsetNanoseconds]]). - // 5. Return 𝔽(GetNamedTimeZoneOffsetNanoseconds(timeZone.[[Identifier]], instant.[[Nanoseconds]])). - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } - - pub(crate) fn get_offset_string_for( - this: &JsValue, - args: &[JsValue], - _: &mut Context, - ) -> JsResult { - // 1. Let timeZone be the this value. - // 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]). - let _tz = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("this value must be a Temporal.TimeZone") - })?; - // 3. Set instant to ? ToTemporalInstant(instant). - let _i = args.get_or_undefined(0); - // TODO: to_temporal_instant is abstract operation for Temporal.Instant objects. - // let instant = to_temporal_instant(i)?; - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - - // 4. Return ? GetOffsetStringFor(timeZone, instant). - } - - pub(crate) fn get_plain_date_time_for( - _: &JsValue, - _: &[JsValue], - _: &mut Context, - ) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } - - pub(crate) fn get_instant_for( - _: &JsValue, - _: &[JsValue], - _: &mut Context, - ) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } - - pub(crate) fn get_possible_instants_for( - _: &JsValue, - _: &[JsValue], - _: &mut Context, - ) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } - - pub(crate) fn get_next_transition( - _: &JsValue, - _: &[JsValue], - _: &mut Context, - ) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } - - pub(crate) fn get_previous_transition( - _: &JsValue, - _: &[JsValue], - _: &mut Context, - ) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } - - pub(crate) fn to_string( - this: &JsValue, - _: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. Let timeZone be the this value. - // 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]). - let tz = this - .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("this value must be a Temporal.TimeZone") - })?; - // 3. Return timeZone.[[Identifier]]. - Ok(JsString::from(tz.slot.id(context)?).into()) - } -} +use crate::{builtins::temporal::to_zero_padded_decimal_string, Context}; // -- TimeZone Abstract Operations -- @@ -303,39 +31,6 @@ pub(super) fn default_time_zone(context: &mut Context) -> String { // TO-DO: full, system-aware implementation (and intl feature) } -/// Abstract operation `CreateTemporalTimeZone ( identifier [ , newTarget ] )` -/// -/// More information: -/// - [ECMAScript specififcation][spec] -/// -/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltimezone -#[allow(clippy::needless_pass_by_value, unused)] -pub(super) fn create_temporal_time_zone( - identifier: String, - new_target: Option, - context: &mut Context, -) -> JsResult { - // 1. If newTarget is not present, set newTarget to %Temporal.TimeZone%. - let new_target = new_target.unwrap_or_else(|| { - context - .realm() - .intrinsics() - .constructors() - .time_zone() - .prototype() - .into() - }); - - // 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.TimeZone.prototype%", « [[InitializedTemporalTimeZone]], [[Identifier]], [[OffsetNanoseconds]] »). - let prototype = - get_prototype_from_constructor(&new_target, StandardConstructors::time_zone, context)?; - - // TODO: Migrate ISO8601 parsing to `boa_temporal` - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) -} - /// Abstract operation `FormatTimeZoneOffsetString ( offsetNanoseconds )` fn format_time_zone_offset_string(offset_nanoseconds: i64) -> String { // 1. Assert: offsetNanoseconds is an integer. diff --git a/core/engine/src/builtins/temporal/zoned_date_time/mod.rs b/core/engine/src/builtins/temporal/zoned_date_time/mod.rs index 3c0e8691d50..75c0516183f 100644 --- a/core/engine/src/builtins/temporal/zoned_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/zoned_date_time/mod.rs @@ -7,32 +7,15 @@ use crate::{ string::StaticJsStrings, Context, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; -use boa_gc::{custom_trace, Finalize, Trace}; +use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; -use temporal_rs::components::{ - calendar::CalendarSlot, tz::TimeZoneSlot, Duration as TemporalDuration, - ZonedDateTime as InnerZdt, -}; - -use super::JsCustomTimeZone; +use temporal_rs::components::{Duration as TemporalDuration, ZonedDateTime as InnerZdt}; /// The `Temporal.ZonedDateTime` object. -#[derive(Debug, Clone, Finalize, JsData)] +#[derive(Debug, Clone, Trace, Finalize, JsData)] +#[boa_gc(unsafe_empty_trace)] pub struct ZonedDateTime { - pub(crate) inner: InnerZdt, -} - -unsafe impl Trace for ZonedDateTime { - custom_trace!(this, mark, { - match this.inner.calendar() { - CalendarSlot::Protocol(custom) => mark(custom), - CalendarSlot::Builtin(_) => {} - } - match this.inner.tz() { - TimeZoneSlot::Protocol(custom) => mark(custom), - TimeZoneSlot::Tz(_) => {} - } - }); + pub(crate) inner: InnerZdt, } impl BuiltInObject for ZonedDateTime { diff --git a/core/gc/src/cell.rs b/core/gc/src/cell.rs index fefef87d449..3666d88d973 100644 --- a/core/gc/src/cell.rs +++ b/core/gc/src/cell.rs @@ -10,6 +10,7 @@ use std::{ fmt::{self, Debug, Display}, hash::Hash, ops::{Deref, DerefMut}, + ptr, }; /// `BorrowFlag` represent the internal state of a `GcCell` and @@ -405,7 +406,7 @@ impl<'a, T: ?Sized, U: ?Sized> GcRefMut<'a, T, U> { { #[allow(trivial_casts)] // SAFETY: This is safe as `GcCellRefMut` is already borrowed, so the value is rooted. - let value = unsafe { &mut *(orig.value as *mut U) }; + let value = unsafe { &mut *ptr::from_mut::(orig.value) }; let ret = GcRefMut { gc_cell: orig.gc_cell, @@ -434,7 +435,7 @@ impl<'a, T: ?Sized, U: ?Sized> GcRefMut<'a, T, U> { { #[allow(trivial_casts)] // SAFETY: This is safe as `GcCellRefMut` is already borrowed, so the value is rooted. - let value = unsafe { &mut *(orig.value as *mut U) }; + let value = unsafe { &mut *ptr::from_mut::(orig.value) }; let ret = GcRefMut { gc_cell: orig.gc_cell, diff --git a/test262_config.toml b/test262_config.toml index d5a1842e242..b6678b3e185 100644 --- a/test262_config.toml +++ b/test262_config.toml @@ -1,4 +1,4 @@ -commit = "c47b716e8d6bea0c4510d449fd22b7ed5f8b0151" +commit = "3a7a72aef5009eb22117231d40f9a5a66a9a595a" [ignored] # Not implemented yet: diff --git a/tests/tester/src/edition.rs b/tests/tester/src/edition.rs index 8cbfaabcc73..a1a289d2a8a 100644 --- a/tests/tester/src/edition.rs +++ b/tests/tester/src/edition.rs @@ -97,6 +97,16 @@ static FEATURE_EDITION: phf::Map<&'static str, SpecEdition> = phf::phf_map! { // https://github.com/tc39/proposal-float16array "Float16Array" => SpecEdition::ESNext, + // Math.sumPrecise + // https://github.com/tc39/proposal-math-sum + "Math.sumPrecise" => SpecEdition::ESNext, + + // Source Phase Imports + // https://github.com/tc39/proposal-source-phase-imports + "source-phase-imports" => SpecEdition::ESNext, + // test262 special specifier + "source-phase-imports-module-source" => SpecEdition::ESNext, + // Part of the next ES15 edition "Atomics.waitAsync" => SpecEdition::ESNext, "regexp-v-flag" => SpecEdition::ESNext, From fc2a6e09969772feba98eaa89aaf89ca4797e925 Mon Sep 17 00:00:00 2001 From: Kevin Ness <46825870+nekevss@users.noreply.github.com> Date: Sun, 7 Jul 2024 07:54:27 -0400 Subject: [PATCH 055/212] Update Temporal rounding and implement additional methods (#3892) * Impl methods and update temporal for new rounding * implement PlainDate until and since methods * Bump temporal-rs commit --- Cargo.lock | 2 +- Cargo.toml | 2 +- .../src/builtins/temporal/duration/mod.rs | 25 +- .../src/builtins/temporal/instant/mod.rs | 31 +- core/engine/src/builtins/temporal/options.rs | 20 +- .../src/builtins/temporal/plain_date/mod.rs | 135 +++++-- .../builtins/temporal/plain_date_time/mod.rs | 333 +++++++++--------- 7 files changed, 327 insertions(+), 221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a4d53881f7..f1dc8007e3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3201,7 +3201,7 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "temporal_rs" version = "0.0.2" -source = "git+https://github.com/boa-dev/temporal.git?rev=c658ac7db4701822cc179d04b56bb9c8fb7e954c#c658ac7db4701822cc179d04b56bb9c8fb7e954c" +source = "git+https://github.com/boa-dev/temporal.git?rev=cf70b50f90865d2c00fb994b7adf8b9e1bd837b8#cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" dependencies = [ "bitflags 2.6.0", "icu_calendar", diff --git a/Cargo.toml b/Cargo.toml index 2e287af24d0..23162901dc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,7 +115,7 @@ intrusive-collections = "0.9.6" cfg-if = "1.0.0" either = "1.13.0" sys-locale = "0.3.1" -temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "c658ac7db4701822cc179d04b56bb9c8fb7e954c" } +temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" } web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.9.0" diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 100884b2b33..9bf5d7b104b 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -18,7 +18,7 @@ use boa_macros::js_str; use boa_profiler::Profiler; use temporal_rs::{ components::Duration as InnerDuration, - options::{RelativeTo, RoundingIncrement, TemporalRoundingMode, TemporalUnit}, + options::{RelativeTo, RoundingIncrement, RoundingOptions, TemporalRoundingMode, TemporalUnit}, }; use super::{ @@ -389,7 +389,7 @@ impl Duration { // 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], // duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]])). - Ok(duration.inner.sign().into()) + Ok((duration.inner.sign() as i8).into()) } /// 7.3.14 get Temporal.Duration.prototype.blank @@ -640,10 +640,11 @@ impl Duration { // NOTE: 6 & 7 unused in favor of `is_none()`. // 6. Let smallestUnitPresent be true. // 7. Let largestUnitPresent be true. + let mut options = RoundingOptions::default(); // 8. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads "relativeTo", ToTemporalRoundingIncrement reads "roundingIncrement" and ToTemporalRoundingMode reads "roundingMode"). // 9. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »). - let largest_unit = get_temporal_unit( + options.largest_unit = get_temporal_unit( &round_to, js_str!("largestUnit"), TemporalUnitGroup::DateTime, @@ -658,15 +659,15 @@ impl Duration { super::to_relative_temporal_object(&round_to, context)?; // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo). - let rounding_increment = + options.increment = get_option::(&round_to, js_str!("roundingIncrement"), context)?; // 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). - let rounding_mode = + options.rounding_mode = get_option::(&round_to, js_str!("roundingMode"), context)?; // 15. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined). - let smallest_unit = get_temporal_unit( + options.smallest_unit = get_temporal_unit( &round_to, js_str!("smallestUnit"), TemporalUnitGroup::DateTime, @@ -676,17 +677,9 @@ impl Duration { // NOTE: execute step 21 earlier before initial values are shadowed. // 21. If smallestUnitPresent is false and largestUnitPresent is false, then - if smallest_unit.is_none() && largest_unit.is_none() { - // a. Throw a RangeError exception. - return Err(JsNativeError::range() - .with_message("smallestUnit or largestUnit must be present.") - .into()); - } + let rounded_duration = duration.inner.round( - rounding_increment, - smallest_unit, - largest_unit, - rounding_mode, + options, &RelativeTo { date: plain_relative_to.as_ref(), zdt: zoned_relative_to.as_ref(), diff --git a/core/engine/src/builtins/temporal/instant/mod.rs b/core/engine/src/builtins/temporal/instant/mod.rs index 3c02f38be55..3dbe6654d69 100644 --- a/core/engine/src/builtins/temporal/instant/mod.rs +++ b/core/engine/src/builtins/temporal/instant/mod.rs @@ -21,10 +21,9 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_macros::js_str; use boa_profiler::Profiler; -use temporal_rs::{ - components::Instant as InnerInstant, - options::{RoundingIncrement, TemporalRoundingMode, TemporalUnit}, -}; +use temporal_rs::{components::Instant as InnerInstant, options::TemporalRoundingMode}; + +use super::options::get_difference_settings; /// The `Temporal.Instant` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] @@ -285,15 +284,9 @@ impl Instant { let other = to_temporal_instant(args.get_or_undefined(0))?; // Fetch the necessary options. - let options = get_options_object(args.get_or_undefined(1))?; - let mode = get_option::(&options, js_str!("roundingMode"), context)?; - let increment = - get_option::(&options, js_str!("roundingIncrement"), context)?; - let smallest_unit = get_option::(&options, js_str!("smallestUnit"), context)?; - let largest_unit = get_option::(&options, js_str!("largestUnit"), context)?; - let result = instant - .inner - .until(&other, mode, increment, smallest_unit, largest_unit)?; + let settings = + get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?; + let result = instant.inner.until(&other, settings)?; create_temporal_duration(result.into(), None, context).map(Into::into) } @@ -314,15 +307,9 @@ impl Instant { // 3. Return ? DifferenceTemporalInstant(since, instant, other, options). let other = to_temporal_instant(args.get_or_undefined(0))?; - let options = get_options_object(args.get_or_undefined(1))?; - let mode = get_option::(&options, js_str!("roundingMode"), context)?; - let increment = - get_option::(&options, js_str!("roundingIncrement"), context)?; - let smallest_unit = get_option::(&options, js_str!("smallestUnit"), context)?; - let largest_unit = get_option::(&options, js_str!("largestUnit"), context)?; - let result = instant - .inner - .since(&other, mode, increment, smallest_unit, largest_unit)?; + let settings = + get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?; + let result = instant.inner.since(&other, settings)?; create_temporal_duration(result.into(), None, context).map(Into::into) } diff --git a/core/engine/src/builtins/temporal/options.rs b/core/engine/src/builtins/temporal/options.rs index 4de73d7fd98..8f7a5df0c76 100644 --- a/core/engine/src/builtins/temporal/options.rs +++ b/core/engine/src/builtins/temporal/options.rs @@ -13,9 +13,10 @@ use crate::{ string::JsStr, Context, JsNativeError, JsObject, JsResult, JsValue, }; +use boa_macros::js_str; use temporal_rs::options::{ - ArithmeticOverflow, DurationOverflow, InstantDisambiguation, OffsetDisambiguation, - RoundingIncrement, TemporalRoundingMode, TemporalUnit, + ArithmeticOverflow, DifferenceSettings, DurationOverflow, InstantDisambiguation, + OffsetDisambiguation, RoundingIncrement, TemporalRoundingMode, TemporalUnit, }; // TODO: Expand docs on the below options. @@ -46,6 +47,21 @@ pub(crate) fn get_temporal_unit( Ok(unit) } +#[inline] +pub(crate) fn get_difference_settings( + options: &JsObject, + context: &mut Context, +) -> JsResult { + let mut settings = DifferenceSettings::default(); + settings.rounding_mode = + get_option::(options, js_str!("roundingMode"), context)?; + settings.increment = + get_option::(options, js_str!("roundingIncrement"), context)?; + settings.smallest_unit = get_option::(options, js_str!("smallestUnit"), context)?; + settings.largest_unit = get_option::(options, js_str!("largestUnit"), context)?; + Ok(settings) +} + #[derive(Debug, Clone, Copy)] #[allow(unused)] pub(crate) enum TemporalUnitGroup { diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index a07fa7d87d6..efbe2aa253e 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -28,7 +28,10 @@ use temporal_rs::{ options::ArithmeticOverflow, }; -use super::{calendar, PlainDateTime, ZonedDateTime}; +use super::{ + calendar, create_temporal_duration, options::get_difference_settings, + to_temporal_duration_record, PlainDateTime, ZonedDateTime, +}; /// The `Temporal.PlainDate` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] @@ -495,22 +498,84 @@ impl PlainDate { .into()) } - fn get_iso_fields(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + fn get_iso_fields(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + // 3. Let fields be OrdinaryObjectCreate(%Object.prototype%). + let fields = JsObject::with_object_proto(context.intrinsics()); + + // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalDate.[[Calendar]]). + fields.create_data_property_or_throw( + js_str!("calendar"), + JsString::from(date.inner.calendar().identifier()?), + context, + )?; + // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(temporalDate.[[ISODay]])). + fields.create_data_property_or_throw(js_str!("isoDay"), date.inner.iso_day(), context)?; + // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", 𝔽(temporalDate.[[ISOMonth]])). + fields.create_data_property_or_throw( + js_str!("isoMonth"), + date.inner.iso_month(), + context, + )?; + // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", 𝔽(temporalDate.[[ISOYear]])). + fields.create_data_property_or_throw(js_str!("isoYear"), date.inner.iso_year(), context)?; + // 8. Return fields. + Ok(fields.into()) } - fn add(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + // 3. Let duration be ? ToTemporalDuration(temporalDurationLike). + let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?; + + // 4. Set options to ? GetOptionsObject(options). + let options = get_options_object(args.get_or_undefined(1))?; + + let overflow = get_option::(&options, js_str!("overflow"), context)?; + + // 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »). + // 6. Return ? AddDate(calendarRec, temporalDate, duration, options). + create_temporal_date(date.inner.add(&duration, overflow)?, None, context).map(Into::into) } - fn subtract(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + // 3. Let duration be ? ToTemporalDuration(temporalDurationLike). + let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?; + + // 4. Set options to ? GetOptionsObject(options). + let options = get_options_object(args.get_or_undefined(1))?; + let overflow = get_option::(&options, js_str!("overflow"), context)?; + + // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration). + // 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »). + // 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options). + create_temporal_date(date.inner.subtract(&duration, overflow)?, None, context) + .map(Into::into) } fn with(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { @@ -525,16 +590,44 @@ impl PlainDate { .into()) } - fn until(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + let other = to_temporal_date(args.get_or_undefined(0), None, context)?; + + // 3. Return ? DifferenceTemporalPlainDate(until, temporalDate, other, options). + let options = get_options_object(args.get_or_undefined(1))?; + let settings = get_difference_settings(&options, context)?; + + create_temporal_duration(date.inner.until(&other.inner, settings)?, None, context) + .map(Into::into) } - fn since(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + // 3. Return ? DifferenceTemporalPlainDate(since, temporalDate, other, options). + let other = to_temporal_date(args.get_or_undefined(0), None, context)?; + + let options = get_options_object(args.get_or_undefined(1))?; + let settings = get_difference_settings(&options, context)?; + + create_temporal_duration(date.inner.since(&other.inner, settings)?, None, context) + .map(Into::into) } fn equals(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 28b312ab538..20937a32b00 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -3,6 +3,7 @@ use crate::{ builtins::{ + options::{get_option, get_options_object}, temporal::{calendar, to_integer_with_truncation}, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, }, @@ -27,8 +28,11 @@ use temporal_rs::{ DateTime as InnerDateTime, }, iso::{IsoDate, IsoDateSlots}, + options::ArithmeticOverflow, }; +use super::to_temporal_duration_record; + /// The `Temporal.PlainDateTime` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDateTime` could contain `Trace` types. @@ -267,6 +271,8 @@ impl IntrinsicObject for PlainDateTime { None, Attribute::CONFIGURABLE, ) + .method(Self::add, js_string!("add"), 2) + .method(Self::subtract, js_string!("subtract"), 2) .build(); } @@ -290,7 +296,7 @@ impl BuiltInConstructor for PlainDateTime { if new_target.is_undefined() { // a. Throw a TypeError exception. return Err(JsNativeError::typ() - .with_message("NewTarget cannot be undefined when contructing PlainDateTime.") + .with_message("NewTarget cannot be undefined when contructing PlainDatedt.") .into()); }; @@ -348,309 +354,320 @@ impl BuiltInConstructor for PlainDateTime { // ==== `PlainDateTimeTime` accessor implmentations ==== impl PlainDateTime { - /// 5.3.3 get `Temporal.PlainDateTime.prototype.calendarId` + /// 5.3.3 get `Temporal.PlainDatedt.prototype.calendarId` fn get_calendar_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - Ok(JsString::from(date.inner.calendar().identifier()?).into()) + Ok(JsString::from(dt.inner.calendar().identifier()?).into()) } - /// 5.3.4 get `Temporal.PlainDateTime.prototype.year` + /// 5.3.4 get `Temporal.PlainDatedt.prototype.year` fn get_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.year()?.into()) + Ok(dt.inner.year()?.into()) } - /// 5.3.5 get `Temporal.PlainDateTime.prototype.month` + /// 5.3.5 get `Temporal.PlainDatedt.prototype.month` fn get_month(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.month()?.into()) + Ok(dt.inner.month()?.into()) } - /// 5.3.6 get Temporal.PlainDateTime.prototype.monthCode + /// 5.3.6 get Temporal.PlainDatedt.prototype.monthCode fn get_month_code(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(JsString::from(date.inner.month_code()?.as_str()).into()) + Ok(JsString::from(dt.inner.month_code()?.as_str()).into()) } - /// 5.3.7 get `Temporal.PlainDateTime.prototype.day` + /// 5.3.7 get `Temporal.PlainDatedt.prototype.day` fn get_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.day()?.into()) + Ok(dt.inner.day()?.into()) } - /// 5.3.8 get `Temporal.PlainDateTime.prototype.hour` + /// 5.3.8 get `Temporal.PlainDatedt.prototype.hour` fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). - let time = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - // 3. Return 𝔽(dateTime.[[ISOHour]]). - Ok(time.inner.hour().into()) + // 3. Return 𝔽(datedt.[[ISOHour]]). + Ok(dt.inner.hour().into()) } - /// 5.3.9 get `Temporal.PlainDateTime.prototype.minute` + /// 5.3.9 get `Temporal.PlainDatedt.prototype.minute` fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). - let time = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - // 3. Return 𝔽(dateTime.[[ISOMinute]]). - Ok(time.inner.minute().into()) + // 3. Return 𝔽(datedt.[[ISOMinute]]). + Ok(dt.inner.minute().into()) } - /// 5.3.10 get `Temporal.PlainDateTime.prototype.second` + /// 5.3.10 get `Temporal.PlainDatedt.prototype.second` fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). - let time = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - // 3. Return 𝔽(dateTime.[[ISOSecond]]). - Ok(time.inner.second().into()) + // 3. Return 𝔽(datedt.[[ISOSecond]]). + Ok(dt.inner.second().into()) } - /// 5.3.11 get `Temporal.PlainDateTime.prototype.millisecond` + /// 5.3.11 get `Temporal.PlainDatedt.prototype.millisecond` fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). - let time = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - // 3. Return 𝔽(dateTime.[[ISOMillisecond]]). - Ok(time.inner.millisecond().into()) + // 3. Return 𝔽(datedt.[[ISOMillisecond]]). + Ok(dt.inner.millisecond().into()) } - /// 5.3.12 get `Temporal.PlainDateTime.prototype.microsecond` + /// 5.3.12 get `Temporal.PlainDatedt.prototype.microsecond` fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). - let time = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - // 3. Return 𝔽(dateTime.[[ISOMicrosecond]]). - Ok(time.inner.microsecond().into()) + // 3. Return 𝔽(datedt.[[ISOMicrosecond]]). + Ok(dt.inner.microsecond().into()) } - /// 5.3.13 get `Temporal.PlainDateTime.prototype.nanosecond` + /// 5.3.13 get `Temporal.PlainDatedt.prototype.nanosecond` fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). - let time = this + let dt = this .as_object() .and_then(JsObject::downcast_ref::) .ok_or_else(|| { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - // 3. Return 𝔽(dateTime.[[ISONanosecond]]). - Ok(time.inner.nanosecond().into()) + // 3. Return 𝔽(datedt.[[ISONanosecond]]). + Ok(dt.inner.nanosecond().into()) } - /// 5.3.14 get `Temporal.PlainDateTime.prototype.dayOfWeek` + /// 5.3.14 get `Temporal.PlainDatedt.prototype.dayOfWeek` fn get_day_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.day_of_week()?.into()) + Ok(dt.inner.day_of_week()?.into()) } - /// 5.3.15 get `Temporal.PlainDateTime.prototype.dayOfYear` + /// 5.3.15 get `Temporal.PlainDatedt.prototype.dayOfYear` fn get_day_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.day_of_year()?.into()) + Ok(dt.inner.day_of_year()?.into()) } - /// 5.3.16 get `Temporal.PlainDateTime.prototype.weekOfYear` + /// 5.3.16 get `Temporal.PlainDatedt.prototype.weekOfYear` fn get_week_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.week_of_year()?.into()) + Ok(dt.inner.week_of_year()?.into()) } - /// 5.3.17 get `Temporal.PlainDateTime.prototype.yearOfWeek` + /// 5.3.17 get `Temporal.PlainDatedt.prototype.yearOfWeek` fn get_year_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.year_of_week()?.into()) + Ok(dt.inner.year_of_week()?.into()) } - /// 5.3.18 get `Temporal.PlainDateTime.prototype.daysInWeek` + /// 5.3.18 get `Temporal.PlainDatedt.prototype.daysInWeek` fn get_days_in_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.days_in_week()?.into()) + Ok(dt.inner.days_in_week()?.into()) } - /// 5.3.19 get `Temporal.PlainDateTime.prototype.daysInMonth` + /// 5.3.19 get `Temporal.PlainDatedt.prototype.daysInMonth` fn get_days_in_month( this: &JsValue, _: &[JsValue], context: &mut Context, ) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.days_in_month()?.into()) + Ok(dt.inner.days_in_month()?.into()) } - /// 5.3.20 get `Temporal.PlainDateTime.prototype.daysInYear` + /// 5.3.20 get `Temporal.PlainDatedt.prototype.daysInYear` fn get_days_in_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.days_in_year()?.into()) + Ok(dt.inner.days_in_year()?.into()) } - /// 5.3.21 get `Temporal.PlainDateTime.prototype.monthsInYear` + /// 5.3.21 get `Temporal.PlainDatedt.prototype.monthsInYear` fn get_months_in_year( this: &JsValue, _: &[JsValue], context: &mut Context, ) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - Ok(date.inner.months_in_year()?.into()) + Ok(dt.inner.months_in_year()?.into()) } - /// 5.3.22 get `Temporal.PlainDateTime.prototype.inLeapYear` + /// 5.3.22 get `Temporal.PlainDatedt.prototype.inLeapYear` fn get_in_leap_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let obj = this + let dt = this .as_object() - .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; - let Some(date) = obj.downcast_ref::() else { - return Err(JsNativeError::typ() - .with_message("the this object must be a PlainDateTime object.") - .into()); - }; + Ok(dt.inner.in_leap_year()?.into()) + } +} + +// ==== PlainDateTime method implementations ==== + +impl PlainDateTime { + fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let dt = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Let duration be ? ToTemporalDuration(temporalDurationLike). + let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?; + + // 4. Set options to ? GetOptionsObject(options). + let options = get_options_object(args.get_or_undefined(1))?; + let overflow = get_option::(&options, js_str!("overflow"), context)?; + + // 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »). + // 6. Return ? AddDate(calendarRec, temporalDate, duration, options). + create_temporal_datetime(dt.inner.add(&duration, overflow)?, None, context).map(Into::into) + } + + fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let dt = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Let duration be ? ToTemporalDuration(temporalDurationLike). + let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?; + + // 4. Set options to ? GetOptionsObject(options). + let options = get_options_object(args.get_or_undefined(1))?; + let overflow = get_option::(&options, js_str!("overflow"), context)?; - Ok(date.inner.in_leap_year()?.into()) + // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration). + // 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »). + // 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options). + create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context) + .map(Into::into) } } @@ -680,7 +697,7 @@ pub(crate) fn create_temporal_datetime( .into() }; - // 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDateTime.prototype%", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »). + // 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDatedt.prototype%", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »). let prototype = get_prototype_from_constructor( &new_target, StandardConstructors::plain_date_time, From 183e763c32710e4e3ea83ba762cf815b7a89cd1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 08:40:20 -0600 Subject: [PATCH 056/212] Bump the rust-dependencies group with 4 updates (#3899) Bumps the rust-dependencies group with 4 updates: [serde_json](https://github.com/serde-rs/json), [serde](https://github.com/serde-rs/serde), [trybuild](https://github.com/dtolnay/trybuild) and [syn](https://github.com/dtolnay/syn). Updates `serde_json` from 1.0.119 to 1.0.120 - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.119...v1.0.120) Updates `serde` from 1.0.203 to 1.0.204 - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.203...v1.0.204) Updates `trybuild` from 1.0.96 to 1.0.97 - [Release notes](https://github.com/dtolnay/trybuild/releases) - [Commits](https://github.com/dtolnay/trybuild/compare/1.0.96...1.0.97) Updates `syn` from 2.0.68 to 2.0.69 - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.68...2.0.69) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: trybuild dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1dc8007e3b..293591371d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2936,9 +2936,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -2955,9 +2955,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -2966,9 +2966,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.119" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -3163,9 +3163,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.68" +version = "2.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" dependencies = [ "proc-macro2", "quote", @@ -3498,9 +3498,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" +checksum = "5b1e5645f2ee8025c2f1d75e1138f2dd034d74e6ba54620f3c569ba2a2a1ea06" dependencies = [ "glob", "serde", diff --git a/Cargo.toml b/Cargo.toml index 23162901dc1..f46369a4e3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,8 +67,8 @@ pollster = "0.3.0" regex = "1.10.5" regress = { version = "0.10.0", features = ["utf16"] } rustc-hash = { version = "2.0.0", default-features = false } -serde_json = "1.0.119" -serde = "1.0.203" +serde_json = "1.0.120" +serde = "1.0.204" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" @@ -77,7 +77,7 @@ tinystr = "0.7.5" log = "0.4.22" simple_logger = "5.0.0" cargo_metadata = "0.18.1" -trybuild = "1.0.95" +trybuild = "1.0.97" rayon = "1.10.0" toml = "0.8.14" color-eyre = "0.6.3" @@ -94,7 +94,7 @@ isahc = "1.7.2" rustyline = { version = "14.0.0", default-features = false } dhat = "0.3.3" quote = "1.0.36" -syn = { version = "2.0.68", default-features = false } +syn = { version = "2.0.69", default-features = false } proc-macro2 = "1.0" synstructure = "0.13" measureme = "11.0.1" From 841104e95f2183c749993248e6f3ec4842dfbca7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:14:24 +0200 Subject: [PATCH 057/212] Bump zerovec from 0.10.3 to 0.10.4 (#3901) Bumps [zerovec](https://github.com/unicode-org/icu4x) from 0.10.3 to 0.10.4. - [Release notes](https://github.com/unicode-org/icu4x/releases) - [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md) - [Commits](https://github.com/unicode-org/icu4x/compare/ind/zerovec@0.10.3...ind/zerovec@0.10.4) --- updated-dependencies: - dependency-name: zerovec dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 293591371d5..0cd54df878b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4109,9 +4109,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7fce6acea41ceb5b97f7aee91a5d876d5fbca1f847cb60e13afecd7093cad0" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "databake", "serde", From 6a6fa6ae8c2f41768c03d1e0e2a11d0785ad1690 Mon Sep 17 00:00:00 2001 From: Jason Williams <936006+jasonwilliams@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:31:40 +0100 Subject: [PATCH 058/212] format code in comments (#3902) --- core/engine/src/context/hooks.rs | 3 +- core/engine/src/context/mod.rs | 12 +-- core/engine/src/error.rs | 11 +-- core/engine/src/object/builtins/jsdataview.rs | 3 +- core/engine/src/object/builtins/jsdate.rs | 4 +- core/engine/src/object/builtins/jsmap.rs | 15 +--- core/engine/src/object/builtins/jspromise.rs | 66 +++++---------- core/engine/src/object/datatypes.rs | 3 +- core/engine/src/value/conversions/convert.rs | 9 +- core/interop/src/lib.rs | 84 ++++++++++++------- core/runtime/src/lib.rs | 2 +- 11 files changed, 97 insertions(+), 115 deletions(-) diff --git a/core/engine/src/context/hooks.rs b/core/engine/src/context/hooks.rs index 3ddc0bc2a37..eeaf5e643d8 100644 --- a/core/engine/src/context/hooks.rs +++ b/core/engine/src/context/hooks.rs @@ -45,8 +45,7 @@ use time::util::local_offset; /// } /// } /// -/// let context = -/// &mut ContextBuilder::new().host_hooks(&Hooks).build().unwrap(); +/// let context = &mut ContextBuilder::new().host_hooks(&Hooks).build().unwrap(); /// let result = context.eval(Source::from_bytes(r#"eval("let a = 5")"#)); /// assert_eq!( /// result.unwrap_err().to_string(), diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index ff8c391a1a0..f5f43a0ed76 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -220,11 +220,7 @@ impl Context { /// let mut context = Context::default(); /// /// context - /// .register_global_property( - /// js_str!("myPrimitiveProperty"), - /// 10, - /// Attribute::all(), - /// ) + /// .register_global_property(js_str!("myPrimitiveProperty"), 10, Attribute::all()) /// .expect("property shouldn't exist"); /// /// let object = ObjectInitializer::new(&mut context) @@ -232,11 +228,7 @@ impl Context { /// .property(js_str!("y"), 1, Attribute::all()) /// .build(); /// context - /// .register_global_property( - /// js_str!("myObjectProperty"), - /// object, - /// Attribute::all(), - /// ) + /// .register_global_property(js_str!("myObjectProperty"), object, Attribute::all()) /// .expect("property shouldn't exist"); /// ``` pub fn register_global_property( diff --git a/core/engine/src/error.rs b/core/engine/src/error.rs index 44adccd7f8f..4ad9c622930 100644 --- a/core/engine/src/error.rs +++ b/core/engine/src/error.rs @@ -34,7 +34,10 @@ use thiserror::Error; /// let cause = JsError::from_opaque(js_str!("error!").into()); /// /// assert!(cause.as_opaque().is_some()); -/// assert_eq!(cause.as_opaque().unwrap(), &JsValue::from(js_str!("error!"))); +/// assert_eq!( +/// cause.as_opaque().unwrap(), +/// &JsValue::from(js_str!("error!")) +/// ); /// /// let native_error: JsError = JsNativeError::typ() /// .with_message("invalid type!") @@ -170,8 +173,7 @@ impl JsError { /// # use boa_engine::{Context, JsError, JsNativeError}; /// # use boa_engine::builtins::error::ErrorObject; /// let context = &mut Context::default(); - /// let error: JsError = - /// JsNativeError::eval().with_message("invalid script").into(); + /// let error: JsError = JsNativeError::eval().with_message("invalid script").into(); /// let error_val = error.to_opaque(context); /// /// assert!(error_val.as_object().unwrap().is::()); @@ -346,8 +348,7 @@ impl JsError { /// /// ```rust /// # use boa_engine::{JsError, JsNativeError, JsValue}; - /// let error: JsError = - /// JsNativeError::error().with_message("Unknown error").into(); + /// let error: JsError = JsNativeError::error().with_message("Unknown error").into(); /// /// assert!(error.as_native().is_some()); /// diff --git a/core/engine/src/object/builtins/jsdataview.rs b/core/engine/src/object/builtins/jsdataview.rs index fdcf05b375b..b5157155b36 100644 --- a/core/engine/src/object/builtins/jsdataview.rs +++ b/core/engine/src/object/builtins/jsdataview.rs @@ -23,8 +23,7 @@ use std::ops::Deref; /// let array_buffer = JsArrayBuffer::new(4, context)?; /// /// // Create a new Dataview from pre-existing ArrayBuffer -/// let data_view = -/// JsDataView::from_js_array_buffer(array_buffer, None, None, context)?; +/// let data_view = JsDataView::from_js_array_buffer(array_buffer, None, None, context)?; /// /// # Ok(()) /// # } diff --git a/core/engine/src/object/builtins/jsdate.rs b/core/engine/src/object/builtins/jsdate.rs index c647ac5704e..cadf65877a8 100644 --- a/core/engine/src/object/builtins/jsdate.rs +++ b/core/engine/src/object/builtins/jsdate.rs @@ -14,9 +14,7 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime}; /// Create a `JsDate` object and set date to December 4 1995 /// /// ``` -/// use boa_engine::{ -/// js_str, object::builtins::JsDate, Context, JsResult, JsValue, -/// }; +/// use boa_engine::{js_str, object::builtins::JsDate, Context, JsResult, JsValue}; /// /// fn main() -> JsResult<()> { /// // JS mutable Context diff --git a/core/engine/src/object/builtins/jsmap.rs b/core/engine/src/object/builtins/jsmap.rs index d34868c56e5..196907d0273 100644 --- a/core/engine/src/object/builtins/jsmap.rs +++ b/core/engine/src/object/builtins/jsmap.rs @@ -111,10 +111,7 @@ impl JsMap { /// let js_array = JsArray::new(context); /// /// // Create a `[key, value]` pair of JsValues and add it to the `JsArray` as a `JsArray` - /// let vec_one: Vec = vec![ - /// js_str!("first-key").into(), - /// js_str!("first-value").into() - /// ]; + /// let vec_one: Vec = vec![js_str!("first-key").into(), js_str!("first-value").into()]; /// js_array.push(JsArray::from_iter(vec_one, context), context)?; /// /// // Create a `JsMap` from the `JsArray` using it's iterable property. @@ -232,10 +229,7 @@ impl JsMap { /// js_map.set(js_str!("foo"), js_str!("bar"), context)?; /// js_map.set(2, 4, context)?; /// - /// assert_eq!( - /// js_map.get(js_str!("foo"), context)?, - /// js_str!("bar").into() - /// ); + /// assert_eq!(js_map.get(js_str!("foo"), context)?, js_str!("bar").into()); /// assert_eq!(js_map.get(2, context)?, 4.into()); /// # Ok(()) /// # } @@ -296,10 +290,7 @@ impl JsMap { /// js_map.delete(js_str!("foo"), context)?; /// /// assert_eq!(js_map.get_size(context)?, 1.into()); - /// assert_eq!( - /// js_map.get(js_str!("foo"), context)?, - /// JsValue::undefined() - /// ); + /// assert_eq!(js_map.get(js_str!("foo"), context)?, JsValue::undefined()); /// # Ok(()) /// # } /// ``` diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index 7f610f670d4..9d3358c4bf8 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -34,20 +34,14 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// # fn main() -> Result<(), Box> { /// let context = &mut Context::default(); /// -/// context.register_global_property( -/// js_str!("finally"), -/// false, -/// Attribute::all(), -/// ); +/// context.register_global_property(js_str!("finally"), false, Attribute::all()); /// /// let promise = JsPromise::new( /// |resolvers, context| { /// let result = js_str!("hello world!").into(); -/// resolvers.resolve.call( -/// &JsValue::undefined(), -/// &[result], -/// context, -/// )?; +/// resolvers +/// .resolve +/// .call(&JsValue::undefined(), &[result], context)?; /// Ok(JsValue::undefined()) /// }, /// context, @@ -57,8 +51,7 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// .then( /// Some( /// NativeFunction::from_fn_ptr(|_, args, _| { -/// Err(JsError::from_opaque(args.get_or_undefined(0).clone()) -/// .into()) +/// Err(JsError::from_opaque(args.get_or_undefined(0).clone()).into()) /// }) /// .to_js_function(context.realm()), /// ), @@ -66,10 +59,8 @@ use boa_gc::{Finalize, Gc, GcRefCell, Trace}; /// context, /// ) /// .catch( -/// NativeFunction::from_fn_ptr(|_, args, _| { -/// Ok(args.get_or_undefined(0).clone()) -/// }) -/// .to_js_function(context.realm()), +/// NativeFunction::from_fn_ptr(|_, args, _| Ok(args.get_or_undefined(0).clone())) +/// .to_js_function(context.realm()), /// context, /// ) /// .finally( @@ -140,11 +131,9 @@ impl JsPromise { /// let promise = JsPromise::new( /// |resolvers, context| { /// let result = js_string!("hello world").into(); - /// resolvers.resolve.call( - /// &JsValue::undefined(), - /// &[result], - /// context, - /// )?; + /// resolvers + /// .resolve + /// .call(&JsValue::undefined(), &[result], context)?; /// Ok(JsValue::undefined()) /// }, /// context, @@ -379,10 +368,7 @@ impl JsPromise { /// # }; /// let context = &mut Context::default(); /// - /// let promise = JsPromise::reject( - /// JsError::from_opaque(js_string!("oops!").into()), - /// context, - /// ); + /// let promise = JsPromise::reject(JsError::from_opaque(js_string!("oops!").into()), context); /// /// assert_eq!( /// promise.state(), @@ -467,11 +453,9 @@ impl JsPromise { /// /// let promise = JsPromise::new( /// |resolvers, context| { - /// resolvers.resolve.call( - /// &JsValue::undefined(), - /// &[255.255.into()], - /// context, - /// )?; + /// resolvers + /// .resolve + /// .call(&JsValue::undefined(), &[255.255.into()], context)?; /// Ok(JsValue::undefined()) /// }, /// context, @@ -534,11 +518,9 @@ impl JsPromise { /// |resolvers, context| { /// let error = JsNativeError::typ().with_message("thrown"); /// let error = error.to_opaque(context); - /// resolvers.reject.call( - /// &JsValue::undefined(), - /// &[error.into()], - /// context, - /// )?; + /// resolvers + /// .reject + /// .call(&JsValue::undefined(), &[error.into()], context)?; /// Ok(JsValue::undefined()) /// }, /// context, @@ -592,21 +574,15 @@ impl JsPromise { /// # fn main() -> Result<(), Box> { /// let context = &mut Context::default(); /// - /// context.register_global_property( - /// js_string!("finally"), - /// false, - /// Attribute::all(), - /// )?; + /// context.register_global_property(js_string!("finally"), false, Attribute::all())?; /// /// let promise = JsPromise::new( /// |resolvers, context| { /// let error = JsNativeError::typ().with_message("thrown"); /// let error = error.to_opaque(context); - /// resolvers.reject.call( - /// &JsValue::undefined(), - /// &[error.into()], - /// context, - /// )?; + /// resolvers + /// .reject + /// .call(&JsValue::undefined(), &[error.into()], context)?; /// Ok(JsValue::undefined()) /// }, /// context, diff --git a/core/engine/src/object/datatypes.rs b/core/engine/src/object/datatypes.rs index 9b892bb07ee..4bf28fe4ae9 100644 --- a/core/engine/src/object/datatypes.rs +++ b/core/engine/src/object/datatypes.rs @@ -33,8 +33,7 @@ use super::internal_methods::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS}; /// counter: usize, /// } /// -/// let object = -/// JsObject::from_proto_and_data(None, CustomStruct { counter: 5 }); +/// let object = JsObject::from_proto_and_data(None, CustomStruct { counter: 5 }); /// /// assert_eq!(object.downcast_ref::().unwrap().counter, 5); /// ``` diff --git a/core/engine/src/value/conversions/convert.rs b/core/engine/src/value/conversions/convert.rs index 397afa434f2..ea92b23943b 100644 --- a/core/engine/src/value/conversions/convert.rs +++ b/core/engine/src/value/conversions/convert.rs @@ -31,9 +31,12 @@ use crate::{Context, JsResult, JsString, JsValue}; /// # use boa_engine::{Context, js_string, JsValue}; /// # use boa_engine::value::{Convert, TryFromJs}; /// # let mut context = Context::default(); -/// let Convert(conv0): Convert = Convert::try_from_js(&JsValue::Integer(0), &mut context).unwrap(); -/// let Convert(conv5): Convert = Convert::try_from_js(&JsValue::Integer(5), &mut context).unwrap(); -/// let Convert(conv_nan): Convert = Convert::try_from_js(&JsValue::Rational(f64::NAN), &mut context).unwrap(); +/// let Convert(conv0): Convert = +/// Convert::try_from_js(&JsValue::Integer(0), &mut context).unwrap(); +/// let Convert(conv5): Convert = +/// Convert::try_from_js(&JsValue::Integer(5), &mut context).unwrap(); +/// let Convert(conv_nan): Convert = +/// Convert::try_from_js(&JsValue::Rational(f64::NAN), &mut context).unwrap(); /// /// assert_eq!(conv0, false); /// assert_eq!(conv5, true); diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs index 54cfc27d547..a15ab16c1b1 100644 --- a/core/interop/src/lib.rs +++ b/core/interop/src/lib.rs @@ -58,7 +58,13 @@ impl + Clone> IntoJsModule fo /// # let mut context = Context::default(); /// let f = |a: i32, b: i32| a + b; /// let f = unsafe { f.into_js_function_unsafe(&mut context) }; -/// let result = f.call(&JsValue::undefined(), &[JsValue::from(1), JsValue::from(2)], &mut context).unwrap(); +/// let result = f +/// .call( +/// &JsValue::undefined(), +/// &[JsValue::from(1), JsValue::from(2)], +/// &mut context, +/// ) +/// .unwrap(); /// assert_eq!(result, JsValue::new(3)); /// ``` /// @@ -79,8 +85,10 @@ impl + Clone> IntoJsModule fo /// move |a: i32| *x.borrow_mut() += a /// }; /// let f = unsafe { f.into_js_function_unsafe(&mut context) }; -/// f.call(&JsValue::undefined(), &[JsValue::from(1)], &mut context).unwrap(); -/// f.call(&JsValue::undefined(), &[JsValue::from(4)], &mut context).unwrap(); +/// f.call(&JsValue::undefined(), &[JsValue::from(1)], &mut context) +/// .unwrap(); +/// f.call(&JsValue::undefined(), &[JsValue::from(4)], &mut context) +/// .unwrap(); /// assert_eq!(*x.borrow(), 5); /// ``` pub trait UnsafeIntoJsFunction: private::IntoJsFunctionSealed { @@ -103,11 +111,13 @@ pub trait UnsafeIntoJsFunction: private::IntoJsFunctionSealed: private::IntoJsFunctionSealed + Copy { @@ -155,14 +165,19 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for T { /// # use boa_interop::{IntoJsFunctionCopied, JsRest}; /// # let mut context = Context::default(); /// let sums = (|args: JsRest, context: &mut Context| -> i32 { -/// args.iter().map(|i| i.try_js_into::(context).unwrap()).sum::() -/// }).into_js_function_copied(&mut context); +/// args.iter() +/// .map(|i| i.try_js_into::(context).unwrap()) +/// .sum::() +/// }) +/// .into_js_function_copied(&mut context); /// -/// let result = sums.call( -/// &JsValue::undefined(), -/// &[JsValue::from(1), JsValue::from(2), JsValue::from(3)], -/// &mut context -/// ).unwrap(); +/// let result = sums +/// .call( +/// &JsValue::undefined(), +/// &[JsValue::from(1), JsValue::from(2), JsValue::from(3)], +/// &mut context, +/// ) +/// .unwrap(); /// assert_eq!(result, JsValue::new(6)); /// ``` #[derive(Debug, Clone)] @@ -224,15 +239,22 @@ impl<'a> IntoIterator for JsRest<'a> { /// # use boa_engine::{Context, JsValue}; /// # use boa_interop::{IntoJsFunctionCopied, JsAll}; /// # let mut context = Context::default(); -/// let sums = (|args: JsAll, context: &mut Context| -> i32 { -/// args.iter().sum() -/// }).into_js_function_copied(&mut context); +/// let sums = (|args: JsAll, context: &mut Context| -> i32 { args.iter().sum() }) +/// .into_js_function_copied(&mut context); /// -/// let result = sums.call( -/// &JsValue::undefined(), -/// &[JsValue::from(1), JsValue::from(2), JsValue::from(3), JsValue::Boolean(true), JsValue::from(4)], -/// &mut context -/// ).unwrap(); +/// let result = sums +/// .call( +/// &JsValue::undefined(), +/// &[ +/// JsValue::from(1), +/// JsValue::from(2), +/// JsValue::from(3), +/// JsValue::Boolean(true), +/// JsValue::from(4), +/// ], +/// &mut context, +/// ) +/// .unwrap(); /// assert_eq!(result, JsValue::new(6)); /// ``` #[derive(Debug, Clone)] @@ -316,20 +338,22 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for JsThis { /// For example, /// ``` /// # use boa_engine::{Context, Finalize, JsData, JsValue, Trace}; -/// use boa_interop::{IntoJsFunctionCopied, ContextData}; +/// use boa_interop::{ContextData, IntoJsFunctionCopied}; /// /// #[derive(Clone, Debug, Finalize, JsData, Trace)] /// struct CustomHostDefinedStruct { -/// #[unsafe_ignore_trace] -/// pub counter: usize, +/// #[unsafe_ignore_trace] +/// pub counter: usize, /// } /// let mut context = Context::default(); /// context.insert_data(CustomHostDefinedStruct { counter: 123 }); -/// let f = (|ContextData(host): ContextData| { -/// host.counter + 1 -/// }).into_js_function_copied(&mut context); +/// let f = (|ContextData(host): ContextData| host.counter + 1) +/// .into_js_function_copied(&mut context); /// -/// assert_eq!(f.call(&JsValue::undefined(), &[], &mut context), Ok(JsValue::new(124))); +/// assert_eq!( +/// f.call(&JsValue::undefined(), &[], &mut context), +/// Ok(JsValue::new(124)) +/// ); /// ``` #[derive(Debug, Clone)] pub struct ContextData(pub T); diff --git a/core/runtime/src/lib.rs b/core/runtime/src/lib.rs index 16d0da97524..f25a0ba6e93 100644 --- a/core/runtime/src/lib.rs +++ b/core/runtime/src/lib.rs @@ -6,7 +6,7 @@ //! 1. Add **boa_runtime** as a dependency to your project along with **boa_engine**. //! //! ``` -//! use boa_engine::{ Context, Source, property::Attribute, js_string }; +//! use boa_engine::{js_string, property::Attribute, Context, Source}; //! use boa_runtime::Console; //! //! // Create the context. From 58d0fe64d290d4152865f7c669970ac3a9fc1f6e Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 8 Jul 2024 14:58:01 -0700 Subject: [PATCH 059/212] Add a js_class to implement the Class trait without boilerplate (#3872) * Add a js_class to implement the Class trait without boilerplate This also adds a JsInstance that verifies that `this` is of the proper class, and an `Ignore` that ignore arguments. The syntax is a bit special because of limitations of macro_rules. For example, the way fields are defined. It was impossible to keep both assignments (e.g. `public field = 123;`) and dynamic fields. This reduces significantly the boilerplate for declaring classes. The example in class.rs is more than twice as long (if you remove comments) than the macro is. * clippies * Fix usage of macros * Fix some imports issues * Fix some imports issues, again * Add a way to alias a function, e.g. change case * Address comment and get rid of unwraps --- core/engine/src/object/mod.rs | 7 ++ core/interop/src/lib.rs | 158 +++++++++++++++++++++++++++ core/interop/src/macros.rs | 195 ++++++++++++++++++++++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 core/interop/src/macros.rs diff --git a/core/engine/src/object/mod.rs b/core/engine/src/object/mod.rs index b6696e6e665..c7d4e13ceef 100644 --- a/core/engine/src/object/mod.rs +++ b/core/engine/src/object/mod.rs @@ -247,6 +247,13 @@ impl Object { &self.data } + /// Returns the data of the object. + #[inline] + #[must_use] + pub fn data_mut(&mut self) -> &mut T { + &mut self.data + } + /// Gets the prototype instance of this object. #[inline] #[must_use] diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs index a15ab16c1b1..154e7d4a1ce 100644 --- a/core/interop/src/lib.rs +++ b/core/interop/src/lib.rs @@ -1,13 +1,19 @@ //! Interop utilities between Boa and its host. use boa_engine::module::SyntheticModuleInitializer; +use boa_engine::object::Object; use boa_engine::value::TryFromJs; use boa_engine::{ Context, JsNativeError, JsResult, JsString, JsValue, Module, NativeFunction, NativeObject, }; +use std::ops::Deref; + +pub use boa_engine; +use boa_gc::{GcRef, GcRefMut}; pub use boa_macros; pub mod loaders; +pub mod macros; /// Internal module only. pub(crate) mod private { @@ -154,6 +160,20 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for T { } } +/// An argument that would be ignored in a JS function. +#[derive(Debug, Clone, Copy)] +pub struct Ignore; + +impl<'a> TryFromJsArgument<'a> for Ignore { + fn try_from_js_argument( + _this: &'a JsValue, + rest: &'a [JsValue], + _: &mut Context, + ) -> JsResult<(Self, &'a [JsValue])> { + Ok((Ignore, &rest[1..])) + } +} + /// An argument that when used in a JS function will empty the list /// of JS arguments as `JsValue`s. This can be used for having the /// rest of the arguments in a function. It should be the last @@ -327,6 +347,67 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for JsThis { } } +impl Deref for JsThis { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Captures a class instance from the `this` value in a JS function. The class +/// will be a non-mutable reference of Rust type `T`, if it is an instance of `T`. +/// +/// To have more flexibility on the parsing of the `this` value, you can use the +/// [`JsThis`] capture instead. +#[derive(Debug, Clone)] +pub struct JsClass { + inner: boa_engine::JsObject, +} + +impl JsClass { + /// Borrow a reference to the class instance of type `T`. + /// + /// # Panics + /// + /// Panics if the object is currently borrowed. + /// + /// This does not panic if the type is wrong, as the type is checked + /// during the construction of the `JsClass` instance. + #[must_use] + pub fn borrow(&self) -> GcRef<'_, T> { + GcRef::map(self.inner.borrow(), |obj| obj.data()) + } + + /// Borrow a mutable reference to the class instance of type `T`. + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[must_use] + pub fn borrow_mut(&self) -> GcRefMut<'_, Object, T> { + GcRefMut::map(self.inner.borrow_mut(), |obj| obj.data_mut()) + } +} + +impl<'a, T: NativeObject + 'static> TryFromJsArgument<'a> for JsClass { + fn try_from_js_argument( + this: &'a JsValue, + rest: &'a [JsValue], + _context: &mut Context, + ) -> JsResult<(Self, &'a [JsValue])> { + if let Some(object) = this.as_object() { + if let Ok(inner) = object.clone().downcast::() { + return Ok((JsClass { inner }, rest)); + } + } + + Err(JsNativeError::typ() + .with_message("invalid this for class method") + .into()) + } +} + /// Captures a [`ContextData`] data from the [`Context`] as a JS function argument, /// based on its type. /// @@ -531,3 +612,80 @@ fn can_throw_exception() { Some(&JsString::from("from javascript").into()) ); } + +#[test] +fn class() { + use boa_engine::class::{Class, ClassBuilder}; + use boa_engine::{js_string, JsValue, Source}; + use boa_macros::{Finalize, JsData, Trace}; + use std::rc::Rc; + + #[derive(Debug, Trace, Finalize, JsData)] + struct Test { + value: i32, + } + + impl Test { + #[allow(clippy::needless_pass_by_value)] + fn get_value(this: JsClass) -> i32 { + this.borrow().value + } + + #[allow(clippy::needless_pass_by_value)] + fn set_value(this: JsClass, new_value: i32) { + (*this.borrow_mut()).value = new_value; + } + } + + impl Class for Test { + const NAME: &'static str = "Test"; + + fn init(class: &mut ClassBuilder<'_>) -> JsResult<()> { + let get_value = Self::get_value.into_js_function_copied(class.context()); + class.method(js_string!("getValue"), 0, get_value); + let set_value = Self::set_value.into_js_function_copied(class.context()); + class.method(js_string!("setValue"), 1, set_value); + Ok(()) + } + + fn data_constructor( + _new_target: &JsValue, + _args: &[JsValue], + _context: &mut Context, + ) -> JsResult { + Ok(Self { value: 123 }) + } + } + + let loader = Rc::new(loaders::HashMapModuleLoader::new()); + let mut context = Context::builder() + .module_loader(loader.clone()) + .build() + .unwrap(); + + context.register_global_class::().unwrap(); + + let source = Source::from_bytes( + r" + let t = new Test(); + if (t.getValue() != 123) { + throw 'invalid value'; + } + t.setValue(456); + if (t.getValue() != 456) { + throw 'invalid value 456'; + } + ", + ); + let root_module = Module::parse(source, None, &mut context).unwrap(); + + let promise_result = root_module.load_link_evaluate(&mut context); + context.run_jobs(); + + // Checking if the final promise didn't return an error. + assert!( + promise_result.state().as_fulfilled().is_some(), + "module didn't execute successfully! Promise: {:?}", + promise_result.state() + ); +} diff --git a/core/interop/src/macros.rs b/core/interop/src/macros.rs new file mode 100644 index 00000000000..ed5c280024d --- /dev/null +++ b/core/interop/src/macros.rs @@ -0,0 +1,195 @@ +//! Module declaring interop macro rules. + +/// Declare a JavaScript class, in a simpler way. +/// +/// This can make declaration of JavaScript classes easier by using an hybrid +/// declarative approach. The class itself follows a closer syntax to JavaScript +/// while the method arguments/results and bodies are written in Rust. +/// +/// This only declares the Boa interop parts of the class. The actual type must +/// be declared separately as a Rust type, along with necessary derives and +/// traits. +/// +/// Here's an example using the animal class declared in [`boa_engine::class`]: +/// # Example +/// ``` +/// # use boa_engine::{JsString, JsData, js_string}; +/// # use boa_gc::{Finalize, Trace}; +/// use boa_interop::{js_class, Ignore, JsClass}; +/// +/// #[derive(Clone, Trace, Finalize, JsData)] +/// pub enum Animal { +/// Cat, +/// Dog, +/// Other, +/// } +/// +/// js_class! { +/// // Implement [`Class`] trait for the `Animal` enum. +/// class Animal { +/// // This sets a field on the JavaScript object. The arguments to +/// // `init` are the arguments passed to the constructor. This +/// // function MUST return the value to be set on the field. If this +/// // returns a `JsResult`, it will be unwrapped and error out during +/// // construction of the object. +/// public age(_name: Ignore, age: i32) -> i32 { +/// age +/// } +/// +/// // This is called when a new instance of the class is created in +/// // JavaScript, e.g. `new Animal("cat")`. +/// // This method is mandatory and MUST return `JsResult`. +/// constructor(name: String) { +/// match name.as_str() { +/// "cat" => Ok(Animal::Cat), +/// "dog" => Ok(Animal::Dog), +/// _ => Ok(Animal::Other), +/// } +/// } +/// +/// // Declare a function on the class itself. +/// // There is a current limitation using `self` in methods, so the +/// // instance must be accessed using an actual argument. +/// fn speak(this: JsClass) -> JsString { +/// match *this.borrow() { +/// Animal::Cat => js_string!("meow"), +/// Animal::Dog => js_string!("woof"), +/// Animal::Other => js_string!(r"¯\_(ツ)_/¯"), +/// } +/// } +/// } +/// } +/// +/// fn main() { +///# use boa_engine::{Context, JsString, Source, js_str}; +/// +/// let mut context = Context::default(); +/// +/// context.register_global_class::().unwrap(); +/// +/// let result = context.eval(Source::from_bytes(r#" +/// let pet = new Animal("dog", 3); +/// +/// `My pet is ${pet.age} years old. Right, buddy? - ${pet.speak()}!` +/// "#)).expect("Could not evaluate script"); +/// +/// assert_eq!( +/// result.as_string().unwrap(), +/// &js_str!("My pet is 3 years old. Right, buddy? - woof!") +/// ); +/// } +/// ``` +#[macro_export] +macro_rules! js_class { + ( + class $class_name: ident $(as $class_js_name: literal)? { + $( + $(#[$field_attr: meta])* + public $field_name: ident + ( $( $field_arg: ident: $field_arg_type: ty ),* ) -> $field_ty: ty + $field_body: block + )* + + $(#[$constructor_attr: meta])* + constructor( $( $ctor_arg: ident: $ctor_arg_ty: ty ),* ) + $constructor_body: block + + $( + $(#[$method_attr: meta])* + fn $method_name: ident $( as $method_js_name: literal )? + ( $( $fn_arg: ident: $fn_arg_type: ty ),* ) + $(-> $result_type: ty)? + $method_body: block + )* + } + ) => { + impl $crate::boa_engine::class::Class for $class_name { + + const NAME: &'static str = $crate::__js_class_name!($class_name, $($class_js_name)?); + + const LENGTH: usize = $crate::__count!( $( $ctor_arg )* ); + + fn init(class: &mut $crate::boa_engine::class::ClassBuilder<'_>) -> $crate::boa_engine::JsResult<()> { + // Add all methods to the class. + $( + fn $method_name ( $($fn_arg: $fn_arg_type),* ) -> $( $result_type )? + $method_body + + let function = $crate::IntoJsFunctionCopied::into_js_function_copied( + $method_name, + class.context(), + ); + + let function_name = $crate::__js_class_name!($method_name, $($method_js_name)?); + + class.method( + $crate::boa_engine::JsString::from(function_name), + $crate::__count!($( $fn_arg )*), + function, + ); + )* + + Ok(()) + } + + fn data_constructor( + new_target: &$crate::boa_engine::JsValue, + args: &[$crate::boa_engine::JsValue], + context: &mut $crate::boa_engine::Context, + ) -> $crate::boa_engine::JsResult<$class_name> { + let rest = args; + $( + let ($ctor_arg, rest) : ($ctor_arg_ty, _) = $crate::TryFromJsArgument::try_from_js_argument(new_target, rest, context)?; + )* + + $constructor_body + } + + fn object_constructor( + instance: &$crate::boa_engine::JsObject, + args: &[$crate::boa_engine::JsValue], + context: &mut $crate::boa_engine::Context + ) -> $crate::boa_engine::JsResult<()> { + $( + fn $field_name ( $($field_arg: $field_arg_type),* ) -> $field_ty + $field_body + + let function = $crate::IntoJsFunctionCopied::into_js_function_copied( + $field_name, + context, + ); + + instance.set( + $crate::boa_engine::JsString::from(stringify!($field_name)), + function.call(&$crate::boa_engine::JsValue::undefined(), args, context)?, + false, + context + ); + )* + + Ok(()) + } + + } + } +} + +/// Internal macro to get the JavaScript class name. +#[macro_export] +macro_rules! __js_class_name { + ($class_name: ident, $class_js_name: literal) => { + $class_js_name + }; + ($class_name: ident,) => { + stringify!($class_name) + }; +} + +/// Internal macro to get the JavaScript class length. +#[macro_export] +macro_rules! __count { + () => (0); + ($_: ident $($rest: ident)*) => { + 1 + $crate::__count!($($rest)*) + }; +} From 5bf2e92d2f1b0cd97071ed19d441f77808ee715e Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:04:19 +0100 Subject: [PATCH 060/212] Fix HomeObject for private class methods (#3897) --- core/engine/src/bytecompiler/class.rs | 12 ++++++++++++ core/engine/src/vm/opcode/mod.rs | 2 +- core/engine/src/vm/opcode/push/class/private.rs | 13 ++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index f18b9f6f9e0..04d30875486 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -423,21 +423,33 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::PushClassPrivateSetter, index); } MethodDefinition::Ordinary(expr) => { + self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); + self.emit_opcode(Opcode::Dup); + self.emit(Opcode::RotateRight, &[Operand::U8(4)]); self.method(expr.into()); let index = self.get_or_insert_private_name(*name); self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index); } MethodDefinition::Async(expr) => { + self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); + self.emit_opcode(Opcode::Dup); + self.emit(Opcode::RotateRight, &[Operand::U8(4)]); self.method(expr.into()); let index = self.get_or_insert_private_name(*name); self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index); } MethodDefinition::Generator(expr) => { + self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); + self.emit_opcode(Opcode::Dup); + self.emit(Opcode::RotateRight, &[Operand::U8(4)]); self.method(expr.into()); let index = self.get_or_insert_private_name(*name); self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index); } MethodDefinition::AsyncGenerator(expr) => { + self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); + self.emit_opcode(Opcode::Dup); + self.emit(Opcode::RotateRight, &[Operand::U8(4)]); self.method(expr.into()); let index = self.get_or_insert_private_name(*name); self.emit_with_varying_operand(Opcode::PushClassPrivateMethod, index); diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index 15a362159dc..f3a318192a6 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -1450,7 +1450,7 @@ generate_opcodes! { /// /// Operands: index: `u32` /// - /// Stack: class, method **=>** + /// Stack: class, class_proto, method **=>** PushClassPrivateMethod { index: VaryingOperand }, /// Deletes a property by name of an object. diff --git a/core/engine/src/vm/opcode/push/class/private.rs b/core/engine/src/vm/opcode/push/class/private.rs index 032c3300006..d40b355597c 100644 --- a/core/engine/src/vm/opcode/push/class/private.rs +++ b/core/engine/src/vm/opcode/push/class/private.rs @@ -20,6 +20,12 @@ impl PushClassPrivateMethod { let name = context.vm.frame().code_block().constant_string(index); let method = context.vm.pop(); let method_object = method.as_callable().expect("method must be callable"); + let class_proto = context.vm.pop(); + let class_proto_object = class_proto + .as_object() + .expect("class_proto must be function object"); + let class = context.vm.pop(); + let class_object = class.as_object().expect("class must be function object"); let name_string = js_string!(js_str!("#"), &name); let desc = PropertyDescriptor::builder() @@ -35,9 +41,10 @@ impl PushClassPrivateMethod { &mut InternalMethodContext::new(context), ) .expect("failed to set name property on private method"); - - let class = context.vm.pop(); - let class_object = class.as_object().expect("class must be function object"); + method_object + .downcast_mut::() + .expect("method must be function object") + .set_home_object(class_proto_object.clone()); class_object .downcast_mut::() From 36eeea1c7277a0c07a53678d197400ef6373b0e6 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:51:35 +0100 Subject: [PATCH 061/212] Fix evaluation order in destructive property assignments (#3895) --- .../declaration/declaration_pattern.rs | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/core/engine/src/bytecompiler/declaration/declaration_pattern.rs b/core/engine/src/bytecompiler/declaration/declaration_pattern.rs index e7b7928f59a..efde60a13de 100644 --- a/core/engine/src/bytecompiler/declaration/declaration_pattern.rs +++ b/core/engine/src/bytecompiler/declaration/declaration_pattern.rs @@ -117,31 +117,57 @@ impl ByteCompiler<'_> { } => { self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup); - match name { - PropertyName::Literal(name) => { - self.emit_get_property_by_name(*name); - } - PropertyName::Computed(node) => { - self.compile_expr(node, true); - if rest_exits { - self.emit_opcode(Opcode::GetPropertyByValuePush); - } else { - self.emit_opcode(Opcode::GetPropertyByValue); - } - } - } - - if let Some(init) = default_init { - let skip = - self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined); - self.compile_expr(init, true); - self.patch_jump(skip); + if let PropertyName::Computed(node) = &name { + self.compile_expr(node, true); + self.emit_opcode(Opcode::Swap); } self.access_set( Access::Property { access }, false, - ByteCompiler::access_set_top_of_stack_expr_fn, + |compiler: &mut ByteCompiler<'_>, level: u8| { + match level { + 0 => {} + 1 => compiler.emit_opcode(Opcode::Swap), + _ => { + compiler.emit( + Opcode::RotateLeft, + &[Operand::U8(level + 1)], + ); + } + } + compiler.emit_opcode(Opcode::Dup); + + match name { + PropertyName::Literal(name) => { + compiler.emit_get_property_by_name(*name); + } + PropertyName::Computed(_) => { + compiler.emit( + Opcode::RotateLeft, + &[Operand::U8(level + 3)], + ); + if rest_exits { + compiler + .emit_opcode(Opcode::GetPropertyByValuePush); + compiler.emit_opcode(Opcode::Swap); + compiler.emit( + Opcode::RotateRight, + &[Operand::U8(level + 2)], + ); + } else { + compiler.emit_opcode(Opcode::GetPropertyByValue); + } + } + } + + if let Some(init) = default_init { + let skip = compiler + .emit_opcode_with_operand(Opcode::JumpIfNotUndefined); + compiler.compile_expr(init, true); + compiler.patch_jump(skip); + } + }, ); if rest_exits && name.computed().is_some() { From 5889a767d1b91d230813d2bf6e0588dfd00c9e5e Mon Sep 17 00:00:00 2001 From: Kevin Ness <46825870+nekevss@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:17:39 -0400 Subject: [PATCH 062/212] Updates to temporal_rs version and temporal methods (#3900) * Updates to temporal_rs version and temporal methods * Apply review feedback for relativeTo * Forgot Date.prototype.toPlainDateTime * Fix doc on toPlainDateTime --- Cargo.lock | 5 +- Cargo.toml | 2 +- .../src/builtins/temporal/duration/mod.rs | 66 +++++++-- core/engine/src/builtins/temporal/mod.rs | 19 +-- .../src/builtins/temporal/plain_date/mod.rs | 85 ++++++++--- .../builtins/temporal/plain_date_time/mod.rs | 134 +++++++++++++++++- .../src/builtins/temporal/plain_time/mod.rs | 124 +++++++++++++++- 7 files changed, 394 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cd54df878b..9838693ac49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3200,8 +3200,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "temporal_rs" -version = "0.0.2" -source = "git+https://github.com/boa-dev/temporal.git?rev=cf70b50f90865d2c00fb994b7adf8b9e1bd837b8#cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6f351ef929946476b4107c09348c9e25ba1155ff8e3867b1914878a0fb553f" dependencies = [ "bitflags 2.6.0", "icu_calendar", diff --git a/Cargo.toml b/Cargo.toml index f46369a4e3f..94ee7bdeec4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,7 +115,7 @@ intrusive-collections = "0.9.6" cfg-if = "1.0.0" either = "1.13.0" sys-locale = "0.3.1" -temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "cf70b50f90865d2c00fb994b7adf8b9e1bd837b8" } +temporal_rs = "0.0.3" web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.9.0" diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 9bf5d7b104b..c5e07e116b5 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -180,6 +180,7 @@ impl IntrinsicObject for Duration { None, Attribute::CONFIGURABLE, ) + .static_method(Self::from, js_string!("from"), 1) .method(Self::with, js_string!("with"), 1) .method(Self::negated, js_string!("negated"), 0) .method(Self::abs, js_string!("abs"), 0) @@ -412,7 +413,26 @@ impl Duration { } } -// -- Duration Method implementations -- +// ==== Duration methods implementations ==== + +impl Duration { + fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let item = args.get_or_undefined(0); + // 1. If item is an Object and item has an [[InitializedTemporalDuration]] internal slot, then + if let Some(duration) = item.as_object().and_then(JsObject::downcast_ref::) { + // a. Return ! CreateTemporalDuration(item.[[Years]], item.[[Months]], item.[[Weeks]], + // item.[[Days]], item.[[Hours]], item.[[Minutes]], item.[[Seconds]], item.[[Milliseconds]], + // item.[[Microseconds]], item.[[Nanoseconds]]). + return create_temporal_duration(duration.inner, None, context).map(Into::into); + } + + // 2. Return ? ToTemporalDuration(item). + create_temporal_duration(to_temporal_duration_record(item, context)?, None, context) + .map(Into::into) + } +} + +// ==== Duration.prototype method implementations ==== impl Duration { /// 7.3.15 `Temporal.Duration.prototype.with ( temporalDurationLike )` @@ -581,17 +601,45 @@ impl Duration { } /// 7.3.18 `Temporal.Duration.prototype.add ( other [ , options ] )` - pub(crate) fn add(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::range() - .with_message("not yet implemented.") - .into()) + pub(crate) fn add( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // 1.Let duration be the this value. + // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + let duration = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("this value must be a Duration object.") + })?; + + // 3. Return ? AddDurations(add, duration, other). + let other = to_temporal_duration_record(args.get_or_undefined(0), context)?; + + create_temporal_duration(duration.inner.add(&other)?, None, context).map(Into::into) } /// 7.3.19 `Temporal.Duration.prototype.subtract ( other [ , options ] )` - pub(crate) fn subtract(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::range() - .with_message("not yet implemented.") - .into()) + pub(crate) fn subtract( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // 1.Let duration be the this value. + // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + let duration = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("this value must be a Duration object.") + })?; + + let other = to_temporal_duration_record(args.get_or_undefined(0), context)?; + + // 3. Return ? AddDurations(add, duration, other). + create_temporal_duration(duration.inner.subtract(&other)?, None, context).map(Into::into) } /// 7.3.20 `Temporal.Duration.prototype.round ( roundTo )` diff --git a/core/engine/src/builtins/temporal/mod.rs b/core/engine/src/builtins/temporal/mod.rs index 1988982929d..085ebd2eb89 100644 --- a/core/engine/src/builtins/temporal/mod.rs +++ b/core/engine/src/builtins/temporal/mod.rs @@ -34,7 +34,7 @@ use crate::{ realm::Realm, string::StaticJsStrings, value::Type, - Context, JsBigInt, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, + Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_macros::js_str; use boa_profiler::Profiler; @@ -250,15 +250,18 @@ pub(crate) fn to_relative_temporal_object( ) -> RelativeTemporalObjectResult { let relative_to = options.get(PropertyKey::from(js_str!("relativeTo")), context)?; let plain_date = match relative_to { - JsValue::String(relative_to_str) => Some(relative_to_str.into()), - JsValue::Object(relative_to_obj) => Some(relative_to_obj.into()), - _ => None, - } - .map(|plane_date| Ok::<_, JsError>(to_temporal_date(&plane_date, None, context)?.inner)) - .transpose()?; + JsValue::String(relative_to_str) => JsValue::from(relative_to_str), + JsValue::Object(relative_to_obj) => JsValue::from(relative_to_obj), + _ => { + return Err(JsNativeError::typ() + .with_message("Invalid type for converting to relativeTo object") + .into()) + } + }; + let plain_date = to_temporal_date(&plain_date, None, context)?; // TODO: Implement TemporalZonedDateTime conversion when ZonedDateTime is implemented - Ok((plain_date, None)) + Ok((Some(plain_date), None)) } // 13.22 `LargerOfTwoTemporalUnits ( u1, u2 )` diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index efbe2aa253e..c05f33b1db8 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -29,8 +29,8 @@ use temporal_rs::{ }; use super::{ - calendar, create_temporal_duration, options::get_difference_settings, - to_temporal_duration_record, PlainDateTime, ZonedDateTime, + calendar, create_temporal_datetime, create_temporal_duration, options::get_difference_settings, + to_temporal_duration_record, to_temporal_time, PlainDateTime, ZonedDateTime, }; /// The `Temporal.PlainDate` object. @@ -212,6 +212,7 @@ impl IntrinsicObject for PlainDate { None, Attribute::CONFIGURABLE, ) + .static_method(Self::from, js_string!("from"), 2) .method(Self::to_plain_year_month, js_string!("toPlainYearMonth"), 0) .method(Self::to_plain_month_day, js_string!("toPlainMonthDay"), 0) .method(Self::get_iso_fields, js_string!("getISOFields"), 0) @@ -222,6 +223,7 @@ impl IntrinsicObject for PlainDate { .method(Self::until, js_string!("until"), 2) .method(Self::since, js_string!("since"), 2) .method(Self::equals, js_string!("equals"), 1) + .method(Self::to_plain_datetime, js_string!("toPlainDateTime"), 1) .build(); } @@ -276,7 +278,7 @@ impl PlainDate { JsNativeError::typ().with_message("the this object must be a PlainDate object.") })?; - Ok(JsString::from(date.inner.calendar().identifier()?).into()) + Ok(JsString::from(date.inner.calendar().identifier()).into()) } /// 3.3.4 get `Temporal.PlainDate.prototype.year` @@ -483,6 +485,28 @@ impl PlainDate { } } +// ==== `PlainDate` method implementations ==== + +impl PlainDate { + fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let item = args.get_or_undefined(0); + let options = args.get(1); + + if let Some(date) = item.as_object().and_then(JsObject::downcast_ref::) { + let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?; + let _ = get_option::(&options, js_str!("overflow"), context)?; + return create_temporal_date(date.inner.clone(), None, context).map(Into::into); + } + + create_temporal_date( + to_temporal_date(item, options.cloned(), context)?, + None, + context, + ) + .map(Into::into) + } +} + // ==== `PlainDate.prototype` method implementation ==== impl PlainDate { @@ -514,7 +538,7 @@ impl PlainDate { // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalDate.[[Calendar]]). fields.create_data_property_or_throw( js_str!("calendar"), - JsString::from(date.inner.calendar().identifier()?), + JsString::from(date.inner.calendar().identifier()), context, )?; // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(temporalDate.[[ISODay]])). @@ -606,8 +630,7 @@ impl PlainDate { let options = get_options_object(args.get_or_undefined(1))?; let settings = get_difference_settings(&options, context)?; - create_temporal_duration(date.inner.until(&other.inner, settings)?, None, context) - .map(Into::into) + create_temporal_duration(date.inner.until(&other, settings)?, None, context).map(Into::into) } fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { @@ -626,14 +649,44 @@ impl PlainDate { let options = get_options_object(args.get_or_undefined(1))?; let settings = get_difference_settings(&options, context)?; - create_temporal_duration(date.inner.since(&other.inner, settings)?, None, context) - .map(Into::into) + create_temporal_duration(date.inner.since(&other, settings)?, None, context).map(Into::into) } - fn equals(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + let other = to_temporal_date(args.get_or_undefined(0), None, context)?; + + Ok((date.inner == other).into()) + } + + /// 3.3.30 `Temporal.PlainDate.prototype.toPlainDateTime ( [ temporalTime ] )` + fn to_plain_datetime( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDate object.") + })?; + + // 3. Set temporalTime to ? ToTemporalTimeOrMidnight(temporalTime). + let time = args + .first() + .map(|v| to_temporal_time(v, None, context)) + .transpose()?; + // 4. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]). + create_temporal_datetime(date.inner.to_date_time(time)?, None, context).map(Into::into) } } @@ -699,7 +752,7 @@ pub(crate) fn to_temporal_date( item: &JsValue, options: Option, context: &mut Context, -) -> JsResult { +) -> JsResult { // 1. If options is not present, set options to undefined. let options = options.unwrap_or(JsValue::undefined()); @@ -711,7 +764,7 @@ pub(crate) fn to_temporal_date( if let Some(object) = item.as_object() { // a. If item has an [[InitializedTemporalDate]] internal slot, then if let Some(date) = object.downcast_ref::() { - return Ok(PlainDate::new(date.inner.clone())); + return Ok(date.inner.clone()); // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then } else if let Some(data) = object.downcast_ref::() { return Err(JsNativeError::range() @@ -731,7 +784,7 @@ pub(crate) fn to_temporal_date( let date = InnerDate::from_datetime(date_time.inner()); // ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]). - return Ok(PlainDate::new(date)); + return Ok(date); } // d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item). @@ -763,5 +816,5 @@ pub(crate) fn to_temporal_date( .parse::() .map_err(|err| JsNativeError::range().with_message(err.to_string()))?; - Ok(PlainDate::new(result)) + Ok(result) } diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 20937a32b00..9539a540d39 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -31,7 +31,7 @@ use temporal_rs::{ options::ArithmeticOverflow, }; -use super::to_temporal_duration_record; +use super::{to_temporal_duration_record, PlainDate, ZonedDateTime}; /// The `Temporal.PlainDateTime` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] @@ -271,6 +271,7 @@ impl IntrinsicObject for PlainDateTime { None, Attribute::CONFIGURABLE, ) + .static_method(Self::from, js_string!("from"), 2) .method(Self::add, js_string!("add"), 2) .method(Self::subtract, js_string!("subtract"), 2) .build(); @@ -363,7 +364,7 @@ impl PlainDateTime { JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") })?; - Ok(JsString::from(dt.inner.calendar().identifier()?).into()) + Ok(JsString::from(dt.inner.calendar().identifier()).into()) } /// 5.3.4 get `Temporal.PlainDatedt.prototype.year` @@ -621,7 +622,33 @@ impl PlainDateTime { } } -// ==== PlainDateTime method implementations ==== +// ==== PlainDateTime method implemenations ==== + +impl PlainDateTime { + fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let item = args.get_or_undefined(0); + // 1. Set options to ? GetOptionsObject(options). + let options = args.get(1); + // 2. If item is an Object and item has an [[InitializedTemporalDateTime]] internal slot, then + let dt = if let Some(pdt) = item.as_object().and_then(JsObject::downcast_ref::) { + // a. Perform ? GetTemporalOverflowOption(options). + let options = get_options_object(args.get_or_undefined(1))?; + let _ = get_option::(&options, js_str!("overflow"), context)?; + // b. Return ! CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], + // item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]], + // item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]], + // item.[[Calendar]]). + pdt.inner.clone() + } else { + to_temporal_datetime(item, options.cloned(), context)? + }; + + // 3. Return ? ToTemporalDateTime(item, options). + create_temporal_datetime(dt, None, context).map(Into::into) + } +} + +// ==== PlainDateTime.prototype method implementations ==== impl PlainDateTime { fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { @@ -669,6 +696,30 @@ impl PlainDateTime { create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context) .map(Into::into) } + + fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let dt = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Set other to ? ToTemporalDateTime(other). + let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?; + + // 4. Let result be CompareISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], + // dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], + // dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], + // dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], + // other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], + // other.[[ISOMicrosecond]], other.[[ISONanosecond]]). + // 5. If result is not 0, return false. + // 6. Return ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]). + Ok((dt.inner == other).into()) + } } // ==== `PlainDateTime` Abstract Operations` ==== @@ -719,3 +770,80 @@ pub(crate) fn create_temporal_datetime( // 16. Return object. Ok(obj) } + +pub(crate) fn to_temporal_datetime( + value: &JsValue, + options: Option, + context: &mut Context, +) -> JsResult { + // 1. If options is not present, set options to undefined. + let options = get_options_object(&options.unwrap_or(JsValue::undefined()))?; + // 2. Let resolvedOptions be ? SnapshotOwnProperties(! GetOptionsObject(options), null). + // 3. If item is an Object, then + if let Some(object) = value.as_object() { + // a. If item has an [[InitializedTemporalDateTime]] internal slot, then + if let Some(dt) = object.downcast_ref::() { + // i. Return item. + return Ok(dt.inner.clone()); + // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then + } else if let Some(_zdt) = object.downcast_ref::() { + // i. Perform ? GetTemporalOverflowOption(resolvedOptions). + let _ = get_option::(&options, js_str!("overflow"), context)?; + // ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]). + // iii. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(item.[[TimeZone]], « get-offset-nanoseconds-for »). + // iv. Return ? GetPlainDateTimeFor(timeZoneRec, instant, item.[[Calendar]]). + return Err(JsNativeError::range() + .with_message("Not yet implemented.") + .into()); + // c. If item has an [[InitializedTemporalDate]] internal slot, then + } else if let Some(date) = object.downcast_ref::() { + // i. Perform ? GetTemporalOverflowOption(resolvedOptions). + let _ = get_option::(&options, js_str!("overflow"), context)?; + // ii. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, item.[[Calendar]]). + return Ok(InnerDateTime::new( + date.inner.iso_year(), + date.inner.iso_month().into(), + date.inner.iso_day().into(), + 0, + 0, + 0, + 0, + 0, + 0, + date.inner.calendar().clone(), + )?); + } + // d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item). + // e. Let calendarRec be ? CreateCalendarMethodsRecord(calendar, « date-from-fields, fields »). + // f. Let fields be ? PrepareCalendarFields(calendarRec, item, « "day", "month", + // "monthCode", "year" », « "hour", "microsecond", "millisecond", "minute", + // "nanosecond", "second" », «»). + // g. Let result be ? InterpretTemporalDateTimeFields(calendarRec, fields, resolvedOptions). + // TODO: Implement d-g. + return Err(JsNativeError::range() + .with_message("Not yet implemented.") + .into()); + } + // 4. Else, + // a. If item is not a String, throw a TypeError exception. + let Some(string) = value.as_string() else { + return Err(JsNativeError::typ() + .with_message("Cannot convert unrecognized value to PlainDateTime.") + .into()); + }; + // b. Let result be ? ParseTemporalDateTimeString(item). + // c. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true. + // d. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], + // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true. + // e. Let calendar be result.[[Calendar]]. + // f. If calendar is empty, set calendar to "iso8601". + // g. If IsBuiltinCalendar(calendar) is false, throw a RangeError exception. + // h. Set calendar to CanonicalizeUValue("ca", calendar). + let date = string.to_std_string_escaped().parse::()?; + // i. Perform ? GetTemporalOverflowOption(resolvedOptions). + let _ = get_option::(&options, js_str!("overflow"), context)?; + // 5. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], + // result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], + // result.[[Microsecond]], result.[[Nanosecond]], calendar). + Ok(date) +} diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index adba3b53ed6..8f184c0d1e1 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -23,7 +23,7 @@ use temporal_rs::{ use super::{ options::{get_temporal_unit, TemporalUnitGroup}, - to_integer_with_truncation, to_temporal_duration_record, + to_integer_with_truncation, to_temporal_duration_record, PlainDateTime, ZonedDateTime, }; /// The `Temporal.PlainTime` object. @@ -75,7 +75,7 @@ impl IntrinsicObject for PlainTime { js_string!("hour"), Some(get_hour), None, - Attribute::default(), + Attribute::CONFIGURABLE, ) .accessor( js_string!("minute"), @@ -107,9 +107,11 @@ impl IntrinsicObject for PlainTime { None, Attribute::CONFIGURABLE, ) + .static_method(Self::from, js_string!("from"), 2) .method(Self::add, js_string!("add"), 1) .method(Self::subtract, js_string!("subtract"), 1) .method(Self::round, js_string!("round"), 1) + .method(Self::equals, js_string!("equals"), 1) .method(Self::get_iso_fields, js_string!("getISOFields"), 0) .method(Self::value_of, js_string!("valueOf"), 0) .build(); @@ -287,6 +289,36 @@ impl PlainTime { // ==== PlainTime method implementations ==== +impl PlainTime { + fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let item = args.get_or_undefined(0); + // 1. Set options to ? GetOptionsObject(options). + // 2. Let overflow be ? GetTemporalOverflowOption(options). + let overflow = get_option::( + &get_options_object(args.get_or_undefined(1))?, + js_str!("overflow"), + context, + )?; + // 3. If item is an Object and item has an [[InitializedTemporalTime]] internal slot, then + let time = if let Some(time) = item + .as_object() + .and_then(JsObject::downcast_ref::) + { + // a. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]], + // item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], + // item.[[ISONanosecond]]). + time.inner + } else { + to_temporal_time(item, overflow, context)? + }; + + // 4. Return ? ToTemporalTime(item, overflow). + create_temporal_time(time, None, context).map(Into::into) + } +} + +// ==== PlainTime.prototype method implementations ==== + impl PlainTime { /// 4.3.9 Temporal.PlainTime.prototype.add ( temporalDurationLike ) fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { @@ -394,6 +426,29 @@ impl PlainTime { create_temporal_time(result, None, context).map(Into::into) } + /// 4.3.15 Temporal.PlainTime.prototype.equals ( other ) + fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let temporalTime be the this value. + // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainTime object.") + })?; + + // 3. Set other to ? ToTemporalTime(other). + let other = to_temporal_time(args.get_or_undefined(0), None, context)?; + // 4. If temporalTime.[[ISOHour]] ≠ other.[[ISOHour]], return false. + // 5. If temporalTime.[[ISOMinute]] ≠ other.[[ISOMinute]], return false. + // 6. If temporalTime.[[ISOSecond]] ≠ other.[[ISOSecond]], return false. + // 7. If temporalTime.[[ISOMillisecond]] ≠ other.[[ISOMillisecond]], return false. + // 8. If temporalTime.[[ISOMicrosecond]] ≠ other.[[ISOMicrosecond]], return false. + // 9. If temporalTime.[[ISONanosecond]] ≠ other.[[ISONanosecond]], return false. + // 10. Return true. + Ok((time.inner == other).into()) + } + /// 4.3.18 Temporal.PlainTime.prototype.getISOFields ( ) fn get_iso_fields(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { // 1. Let temporalTime be the this value. @@ -484,3 +539,68 @@ pub(crate) fn create_temporal_time( // 10. Return object. Ok(obj) } + +pub(crate) fn to_temporal_time( + value: &JsValue, + _overflow: Option, + _context: &mut Context, +) -> JsResult