From 67429a7a31f465a0ebc9803dd6940d477c4af90a Mon Sep 17 00:00:00 2001 From: UnlegitDqrk Date: Mon, 2 Mar 2026 15:49:57 +0100 Subject: [PATCH] Fixed issue: #55 --- README.md | 19 +++ .../java/org/luaj/vm2/libs/IoLib$File.class | Bin 2210 -> 2004 bytes .../java/org/luaj/vm2/libs/IoLib$IoLibV.class | Bin 3272 -> 3493 bytes .../vm2/libs/IoLib$LinesIterator$close.class | Bin 0 -> 1265 bytes .../vm2/libs/IoLib$LinesIterator$invoke.class | Bin 0 -> 782 bytes .../vm2/libs/IoLib$LinesIterator$next.class | Bin 0 -> 787 bytes .../luaj/vm2/libs/IoLib$LinesIterator.class | Bin 0 -> 2235 bytes .../main/java/org/luaj/vm2/libs/IoLib.class | Bin 13260 -> 14095 bytes .../main/java/org/luaj/vm2/libs/IoLib.java | 123 ++++++++++++++++-- .../test/java/org/luaj/vm2/FragmentsTest.java | 42 ++++++ 10 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$close.class create mode 100644 core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$invoke.class create mode 100644 core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$next.class create mode 100644 core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator.class diff --git a/README.md b/README.md index 239ad5c7..b76e3d9b 100644 --- a/README.md +++ b/README.md @@ -1023,7 +1023,10 @@ and at http://luaj.sour
  • debug code may not be completely removed by some obfuscators
  • tail calls are not tracked in debug information
  • mixing different versions of luaj in the same java vm is not supported +
  • LuaJ runs on the host VM garbage collector, so object lifetime, weak reference timing, and finalization behavior are not identical to native Lua +
  • the __gc metamethod is not supported as a reliable Lua finalization mechanism
  • values associated with weak keys may linger longer than expected +
  • cascading weak-table collection can require multiple host GC cycles
  • behavior of luaj when a SecurityManager is used has not been fully characterized
  • negative zero is treated as identical to integer value zero throughout luaj
  • lua compiled into java bytecode using luajc cannot use string.dump() or xpcall() @@ -1031,6 +1034,22 @@ and at http://luaj.sour
  • shared metatables for string, bool, etc are shared across Globals instances in the same class loader
  • orphaned threads will not be collected unless garbage collection is run and sufficient time elapses +

    Garbage Collection And Resources

    +LuaJ does not implement the same garbage collector semantics as native Lua. Garbage collection is delegated to the host JVM or CLDC runtime, so collectgarbage() is only a hint and should not be used as a resource-management primitive. +

    +In particular: +

      +
    • Do not rely on garbage collection to close files, sockets, database handles, or other resources. +
    • Always call close() explicitly on files and iterators that own files. +
    • io.lines(filename) opens a file implicitly. If iteration is abandoned early, that file may remain open until explicitly collected by the host runtime. +
    • Prefer local f = assert(io.open(...)) together with f:lines() and an explicit f:close() when deterministic cleanup matters. +
    • For implicit line iterators that need deterministic early cleanup, use io.linesx(filename) and call iterator:close(). +
    • file:linesx() provides the same closable iterator API for already-open files. +
    • On Windows, leaked file handles can prevent rename or delete operations until the process exits. +
    • On JME/CLDC, finalization support may be absent, so explicit close is mandatory. +
    +

    +Short-lived locals may also remain reachable longer than expected because host stack/register reuse is implementation-dependent. If prompt reclamation matters, isolate temporary allocations in functions and clear large references explicitly instead of assuming block exit is enough.

    File Character Encoding

    Source files can be considered encoded in UTF-8 or ISO-8859-1 and results should be as expected, with literal string contianing quoted characters compiling to the same byte sequences as the input. diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib$File.class b/core/src/main/java/org/luaj/vm2/libs/IoLib$File.class index 5c64b50ab9fca250b8248bb5ed1e126bead475ba..ab2d50968ddabf13404071baaf89b171aff2fe6a 100644 GIT binary patch delta 644 zcmZutIZqo=5dNO`?XF`EV@JVUK}cW)qDUwx!eB0QV~#+$0^~GiqbwjB$p|4K0X=lD zrGc6P8Wd5`&`~1_8hR>z0pi;g3dTwCX6F0mo0&Ir5V-XFF78jy0Q6$v*tg?Rcb?5s z6YT;o7ZYoVdOKYoAMIUFS}Ut|I;B!taivyUHz%Mcm(v+bz-wpRXhA?`ENe*}Y9(r4 z+f{usS}`qP{<4>?XqE6AnDJp2b0)sz;{(2`J$WsE3WSx=L+VBr8YboiDj%V#&h?7C z03-rvga;9It51ksEQxMp#--?v{O2@wuV+Q##jUH zT$yoY;>HaoZo~x|7P5Ek%0J;hFrGJ2+B7ca-Fwfu_r812ydVDe-P-rx+us0;svO?puql zgf{>M-8c^iQq~98P~!r(bwlu+;nskQ?u#x(U;-BjUm~Q?CWuTT(1u*b7-=CzXk)0D z&0N7$8HEg<%Rto0~gg?;; zG+V4Ny0x1a{+rkt-NKQt7}>@s&|b32B)$;22AkCxIcfJtw;DM97G9RdH=s;Kzu@c! zw6wG%JDW^b;l&=R1^78Mh{!n(@_7Q!$UZ0EAh(7dtTSVcgRf%{FKuOfP^MK@PpFu4 q?}{_WpPsdQ-Nw;7w$qQhv>wj2$nPEsjO4T)^kZ33P(%r3g#G~TwQ(r` diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib$IoLibV.class b/core/src/main/java/org/luaj/vm2/libs/IoLib$IoLibV.class index a3771b78c24f42a081ddd9ed774b7e7a482c9476..77aee82c541375256faac774abc35aeae3b8ca99 100644 GIT binary patch delta 1452 zcmYjRZERCj7=GUMT#I zj0ij<#X>1=PRFx`RG$@S!U953q&c22B29@@M(Kt?^Ax=~(Uxg(!GjioI5rZTsn%>f znI$-Up^&Hw$*~dw&mn2q< zThM7Pa)cMZOt3YzrDe}o1hyhca9!`2F^o-8?iSdF&Kna=tjmK}1$LmvJnxuY-Yf9B zT#8zFJQ*|89_A zfF94N4T^DDzN#msVV}SNbl!%Eo$JE^fp>AxG@Y|<@57+LdpO7^kxi}5rsK&CF1YZ4 zz+oIQ&pT_KLjp(fk$J0D?>sIrjE~Lr+8CWOr|WCXo%+IiPw;V9@G^jd z;3RKn1Mc_>2~8{F$K%oYDJt^ny4&j(o_Y;^{~-XI2A2j8zky`)H-v^y8|HCy4Yq51 g{cZS+$131kc3LrEl(~cz`6K!qUj7{r&e-k$0~A?JNB{r; delta 1229 zcmYk4YiyHM7{~ulFYnv62WVDt1&X1`WTU&X1eA+Hh7&4b5H3#Rw9NG#d!e+m?Z(hy zJ8wXl40ySW>jrFW;w8h7mt`@2G(`DgB4144i-{zDF_CW&F3&kIvnK!Zd*0_fmvefa zk$E@g_2u0GlCv9d8>5U0& z$1;L1)@CM*SZgAcQT`7FTBrHj5}lcLF9I+G+OUJ5r8=@^GD~omN280PvXm*X6T1jA zo|VcN)}*WPrQHHa+4gyZgqbulvQUS>M@U<%+z~A+(1|W9nk$re(R01wN7G zyi?_jUDCZz;8S${U+?)v0>}&W;xkLr%0gcVd@0YaZfYjuMz>7dFK_?{t*G{H_z)qm zv!$!0#!S^jH#T&)8hn{l(u)GV7C4N4t5{z+zks6x$IwskWK&zRX*0RQ3onifoWMzI zo4#Bd5IBu9)(O2{J11}+7pyz_Pc&kk@ods`d`AfJQ-rL4JS%Gl`GLZG3Szn=H{iU+ z24RR-FC1)^!bLB$N5@582d%e@B4N2(3>WW~p@_;cixweB5fwEAp-DyqP6x-ydSj96D80&wZ0}V7zX{O6+Ro}>-DF*79bgTy zerL6_j&OJGrpgJN8vhYz$H6z@k<0TOB#=?eBBz+gA;mtNQXIf# z#bMk~oWO62xA2GJZQN13i@z1`;XlRuc+AWd@PtaFgMzd`F-(gU>*y`T_4J-%Gqo$4 z)Fa8WGQ@{WBgB9362xgK67&kP^eXbS41Kg51GECev=S5a8g9|+xJ|2YmsaB*)#E;` z;m&X5xNQT4=}oGmwX~krQ8TwS+1f{2C{G)xml~-+8>yc*(I9Q6VcLqz+_)BVA0n>n zKK=y)9*@W83wr!PzX;UpC_KW8yy_TU!+HSc10FUfu5g|MqZm^pe1q9?t{pu2gsTYR JTjuvJ*Hhw55xM{X diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$close.class b/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$close.class new file mode 100644 index 0000000000000000000000000000000000000000..5a1b63583fb645e97d9c65f5cfa9a0b1801fe01e GIT binary patch literal 1265 zcma)6NmCO+6#jY=7>E-H5Q!inU0!i;m6EVex%evv}S#@~*BkM55W6nywnxM`IWx~o
    {Gka8@(8opHFOI;Isa>ZZ*ZTAdxu4$m-*W&g`A=|cxPWdsy-p_^f- z(Q4J;i&i=98bwDl4RW!d8JhEi!4r*bv?GY?GI|w+aD!pA(fl7|38O~0#K5)$LcfBW z7+~;jYdQrn*pz#k-ih)cZp#=_FpLp~fo491GDDkY|8_=%w}f$6!KetfrS>3WjG?Di z8O=;&R+rxv`JM>XhX`T{;uvS}mARASwyl;4G8nyFwl&Anj54u`U{b-9&~9_g`mK*@ zIjvv@v*e9imT6HzFSo(DaqMcuSy$aqb(beaFWgmd5BDiK>#trfGxRm6E}5pzRU^p| zk6wLj)-B1{2H~a55(U#K`Y7*K3*1^)3nF8I9Cvoi(o5A+tDO3adf8i4MF&s+g@3`b z8Gi=y<)yv%S;I^N@bqg&WdR}GM+Ppn$(&> z$ruK=Qo3r}oCy1`Oa}!ZpfOG9?4SW=fq)n;k|g?d;Q_4==`;opNkC_O{LLBI<_YBZ zC-^@I6Fj1|m3Bzmm?yb~M8aW6Vu4m+|BkG^w5Q|8kdM)Gh`GAdcZyqwh+G`*9HB2a zaf)bu@+;)YBgk}^I74DnsCZA%^AU6J2{lAL9;VKcYVJpfKcpRmqzZ;517T?wqmque zRK>ishb3trkI8>8yd@ctAMn#E<4KDC-zhc^p3$p>MJ(0!6n)Boe!34Lg*3VeKPYB{ Kh9rd-UhgkVelqC* literal 0 HcmV?d00001 diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$invoke.class b/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$invoke.class new file mode 100644 index 0000000000000000000000000000000000000000..22bc150cf84e274644a8605663a20c0da5910f47 GIT binary patch literal 782 zcma)4Yikoh6g@X-v+26c%WCVRT5H-SQM&5ah#m!G}hou0^C=jY6F%eq<||8%Zd*GnLfu6ZX3QMQr;hq3od&po$t{Yo65?_OTrvrh4F% z(Olx8(#ky|wdsozGOeT^e)*v1Z{BBM>zegQPtz?c94 literal 0 HcmV?d00001 diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$next.class b/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator$next.class new file mode 100644 index 0000000000000000000000000000000000000000..94dfca25c259b971d964a3d37ff84886e74888bc GIT binary patch literal 787 zcma)4TWb?R6#gb@v+26ct<~0ht!Z-+y6S60kYX_qqfkldo3rVVPTb7Unc3jaQt*Zk z{s4cJcqS+qV?Y+p`S#5D&i(t3uipSXMI%HG`2Z^sR#9N6j`@_wnUiV!A{mRJvkWC8 zE;4CEU#otWrVRVd-pnoKYV^yk(^@pGp@_8rVT4PF7~0y5;xyyqcsh9)r!ui|SNCL6 z?@1+W*9pU&HgzT5I);Kfm$v?Zq0##yaV@gyZn<+*U@`s^izod}v1ZKfdARKKCOgTFHT&b_px@K|SND2}9$ z9L{<>@H?fJ`og+r+PVOD8Mc-Tf+6ZECCp*Utrb+t`rH<&4L=#&rxPgAp|MKE9|55p zeN|i~d5!)W$P)s}?R%$Rz)n6QXn)4~dr!f2k_FPpy~Yi~s{|G?aPJ-^ug mfZ!80-Vy$jm{9!NWXqA~otfT6opg;Xo7lx3p(3Gu)P4dF@4=%0 literal 0 HcmV?d00001 diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator.class b/core/src/main/java/org/luaj/vm2/libs/IoLib$LinesIterator.class new file mode 100644 index 0000000000000000000000000000000000000000..7d463b54c110b02ec42bdf6f66398e7614ba5757 GIT binary patch literal 2235 zcma)7ZC4Xl5Pt3@uyMP*1rTV_N(-87r~ztK2w)}UC7J{%7z$O|4P3**lEvK(r7s`) zH{=ia6*-c6$~pbm5B*J^+PNE`CYWpPBp5zkmJ(U<$vg;HXm&&`^&C zhSn`(&xjR0(~8aIw#2&UGBi2jS3>_7EsiLuDk3e3{@d>p2`KEXBkR>zSeLblMEUGv?DyjE7NYU z)?rmNICmnG{YIl|B%(F=7;LDR!K{L$h6i}a&{HcrNmPcQ>6XoWs;|oP(i$?Dqje@p z*mc&(L&XBVRj{aG36Dsvh8qgYv>jn=Fr=!Idxt}Q(NsD*V|rEQXWO!pmNk5b6=I9& z7EEd`fp9WHB{qZaH4Gu7;0Z&U-)>AhmYkc~Ul+TcX%|#H#t#~Pgu$TZg_jntYvc)x zb}G03+tVpWr1rXo4XGXU?DDR>!>1bZ*d+JDab)+2zK7;j(O2BtmWFMKb09O9nPOO| z2~SnARO7Z<1Guc@zr8_uHOoGkDemNibK2ip(!$%cH|DA35dDNhy(TDYUMey)E_%lL zcG}qWwH1)Gr5YE4ZqY6}>!Qpf)o;i*r4}8P5w16DyHxvHvQQ9C!ZKV}&@qiw9Xn9A zZYUQQ1_{kkx2?cl{B* zI>Cq(#7h{{yX#B1xBQYkFcj!the{#cMiak-Hhvdfd<4CG6fqvhI3L3_A4ir?;0eD6 zoAg@)e&{qi|8VT$8XZUBvmVsZ7x-O&g~S2H{R(X*Oh1Qmgw!Fe6uCpc-V)|hG*1L8vT3*6MPm^Jc$RKEO-iOp2jkt!x~?J!RL|V zi^%gOSo{%w<5@hT42KbHt$Tx83VPmB@=T$Nr@zN<(GE@vn)++JL8}5MK|eyqBTJ4V p_WW@HKhdbul?SO8?GsgIX#6=4pm&R2JzA3&*$Aj*2k{F+{{a}aCK&(# literal 0 HcmV?d00001 diff --git a/core/src/main/java/org/luaj/vm2/libs/IoLib.class b/core/src/main/java/org/luaj/vm2/libs/IoLib.class index de1e4890b79df6b1da7ffab58b86c924906b48b1..a68452c3fa8b1bcec98487bc59efcbcf7fa82204 100644 GIT binary patch delta 6556 zcmai2d3;nwwmzqNyWP1-I^9V~XCWa8S-KMzfq+4A5(p9q0gMnLC!F>tvzPjB>Zu8#n_x|Wxb?Tn=JEu-n z-{)Iy>2N%IV)s5GsuJzn9El<}jb7J$j-ukX zm`whTCWYR{WZ9}N7l{b_yDGg$??cH-zyBhpRPER>w|0xeX8lm5!}O8X?I>OFiAtZ+ z?eMbh+-Y;>pX;Dxy3-C<|D)0ua@~UU>RP#SRHZKmIIGS%N5=EDO5acmg#BGzovVTl z8c*L!)!%8^abx&LZEjpv_RlK)LbIRY%@?F$gKn;{uf|T*x#}bJ{&TSxd5)6)td*RfgKjUX^`Z0-YU= zU2w=$R9r2WN2xrT$7mnMkFlPn@;Dx^4NLG^%T%7oleDuFW?9Qsp3G-x8xjhOr>K0E zt|GXu(+?cDLgh-iUDuQBHI5llb2U?Xk#^GMaoTwn*VuTr%C$U4 zLe1_}?b5`gvROP&<#V_WnQmXQbY0ND5@}vkDf#ECyg)K5?Mnj9%`5%7iG?aR@Oj$f zi8W&vt9(A6hxg50{>G+8R0J8eZq}}H=7pB4+$y86w)s0+g3FO3zDVUZZpYHH<&9kx!AK^iDjWcCU27U8%f%{p zF>tN8GOR9sFlFEs;YMMT;t{t5Fg_eWS{oc(XR% zHHPoh{I1+kH<_fiyQN5pgS+`2St<8MB3@wQ|7J>F(YU&CbZcOA)tu>7YnS;ugROxM z$=ajxeR|ETY`w%U7yhR51N?WUxL}|X$*$D4t=hNTqk zzSJTfuDzVP&XUKJwn!T>%(G+putbh?ENKlai8NDfRoaGlb&0ewuoCCl%XQi_o^e*2 zn9k&rUBna9Me>)m1y=fPVg{2}zDguw27HaCGury>`Uyqz(s!SzREx#xH5>M zpY0DW4>YY*#B8QgZAN;&c6xdo&(W&Vr~rc({bzD}*x$wf>_1AD$b&;%8QT$pGT=R3p&Vu?*$sM0;6QS)Sc z2*BcF{6>~!SV&BiPf&Mu{hjiC}!NRWwhTlN<8|dSpPZ;QvlrCt97HH=}7-$n{vw^mNwi;*~Xho+H zKd=*aEI14daiCQL9S=IeKqrEB8t5d@E(7fboot{}K&Kk$VW86_twfS1JX+@P5piX> zFj#OjvPjov3?CWFfODx>A(~BQ+C_HSO$oGz+;qfjmW>>onRsi)n1vNI@l#gNzE4>} zCyrgA|D}iiMh{3H*MMpe*&+webnV3OEfvzOayUZt8!i(k$Vo^D3mVBL90683lWjDY z6{_brTFfdnaXhVHCtb`*w4PmJg!Vyp8mDW&WKWzl7$PH39!5!=M%frgvPX=7g?JJt zD9$EwnSUVf7K^hc|&Ov{s z!k-wyA_z((vvY@O+eUajQrLwQ60d@1t0^Ce_3>Juv@SQ_Ry@dEVc@(JoR@(!1kTH& zoZcws*4(iMRIdT5*OHz8fZcx`ki9mFs;Qq+;hCk5}79_e56stmqwARBL_4BkXJycxcm^QYU!402A% z*(-4uI_?oz?uNh?2;2jKd!qtlAt2K`FF!MMU?@=$x9>xi?~n21vfT7!9x8A>6P<^8DBbnk;EM!v z$!>aqsE@YROLHYzVR|uYFoJA4_6_lVQuzR?^&m>=F&fQJ&?J6R&$pG#YFwNH#wSKn z1Jhn~A{n*I>kHE>P;2s8!t`*McJ<0OZrm!*ftvLH3+kyh%Dn`+(>XH=b1q_**A0hP z4?eJ&INc{STt$fGd)r&Ox}|7(}JO_2%F_-SzLmxsKL5v1lFYqYsAaQ18L~ zHqd$pXdMDt?*Xk3hL8%yIb!lQc`$|%fK0|<*F%V@vq%V^#0Z`d*~Ac70YQoOMBjG$ zI82{y1PmzI9xB4Z=YtEE_s}rk_8D|!aOJBWvTmo1L*7Sr(HCf~M<|z%QW<}tdu}Il zlG7P&0B4FR`i8o^Fe7wqH+>(0!($H9EgQ-1`k{}0s`q-#rAR`#C37QrJQlqBbt5@- zo=v34@wmq_WH(Y{$H>axl83*eLjIn{@DDVTf7IZ%i2Q9j-e#3FnD) z{fgq0ltG_FGK|NIv-CF)6#IPgjozXn#Zr9GQ!LAnzHOdpNJb z>&q;Cgh%$4n?2?}g7zYV_BiZ7Wxg=Xr#F;a!+fST(U&vs zcYQpy+}1**D7D;*NI(NS?beJ%csdT%VrN75p;6L_eJgUt0mY7QRh55FIDcxj~$3lrItB*Gf`5Q^@ z)7WRvkVia61>$+~iWhXC6LCUR$_wK0GD^0I-x>aG;7W-vA5r{=BOAC+&`o=|yWtVO zt2SdJ)xw>>Xg#A#LtA=IzK9c}s5HeK<}E#x9i5!)lPnEo=FOxIEPc~MO1VYIjoVF5 zr1P)Xzj{Ot5cl`-KV*+na5}d0R#=x!v=Vhb5 zK17Y;U0NYN#9jP-3WyJ=OB|-l#7BVtW4cis*25h`j($>Y+})3}N>zU)rJr1U62X~K z;LLbKcx>$_Gbc?4E`}y#m!Br1S;ouX3>G}*8Wbp-d<`L}7qe$RadL(i3Xm#^6nY&PoHGHIu zTGL=n-b`jjlUCz(^CS3VwF~23bS^pWkB$e8H1@+|v(a8#?xOgTK7PE9pFQ=;k&6_O z_!cSojzUiHJ$AuQXl*}Jk@$tipovcqIHF-ZQFip{02b$+tTcnK4*9(->gz(81F&ZU znHQ$`valn&$-GV0PkqUgCy(NSi^@9rF<1=uTa$?_CUc15O%_Tp*~o2DXqd^a8^#p^ ztuo$N(Tb=*6-S!mSGqZ(eQtiKk5hNh=Wc#QpM31**YwE|*)8R(JKX$+KDpJY?xpb! zCO5xT*MMgF@45zyn?rBcHCWyJPF;h|&4=n56gR(H*Wkp``xvnH0R}7{#$b2zM;Oot zKf!=6?4Q={pj@Z&f2f5%sDM6bfG8Av1`#ZMjsa^@a?~~eg1fQYCByLvm}oEhm?_2*pu zd5-=(SAQ?&(^863JEyLdc delta 6076 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 wi3XnBp84V-iS52ndB closable iterator + public Varargs _io_linesx(Varargs args) { + String filename = args.optjstring(1, null); + File infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r"); + checkopen(infile); + return linesx(infile, filename != null, args.subargs(2)); + } + // io.read(...) -> (...) public Varargs _io_read(Varargs args) throws IOException { checkopen(input()); @@ -447,6 +459,11 @@ public class IoLib extends TwoArgFunction { return lines(checkfile(args.arg1()), false, args.subargs(2)); } + // file:linesx(...) -> closable iterator + public Varargs _file_linesx(Varargs args) { + return linesx(checkfile(args.arg1()), false, args.subargs(2)); + } + // file:read(...) -> (...) public Varargs _file_read(LuaValue file, Varargs subargs) throws IOException { return ioread(checkfile(file),subargs); @@ -481,7 +498,7 @@ public class IoLib extends TwoArgFunction { if ( f == null ) argerror(1, "not a file: " + file); if ( f.isclosed() ) error("file is already closed"); Varargs ret = ioread(f, args); - if (toclose && ret.isnil(1) && f.eof()) f.close(); + if (toclose && ret.isnil(1) && !f.isclosed()) f.close(); return ret; } @@ -532,6 +549,84 @@ public class IoLib extends TwoArgFunction { } } + private Varargs linesx(final File f, boolean toclose, Varargs args) { + try { + return new LinesIterator(f, toclose, args); + } catch ( Exception e ) { + return error("linesx: "+e); + } + } + + private final class LinesIterator extends LuaTable { + private final File file; + private final boolean toclose; + private final Varargs args; + private boolean closed; + + LinesIterator(File file, boolean toclose, Varargs args) { + this.file = file; + this.toclose = toclose; + this.args = args.dealias(); + set("next", new next()); + set("close", new close()); + LuaTable mt = new LuaTable(); + mt.set(CALL, new invoke()); + setmetatable(mt); + } + + private Varargs nextLine() { + if (closed || file.isclosed()) { + return NIL; + } + try { + Varargs ret = ioread(file, args); + if (toclose && ret.isnil(1) && !file.isclosed()) { + file.close(); + closed = true; + } + return ret; + } catch (IOException ioe) { + if (!file.isclosed()) { + try { + file.close(); + } catch (IOException ignore) { + } + } + closed = true; + String s = ioe.getMessage(); + error(s != null ? s : ioe.toString()); + return NONE; + } + } + + private final class invoke extends VarArgFunction { + public Varargs invoke(Varargs args) { + return nextLine(); + } + } + + private final class next extends VarArgFunction { + public Varargs invoke(Varargs args) { + return nextLine(); + } + } + + private final class close extends OneArgFunction { + public LuaValue call(LuaValue self) { + if (!closed && !file.isclosed()) { + try { + file.close(); + } catch (IOException ioe) { + String s = ioe.getMessage(); + error(s != null ? s : ioe.toString()); + } + } + closed = true; + return TRUE; + } + } + } + private static Varargs iowrite(File f, Varargs args) throws IOException { for ( int i=1, n=args.narg(); i<=n; i++ ) f.write( args.checkstring(i) ); @@ -685,4 +780,4 @@ public class IoLib extends TwoArgFunction { -} \ No newline at end of file +} diff --git a/jse/src/test/java/org/luaj/vm2/FragmentsTest.java b/jse/src/test/java/org/luaj/vm2/FragmentsTest.java index 70e1f087..6971c9ce 100644 --- a/jse/src/test/java/org/luaj/vm2/FragmentsTest.java +++ b/jse/src/test/java/org/luaj/vm2/FragmentsTest.java @@ -26,6 +26,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.Reader; import java.io.StringReader; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.sql.DriverManager; import java.sql.SQLException; import java.util.concurrent.atomic.AtomicReference; @@ -438,6 +440,46 @@ public class FragmentsTest extends TestSuite { } } + public void testIoLinesClosesImplicitFileAtEnd() throws Exception { + File file = writeTempFile("lines-close", "a\nb\n"); + try { + Globals globals = JsePlatform.standardGlobals(); + Varargs iterTriplet = globals.get("io").get("lines").invoke(LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(file.getAbsolutePath()) + })); + LuaValue iter = iterTriplet.arg1(); + assertEquals(LuaValue.valueOf("a"), iter.call()); + assertEquals(LuaValue.valueOf("b"), iter.call()); + assertEquals(LuaValue.NIL, iter.call()); + + Field f = iter.getClass().getDeclaredField("f"); + f.setAccessible(true); + Object fileHandle = f.get(iter); + Method isclosed = fileHandle.getClass().getDeclaredMethod("isclosed"); + isclosed.setAccessible(true); + assertEquals(Boolean.TRUE, isclosed.invoke(fileHandle)); + } finally { + file.delete(); + } + } + + public void testIoLinesxCanBeClosedEarly() throws Exception { + File file = writeTempFile("linesx-close", "a\nb\n"); + try { + Globals globals = JsePlatform.standardGlobals(); + Varargs result = globals.load( + "local it = io.linesx(" + quote(file.getAbsolutePath()) + ")\n" + + "local first = it()\n" + + "it:close()\n" + + "return first, it()\n", + "linesx.lua").invoke(); + assertEquals(LuaValue.valueOf("a"), result.arg1()); + assertEquals(LuaValue.NIL, result.arg(2)); + } finally { + file.delete(); + } + } + public void testTableMove() { runFragment( LuaValue.varargsOf(new LuaValue[] {