143 Commits

Author SHA1 Message Date
qiaoenxin
4c25b88076 •Runtime upgrade to Java 8 or newer.
•Feature: Support Java 21 VirtualThread
2024-11-08 11:06:07 +08:00
Enyby
daf3da94e3 Fix #66: Broken license link. 2020-04-01 19:36:00 +03:00
Enyby
30e60a883e Create LICENSE
Source: https://web.archive.org/web/20140514153921/http://sourceforge.net/dbimage.php?id=196142
2020-04-01 19:31:39 +03:00
Enyby
27edcc9a92 Fix possible error in rare cases in LuaTable:
java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
2019-12-29 16:46:58 +02:00
Enyby
d6737c0bb3 Improve work with weak keys.
```
for _, m in ipairs({'', 'k', 'kv', 'v'}) do
	print('test', m)
	a = {}; setmetatable(a, {__mode = m});
	a[1], a[2], a[3] = {}, {}, {};
	for k, v in pairs(a) do
		print(k, v)
	end
end
print('ok')
```
2019-12-16 15:31:13 +02:00
Enyby
d201bc3012 Fix work with weak tables.
```
a = {}; setmetatable(a, {__mode = 'vk'});
a[1], a[2], a[3] = {}, {}, {};
for k, v in pairs(a) do
	print(k, v)
end
print('ok')
```
2019-12-15 20:39:26 +02:00
Enyby
ca64666242 Fix metamethods for compare with numbers.
```
t = {}
t.__lt = function (a,b,c)
  collectgarbage()
  assert(c == nil)
  if type(a) == 'table' then a = a.x end
  if type(b) == 'table' then b = b.x end
 return a<b, "dummy"
end

function Op(x) return setmetatable({x=x}, t) end

local function test ()
  assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
  assert(not(1 < Op(1)) and (Op(1) < 2) and not(2 < Op(1)))
  assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
  assert(not('a' < Op('a')) and (Op('a') < 'b') and not(Op('b') < Op('a')))
  assert((Op(1)<=Op(1)) and (Op(1)<=Op(2)) and not(Op(2)<=Op(1)))
  assert((Op('a')<=Op('a')) and (Op('a')<=Op('b')) and not(Op('b')<=Op('a')))
  assert(not(Op(1)>Op(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1)))
  assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a')))
  assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1)))
  assert((1 >= Op(1)) and not(1 >= Op(2)) and (Op(2) >= 1))
  assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
  assert(('a' >= Op('a')) and not(Op('a') >= 'b') and (Op('b') >= Op('a')))
end

test()
```
2019-11-11 02:47:19 +02:00
Enyby
725cf89b6f Fix metamethods for compare with string. 2019-11-11 02:29:20 +02:00
Enyby
da0b06555a Fix pattern error message. 2019-11-09 23:26:48 +02:00
Enyby
ee2d5284e7 Fix '%b' pattern error message. 2019-11-09 23:25:58 +02:00
Enyby
0f0ec4bf7b Add check for too complex patterns.
```
-- bug since 2.5 (C-stack overflow)
do
  local function f (size)
    local s = string.rep("a", size)
    local p = string.rep(".?", size)
    return pcall(string.match, s, p)
  end
  local r, m = f(80)
  assert(r and #m == 80)
  r, m = f(200000)
  assert(not r and string.find(m, "too complex"), tostring(r)..", "..tostring(m))
end
```
2019-11-09 23:25:07 +02:00
Enyby
5813d56f89 Improve error messages for invalid capture index. 2019-11-09 23:21:39 +02:00
Enyby
8c42c4712b Fix empty matches in patterns.
```
do   -- new (5.3.3) semantics for empty matches
  assert(string.gsub("a b cd", " *", "-") == "-a-b-c-d-")

  local res = ""
  local sub = "a  \nbc\t\td"
  local i = 1
  for p, e in string.gmatch(sub, "()%s*()") do
    res = res .. string.sub(sub, i, p - 1) .. "-"
    i = e
  end
  assert(res == "-a-b-c-d-")
end
```
2019-11-09 23:20:06 +02:00
Enyby
6bc8fd6b1b Fix numeric for add order. 2019-11-09 23:12:31 +02:00
Enyby
bf663878cb Add support for metatags to table lib methods: sort, insert, remove, unpack.
```
do   -- testing table library with metamethods
  local function test (proxy, t)
    for i = 1, 10 do
      table.insert(proxy, 1, i)
    end
    assert(#proxy == 10 and #t == 10, tostring(#proxy)..'; '..tostring(#t))
    for i = 1, 10 do
      assert(t[i] == 11 - i)
    end
    table.sort(proxy)
    for i = 1, 10 do
      assert(t[i] == i and proxy[i] == i, i..': '..tostring(proxy[i])..'; '..tostring(t[i]))
    end
    assert(table.concat(proxy, ",") == "1,2,3,4,5,6,7,8,9,10")
    for i = 1, 8 do
      assert(table.remove(proxy, 1) == i)
    end
    assert(#proxy == 2 and #t == 2)
    local a, b, c = table.unpack(proxy)
    assert(a == 9 and b == 10 and c == nil)
  end

  -- all virtual
  local t = {}
  local proxy = setmetatable({}, {
    __len = function () return #t end,
    __index = t,
    __newindex = t,
  })
  test(proxy, t)

  -- only __newindex
  local count = 0
  t = setmetatable({}, {
    __newindex = function (t,k,v) count = count + 1; rawset(t,k,v) end})
  test(t, t)
  assert(count == 10)   -- after first 10, all other sets are not new

  -- no __newindex
  t = setmetatable({}, {
    __index = function (_,k) return k + 1 end,
    __len = function (_) return 5 end})
  assert(table.concat(t, ";") == "2;3;4;5;6")

end

function check (a, f)
  f = f or function (x,y) return x<y end;
  for n = #a, 2, -1 do
    local cmp = f(a[n], a[n-1])
    if cmp then print(tostring(a)..'\n'..n..': "'..tostring(a[n])..'" < "'..tostring(a[n-1])..'"') end
    assert(not cmp)
  end
end

for b = 1, 2 do
	a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
	     "Oct", "Nov", "Dec"}
	if b == 2 then a[15] = 'Aaa' a[14] = 'Iii' a[13] = 'Mmm' end
	print(#a)
	table.sort(a)
	check(a)
end
```
2019-11-04 07:57:49 +02:00
Enyby
e120008f9b Validate table.remove pos. 2019-11-03 16:08:01 +02:00
Enyby
9a20aa8077 Check pos bounds for table.insert. 2019-11-03 15:55:32 +02:00
Enyby
99f21b6277 Speed up table.sort. 2019-11-03 14:03:53 +02:00
Enyby
53bd4bf71f Fix compiler Bug{
what = [[label between local definitions can mix-up their initializations]],
report = [[Karel Tuma, 2016/03/01]],
since = [[5.2]],
fix = nil,
example = [[
do
  local k = 0
  local x
  ::foo::
  local y       -- should be reset to nil after goto, but it is not
  assert(not y)
  y = true
  k = k + 1
  if k < 2 then goto foo end
end
]],
patch = [[
--- lparser.c	2015/11/02 16:09:30	2.149
+++ lparser.c	2016/03/03 12:03:37
@@ -1226,7 +1226,7 @@
   checkrepeated(fs, ll, label);  /* check for repeated labels */
   checknext(ls, TK_DBCOLON);  /* skip double colon */
   /* create new entry for this label */
-  l = newlabelentry(ls, ll, label, line, fs->pc);
+  l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
   skipnoopstat(ls);  /* skip other no-op statements */
   if (block_follow(ls, 0)) {  /* label is last no-op statement in the block? */
     /* assume that locals are already out of scope */
]]
}
2019-11-02 17:02:21 +02:00
Enyby
b57eb247ba Fix table.unpack. 2019-11-02 15:18:48 +02:00
Enyby
05e82f1c3f Add package.config. #49 2019-10-27 23:08:44 +02:00
Enyby
9b2f0a2805 Fix os.tmpname. 2019-10-22 07:00:21 +03:00
Enyby
ef8175050b Remove unused field. 2019-10-21 23:26:10 +03:00
Enyby
0d2aa6cc54 Switch little-endian by default as original Lua does. 2019-10-21 23:25:30 +03:00
Enyby
fe7bd07450 Fix lexer bugs.
Wrong work with spaces.
2019-10-21 10:20:06 +03:00
Enyby
f0e9348ae2 Fix lexer bugs.
Already handled by inside isalnum.
2019-10-21 10:17:26 +03:00
Enyby
22e7a8c620 Fix lexer bugs.
Already handled by case above.
2019-10-21 10:16:43 +03:00
Enyby
c8461b8128 Fix pattern classes in string lib.
```
local ref = {
}
for cl in string.gmatch('acdglpsuwxACDGLPSUWX', '.') do
	local list = ''
	for i = 0, 255 do
		if string.match(string.char(i), '%'..cl) then
			list = list..i..','
		end
	end
	if ref[cl] then
		assert(ref[cl] == list, cl..':\n'..list..'\n'..ref[cl])
	else
		print(cl..' = "'..list..'",')
	end
end

print('+')
```
2019-10-21 08:43:53 +03:00
Enyby
3a6c382570 Fix 'error' call. #60 2019-10-20 19:19:14 +03:00
Enyby
4db34780b7 Fix build error. 2019-10-20 18:43:10 +03:00
Enyby
ac3475deee Improved error messages for lib functions. 2019-10-20 07:19:34 +03:00
Enyby
af35c4d89e Improve error messages for base lib. 2019-10-20 07:17:46 +03:00
Enyby
c71f277697 Improve error messages for lib table. 2019-10-20 07:07:17 +03:00
Enyby
5fe0a3950d Improve get name for func. 2019-10-20 06:45:53 +03:00
Enyby
169202362e Fix error message for collectgarbage invalid option. 2019-10-20 00:34:54 +03:00
Enyby
5609d8c92b Add check mode for io.open. #57 2019-10-19 20:53:56 +03:00
Enyby
dbab0aed01 Fix odd varargs methods. 2019-10-14 18:33:25 +03:00
Enyby
f8d7731b56 Fix NPE on getobjname in some cases. 2019-10-14 14:12:08 +03:00
Enyby
2f5aa594bd Fix getobjname for get constant name if it stored on register. 2019-10-14 14:11:38 +03:00
Enyby
edfe1a5fde Add check for io.popen modes. 2019-10-13 06:16:21 +03:00
Enyby
6efb6f000e Improve error message for file:seek and file:setvbuf. 2019-10-13 05:58:32 +03:00
Enyby
f3b8a1eddc Fix default setvbuf size. 2019-10-12 19:12:44 +03:00
Enyby
20eca5760d Fix detect io lib read modes like file:read('*all'). 2019-10-12 17:57:37 +03:00
Enyby
3613bc0862 Add check for values passed to file:vsetbuf and file:seek. 2019-10-12 17:51:04 +03:00
Enyby
6985980572 Merge branch 'master' of https://github.com/luaj/luaj 2019-10-12 16:09:03 +03:00
Enyby
60d130cecc Fix corrupted args for io.lines and file:lines on reuse stack elements. 2019-10-12 16:08:36 +03:00
Enyby
b705eb05f4 Merge pull request #59 from Mikhael-Danilov/patch-1
Fix link
2019-10-07 21:53:17 +03:00
Mikhael-Danilov
d0bb0409a3 Fix link
Fix link to examples/android/src/android/LuajViewLuajView.java
2019-10-07 20:23:45 +03:00
Enyby
db58e1808b Fix load script from func. 2019-10-07 18:08:47 +03:00
Enyby
e2ede7f91c Fix call io.lines(nil, ...). 2019-10-07 17:11:13 +03:00
Enyby
a50deaa75c Fix raise error from io.lines and file:lines. 2019-10-07 14:52:12 +03:00
Enyby
f9f78b81da Allow read zero bytes in io lib methods. 2019-10-07 14:26:21 +03:00
Enyby
984fa30bf6 Fix read all stdin in JSE. 2019-10-07 00:25:07 +03:00
Enyby
65beda4c2b Fix read all at EOF. 2019-10-07 00:23:34 +03:00
Enyby
934a8fc57b Fix io.read(0) on EOF. 2019-10-06 23:49:04 +03:00
Enyby
832ec739ea Close not closed file on GC. #55
Relevant only for J2SE, not for J2ME.
2019-10-06 23:35:45 +03:00
Enyby
8345bee6b4 Fix io.lines and file:lines do not honor additional params. #52
Fix io.lines do not close file on EOF. #53
Improve error message for io.lines and file:lines on closed file.
2019-10-06 19:09:01 +03:00
Enyby
83f2e1d96a Fix io.read and file:read do not support '*L' format. #54 2019-10-06 18:53:15 +03:00
Enyby
14745ba76a Fix call io.read, file:read without params. 2019-10-06 18:40:31 +03:00
Enyby
c62ba1f22e File tostring respect closed. 2019-10-06 16:54:44 +03:00
Enyby
868928779f Fix rewrite io.input inside io.lines.
```
do
	local t = os.tmpname()
	local f = io.open(t, 'w')
	f:write('test')
	f:close()

	local i = io.input()
	for l in io.lines(t) do end
	local n = io.input()
	assert(n == i, tostring(n).." ~= "..tostring(i))
	os.remove(t)
	print('+')
end
```
2019-10-06 16:43:11 +03:00
Enyby
f383c27728 We not in C anymore. 2019-09-23 01:54:27 +03:00
Enyby
e7b11110a3 Raise lexerror if failed parse number as double. 2019-09-23 01:53:01 +03:00
Enyby
0fa27c3783 EOZ (-1) not a space char.
Fix bug with endless loop at not ended \z sequence.

```
local function lexerror (s, err)
  local st, msg = load('return ' .. s, '')
  if err ~= '<eof>' then err = err .. "'" end
  assert(not st and string.find(msg, "near .-" .. err))
end

lexerror("'alo \\z  \n\n", "<eof>")
```
2019-09-23 00:39:42 +03:00
Enyby
72a71e5a65 Merge branch 'master' of https://github.com/luaj/luaj 2019-09-21 07:18:37 +03:00
Enyby
6031d6b479 Fix lua error message on bad arg for 'load'. 2019-09-21 07:18:08 +03:00
Enyby
c1834733c1 Merge pull request #45 from zaoqi/patch-1
readme: fix typo
2019-08-10 02:18:32 +03:00
Zaoqi
10f88d2c31 readme: fix typo 2019-08-09 21:44:25 +08:00
Enyby
6d2deb4cb6 Fix use function as source for load call. 2019-08-05 04:07:09 +03:00
Enyby
5f52729fc9 Fix OP_JMP description. 2019-07-15 05:29:37 +03:00
Enyby
e6736857b6 Add support for OP_LOADKX. #43 2019-06-30 22:18:19 +03:00
Enyby
5c5176727a Merge branch 'master' of https://github.com/luaj/luaj 2019-05-05 17:23:27 +03:00
Enyby
7721380e20 Fix NPE on error. #40 2019-05-05 17:22:24 +03:00
Enyby
16aa199d40 Add debug info to build. 2019-05-05 17:21:25 +03:00
Enyby
177fe4e09f Fix path to build files in Readme.md 2019-03-17 03:12:52 +02:00
Enyby
a7c8a408ce Fix build bug. #33 2019-03-17 03:06:05 +02:00
Enyby
84b0dd171e Fix error messages for pass tests. 2019-03-09 23:35:22 +02:00
Enyby
898208365d Fix print for pass tests. 2019-03-09 23:18:47 +02:00
Enyby
ffb686556f Fix loadlib. 2019-03-09 23:00:28 +02:00
Enyby
6694c375c9 Fix build errors for j2me. #32 2019-03-09 17:38:32 +02:00
Enyby
e70eb5edc2 Fix build bug. #32 2019-03-09 15:49:56 +02:00
Enyby
9688d3c5fc Remove libs dependency from PackageLib. 2019-01-13 19:23:44 +02:00
Enyby
6f5af581e8 Fix string.gsub for invalid use '%' in replacement string
Check code:
```
print(pcall(load('string.gsub("test", "%S", "A%")')))
print(pcall(load('string.gsub("test", "%S", "%A")')))
```
2019-01-03 18:09:00 +02:00
Enyby
870aee2cae Improved opt methods for numbers in LuaString 2018-12-29 19:49:43 +02:00
Enyby
c2ec9edf39 Deny use NaN as index in rawset. 2018-12-24 22:34:49 +02:00
Enyby
cfb3aaca61 Fix math.min and math.max.
Now similar to native Lua and reference: `Returns the argument with the maximum value, according to the Lua operator <. (integer/float) `
2018-12-24 20:52:23 +02:00
Enyby
4105cebf1e Fix math.fmod for int values. 2018-12-24 20:37:39 +02:00
Enyby
339f004eb7 More compat with lua tests. 2018-12-24 19:30:11 +02:00
Enyby
05a604645d Fix modulo for edge cases.
Reference code:
```
for i,lhs in ipairs({-math.huge, -10.0, 0.0, 10.0, math.huge}) do
for j,rhs in ipairs({-math.huge, -10.0, 0.0, 10.0, math.huge}) do
print(lhs, rhs, lhs % rhs)
end
end
```
2018-12-24 18:30:35 +02:00
Enyby
63ead7aac7 Fix string.format for long integers. 2018-12-24 11:45:06 +02:00
Enyby
c9ba2d4bb1 Fix math.modf for inf/-inf and int values. 2018-12-23 17:48:31 +02:00
Enyby
92cee0812d Fix JSE math.log for second arg. 2018-12-23 17:06:34 +02:00
Enyby
e61e5694fa Fix string.match for empty pattern 2018-12-16 06:38:23 +02:00
Enyby
47f8d6e6fc Fix a frontier pattern match
We're not in C anymore. LuaString are not null terminated.
Also need quotes in error messages.
2018-12-11 23:44:39 +02:00
Enyby
14ac4bb7f9 Merge pull request #21 from lorenzos/patch-1
Check the type before reusing a NumberValueEntry
2018-11-02 23:11:36 +02:00
Lorenzo Stanco
b8aaaafb68 Check the type before reusing a NumberValueEntry
Fixes #20
2018-10-31 15:57:02 +01:00
Enyby
88a557033b Optimize get new stack for calls. 2018-09-20 20:14:02 +03:00
Enyby
24a8559ceb Fix make LuaString from copy. 2018-09-17 16:32:09 +03:00
Enyby
028e03e4bc Avoid synthetic methods 2018-09-16 18:49:26 +03:00
Enyby
5c1f7d2ab2 Make const final 2018-09-16 18:45:52 +03:00
Enyby
9db1254160 Avoid synthetic methods 2018-09-16 18:44:36 +03:00
Enyby
f899c709ff Remove unused var. 2018-09-16 18:43:39 +03:00
Enyby
9c9e193cc4 Make consts final. 2018-09-16 18:40:06 +03:00
Enyby
e9db487d97 Improve convert function to string. 2018-09-16 18:38:49 +03:00
Enyby
c6fe5d3ed3 Avoid synthetic methods 2018-09-16 17:15:54 +03:00
Enyby
52ab872d08 Improve rawopenfile 2018-09-16 17:11:28 +03:00
Enyby
5d3041c6be Improved get/setLocal. 2018-09-16 17:09:18 +03:00
Enyby
8d544729a8 Avoid synthetic methods. 2018-09-16 17:05:52 +03:00
Enyby
bd57dddef0 Imrove traceback build. 2018-09-16 17:03:35 +03:00
Enyby
d509e21cc6 Make static classes. 2018-09-16 17:02:24 +03:00
Enyby
5b67841232 Avoid synthetic methods 2018-09-16 16:58:51 +03:00
Enyby
bd4dac18ff Make classes static to avoid leak parent instance. 2018-09-16 16:56:53 +03:00
Enyby
e02a82ef8d Improve print. 2018-09-16 16:53:20 +03:00
Enyby
70a5086097 Make constants final. 2018-09-16 16:47:17 +03:00
Enyby
ad20f939b1 Fixed typo in JseOsLib 2018-09-16 16:46:13 +03:00
Enyby
e7058bcd6a Improved math.atan/atan2. 2018-09-16 16:44:21 +03:00
Enyby
00c7e266ba Used BufferedInputStream for JseBaseLib 2018-09-16 16:36:40 +03:00
Enyby
e75c5e2fca Fix Visitor bug. 2018-09-16 16:33:42 +03:00
Enyby
ed46675a25 Improved concat values. 2018-09-16 16:08:53 +03:00
Enyby
8fbca25dce Fixed table.unpack 2018-09-16 16:03:09 +03:00
Enyby
d0ed5b80e5 Improved error messages. 2018-09-16 15:52:14 +03:00
Enyby
9b77084109 Improved error messages. 2018-09-16 15:39:24 +03:00
Enyby
4f410fba6e Fixed metatag processing for equality. 2018-09-16 15:30:41 +03:00
Enyby
f164c1cd28 Added optional param stripDebug to string.dump 2018-09-16 15:21:16 +03:00
Enyby
63ca0f94af Removed synthetic accessors. 2018-09-16 15:13:06 +03:00
Enyby
5bd9827af1 Improve error message for string.char. 2018-09-16 15:09:36 +03:00
Enyby
0a5cccc50f Fix issue with frontier patterns #3 2018-09-16 15:02:29 +03:00
Enyby
12aded74ca Removed unused objects. 2018-09-16 10:01:39 +03:00
Enyby
48dd90f6d7 Removed unnecessary override. 2018-09-16 09:58:16 +03:00
Enyby
6333d9acf7 Merge pull request #17 from Enyby/patch-9
Fix format float numbers
2018-09-16 09:24:04 +03:00
Benjamin P. Jung
d61ed707ef Merge pull request #18 from Enyby/patch-1
Rename README.html to README.md
2018-09-15 21:40:22 +02:00
Enyby
1e521d8900 Rename README.html to README.md 2018-09-14 15:46:14 +03:00
Benjamin P. Jung
bf8ef8d1a9 Merge pull request #15 from Enyby/patch-7
Fix gmatch for pass testsuites
2018-09-14 12:13:34 +02:00
Benjamin P. Jung
69498dbb7c Merge pull request #16 from Enyby/patch-8
Fix build stack traceback in DebugLib

Fixes #7
2018-09-14 12:12:42 +02:00
Benjamin P. Jung
4cc5f4270d Merge pull request #14 from Enyby/patch-6
Fix for proper print OP_SETLIST additional opcode

Closes #7
2018-09-14 12:12:10 +02:00
Enyby
75fa98d13f Fix format float numbers
Fix bug: https://sourceforge.net/p/luaj/bugs/53/ if System support String.format or fallback to old way.
2018-09-14 02:16:42 +03:00
Enyby
a627a868d5 Fix build stack traceback in DebugLib #7
If create huge table with a lot items (> 28000) then additional op code for store `c` out of range of op codes and this loop crash.
This PR add case for OP_SETLIST for skip next op if in it stored `c`.
OP_SETLIST not AMode OP 
70cb74b4d6/src/core/org/luaj/vm2/Lua.java (L319)
70cb74b4d6/src/core/org/luaj/vm2/Lua.java (L335)
2018-09-14 02:13:21 +03:00
Enyby
f073919f64 Print any code 2018-09-14 02:09:43 +03:00
Enyby
27a1dcdd11 Fix gmatch for pass testsuites
Test suites from lua.org:
```
-- tests for gmatch
local a = 0
for i in string.gmatch('abcde', '()') do assert(i == a+1); a=i end
assert(a==6)

t = {n=0}
for w in string.gmatch("first second word", "%w+") do
      t.n=t.n+1; t[t.n] = w
end
assert(t[1] == "first" and t[2] == "second" and t[3] == "word")

t = {3, 6, 9}
for i in string.gmatch ("xuxx uu ppar r", "()(.)%2") do
  assert(i == table.remove(t, 1))
end
assert(#t == 0)

t = {}
for i,j in string.gmatch("13 14 10 = 11, 15= 16, 22=23", "(%d+)%s*=%s*(%d+)") do
  t[tonumber(i)] = tonumber(j)
end
a = 0
for k,v in pairs(t) do assert(k+1 == v+0); a=a+1 end
assert(a == 3)


-- tests for `%f' (`frontiers')

assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x")
assert(string.gsub("[[]] [][] [[[[", "%f[[].", "x") == "x[]] x]x] x[[[")
assert(string.gsub("01abc45de3", "%f[%d]", ".") == ".01abc.45de.3")
assert(string.gsub("01abc45 de3x", "%f[%D]%w", ".") == "01.bc45 de3.")
assert(string.gsub("function", "%f[\1-\255]%w", ".") == ".unction")
assert(string.gsub("function", "%f[^\1-\255]", ".") == "function.")

assert(string.find("a", "%f[a]") == 1)
assert(string.find("a", "%f[^%z]") == 1)
assert(string.find("a", "%f[^%l]") == 2)
assert(string.find("aba", "%f[a%z]") == 3)
assert(string.find("aba", "%f[%z]") == 4)
assert(not string.find("aba", "%f[%l%z]"))
assert(not string.find("aba", "%f[^%l%z]"))

local i, e = string.find(" alo aalo allo", "%f[%S].-%f[%s].-%f[%S]")
assert(i == 2 and e == 5)
local k = string.match(" alo aalo allo", "%f[%S](.-%f[%s].-%f[%S])")
assert(k == 'alo ')

local a = {1, 5, 9, 14, 17,}
for k in string.gmatch("alo alo th02 is 1hat", "()%f[%w%d]") do
  assert(table.remove(a, 1) == k)
end
assert(#a == 0)
```
Code from lua:
https://www.lua.org/source/5.2/lstrlib.c.html
```
static int gmatch_aux (lua_State *L) {
  MatchState ms;
  size_t ls, lp;
  const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
  const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp);
  const char *src;
  ms.L = L;
  ms.matchdepth = MAXCCALLS;
  ms.src_init = s;
  ms.src_end = s+ls;
  ms.p_end = p + lp;
  for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
       src <= ms.src_end;
       src++) {
    const char *e;
    ms.level = 0;
    lua_assert(ms.matchdepth == MAXCCALLS);
    if ((e = match(&ms, src, p)) != NULL) {
      lua_Integer newstart = e-s;
      if (e == src) newstart++;  /* empty match? go at least one position */
      lua_pushinteger(L, newstart);
      lua_replace(L, lua_upvalueindex(3));
      return push_captures(&ms, src, e);
    }
  }
  return 0;  /* not found */
}
```
On empty match need increase string offset.
2018-09-14 02:04:56 +03:00
Enyby
b459724103 Fix for proper print OP_SETLIST additional opcode #7 2018-09-14 01:59:35 +03:00
Benjamin P. Jung
3d22990e3c Migrate CVS project to Git
* Delete .svnignore and move stuff to .gitignore
* Add note to README to show that this is an inofficial fork as of now.
* Change file suffix of README file from .html to .md for better
  Github integration and readability
2018-09-13 11:56:02 +02:00
James Roseborough
194b776317 Add synchronization to CoerceJavaToLua.COERCIONS map. 2018-09-13 11:53:24 +02:00
James Roseborough
828e4be019 Fix JsePlatform.luaMain() to provide an "arg" table in the chunk's environment. 2018-09-13 11:53:00 +02:00
50 changed files with 3240 additions and 2774 deletions

View File

@@ -1,8 +1,8 @@
bin
target
build
lib
jit
bin/
target/
build/
lib/
jit/
*.ser
*.gz
*.jar

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2007 LuaJ. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,27 +1,24 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
# This is a fork!
<div style="border: 1px dotted red; margin: 1.em 0.5em; font-weight: bold; color: red;">
This repository has been forked from the original CVS sources of Luaj.
The commit history has been converted to make sure that the original work of
James Roseborough and Ian Farmer is not lost.
Unfortunately, I was not able to contact either James or Ian to hand over
ownership of the Github organization/repo as I have originally intended to.
The community however seems interested enough to continue work on the original
sources and therefore I have decided to make sure that any useful pull requests
that may add some value to the original code base shall be merged in from now
on.<br>
-- Benjamin P. Jung, Jan. 26th 2018
</div>
<head>
<title>Getting Started with LuaJ</title>
<link rel="stylesheet" type="text/css" href="http://www.lua.org/lua.css">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
</head>
<body>
<hr>
<h1>
<a href="README.html"><img src="http://sourceforge.net/dbimage.php?id=196139" alt="" border="0"></a>
Getting Started with LuaJ
</h1>
James Roseborough, Ian Farmer, Version 3.0.1
<h1>Getting Started with LuaJ</h1>
James Roseborough, Ian Farmer, Version 3.0.2
<p>
<small>
Copyright &copy; 2009-2014 Luaj.org.
Freely available under the terms of the
<a href="http://sourceforge.net/dbimage.php?id=196142">Luaj license</a>.
<a href="LICENSE">Luaj license</a>.
</small>
<hr>
<p>
@@ -120,7 +117,7 @@ in comparison with the standard C distribution.
<td>16.794</td>
<td>11.274</td>
<td>Java</td>
<td>java -cp luaj-jse-3.0.1.jar;bcel-5.2.jar lua <b>-b</b> fannkuch.lua 10</td></tr>
<td>java -cp luaj-jse-3.0.2.jar;bcel-5.2.jar lua <b>-b</b> fannkuch.lua 10</td></tr>
<tr valign="top">
<td></td>
<td></td>
@@ -130,7 +127,7 @@ in comparison with the standard C distribution.
<td>36.894</td>
<td>15.163</td>
<td></td>
<td>java -cp luaj-jse-3.0.1.jar lua -n fannkuch.lua 10</td></tr>
<td>java -cp luaj-jse-3.0.2.jar lua -n fannkuch.lua 10</td></tr>
<tr valign="top">
<td>lua</td>
<td>5.1.4</td>
@@ -186,7 +183,7 @@ It is also faster than Java-lua implementations Jill, Kahlua, and Mochalua for a
From the main distribution directory line type:
<pre>
java -cp lib/luaj-jse-3.0.1.jar lua examples/lua/hello.lua
java -cp luaj-jse-3.0.2.jar lua examples/lua/hello.lua
</pre>
<p>
@@ -198,7 +195,7 @@ You should see the following output:
To see how luaj can be used to acccess most Java API's including swing, try:
<pre>
java -cp lib/luaj-jse-3.0.1.jar lua examples/lua/swingapp.lua
java -cp luaj-jse-3.0.2.jar lua examples/lua/swingapp.lua
</pre>
<p>
@@ -213,8 +210,8 @@ Links to sources:<pre>
From the main distribution directory line type:
<pre>
java -cp lib/luaj-jse-3.0.1.jar luac examples/lua/hello.lua
java -cp lib/luaj-jse-3.0.1.jar lua luac.out
java -cp luaj-jse-3.0.2.jar luac examples/lua/hello.lua
java -cp luaj-jse-3.0.2.jar lua luac.out
</pre>
<p>
@@ -228,8 +225,8 @@ Luaj can compile lua sources or binaries directly to java bytecode if the bcel l
<pre>
ant bcel-lib
java -cp &quot;lib/luaj-jse-3.0.1.jar;lib/bcel-5.2.jar&quot; luajc -s examples/lua -d . hello.lua
java -cp &quot;lib/luaj-jse-3.0.1.jar;.&quot; lua -l hello
java -cp &quot;luaj-jse-3.0.2.jar;lib/bcel-5.2.jar&quot; luajc -s examples/lua -d . hello.lua
java -cp &quot;luaj-jse-3.0.2.jar;.&quot; lua -l hello
</pre>
<p>
@@ -240,7 +237,7 @@ but the compiled classes must be in the class path at runtime, unless runtime ji
<p>
Lua scripts can also be run directly in this mode without precompiling using the <em>lua</em> command with the <b><em>-b</em></b> option and providing the <em>bcel</em> library in the class path:
<pre>
java -cp &quot;lib/luaj-jse-3.0.1.jar;lib/bcel-5.2.jar&quot; lua -b examples/lua/hello.lua
java -cp &quot;luaj-jse-3.0.2.jar;lib/bcel-5.2.jar&quot; lua -b examples/lua/hello.lua
</pre>
@@ -284,7 +281,7 @@ A simple example may be found in
</pre>
<p>
You must include the library <b>lib/luaj-jse-3.0.1.jar</b> in your class path.
You must include the library <b>luaj-jse-3.0.2.jar</b> in your class path.
<h2>Run a script in a MIDlet</h2>
@@ -311,7 +308,7 @@ A simple example may be found in
</pre>
<p>
You must include the library <b>lib/luaj-jme-3.0.1.jar</b> in your midlet jar.
You must include the library <b>luaj-jme-3.0.2.jar</b> in your midlet jar.
<p>
An ant script to build and run the midlet is in
@@ -341,7 +338,7 @@ You can also look up the engine by language "lua" or mimetypes "text/lua" or "ap
All standard aspects of script engines including compiled statements are supported.
<p>
You must include the library <b>lib/luaj-jse-3.0.1.jar</b> in your class path.
You must include the library <b>luaj-jse-3.0.2.jar</b> in your class path.
<p>
A working example may be found in
@@ -352,8 +349,8 @@ A working example may be found in
To compile and run it using Java 1.6 or higher:
<pre>
javac -cp lib/luaj-jse-3.0.1.jar examples/jse/ScriptEngineSample.java
java -cp &quot;lib/luaj-jse-3.0.1.jar;examples/jse&quot; ScriptEngineSample
javac -cp luaj-jse-3.0.2.jar examples/jse/ScriptEngineSample.java
java -cp &quot;luaj-jse-3.0.2.jar;examples/jse&quot; ScriptEngineSample
</pre>
<h2>Excluding the lua bytecode compiler</h2>
@@ -415,7 +412,7 @@ and the math operations include all those supported by Java SE.
Android applications should use the JsePlatform, and can include the <a href="#luajava">Luajava</a> library
to simplify access to underlying Android APIs.
A specialized Globals.finder should be provided to find scripts and data for loading.
See <a href="examples/android/src/android/LuajView">examples/android/src/android/LuajView</a>
See <a href="examples/android/src/android/LuajView.java">examples/android/src/android/LuajView.java</a>
for an example that loads from the "res" Android project directory.
The ant build script is <a href="examples/android/build.xml">examples/android/build.xml</a>.
@@ -593,7 +590,7 @@ The following lua script will open a swing frame on Java SE:
See a longer sample in <em>examples/lua/swingapp.lua</em> for details, including a simple animation loop, rendering graphics, mouse and key handling, and image loading.
Or try running it using:
<pre>
java -cp lib/luaj-jse-3.0.1.jar lua examples/lua/swingapp.lua
java -cp luaj-jse-3.0.2.jar lua examples/lua/swingapp.lua
</pre>
<p>
@@ -842,7 +839,7 @@ For JSE projects, add this dependency for the luaj-jse jar:
&lt;dependency&gt;
&lt;groupId&gt;org.luaj&lt;/groupId&gt;
&lt;artifactId&gt;luaj-jse&lt;/artifactId&gt;
&lt;version&gt;3.0.1&lt;/version&gt;
&lt;version&gt;3.0.2&lt;/version&gt;
&lt;/dependency&gt;
</pre>
while for JME projects, use the luaj-jme jar:
@@ -850,7 +847,7 @@ while for JME projects, use the luaj-jme jar:
&lt;dependency&gt;
&lt;groupId&gt;org.luaj&lt;/groupId&gt;
&lt;artifactId&gt;luaj-jme&lt;/artifactId&gt;
&lt;version&gt;3.0.1&lt;/version&gt;
&lt;version&gt;3.0.2&lt;/version&gt;
&lt;/dependency&gt;
</pre>
@@ -880,7 +877,7 @@ Unit test scripts can be found in these locations
test/lua/*.lua
test/lua/errors/*.lua
test/lua/perf/*.lua
test/lua/luaj3.0.1-tests.zip
test/lua/luaj3.0.2-tests.zip
</pre>
<h2>Code coverage</h2>
@@ -930,7 +927,7 @@ Files are no longer hosted at LuaForge.
<li>Enhance javadoc, put it <a href="docs/api/index.html">in distribution</a>
and at <a href="http://luaj.sourceforge.net/api/2.0/index.html">http://luaj.sourceforge.net/api/2.0/</a></li>
<li>Major refactor of luajava type coercion logic, improve method selection.</li>
<li>Add lib/luaj-sources-2.0.2.jar for easier integration into an IDE such as Netbeans </li>
<li>Add luaj-sources-2.0.2.jar for easier integration into an IDE such as Netbeans </li>
<tr valign="top"><td>&nbsp;&nbsp;<b>2.0.3</b></td><td><ul>
<li>Improve coroutine state logic including let unreferenced coroutines be garbage collected </li>
@@ -1017,6 +1014,16 @@ and at <a href="http://luaj.sourceforge.net/api/2.0/index.html">http://luaj.sour
<li>Move online docs to <a href="http://luaj.org/luaj/3.0/api/index.html">http://luaj.org/luaj/3.0/api/</a></li>
<li>Fix os.time() conversions for pm times.</li>
<tr valign="top"><td>&nbsp;&nbsp;<b>3.0.2</b></td><td><ul>
<li>Fix JsePlatform.luaMain() to provide an "arg" table in the chunk's environment.</li>
<li>Let JsePlatform.luaMain() return values returned by the main chunk.</li>
<li>Add synchronization to CoerceJavaToLua.COERCIONS map.</li>
</ul></td></tr>
<tr valign="top"><td>&nbsp;&nbsp;<b>3.1.0</b></td><td><ul>
<li>Runtime upgrade to Java 8 or newer.</li>
<li>Feature: Support Java 21 VirtualThread</li>
</ul></td></tr>
</table></td></tr></table>

View File

@@ -14,7 +14,7 @@
<attribute name="jars" default="**/*.jar"/>
<sequential>
<mkdir dir="lib"/>
<get src="http://luaj.sourceforge.net/lib/@{zipname}.tar.gz"
<get src="https://luaj.sourceforge.net/lib/@{zipname}.tar.gz"
dest="lib/@{zipname}.tar.gz"/>
<gunzip src="lib/@{zipname}.tar.gz" dest="lib/@{zipname}.tar"/>
<untar src="lib/@{zipname}.tar" dest="lib" overwrite="true">

View File

@@ -81,18 +81,24 @@
<pathelement path="lib/midpapi20.jar"/>
<pathelement path="lib/mmapi.jar"/>
</path>
<javac destdir="build/jme/classes" encoding="utf-8" source="1.3" target="1.2" bootclasspathref="wtk-libs"
srcdir="build/jme/src"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.3" target="1.3"
<javac includeantruntime="false" destdir="build/jme/classes" encoding="utf-8" source="1.8" target="1.8"
debug="on"
srcdir="build/jme/src">
<classpath refid="wtk-libs"/>
</javac>
<javac includeantruntime="false" destdir="build/jse/classes" encoding="utf-8" source="1.8" target="1.8"
classpath="lib/bcel-5.2.jar"
debug="on"
srcdir="build/jse/src"
excludes="**/script/*,**/Lua2Java*,**/server/*,lua*"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.5" target="1.5"
<javac includeantruntime="false" destdir="build/jse/classes" encoding="utf-8" source="1.8" target="1.8"
classpath="build/jse/classes"
debug="on"
srcdir="build/jse/src"
includes="**/script/*,**/Lua2Java*,**/server/*"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.3" target="1.3"
<javac includeantruntime="false" destdir="build/jse/classes" encoding="utf-8" source="1.8" target="1.8"
classpath="build/jse/classes"
debug="on"
srcdir="build/jse/src"
includes="lua*"/>
</target>

View File

@@ -11,13 +11,13 @@ import org.luaj.vm2.lib.Bit32Lib;
import org.luaj.vm2.lib.CoroutineLib;
import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.TableLib;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.lib.jse.JseBaseLib;
import org.luaj.vm2.lib.jse.JseIoLib;
import org.luaj.vm2.lib.jse.JseMathLib;
import org.luaj.vm2.lib.jse.JseOsLib;
import org.luaj.vm2.lib.jse.JseStringLib;
import org.luaj.vm2.lib.jse.LuajavaLib;
/**
@@ -76,7 +76,7 @@ public class SampleApplet extends Applet implements ResourceFinder {
globals.load(new PackageLib());
globals.load(new Bit32Lib());
globals.load(new TableLib());
globals.load(new StringLib());
globals.load(new JseStringLib());
globals.load(new CoroutineLib());
globals.load(new JseMathLib());
globals.load(new JseIoLib());

View File

@@ -1,12 +1,26 @@
import org.luaj.vm2.*;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaBoolean;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.*;
import org.luaj.vm2.lib.jse.*;
import org.luaj.vm2.lib.Bit32Lib;
import org.luaj.vm2.lib.DebugLib;
import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.TableLib;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
import org.luaj.vm2.lib.jse.JseBaseLib;
import org.luaj.vm2.lib.jse.JseMathLib;
import org.luaj.vm2.lib.jse.JseStringLib;
/** Simple program that illustrates basic sand-boxing of client scripts
* in a server environment.
*
* <p>Although this sandboxing is done primarily in Java here, these
* <p>Although this sandboxing is done primarily in Java here, these
* same techniques should all be possible directly from lua using metatables,
* and examples are shown in examples/lua/samplesandboxed.lua.
*
@@ -29,7 +43,7 @@ public class SampleSandboxed {
server_globals = new Globals();
server_globals.load(new JseBaseLib());
server_globals.load(new PackageLib());
server_globals.load(new StringLib());
server_globals.load(new JseStringLib());
// To load scripts, we occasionally need a math library in addition to compiler support.
// To limit scripts using the debug library, they must be closures, so we only install LuaC.
@@ -47,7 +61,7 @@ public class SampleSandboxed {
runScriptInSandbox( "return getmetatable('abc').len" );
runScriptInSandbox( "return getmetatable('abc').__index" );
// Example user scripts that attempt rogue operations, and will fail.
// Example user scripts that attempt rogue operations, and will fail.
runScriptInSandbox( "return setmetatable('abc', {})" );
runScriptInSandbox( "getmetatable('abc').len = function() end" );
runScriptInSandbox( "getmetatable('abc').__index = {}" );
@@ -61,9 +75,9 @@ public class SampleSandboxed {
LuaValue.ADD, new TwoArgFunction() {
public LuaValue call(LuaValue x, LuaValue y) {
return LuaValue.valueOf(
(x == TRUE ? 1.0 : x.todouble()) +
(x == TRUE ? 1.0 : x.todouble()) +
(y == TRUE ? 1.0 : y.todouble()) );
}
}
},
}));
runScriptInSandbox( "return 5 + 6, 5 + true, false + 6" );
@@ -75,21 +89,21 @@ public class SampleSandboxed {
// that contain functions that can be abused.
static void runScriptInSandbox(String script) {
// Each script will have it's own set of globals, which should
// Each script will have it's own set of globals, which should
// prevent leakage between scripts running on the same server.
Globals user_globals = new Globals();
user_globals.load(new JseBaseLib());
user_globals.load(new PackageLib());
user_globals.load(new Bit32Lib());
user_globals.load(new TableLib());
user_globals.load(new StringLib());
user_globals.load(new JseStringLib());
user_globals.load(new JseMathLib());
// This library is dangerous as it gives unfettered access to the
// entire Java VM, so it's not suitable within this lightweight sandbox.
// entire Java VM, so it's not suitable within this lightweight sandbox.
// user_globals.load(new LuajavaLib());
// Starting coroutines in scripts will result in threads that are
// Starting coroutines in scripts will result in threads that are
// not under the server control, so this libary should probably remain out.
// user_globals.load(new CoroutineLib());
@@ -98,12 +112,12 @@ public class SampleSandboxed {
// user_globals.load(new JseIoLib());
// user_globals.load(new JseOsLib());
// Loading and compiling scripts from within scripts may also be
// Loading and compiling scripts from within scripts may also be
// prohibited, though in theory it should be fairly safe.
// LoadState.install(user_globals);
// LuaC.install(user_globals);
// The debug library must be loaded for hook functions to work, which
// The debug library must be loaded for hook functions to work, which
// allow us to limit scripts to run a certain number of instructions at a time.
// However we don't wish to expose the library in the user globals,
// so it is immediately removed from the user globals once created.
@@ -111,18 +125,18 @@ public class SampleSandboxed {
LuaValue sethook = user_globals.get("debug").get("sethook");
user_globals.set("debug", LuaValue.NIL);
// Set up the script to run in its own lua thread, which allows us
// Set up the script to run in its own lua thread, which allows us
// to set a hook function that limits the script to a specific number of cycles.
// Note that the environment is set to the user globals, even though the
// Note that the environment is set to the user globals, even though the
// compiling is done with the server globals.
LuaValue chunk = server_globals.load(script, "main", user_globals);
LuaThread thread = new LuaThread(user_globals, chunk);
// Set the hook function to immediately throw an Error, which will not be
// Set the hook function to immediately throw an Error, which will not be
// handled by any Lua code other than the coroutine.
LuaValue hookfunc = new ZeroArgFunction() {
public LuaValue call() {
// A simple lua error may be caught by the script, but a
// A simple lua error may be caught by the script, but a
// Java Error will pass through to top and stop the script.
throw new Error("Script overran resource limits.");
}

View File

@@ -31,10 +31,10 @@ import java.io.InputStream;
* <p>
* The {@link LoadState} class provides the default {@link Globals.Undumper}
* which is used to undump a string of bytes that represent a lua binary file
* using either the C-based lua compiler, or luaj's
* using either the C-based lua compiler, or luaj's
* {@link org.luaj.vm2.compiler.LuaC} compiler.
* <p>
* The canonical method to load and execute code is done
* The canonical method to load and execute code is done
* indirectly using the Globals:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
@@ -44,10 +44,10 @@ import java.io.InputStream;
* This should work regardless of which {@link Globals.Compiler} or {@link Globals.Undumper}
* have been installed.
* <p>
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform}
* to construct globals, the {@link LoadState} default undumper is installed
* as the default {@link Globals.Undumper}.
* as the default {@link Globals.Undumper}.
* <p>
*
* A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState} class
@@ -60,7 +60,7 @@ import java.io.InputStream;
* byte[] lua_binary_file_bytes = o.toByteArray();
* } </pre>
*
* The {@link LoadState}'s default undumper {@link #instance}
* The {@link LoadState}'s default undumper {@link #instance}
* may be used directly to undump these bytes:
* <pre> {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
@@ -99,7 +99,7 @@ public class LoadState {
/** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
// type constants
// type constants
public static final int LUA_TINT = (-2);
public static final int LUA_TNONE = (-1);
public static final int LUA_TNIL = 0;
@@ -155,7 +155,6 @@ public class LoadState {
private static final LuaValue[] NOVALUES = {};
private static final Prototype[] NOPROTOS = {};
private static final LocVars[] NOLOCVARS = {};
private static final LuaString[] NOSTRVALUES = {};
private static final Upvaldesc[] NOUPVALDESCS = {};
private static final int[] NOINTS = {};
@@ -168,17 +167,17 @@ public class LoadState {
}
/** Load a 4-byte int value from the input stream
* @return the int value laoded.
* @return the int value laoded.
**/
int loadInt() throws IOException {
is.readFully(buf,0,4);
return luacLittleEndian?
return luacLittleEndian?
(buf[3] << 24) | ((0xff & buf[2]) << 16) | ((0xff & buf[1]) << 8) | (0xff & buf[0]):
(buf[0] << 24) | ((0xff & buf[1]) << 16) | ((0xff & buf[2]) << 8) | (0xff & buf[3]);
}
/** Load an array of int values from the input stream
* @return the array of int values laoded.
* @return the array of int values laoded.
**/
int[] loadIntArray() throws IOException {
int n = loadInt();
@@ -192,7 +191,7 @@ public class LoadState {
is.readFully(buf,0,m);
int[] array = new int[n];
for ( int i=0, j=0; i<n; ++i, j+=4 )
array[i] = luacLittleEndian?
array[i] = luacLittleEndian?
(buf[j+3] << 24) | ((0xff & buf[j+2]) << 16) | ((0xff & buf[j+1]) << 8) | (0xff & buf[j+0]):
(buf[j+0] << 24) | ((0xff & buf[j+1]) << 16) | ((0xff & buf[j+2]) << 8) | (0xff & buf[j+3]);
@@ -200,7 +199,7 @@ public class LoadState {
}
/** Load a long value from the input stream
* @return the long value laoded.
* @return the long value laoded.
**/
long loadInt64() throws IOException {
int a,b;
@@ -215,7 +214,7 @@ public class LoadState {
}
/** Load a lua strin gvalue from the input stream
* @return the {@link LuaString} value laoded.
* @return the {@link LuaString} value laoded.
**/
LuaString loadString() throws IOException {
int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt();
@@ -227,7 +226,7 @@ public class LoadState {
}
/**
* Convert bits in a long value to a {@link LuaValue}.
* Convert bits in a long value to a {@link LuaValue}.
* @param bits long value containing the bits
* @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds to the bits provided.
*/
@@ -251,7 +250,7 @@ public class LoadState {
return LuaValue.valueOf( Double.longBitsToDouble(bits) );
}
/**
/**
* Load a number from a binary chunk
* @return the {@link LuaValue} loaded
* @throws IOException if an i/o exception occurs
@@ -335,7 +334,7 @@ public class LoadState {
f.upvalues[i].name = loadString();
}
/**
/**
* Load a function prototype from the input stream
* @param p name of the source
* @return {@link Prototype} instance that was loaded
@@ -366,8 +365,8 @@ public class LoadState {
}
/**
* Load the lua chunk header values.
* @throws IOException if an i/o exception occurs.
* Load the lua chunk header values.
* @throws IOException if an i/o exception occurs.
*/
public void loadHeader() throws IOException {
luacVersion = is.readByte();
@@ -392,7 +391,7 @@ public class LoadState {
*/
public static Prototype undump(InputStream stream, String chunkname) throws IOException {
// check rest of signature
if ( stream.read() != LUA_SIGNATURE[0]
if ( stream.read() != LUA_SIGNATURE[0]
|| stream.read() != LUA_SIGNATURE[1]
|| stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[3] )

View File

@@ -23,10 +23,10 @@ package org.luaj.vm2;
/**
* Constants for lua limits and opcodes.
* Constants for lua limits and opcodes.
* <p>
* This is a direct translation of C lua distribution header file constants
* for bytecode creation and processing.
* for bytecode creation and processing.
*/
public class Lua {
/** version is supplied by ant build task */
@@ -80,7 +80,6 @@ public class Lua {
public static final int POS_Bx = POS_C;
public static final int POS_Ax = POS_A;
public static final int MAX_OP = ((1<<SIZE_OP)-1);
public static final int MAXARG_A = ((1<<SIZE_A)-1);
public static final int MAXARG_B = ((1<<SIZE_B)-1);
@@ -89,17 +88,18 @@ public class Lua {
public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */
public static final int MAXARG_Ax = ((1<<SIZE_Ax)-1);
public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP;
public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A;
public static final int MASK_B = ((1<<SIZE_B)-1)<<POS_B;
public static final int MASK_C = ((1<<SIZE_C)-1)<<POS_C;
public static final int MASK_Bx = ((1<<SIZE_Bx)-1)<<POS_Bx;
public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP;
public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A;
public static final int MASK_B = ((1<<SIZE_B)-1)<<POS_B;
public static final int MASK_C = ((1<<SIZE_C)-1)<<POS_C;
public static final int MASK_Bx = ((1<<SIZE_Bx)-1)<<POS_Bx;
public static final int MASK_Ax = ((1<<SIZE_Ax)-1)<<POS_Ax;
public static final int MASK_NOT_OP = ~MASK_OP;
public static final int MASK_NOT_A = ~MASK_A;
public static final int MASK_NOT_B = ~MASK_B;
public static final int MASK_NOT_C = ~MASK_C;
public static final int MASK_NOT_Bx = ~MASK_Bx;
public static final int MASK_NOT_OP = ~MASK_OP;
public static final int MASK_NOT_A = ~MASK_A;
public static final int MASK_NOT_B = ~MASK_B;
public static final int MASK_NOT_C = ~MASK_C;
public static final int MASK_NOT_Bx = ~MASK_Bx;
/*
** the following macros help to manipulate instructions
@@ -208,13 +208,13 @@ public class Lua {
public static final int OP_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */
public static final int OP_JMP = 23; /* sBx pc+=sBx */
public static final int OP_JMP = 23; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_TAILCALL = 30; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
@@ -225,7 +225,7 @@ public class Lua {
public static final int OP_FORPREP = 33; /* A sBx R(A)-=R(A+2); pc+=sBx */
public static final int OP_TFORCALL = 34; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
public static final int OP_TFORLOOP = 35; /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
public static final int OP_TFORLOOP = 35; /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
@@ -237,11 +237,11 @@ public class Lua {
public static final int NUM_OPCODES = OP_EXTRAARG + 1;
/* pseudo-opcodes used in parsing only. */
public static final int OP_GT = 63; // >
public static final int OP_GT = 63; // >
public static final int OP_GE = 62; // >=
public static final int OP_NEQ = 61; // ~=
public static final int OP_AND = 60; // and
public static final int OP_OR = 59; // or
public static final int OP_NEQ = 61; // ~=
public static final int OP_AND = 60; // and
public static final int OP_OR = 59; // or
/*===========================================================================
Notes:
@@ -271,7 +271,7 @@ public class Lua {
** bits 4-5: B arg mode
** bit 6: instruction set register A
** bit 7: operator is a test
*/
*/
public static final int OpArgN = 0; /* argument is not used */
public static final int OpArgU = 1; /* argument is used */
@@ -353,7 +353,7 @@ public class Lua {
source = "[string \""+source;
end = "\"]";
}
int n = source.length() + end.length();
int n = source.length() + end.length();
if ( n > MAXSRC )
source = source.substring(0,MAXSRC-end.length()-3) + "...";
return source + end;

View File

@@ -21,23 +21,25 @@
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.lib.DebugLib.CallFrame;
/**
* Extension of {@link LuaFunction} which executes lua bytecode.
* Extension of {@link LuaFunction} which executes lua bytecode.
* <p>
* A {@link LuaClosure} is a combination of a {@link Prototype}
* A {@link LuaClosure} is a combination of a {@link Prototype}
* and a {@link LuaValue} to use as an environment for execution.
* Normally the {@link LuaValue} is a {@link Globals} in which case the environment
* will contain standard lua libraries.
* will contain standard lua libraries.
*
* <p>
* There are three main ways {@link LuaClosure} instances are created:
* <ul>
* <ul>
* <li>Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}</li>
* <li>Construct it indirectly by loading a chunk via {@link Globals#load(java.io.Reader, String)}
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode processing
* </ul>
* <p>
* To construct it directly, the {@link Prototype} is typically created via a compiler such as
* To construct it directly, the {@link Prototype} is typically created via a compiler such as
* {@link org.luaj.vm2.compiler.LuaC}:
* <pre> {@code
* String script = "print( 'hello, world' )";
@@ -46,9 +48,9 @@ package org.luaj.vm2;
* LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }</pre>
* }</pre>
* <p>
* To construct it indirectly, the {@link Globals#load(java.io.Reader, String)} method may be used:
* To construct it indirectly, the {@link Globals#load(java.io.Reader, String)} method may be used:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script");
@@ -56,11 +58,11 @@ package org.luaj.vm2;
* c.call();
* }</pre>
* <p>
* In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode
* In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode
* compiling using LuaJC is installed, because no LuaClosure is created in that case
* and the value returned is a {@link LuaFunction} but not a {@link LuaClosure}.
* <p>
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a {@link LuaValue},
* <p>
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a {@link LuaValue},
* all the value operations can be used directly such as:
* <ul>
* <li>{@link LuaValue#call()}</li>
@@ -71,7 +73,7 @@ package org.luaj.vm2;
* <li>{@link LuaValue#method(String,LuaValue)}</li>
* <li>{@link LuaValue#invokemethod(String)}</li>
* <li>{@link LuaValue#invokemethod(String,Varargs)}</li>
* <li> ...</li>
* <li> ...</li>
* </ul>
* @see LuaValue
* @see LuaFunction
@@ -92,19 +94,24 @@ public class LuaClosure extends LuaFunction {
/** Create a closure around a Prototype with a specific environment.
* If the prototype has upvalues, the environment will be written into the first upvalue.
* @param p the Prototype to construct this Closure for.
* @param p the Prototype to construct this Closure for.
* @param env the environment to associate with the closure.
*/
public LuaClosure(Prototype p, LuaValue env) {
this.p = p;
this.initupvalue1(env);
globals = env instanceof Globals? (Globals) env: null;
}
public void initupvalue1(LuaValue env) {
if (p.upvalues == null || p.upvalues.length == 0)
this.upValues = NOUPVALUES;
else {
this.upValues = new UpValue[p.upvalues.length];
this.upValues[0] = new UpValue(new LuaValue[] {env}, 0);
}
globals = env instanceof Globals? (Globals) env: null;
}
public boolean isclosure() {
return true;
@@ -118,26 +125,24 @@ public class LuaClosure extends LuaFunction {
return this;
}
public LuaValue getmetatable() {
return s_metatable;
}
public String tojstring() {
return "function: " + p.toString();
}
private LuaValue[] getNewStack() {
int max = p.maxstacksize;
LuaValue[] stack = new LuaValue[max];
System.arraycopy(NILS, 0, stack, 0, max);
return stack;
}
public final LuaValue call() {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 0; i < p.numparams; ++i )
stack[i] = NIL;
LuaValue[] stack = getNewStack();
return execute(stack,NONE).arg1();
}
public final LuaValue call(LuaValue arg) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
System.arraycopy(NILS, 0, stack, 0, p.maxstacksize);
for (int i = 1; i < p.numparams; ++i )
stack[i] = NIL;
LuaValue[] stack = getNewStack();
switch ( p.numparams ) {
default: stack[0]=arg; return execute(stack,NONE).arg1();
case 0: return execute(stack,arg).arg1();
@@ -145,9 +150,7 @@ public class LuaClosure extends LuaFunction {
}
public final LuaValue call(LuaValue arg1, LuaValue arg2) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 2; i < p.numparams; ++i )
stack[i] = NIL;
LuaValue[] stack = getNewStack();
switch ( p.numparams ) {
default: stack[0]=arg1; stack[1]=arg2; return execute(stack,NONE).arg1();
case 1: stack[0]=arg1; return execute(stack,arg2).arg1();
@@ -156,9 +159,7 @@ public class LuaClosure extends LuaFunction {
}
public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 3; i < p.numparams; ++i )
stack[i] = NIL;
LuaValue[] stack = getNewStack();
switch ( p.numparams ) {
default: stack[0]=arg1; stack[1]=arg2; stack[2]=arg3; return execute(stack,NONE).arg1();
case 2: stack[0]=arg1; stack[1]=arg2; return execute(stack,arg3).arg1();
@@ -172,9 +173,9 @@ public class LuaClosure extends LuaFunction {
}
public final Varargs onInvoke(Varargs varargs) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
LuaValue[] stack = getNewStack();
for ( int i=0; i<p.numparams; i++ )
stack[i] = varargs.arg(i+1);
stack[i] = varargs.arg(i+1);
return execute(stack,p.is_vararg!=0? varargs.subargs(p.numparams+1): NONE);
}
@@ -192,13 +193,13 @@ public class LuaClosure extends LuaFunction {
// allow for debug hooks
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall( this, varargs, stack );
globals.debuglib.onCall( this, varargs, stack );
// process instructions
try {
for (; true; ++pc) {
if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction( pc, v, top );
globals.debuglib.onInstruction( pc, v, top );
// pull out instruction
i = code[pc];
@@ -215,6 +216,17 @@ public class LuaClosure extends LuaFunction {
stack[a] = k[i>>>14];
continue;
case Lua.OP_LOADKX:/* A R(A) := Kst(extra arg) */
++pc;
i = code[pc];
if ((i & 0x3f) != Lua.OP_EXTRAARG) {
int op = i & 0x3f;
throw new LuaError("OP_EXTRAARG expected after OP_LOADKX, got " +
(op < Print.OPNAMES.length - 1 ? Print.OPNAMES[op] : "UNKNOWN_OP_" + op));
}
stack[a] = k[i>>>6];
continue;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
stack[a] = (i>>>23!=0)? LuaValue.TRUE: LuaValue.FALSE;
if ((i&(0x1ff<<14)) != 0)
@@ -301,8 +313,8 @@ public class LuaClosure extends LuaFunction {
{
if ( c > b+1 ) {
Buffer sb = stack[c].buffer();
while ( --c>=b )
sb = stack[c].concat(sb);
while ( --c>=b )
sb.concatTo(stack[c]);
stack[a] = sb.value();
} else {
stack[a] = stack[c-1].concat(stack[c]);
@@ -310,7 +322,7 @@ public class LuaClosure extends LuaFunction {
}
continue;
case Lua.OP_JMP: /* sBx pc+=sBx */
case Lua.OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
pc += (i>>>14)-0x1ffff;
if (a > 0) {
for (--a, b = openups.length; --b>=0; )
@@ -322,31 +334,31 @@ public class LuaClosure extends LuaFunction {
continue;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).eq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).eq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
++pc;
continue;
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lt_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lt_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
++pc;
continue;
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lteq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lteq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
++pc;
continue;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
if ( stack[a].toboolean() != ((i&(0x1ff<<14))!=0) )
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
if ( stack[a].toboolean() != ((i&(0x1ff<<14))!=0) )
++pc;
continue;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
/* note: doc appears to be reversed */
if ( (o=stack[i>>>23]).toboolean() != ((i&(0x1ff<<14))!=0) )
if ( (o=stack[i>>>23]).toboolean() != ((i&(0x1ff<<14))!=0) )
++pc;
else
stack[a] = o; // TODO: should be sBx?
stack[a] = o; // TODO: should be sBx?
continue;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
@@ -364,9 +376,9 @@ public class LuaClosure extends LuaFunction {
default:
b = i>>>23;
c = (i>>14)&0x1ff;
v = stack[a].invoke(b>0?
v = stack[a].invoke(b>0?
varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top
varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top
if ( c > 0 ) {
v.copyto(stack, a, c-1);
v = NONE;
@@ -385,18 +397,18 @@ public class LuaClosure extends LuaFunction {
case (4<<Lua.POS_B): return new TailcallVarargs(stack[a], varargsOf(stack[a+1],stack[a+2],stack[a+3]));
default:
b = i>>>23;
v = b>0?
v = b>0?
varargsOf(stack,a+1,b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top
varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top
return new TailcallVarargs( stack[a], v );
}
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
b = i>>>23;
switch ( b ) {
case 0: return varargsOf(stack, a, top-v.narg()-a, v);
case 0: return varargsOf(stack, a, top-v.narg()-a, v);
case 1: return NONE;
case 2: return stack[a];
case 2: return stack[a];
default:
return varargsOf(stack, a, b-1);
}
@@ -405,7 +417,7 @@ public class LuaClosure extends LuaFunction {
{
LuaValue limit = stack[a + 1];
LuaValue step = stack[a + 2];
LuaValue idx = step.add(stack[a]);
LuaValue idx = stack[a].add(step);
if (step.gt_b(0)? idx.lteq_b(limit): idx.gteq_b(limit)) {
stack[a] = idx;
stack[a + 3] = idx;
@@ -449,7 +461,7 @@ public class LuaClosure extends LuaFunction {
o = stack[a];
if ( (b=i>>>23) == 0 ) {
b = top - a - 1;
int m = b - v.narg();
int m = b - v.narg();
int j=1;
for ( ;j<=m; j++ )
o.set(offset+j, stack[a + j]);
@@ -483,11 +495,11 @@ public class LuaClosure extends LuaFunction {
if ( b == 0 ) {
top = a + (b = varargs.narg());
v = varargs;
} else {
} else {
for ( int j=1; j<b; ++j )
stack[a+j-1] = varargs.arg(j);
}
continue;
continue;
case Lua.OP_EXTRAARG:
throw new java.lang.IllegalArgumentException("Uexecutable opcode: OP_EXTRAARG");
@@ -516,7 +528,7 @@ public class LuaClosure extends LuaFunction {
/**
* Run the error hook if there is one
* @param msg the message to use in error hook processing.
* @param msg the message to use in error hook processing.
* */
String errorHook(String msg, int level) {
if (globals == null ) return msg;
@@ -537,8 +549,24 @@ public class LuaClosure extends LuaFunction {
}
private void processErrorHooks(LuaError le, Prototype p, int pc) {
le.fileline = (p.source != null? p.source.tojstring(): "?") + ":"
+ (p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length? String.valueOf(p.lineinfo[pc]): "?");
String file = "?";
int line = -1;
{
CallFrame frame = null;
if (globals != null && globals.debuglib != null) {
frame = globals.debuglib.getCallFrame(le.level);
if (frame != null) {
String src = frame.shortsource();
file = src != null ? src : "?";
line = frame.currentline();
}
}
if (frame == null) {
file = p.source != null? p.source.tojstring(): "?";
line = p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length ? p.lineinfo[pc] : -1;
}
}
le.fileline = file + ":" + line;
le.traceback = errorHook(le.getMessage(), le.level);
}

View File

@@ -24,18 +24,18 @@ package org.luaj.vm2;
import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java double as its value.
* Extension of {@link LuaNumber} which can hold a Java double as its value.
* <p>
* These instance are not instantiated directly by clients, but indirectly
* These instance are not instantiated directly by clients, but indirectly
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)}
* functions. This ensures that values which can be represented as int
* are wrapped in {@link LuaInteger} instead of {@link LuaDouble}.
* functions. This ensures that values which can be represented as int
* are wrapped in {@link LuaInteger} instead of {@link LuaDouble}.
* <p>
* Almost all API's implemented in LuaDouble are defined and documented in {@link LuaValue}.
* <p>
* However the constants {@link #NAN}, {@link #POSINF}, {@link #NEGINF},
* {@link #JSTR_NAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be useful
* when dealing with Nan or Infinite values.
* {@link #JSTR_NAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be useful
* when dealing with Nan or Infinite values.
* <p>
* LuaDouble also defines functions for handling the unique math rules of lua devision and modulo in
* <ul>
@@ -43,7 +43,7 @@ import org.luaj.vm2.lib.MathLib;
* <li>{@link #ddiv_d(double, double)}</li>
* <li>{@link #dmod(double, double)}</li>
* <li>{@link #dmod_d(double, double)}</li>
* </ul>
* </ul>
* <p>
* @see LuaValue
* @see LuaNumber
@@ -90,7 +90,7 @@ public class LuaDouble extends LuaNumber {
}
public boolean islong() {
return v == (long) v;
return v == (long) v;
}
public byte tobyte() { return (byte) (long) v; }
@@ -151,12 +151,12 @@ public class LuaDouble extends LuaNumber {
/** Divide two double numbers according to lua math, and return a {@link LuaValue} result.
* @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division.
* @return {@link LuaValue} for the result of the division,
* @return {@link LuaValue} for the result of the division,
* taking into account positive and negiative infinity, and Nan
* @see #ddiv_d(double, double)
* @see #ddiv_d(double, double)
*/
public static LuaValue ddiv(double lhs, double rhs) {
return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF;
return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF;
}
/** Divide two double numbers according to lua math, and return a double result.
@@ -166,54 +166,68 @@ public class LuaDouble extends LuaNumber {
* @see #ddiv(double, double)
*/
public static double ddiv_d(double lhs, double rhs) {
return rhs!=0? lhs / rhs: lhs>0? Double.POSITIVE_INFINITY: lhs==0? Double.NaN: Double.NEGATIVE_INFINITY;
return rhs!=0? lhs / rhs: lhs>0? Double.POSITIVE_INFINITY: lhs==0? Double.NaN: Double.NEGATIVE_INFINITY;
}
/** Take modulo double numbers according to lua math, and return a {@link LuaValue} result.
* @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo.
* @return {@link LuaValue} for the result of the modulo,
* @return {@link LuaValue} for the result of the modulo,
* using lua's rules for modulo
* @see #dmod_d(double, double)
* @see #dmod_d(double, double)
*/
public static LuaValue dmod(double lhs, double rhs) {
return rhs!=0? valueOf( lhs-rhs*Math.floor(lhs/rhs) ): NAN;
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return NAN;
if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? POSINF : valueOf(lhs);
}
if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? NEGINF : valueOf(lhs);
}
return valueOf( lhs-rhs*Math.floor(lhs/rhs) );
}
/** Take modulo for double numbers according to lua math, and return a double result.
* @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo.
* @return double value for the result of the modulo,
* @return double value for the result of the modulo,
* using lua's rules for modulo
* @see #dmod(double, double)
*/
public static double dmod_d(double lhs, double rhs) {
return rhs!=0? lhs-rhs*Math.floor(lhs/rhs): Double.NaN;
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return Double.NaN;
if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? Double.POSITIVE_INFINITY : lhs;
}
if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? Double.NEGATIVE_INFINITY : lhs;
}
return lhs-rhs*Math.floor(lhs/rhs);
}
// relational operators
public LuaValue lt( LuaValue rhs ) { return rhs.gt_b(v)? LuaValue.TRUE: FALSE; }
public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs.gt_b(v); }
public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; }
public boolean lt_b( double rhs ) { return v < rhs; }
public LuaValue lteq( LuaValue rhs ) { return rhs.gteq_b(v)? LuaValue.TRUE: FALSE; }
public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; }
public boolean lteq_b( LuaValue rhs ) { return rhs.gteq_b(v); }
public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
public LuaValue gt( LuaValue rhs ) { return rhs.lt_b(v)? LuaValue.TRUE: FALSE; }
public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); }
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; }
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; }
public boolean gt_b( LuaValue rhs ) { return rhs.lt_b(v); }
public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; }
public boolean gt_b( double rhs ) { return v > rhs; }
public LuaValue gteq( LuaValue rhs ) { return rhs.lteq_b(v)? LuaValue.TRUE: FALSE; }
public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b( LuaValue rhs ) { return rhs.lteq_b(v); }
public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
@@ -222,17 +236,17 @@ public class LuaDouble extends LuaNumber {
public String tojstring() {
/*
if ( v == 0.0 ) { // never occurs in J2me
if ( v == 0.0 ) { // never occurs in J2me
long bits = Double.doubleToLongBits( v );
return ( bits >> 63 == 0 ) ? "0" : "-0";
}
*/
long l = (long) v;
if ( l == v )
if ( l == v )
return Long.toString(l);
if ( Double.isNaN(v) )
return JSTR_NAN;
if ( Double.isInfinite(v) )
if ( Double.isInfinite(v) )
return (v<0? JSTR_NEGINF: JSTR_POSINF);
return Float.toString((float)v);
}
@@ -254,11 +268,11 @@ public class LuaDouble extends LuaNumber {
}
public LuaNumber optnumber(LuaNumber defval) {
return this;
return this;
}
public boolean isnumber() {
return true;
return true;
}
public boolean isstring() {
@@ -273,14 +287,14 @@ public class LuaDouble extends LuaNumber {
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; }
public String checkjstring() {
public String checkjstring() {
return tojstring();
}
public LuaString checkstring() {
public LuaString checkstring() {
return LuaString.valueOf(tojstring());
}
public boolean isvalidkey() {
return !Double.isNaN(v);
}
}
}

View File

@@ -22,14 +22,14 @@
package org.luaj.vm2;
/**
* Base class for functions implemented in Java.
/**
* Base class for functions implemented in Java.
* <p>
* Direct subclass include {@link org.luaj.vm2.lib.LibFunction}
* which is the base class for
* all built-in library functions coded in Java,
* and {@link LuaClosure}, which represents a lua closure
* whose bytecode is interpreted when the function is invoked.
* Direct subclass include {@link org.luaj.vm2.lib.LibFunction}
* which is the base class for
* all built-in library functions coded in Java,
* and {@link LuaClosure}, which represents a lua closure
* whose bytecode is interpreted when the function is invoked.
* @see LuaValue
* @see LuaClosure
* @see org.luaj.vm2.lib.LibFunction
@@ -57,11 +57,11 @@ public class LuaFunction extends LuaValue {
}
public LuaFunction optfunction(LuaFunction defval) {
return this;
return this;
}
public LuaValue getmetatable() {
return s_metatable;
public LuaValue getmetatable() {
return s_metatable;
}
public String tojstring() {
@@ -72,12 +72,15 @@ public class LuaFunction extends LuaValue {
return valueOf(tojstring());
}
/** Return the last part of the class name, to be used as a function name in tojstring and elsewhere.
/** Return the last part of the class name, to be used as a function name in tojstring and elsewhere.
* @return String naming the last part of the class name after the last dot (.) or dollar sign ($).
* If the first character is '_', it is skipped.
*/
public String classnamestub() {
String s = getClass().getName();
return s.substring(Math.max(s.lastIndexOf('.'),s.lastIndexOf('$'))+1);
int offset = Math.max(s.lastIndexOf('.'), s.lastIndexOf('$')) + 1;
if (s.charAt(offset) == '_') offset++;
return s.substring(offset);
}
/** Return a human-readable name for this function. Returns the last part of the class name by default.

View File

@@ -24,14 +24,14 @@ package org.luaj.vm2;
import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java int as its value.
* Extension of {@link LuaNumber} which can hold a Java int as its value.
* <p>
* These instance are not instantiated directly by clients, but indirectly
* These instance are not instantiated directly by clients, but indirectly
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)}
* functions. This ensures that policies regarding pooling of instances are
* encapsulated.
* functions. This ensures that policies regarding pooling of instances are
* encapsulated.
* <p>
* There are no API's specific to LuaInteger that are useful beyond what is already
* There are no API's specific to LuaInteger that are useful beyond what is already
* exposed in {@link LuaValue}.
*
* @see LuaValue
@@ -61,16 +61,16 @@ public class LuaInteger extends LuaNumber {
*/
public static LuaNumber valueOf(long l) {
int i = (int) l;
return l==i? (i<=255 && i>=-256? intValues[i+256]:
(LuaNumber) new LuaInteger(i)):
return l==i? (i<=255 && i>=-256? intValues[i+256]:
(LuaNumber) new LuaInteger(i)):
(LuaNumber) LuaDouble.valueOf(l);
}
/** The value being held by this instance. */
public final int v;
/**
* Package protected constructor.
/**
* Package protected constructor.
* @see LuaValue#valueOf(int)
**/
LuaInteger(int i) {
@@ -103,15 +103,15 @@ public class LuaInteger extends LuaNumber {
}
public LuaString optstring(LuaString defval) {
return LuaString.valueOf(Integer.toString(v));
return LuaString.valueOf(Integer.toString(v));
}
public LuaValue tostring() {
return LuaString.valueOf(Integer.toString(v));
return LuaString.valueOf(Integer.toString(v));
}
public String optjstring(String defval) {
return Integer.toString(v);
public String optjstring(String defval) {
return Integer.toString(v);
}
public LuaInteger checkinteger() {
@@ -172,48 +172,48 @@ public class LuaInteger extends LuaNumber {
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
// relational operators
public LuaValue lt( LuaValue rhs ) { return rhs.gt_b(v)? TRUE: FALSE; }
public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs.gt_b(v); }
public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; }
public boolean lt_b( double rhs ) { return v < rhs; }
public LuaValue lteq( LuaValue rhs ) { return rhs.gteq_b(v)? TRUE: FALSE; }
public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; }
public boolean lteq_b( LuaValue rhs ) { return rhs.gteq_b(v); }
public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
public LuaValue gt( LuaValue rhs ) { return rhs.lt_b(v)? TRUE: FALSE; }
public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); }
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; }
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; }
public boolean gt_b( LuaValue rhs ) { return rhs.lt_b(v); }
public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; }
public boolean gt_b( double rhs ) { return v > rhs; }
public LuaValue gteq( LuaValue rhs ) { return rhs.lteq_b(v)? TRUE: FALSE; }
public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b( LuaValue rhs ) { return rhs.lteq_b(v); }
public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
// string comparison
public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; }
public int checkint() {
return v;
public int checkint() {
return v;
}
public long checklong() {
return v;
return v;
}
public double checkdouble() {
return v;
}
public String checkjstring() {
return String.valueOf(v);
public String checkjstring() {
return String.valueOf(v);
}
public LuaString checkstring() {
return valueOf( String.valueOf(v) );
public LuaString checkstring() {
return valueOf( String.valueOf(v) );
}
}

View File

@@ -31,28 +31,28 @@ import java.io.PrintStream;
import org.luaj.vm2.lib.MathLib;
/**
* Subclass of {@link LuaValue} for representing lua strings.
* Subclass of {@link LuaValue} for representing lua strings.
* <p>
* Because lua string values are more nearly sequences of bytes than
* Because lua string values are more nearly sequences of bytes than
* sequences of characters or unicode code points, the {@link LuaString}
* implementation holds the string value in an internal byte array.
* implementation holds the string value in an internal byte array.
* <p>
* {@link LuaString} values are not considered mutable once constructed,
* {@link LuaString} values are not considered mutable once constructed,
* so multiple {@link LuaString} values can chare a single byte array.
* <p>
* Currently {@link LuaString}s are pooled via a centrally managed weak table.
* To ensure that as many string values as possible take advantage of this,
* Constructors are not exposed directly. As with number, booleans, and nil,
* To ensure that as many string values as possible take advantage of this,
* Constructors are not exposed directly. As with number, booleans, and nil,
* instance construction should be via {@link LuaValue#valueOf(byte[])} or similar API.
* <p>
* Because of this pooling, users of LuaString <em>must not directly alter the
* bytes in a LuaString</em>, or undefined behavior will result.
* <p>
* When Java Strings are used to initialize {@link LuaString} data, the UTF8 encoding is assumed.
* The functions
* When Java Strings are used to initialize {@link LuaString} data, the UTF8 encoding is assumed.
* The functions
* {@link #lengthAsUtf8(char[])},
* {@link #encodeToUtf8(char[], int, byte[], int)}, and
* {@link #decodeAsUtf8(byte[], int, int)}
* {@link #encodeToUtf8(char[], int, byte[], int)}, and
* {@link #decodeAsUtf8(byte[], int, int)}
* are used to convert back and forth between UTF8 byte arrays and character arrays.
*
* @see LuaValue
@@ -63,15 +63,15 @@ public class LuaString extends LuaValue {
/** The singleton instance for string metatables that forwards to the string functions.
* Typically, this is set to the string metatable as a side effect of loading the string
* library, and is read-write to provide flexible behavior by default. When used in a
* library, and is read-write to provide flexible behavior by default. When used in a
* server environment where there may be roge scripts, this should be replaced with a
* read-only table since it is shared across all lua code in this Java VM.
*/
public static LuaValue s_metatable;
/** The bytes for the string. These <em><b>must not be mutated directly</b></em> because
* the backing may be shared by multiple LuaStrings, and the hash code is
* computed only at construction time.
* the backing may be shared by multiple LuaStrings, and the hash code is
* computed only at construction time.
* It is exposed only for performance and legacy reasons. */
public final byte[] m_bytes;
@@ -84,29 +84,29 @@ public class LuaString extends LuaValue {
/** The hashcode for this string. Computed at construct time. */
private final int m_hashcode;
/** Size of cache of recent short strings. This is the maximum number of LuaStrings that
/** Size of cache of recent short strings. This is the maximum number of LuaStrings that
* will be retained in the cache of recent short strings. Exposed to package for testing. */
static final int RECENT_STRINGS_CACHE_SIZE = 128;
/** Maximum length of a string to be considered for recent short strings caching.
/** Maximum length of a string to be considered for recent short strings caching.
* This effectively limits the total memory that can be spent on the recent strings cache,
* because no LuaString whose backing exceeds this length will be put into the cache.
* because no LuaString whose backing exceeds this length will be put into the cache.
* Exposed to package for testing. */
static final int RECENT_STRINGS_MAX_LENGTH = 32;
/** Simple cache of recently created strings that are short.
* This is simply a list of strings, indexed by their hash codes modulo the cache size
* that have been recently constructed. If a string is being constructed frequently
* from different contexts, it will generally show up as a cache hit and resolve
/** Simple cache of recently created strings that are short.
* This is simply a list of strings, indexed by their hash codes modulo the cache size
* that have been recently constructed. If a string is being constructed frequently
* from different contexts, it will generally show up as a cache hit and resolve
* to the same value. */
private static final class RecentShortStrings {
private static final LuaString recent_short_strings[] =
private static final LuaString recent_short_strings[] =
new LuaString[RECENT_STRINGS_CACHE_SIZE];
}
/**
* Get a {@link LuaString} instance whose bytes match
* the supplied Java String using the UTF8 encoding.
* Get a {@link LuaString} instance whose bytes match
* the supplied Java String using the UTF8 encoding.
* @param string Java String containing characters to encode as UTF8
* @return {@link LuaString} with UTF8 bytes corresponding to the supplied String
*/
@@ -120,7 +120,7 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} for a portion of a byte array.
* <p>
* The array is first be used as the backing for this object, so clients must not change contents.
* If the supplied value for 'len' is more than half the length of the container, the
* If the supplied value for 'len' is more than half the length of the container, the
* supplied byte array will be used as the backing, otherwise the bytes will be copied to a
* new byte array, and cache lookup may be performed.
* <p>
@@ -144,7 +144,7 @@ public class LuaString extends LuaValue {
/** Construct a new LuaString using a copy of the bytes array supplied */
private static LuaString valueFromCopy(byte[] bytes, int off, int len) {
final byte[] copy = new byte[len];
for (int i=0; i<len; ++i) copy[i] = bytes[off+i];
System.arraycopy(bytes, off, copy, 0, len);
return new LuaString(copy, 0, len);
}
@@ -172,11 +172,11 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} using the supplied characters as byte values.
* <p>
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* <p>
* This is most useful for constructing byte sequences that do not conform to UTF8.
* @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
* @return {@link LuaString} wrapping a copy of the byte buffer
* This is most useful for constructing byte sequences that do not conform to UTF8.
* @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
* @return {@link LuaString} wrapping a copy of the byte buffer
*/
public static LuaString valueOf(char[] bytes) {
return valueOf(bytes, 0, bytes.length);
@@ -184,11 +184,11 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} using the supplied characters as byte values.
* <p>
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* <p>
* This is most useful for constructing byte sequences that do not conform to UTF8.
* @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
* @return {@link LuaString} wrapping a copy of the byte buffer
* This is most useful for constructing byte sequences that do not conform to UTF8.
* @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
* @return {@link LuaString} wrapping a copy of the byte buffer
*/
public static LuaString valueOf(char[] bytes, int off, int len) {
byte[] b = new byte[len];
@@ -215,7 +215,7 @@ public class LuaString extends LuaValue {
* The LuaString returned will either be a new LuaString containing the byte array,
* or be an existing LuaString used already having the same value.
* <p>
* The caller must not mutate the contents of the byte array after this call, as
* The caller must not mutate the contents of the byte array after this call, as
* it may be used elsewhere due to recent short string caching.
* @param bytes byte buffer
* @return {@link LuaString} wrapping the byte buffer
@@ -241,11 +241,11 @@ public class LuaString extends LuaValue {
}
public boolean isstring() {
return true;
return true;
}
public LuaValue getmetatable() {
return s_metatable;
public LuaValue getmetatable() {
return s_metatable;
}
public int type() {
@@ -289,20 +289,20 @@ public class LuaString extends LuaValue {
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs, checkarith()); }
// relational operators, these only work with other strings
public LuaValue lt( LuaValue rhs ) { return rhs.strcmp(this)>0? LuaValue.TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs.strcmp(this)>0; }
public LuaValue lt( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)>0? LuaValue.TRUE: FALSE) : super.lt(rhs); }
public boolean lt_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)>0 : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean lt_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
public LuaValue lteq( LuaValue rhs ) { return rhs.strcmp(this)>=0? LuaValue.TRUE: FALSE; }
public boolean lteq_b( LuaValue rhs ) { return rhs.strcmp(this)>=0; }
public LuaValue lteq( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)>=0? LuaValue.TRUE: FALSE) : super.lteq(rhs); }
public boolean lteq_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)>=0 : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean lteq_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
public LuaValue gt( LuaValue rhs ) { return rhs.strcmp(this)<0? LuaValue.TRUE: FALSE; }
public boolean gt_b( LuaValue rhs ) { return rhs.strcmp(this)<0; }
public LuaValue gt( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)<0? LuaValue.TRUE: FALSE) : super.gt(rhs); }
public boolean gt_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)<0 : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean gt_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
public LuaValue gteq( LuaValue rhs ) { return rhs.strcmp(this)<=0? LuaValue.TRUE: FALSE; }
public boolean gteq_b( LuaValue rhs ) { return rhs.strcmp(this)<=0; }
public LuaValue gteq( LuaValue rhs ) { return rhs.isstring() ? (rhs.strcmp(this)<=0? LuaValue.TRUE: FALSE) : super.gteq(rhs); }
public boolean gteq_b( LuaValue rhs ) { return rhs.isstring() ? rhs.strcmp(this)<=0 : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { typerror("attempt to compare string with number"); return false; }
public boolean gteq_b( double rhs ) { typerror("attempt to compare string with number"); return false; }
@@ -310,14 +310,14 @@ public class LuaString extends LuaValue {
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
public LuaValue concatTo(LuaNumber lhs) { return concatTo(lhs.strvalue()); }
public LuaValue concatTo(LuaString lhs) {
public LuaValue concatTo(LuaString lhs) {
byte[] b = new byte[lhs.m_length+this.m_length];
System.arraycopy(lhs.m_bytes, lhs.m_offset, b, 0, lhs.m_length);
System.arraycopy(this.m_bytes, this.m_offset, b, lhs.m_length, this.m_length);
return valueUsing(b, 0, b.length);
}
// string comparison
// string comparison
public int strcmp(LuaValue lhs) { return -lhs.strcmp(this); }
public int strcmp(LuaString rhs) {
for ( int i=0, j=0; i<m_length && j<rhs.m_length; ++i, ++j ) {
@@ -329,9 +329,9 @@ public class LuaString extends LuaValue {
}
/** Check for number in arithmetic, or throw aritherror */
private double checkarith() {
private double checkarith() {
double d = scannumber();
if ( Double.isNaN(d) )
if ( Double.isNaN(d) )
aritherror();
return d;
}
@@ -368,15 +368,15 @@ public class LuaString extends LuaValue {
public boolean isint() {
double d = scannumber();
if ( Double.isNaN(d) )
if ( Double.isNaN(d) )
return false;
int i = (int) d;
return i == d;
}
public boolean islong() {
public boolean islong() {
double d = scannumber();
if ( Double.isNaN(d) )
if ( Double.isNaN(d) )
return false;
long l = (long) d;
return l == d;
@@ -391,35 +391,35 @@ public class LuaString extends LuaValue {
public short toshort() { return (short) toint(); }
public double optdouble(double defval) {
return checknumber().checkdouble();
return checkdouble();
}
public int optint(int defval) {
return checknumber().checkint();
return checkint();
}
public LuaInteger optinteger(LuaInteger defval) {
return checknumber().checkinteger();
public LuaInteger optinteger(LuaInteger defval) {
return checkinteger();
}
public long optlong(long defval) {
return checknumber().checklong();
return checklong();
}
public LuaNumber optnumber(LuaNumber defval) {
return checknumber().checknumber();
return checknumber();
}
public LuaString optstring(LuaString defval) {
return this;
return this;
}
public LuaValue tostring() {
return this;
}
public String optjstring(String defval) {
return tojstring();
public String optjstring(String defval) {
return tojstring();
}
public LuaString strvalue() {
@@ -429,7 +429,7 @@ public class LuaString extends LuaValue {
/** Take a substring using Java zero-based indexes for begin and end or range.
* @param beginIndex The zero-based index of the first character to include.
* @param endIndex The zero-based index of position after the last character.
* @return LuaString which is a substring whose first character is at offset
* @return LuaString which is a substring whose first character is at offset
* beginIndex and extending for (endIndex - beginIndex ) characters.
*/
public LuaString substring( int beginIndex, int endIndex ) {
@@ -476,7 +476,7 @@ public class LuaString extends LuaValue {
return val.raweq(this);
}
public boolean raweq( LuaString s ) {
public boolean raweq( LuaString s ) {
if ( this == s )
return true;
if ( s.m_length != m_length )
@@ -503,7 +503,7 @@ public class LuaString extends LuaValue {
public static boolean equals( byte[] a, int i, byte[] b, int j, int n ) {
if ( a.length < i + n || b.length < j + n )
return false;
while ( --n>=0 )
while ( --n>=0 )
if ( a[i++]!=b[j++] )
return false;
return true;
@@ -535,8 +535,8 @@ public class LuaString extends LuaValue {
return luaByte( index );
}
public String checkjstring() {
return tojstring();
public String checkjstring() {
return tojstring();
}
public LuaString checkstring() {
@@ -552,7 +552,7 @@ public class LuaString extends LuaValue {
}
/**
* Copy the bytes of the string into the given byte array.
* Copy the bytes of the string into the given byte array.
* @param strOffset offset from which to copy
* @param bytes destination byte array
* @param arrayOffset offset in destination
@@ -626,12 +626,12 @@ public class LuaString extends LuaValue {
/**
* Convert to Java String interpreting as utf8 characters.
* Convert to Java String interpreting as utf8 characters.
*
* @param bytes byte array in UTF8 encoding to convert
* @param offset starting index in byte array
* @param length number of bytes to convert
* @return Java String corresponding to the value of bytes interpreted using UTF8
* @return Java String corresponding to the value of bytes interpreted using UTF8
* @see #lengthAsUtf8(char[])
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #isValidUtf8()
@@ -662,7 +662,7 @@ public class LuaString extends LuaValue {
* @see #decodeAsUtf8(byte[], int, int)
* @see #isValidUtf8()
*/
public static int lengthAsUtf8(char[] chars) {
public static int lengthAsUtf8(char[] chars) {
int i,b;
char c;
for ( i=b=chars.length; --i>=0; )
@@ -673,7 +673,7 @@ public class LuaString extends LuaValue {
/**
* Encode the given Java string as UTF-8 bytes, writing the result to bytes
* starting at offset.
* starting at offset.
* <p>
* The string should be measured first with lengthAsUtf8
* to make sure the given byte array is large enough.
@@ -694,11 +694,11 @@ public class LuaString extends LuaValue {
bytes[j++] = (byte) c;
} else if ( c < 0x800 ) {
bytes[j++] = (byte) (0xC0 | ((c>>6) & 0x1f));
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
} else {
bytes[j++] = (byte) (0xE0 | ((c>>12) & 0x0f));
bytes[j++] = (byte) (0x80 | ((c>>6) & 0x3f));
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
}
}
return j - off;
@@ -714,12 +714,12 @@ public class LuaString extends LuaValue {
for (int i=m_offset,j=m_offset+m_length; i<j;) {
int c = m_bytes[i++];
if ( c >= 0 ) continue;
if ( ((c & 0xE0) == 0xC0)
&& i<j
if ( ((c & 0xE0) == 0xC0)
&& i<j
&& (m_bytes[i++] & 0xC0) == 0x80) continue;
if ( ((c & 0xF0) == 0xE0)
&& i+1<j
&& (m_bytes[i++] & 0xC0) == 0x80
if ( ((c & 0xF0) == 0xE0)
&& i+1<j
&& (m_bytes[i++] & 0xC0) == 0x80
&& (m_bytes[i++] & 0xC0) == 0x80) continue;
return false;
}
@@ -728,32 +728,32 @@ public class LuaString extends LuaValue {
// --------------------- number conversion -----------------------
/**
* convert to a number using baee 10 or base 16 if it starts with '0x',
/**
* convert to a number using baee 10 or base 16 if it starts with '0x',
* or NIL if it can't be converted
* @return IntValue, DoubleValue, or NIL depending on the content of the string.
* @see LuaValue#tonumber()
* @see LuaValue#tonumber()
*/
public LuaValue tonumber() {
double d = scannumber();
return Double.isNaN(d)? NIL: valueOf(d);
}
/**
/**
* convert to a number using a supplied base, or NIL if it can't be converted
* @param base the base to use, such as 10
* @return IntValue, DoubleValue, or NIL depending on the content of the string.
* @see LuaValue#tonumber()
* @see LuaValue#tonumber()
*/
public LuaValue tonumber( int base ) {
double d = scannumber( base );
return Double.isNaN(d)? NIL: valueOf(d);
}
/**
* Convert to a number in base 10, or base 16 if the string starts with '0x',
/**
* Convert to a number in base 10, or base 16 if the string starts with '0x',
* or return Double.NaN if it cannot be converted to a number.
* @return double value if conversion is valid, or Double.NaN if not
* @return double value if conversion is valid, or Double.NaN if not
*/
public double scannumber() {
int i=m_offset,j=m_offset+m_length;
@@ -767,10 +767,10 @@ public class LuaString extends LuaValue {
return Double.isNaN(l)? scandouble(i,j): l;
}
/**
/**
* Convert to a number in a base, or return Double.NaN if not a number.
* @param base the base to use between 2 and 36
* @return double value if conversion is valid, or Double.NaN if not
* @return double value if conversion is valid, or Double.NaN if not
*/
public double scannumber(int base) {
if ( base < 2 || base > 36 )
@@ -788,7 +788,7 @@ public class LuaString extends LuaValue {
* @param base the base to use, such as 10
* @param start the index to start searching from
* @param end the first index beyond the search range
* @return double value if conversion is valid,
* @return double value if conversion is valid,
* or Double.NaN if not
*/
private double scanlong( int base, int start, int end ) {
@@ -798,7 +798,7 @@ public class LuaString extends LuaValue {
int digit = m_bytes[i] - (base<=10||(m_bytes[i]>='0'&&m_bytes[i]<='9')? '0':
m_bytes[i]>='A'&&m_bytes[i]<='Z'? ('A'-10): ('a'-10));
if ( digit < 0 || digit >= base )
return Double.NaN;
return Double.NaN;
x = x * base + digit;
if ( x < 0 )
return Double.NaN; // overflow
@@ -810,7 +810,7 @@ public class LuaString extends LuaValue {
* Scan and convert a double value, or return Double.NaN if not a double.
* @param start the index to start searching from
* @param end the first index beyond the search range
* @return double value if conversion is valid,
* @return double value if conversion is valid,
* or Double.NaN if not
*/
private double scandouble(int start, int end) {
@@ -833,7 +833,7 @@ public class LuaString extends LuaValue {
c[i-start] = (char) m_bytes[i];
try {
return Double.parseDouble(new String(c));
} catch ( Exception e ) {
} catch ( Exception e ) {
return Double.NaN;
}
}

View File

@@ -25,23 +25,23 @@ import java.lang.ref.WeakReference;
import java.util.Vector;
/**
* Subclass of {@link LuaValue} for representing lua tables.
* Subclass of {@link LuaValue} for representing lua tables.
* <p>
* Almost all API's implemented in {@link LuaTable} are defined and documented in {@link LuaValue}.
* <p>
* If a table is needed, the one of the type-checking functions can be used such as
* {@link #istable()},
* {@link #istable()},
* {@link #checktable()}, or
* {@link #opttable(LuaTable)}
* {@link #opttable(LuaTable)}
* <p>
* The main table operations are defined on {@link LuaValue}
* The main table operations are defined on {@link LuaValue}
* for getting and setting values with and without metatag processing:
* <ul>
* <li>{@link #get(LuaValue)}</li>
* <li>{@link #set(LuaValue,LuaValue)}</li>
* <li>{@link #rawget(LuaValue)}</li>
* <li>{@link #get(LuaValue)}</li>
* <li>{@link #set(LuaValue,LuaValue)}</li>
* <li>{@link #rawget(LuaValue)}</li>
* <li>{@link #rawset(LuaValue,LuaValue)}</li>
* <li>plus overloads such as {@link #get(String)}, {@link #get(int)}, and so on</li>
* <li>plus overloads such as {@link #get(String)}, {@link #get(int)}, and so on</li>
* </ul>
* <p>
* To iterate over key-value pairs from Java, use
@@ -56,7 +56,7 @@ import java.util.Vector;
* }}</pre>
*
* <p>
* As with other types, {@link LuaTable} instances should be constructed via one of the table constructor
* As with other types, {@link LuaTable} instances should be constructed via one of the table constructor
* methods on {@link LuaValue}:
* <ul>
* <li>{@link LuaValue#tableOf()} empty table</li>
@@ -92,7 +92,7 @@ public class LuaTable extends LuaValue implements Metatable {
hash = NOBUCKETS;
}
/**
/**
* Construct table with preset capacity.
* @param narray capacity of array part
* @param nhash capacity of hash part
@@ -102,9 +102,9 @@ public class LuaTable extends LuaValue implements Metatable {
}
/**
* Construct table with named and unnamed parts.
* Construct table with named and unnamed parts.
* @param named Named elements in order {@code key-a, value-a, key-b, value-b, ... }
* @param unnamed Unnamed elements in order {@code value-1, value-2, ... }
* @param unnamed Unnamed elements in order {@code value-1, value-2, ... }
* @param lastarg Additional unnamed values beyond {@code unnamed.length}
*/
public LuaTable(LuaValue[] named, LuaValue[] unnamed, Varargs lastarg) {
@@ -123,17 +123,17 @@ public class LuaTable extends LuaValue implements Metatable {
}
/**
* Construct table of unnamed elements.
* @param varargs Unnamed elements in order {@code value-1, value-2, ... }
* Construct table of unnamed elements.
* @param varargs Unnamed elements in order {@code value-1, value-2, ... }
*/
public LuaTable(Varargs varargs) {
this(varargs,1);
}
/**
* Construct table of unnamed elements.
* Construct table of unnamed elements.
* @param varargs Unnamed elements in order {@code value-1, value-2, ... }
* @param firstarg the index in varargs of the first argument to include in the table
* @param firstarg the index in varargs of the first argument to include in the table
*/
public LuaTable(Varargs varargs, int firstarg) {
int nskip = firstarg-1;
@@ -152,8 +152,8 @@ public class LuaTable extends LuaValue implements Metatable {
return "table";
}
public boolean istable() {
return true;
public boolean istable() {
return true;
}
public LuaTable checktable() {
@@ -185,17 +185,17 @@ public class LuaTable extends LuaValue implements Metatable {
return v;
}
/**
* Get the length of the array part of the table.
* @return length of the array part, does not relate to count of objects in the table.
/**
* Get the length of the array part of the table.
* @return length of the array part, does not relate to count of objects in the table.
*/
protected int getArrayLength() {
return array.length;
}
/**
* Get the length of the hash part of the table.
* @return length of the hash part, does not relate to count of objects in the table.
/**
* Get the length of the hash part of the table.
* @return length of the hash part, does not relate to count of objects in the table.
*/
protected int getHashLength() {
return hash.length;
@@ -266,8 +266,8 @@ public class LuaTable extends LuaValue implements Metatable {
/** caller must ensure key is not nil */
public void set( LuaValue key, LuaValue value ) {
if (!key.isvalidkey() && !metatag(NEWINDEX).isfunction())
typerror("table index");
if (key == null || !key.isvalidkey() && !metatag(NEWINDEX).isfunction())
throw new LuaError("value ('" + key + "') can not be used as a table index");
if ( m_metatable==null || ! rawget(key).isnil() || ! settable(this,key,value) )
rawset(key, value);
}
@@ -294,35 +294,35 @@ public class LuaTable extends LuaValue implements Metatable {
}
/** Remove the element at a position in a list-table
*
*
* @param pos the position to remove
* @return The removed item, or {@link #NONE} if not removed
*/
public LuaValue remove(int pos) {
int n = rawlen();
int n = length();
if ( pos == 0 )
pos = n;
else if (pos > n)
return NONE;
LuaValue v = rawget(pos);
LuaValue v = get(pos);
for ( LuaValue r=v; !r.isnil(); ) {
r = rawget(pos+1);
rawset(pos++, r);
r = get(pos+1);
set(pos++, r);
}
return v.isnil()? NONE: v;
}
/** Insert an element at a position in a list-table
*
*
* @param pos the position to remove
* @param value The value to insert
*/
public void insert(int pos, LuaValue value) {
if ( pos == 0 )
pos = rawlen()+1;
pos = length()+1;
while ( ! value.isnil() ) {
LuaValue v = rawget( pos );
rawset(pos++, value);
LuaValue v = get( pos );
set(pos++, value);
value = v;
}
}
@@ -347,17 +347,22 @@ public class LuaTable extends LuaValue implements Metatable {
}
public int length() {
return m_metatable != null? len().toint(): rawlen();
if (m_metatable != null) {
LuaValue len = len();
if (!len.isint()) throw new LuaError("table length is not an integer: " + len);
return len.toint();
}
return rawlen();
}
public LuaValue len() {
public LuaValue len() {
final LuaValue h = metatag(LEN);
if (h.toboolean())
return h.call(this);
return LuaInteger.valueOf(rawlen());
}
public int rawlen() {
public int rawlen() {
int a = getArrayLength();
int n = a+1,m=0;
while ( !rawget(n).isnil() ) {
@@ -375,7 +380,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
/**
* Get the next element after a particular key in the table
* Get the next element after a particular key in the table
* @return key,value or nil
*/
public Varargs next( LuaValue key ) {
@@ -390,7 +395,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
}
if ( hash.length == 0 )
error( "invalid key to 'next'" );
error( "invalid key to 'next' 1: " + key );
i = hashSlot( key );
boolean found = false;
for ( Slot slot = hash[i]; slot != null; slot = slot.rest() ) {
@@ -404,7 +409,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
}
if ( !found ) {
error( "invalid key to 'next'" );
error( "invalid key to 'next' 2: " + key );
}
i += 1+array.length;
}
@@ -436,8 +441,8 @@ public class LuaTable extends LuaValue implements Metatable {
}
/**
* Get the next element after a particular key in the
* contiguous array part of a table
* Get the next element after a particular key in the
* contiguous array part of a table
* @return key,value or none
*/
public Varargs inext(LuaValue key) {
@@ -467,7 +472,8 @@ public class LuaTable extends LuaValue implements Metatable {
}
}
if ( checkLoadFactor() ) {
if ( key.isinttype() && key.toint() > 0 ) {
if ( (m_metatable == null || !m_metatable.useWeakValues())
&& key.isinttype() && key.toint() > 0 ) {
// a rehash might make room in the array portion for this key.
rehash( key.toint() );
if ( arrayset(key.toint(), value) )
@@ -512,7 +518,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
}
/**
/**
* Find the hashtable slot to use
* @param key key to look for
* @return slot to use
@@ -714,7 +720,7 @@ public class LuaTable extends LuaValue implements Metatable {
StrongSlot entry = slot.first();
if (entry != null)
newArray[ k - 1 ] = entry.value();
} else {
} else if ( !(slot instanceof DeadSlot) ) {
int j = slot.keyindex( newHashMask );
newHash[j] = slot.relink( newHash[j] );
}
@@ -774,42 +780,45 @@ public class LuaTable extends LuaValue implements Metatable {
//
// implemented heap sort from wikipedia
//
// Only sorts the contiguous array part.
// Only sorts the contiguous array part.
//
/** Sort the table using a comparator.
* @param comparator {@link LuaValue} to be called to compare elements.
*/
public void sort(LuaValue comparator) {
if (len().tolong() >= (long)Integer.MAX_VALUE) throw new LuaError("array too big: " + len().tolong());
if (m_metatable != null && m_metatable.useWeakValues()) {
dropWeakArrayValues();
}
int n = array.length;
while ( n > 0 && array[n-1] == null )
--n;
if ( n > 1 )
heapSort(n, comparator);
int n = length();
if ( n > 1 )
heapSort(n, comparator.isnil() ? null : comparator);
}
private void heapSort(int count, LuaValue cmpfunc) {
heapify(count, cmpfunc);
for ( int end=count-1; end>0; ) {
swap(end, 0);
siftDown(0, --end, cmpfunc);
for ( int end=count; end>1; ) {
LuaValue a = get(end); // swap(end, 1)
set(end, get(1));
set(1, a);
siftDown(1, --end, cmpfunc);
}
}
private void heapify(int count, LuaValue cmpfunc) {
for ( int start=count/2-1; start>=0; --start )
siftDown(start, count - 1, cmpfunc);
for ( int start=count/2; start>0; --start )
siftDown(start, count, cmpfunc);
}
private void siftDown(int start, int end, LuaValue cmpfunc) {
for ( int root=start; root*2+1 <= end; ) {
int child = root*2+1;
for ( int root=start; root*2 <= end; ) {
int child = root*2;
if (child < end && compare(child, child + 1, cmpfunc))
++child;
++child;
if (compare(root, child, cmpfunc)) {
swap(root, child);
LuaValue a = get(root); // swap(root, child)
set(root, get(child));
set(child, a);
root = child;
} else
return;
@@ -817,32 +826,19 @@ public class LuaTable extends LuaValue implements Metatable {
}
private boolean compare(int i, int j, LuaValue cmpfunc) {
LuaValue a, b;
if (m_metatable == null) {
a = array[i];
b = array[j];
} else {
a = m_metatable.arrayget(array, i);
b = m_metatable.arrayget(array, j);
}
LuaValue a = get(i), b = get(j);
if ( a == null || b == null )
return false;
if ( ! cmpfunc.isnil() ) {
if ( cmpfunc != null ) {
return cmpfunc.call(a,b).toboolean();
} else {
return a.lt_b(b);
}
}
private void swap(int i, int j) {
LuaValue a = array[i];
array[i] = array[j];
array[j] = a;
}
/** This may be deprecated in a future release.
/** This may be deprecated in a future release.
* It is recommended to count via iteration over next() instead
* @return count of keys in the table
* @return count of keys in the table
* */
public int keyCount() {
LuaValue k = LuaValue.NIL;
@@ -853,9 +849,9 @@ public class LuaTable extends LuaValue implements Metatable {
}
}
/** This may be deprecated in a future release.
* It is recommended to use next() instead
* @return array of keys in the table
/** This may be deprecated in a future release.
* It is recommended to use next() instead
* @return array of keys in the table
* */
public LuaValue[] keys() {
Vector l = new Vector();
@@ -892,6 +888,11 @@ public class LuaTable extends LuaValue implements Metatable {
/** Unpack the elements from i to j inclusive */
public Varargs unpack(int i, int j) {
if (j < i) return NONE;
int count = j - i;
if (count < 0) throw new LuaError("too many results to unpack: greater " + Integer.MAX_VALUE); // integer overflow
int max = 0x00ffffff;
if (count >= max) throw new LuaError("too many results to unpack: " + count + " (max is " + max + ')');
int n = j + 1 - i;
switch (n) {
case 0: return NONE;
@@ -900,10 +901,14 @@ public class LuaTable extends LuaValue implements Metatable {
default:
if (n < 0)
return NONE;
LuaValue[] v = new LuaValue[n];
while (--n >= 0)
v[n] = get(i+n);
return varargsOf(v);
try {
LuaValue[] v = new LuaValue[n];
while (--n >= 0)
v[n] = get(i+n);
return varargsOf(v);
} catch (OutOfMemoryError e) {
throw new LuaError("too many results to unpack [out of memory]: " + n);
}
}
}
@@ -1231,13 +1236,14 @@ public class LuaTable extends LuaValue implements Metatable {
}
public Entry set(LuaValue value) {
LuaValue n = value.tonumber();
if ( !n.isnil() ) {
this.value = n.todouble();
return this;
} else {
return new NormalEntry( this.key, value );
if (value.type() == TNUMBER) {
LuaValue n = value.tonumber();
if (!n.isnil()) {
this.value = n.todouble();
return this;
}
}
return new NormalEntry( this.key, value );
}
public int keyindex( int mask ) {

View File

@@ -23,6 +23,10 @@ package org.luaj.vm2;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Subclass of {@link LuaValue} that implements
@@ -82,6 +86,19 @@ public class LuaThread extends LuaValue {
*/
public static long thread_orphan_check_interval = 5000;
public static final String USE_PLATFORM_THREAD = "USE_PLATFORM_THREAD";
private static boolean SUPPORT_VIRTUAL_THREAD = false;
static {
try {
Thread.class.getMethod("ofVirtual");
SUPPORT_VIRTUAL_THREAD = true;
} catch (Exception e) {
//e.printStackTrace();
}
}
public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1;
public static final int STATUS_RUNNING = 2;
@@ -184,6 +201,8 @@ public class LuaThread extends LuaValue {
public int bytecodes;
public int status = LuaThread.STATUS_INITIAL;
private Lock locker = new ReentrantLock();
private Condition cond = locker.newCondition();
State(Globals globals, LuaThread lua_thread, LuaValue function) {
this.globals = globals;
@@ -191,68 +210,98 @@ public class LuaThread extends LuaValue {
this.function = function;
}
public synchronized void run() {
public void run() {
locker.lock();
try {
Varargs a = this.args;
this.args = LuaValue.NONE;
this.result = function.invoke(a);
} catch (Throwable t) {
this.error = t.getMessage();
} finally {
this.status = LuaThread.STATUS_DEAD;
this.notify();
}
}
public synchronized Varargs lua_resume(LuaThread new_thread, Varargs args) {
LuaThread previous_thread = globals.running;
try {
globals.running = new_thread;
this.args = args;
if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING;
new Thread(this, "Coroutine-"+(++coroutine_count)).start();
} else {
this.notify();
try {
Varargs a = this.args;
this.args = LuaValue.NONE;
this.result = function.invoke(a);
} catch (Throwable t) {
this.error = t.getMessage();
} finally {
this.status = LuaThread.STATUS_DEAD;
cond.signal();
}
if (previous_thread != null)
previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING;
this.wait();
return (this.error != null?
LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)):
LuaValue.varargsOf(LuaValue.TRUE, this.result));
} catch (InterruptedException ie) {
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
this.error = null;
globals.running = previous_thread;
if (previous_thread != null)
globals.running.state.status =STATUS_RUNNING;
locker.unlock();
}
}
public synchronized Varargs lua_yield(Varargs args) {
public Varargs lua_resume(LuaThread new_thread, Varargs args) {
locker.lock();
try {
this.result = args;
this.status = STATUS_SUSPENDED;
this.notify();
do {
this.wait(thread_orphan_check_interval);
if (this.lua_thread.get() == null) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
LuaThread previous_thread = globals.running;
try {
globals.running = new_thread;
this.args = args;
if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING;
Thread t = null;
if(SUPPORT_VIRTUAL_THREAD) {
LuaValue setting = globals.get(USE_PLATFORM_THREAD);
if(setting.isnil()) {//default
if(Thread.currentThread().isVirtual()) {
t = Thread.ofVirtual().name("Coroutine-"+(++coroutine_count)).start(this);
}
} else {
if(!setting.toboolean()) {
t = Thread.ofVirtual().name("Coroutine-"+(++coroutine_count)).start(this);
}
}
}
if (t == null){
new Thread(this, "Coroutine-"+(++coroutine_count)).start();
}
} else {
cond.signal();
}
} while (this.status == STATUS_SUSPENDED);
return this.args;
} catch (InterruptedException ie) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
if (previous_thread != null)
previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING;
cond.await();
return (this.error != null?
LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)):
LuaValue.varargsOf(LuaValue.TRUE, this.result));
} catch (InterruptedException ie) {
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
this.error = null;
globals.running = previous_thread;
if (previous_thread != null)
globals.running.state.status =STATUS_RUNNING;
}
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
locker.unlock();
}
}
public Varargs lua_yield(Varargs args) {
locker.lock();
try {
try {
this.result = args;
this.status = STATUS_SUSPENDED;
cond.signal();
do {
cond.await(thread_orphan_check_interval,TimeUnit.MILLISECONDS);
if (this.lua_thread.get() == null) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
}
} while (this.status == STATUS_SUSPENDED);
return this.args;
} catch (InterruptedException ie) {
this.status = STATUS_DEAD;
throw new OrphanedThread();
} finally {
this.args = LuaValue.NONE;
this.result = LuaValue.NONE;
}
} finally {
locker.unlock();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -128,6 +128,10 @@ public class Print extends Lua {
}
static void printValue( PrintStream ps, LuaValue v ) {
if (v == null) {
ps.print("null");
return;
}
switch ( v.type() ) {
case LuaValue.TSTRING: printString( ps, (LuaString) v ); break;
default: ps.print( v.tojstring() );
@@ -136,7 +140,7 @@ public class Print extends Lua {
}
static void printConstant(PrintStream ps, Prototype f, int i) {
printValue( ps, f.k[i] );
printValue( ps, i < f.k.length ? f.k[i] : LuaValue.valueOf("UNKNOWN_CONST_" + i) );
}
static void printUpvalue(PrintStream ps, Upvaldesc u) {
@@ -152,7 +156,7 @@ public class Print extends Lua {
int[] code = f.code;
int pc, n = code.length;
for (pc = 0; pc < n; pc++) {
printOpCode(f, pc);
pc = printOpCode(f, pc);
ps.println();
}
}
@@ -161,9 +165,10 @@ public class Print extends Lua {
* Print an opcode in a prototype
* @param f the {@link Prototype}
* @param pc the program counter to look up and print
* @return pc same as above or changed
*/
public static void printOpCode(Prototype f, int pc) {
printOpCode(ps,f,pc);
public static int printOpCode(Prototype f, int pc) {
return printOpCode(ps,f,pc);
}
/**
@@ -171,8 +176,9 @@ public class Print extends Lua {
* @param ps the {@link PrintStream} to print to
* @param f the {@link Prototype}
* @param pc the program counter to look up and print
* @return pc same as above or changed
*/
public static void printOpCode(PrintStream ps, Prototype f, int pc) {
public static int printOpCode(PrintStream ps, Prototype f, int pc) {
int[] code = f.code;
int i = code[pc];
int o = GET_OPCODE(i);
@@ -187,80 +193,67 @@ public class Print extends Lua {
ps.print("[" + line + "] ");
else
ps.print("[-] ");
ps.print(OPNAMES[o] + " ");
switch (getOpMode(o)) {
case iABC:
ps.print( a );
if (getBMode(o) != OpArgN)
ps.print(" "+(ISK(b) ? (-1 - INDEXK(b)) : b));
if (getCMode(o) != OpArgN)
ps.print(" "+(ISK(c) ? (-1 - INDEXK(c)) : c));
break;
case iABx:
if (getBMode(o) == OpArgK) {
ps.print(a + " " + (-1 - bx));
} else {
ps.print(a + " " + (bx));
if (o >= OPNAMES.length - 1) {
ps.print("UNKNOWN_OP_" + o + " ");
} else {
ps.print(OPNAMES[o] + " ");
switch (getOpMode(o)) {
case iABC:
ps.print( a );
if (getBMode(o) != OpArgN)
ps.print(" "+(ISK(b) ? (-1 - INDEXK(b)) : b));
if (getCMode(o) != OpArgN)
ps.print(" "+(ISK(c) ? (-1 - INDEXK(c)) : c));
break;
case iABx:
if (getBMode(o) == OpArgK) {
ps.print(a + " " + (-1 - bx));
} else {
ps.print(a + " " + (bx));
}
break;
case iAsBx:
if (o == OP_JMP)
ps.print( sbx );
else
ps.print(a + " " + sbx);
break;
}
break;
case iAsBx:
if (o == OP_JMP)
ps.print( sbx );
else
ps.print(a + " " + sbx);
break;
}
switch (o) {
case OP_LOADK:
ps.print(" ; ");
printConstant(ps, f, bx);
break;
case OP_GETUPVAL:
case OP_SETUPVAL:
ps.print(" ; ");
printUpvalue(ps, f.upvalues[b]);
break;
case OP_GETTABUP:
ps.print(" ; ");
printUpvalue(ps, f.upvalues[b]);
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
break;
case OP_SETTABUP:
ps.print(" ; ");
printUpvalue(ps, f.upvalues[a]);
ps.print(" ");
if (ISK(b))
printConstant(ps, f, INDEXK(b));
else
ps.print("-");
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
break;
case OP_GETTABLE:
case OP_SELF:
if (ISK(c)) {
switch (o) {
case OP_LOADK:
ps.print(" ; ");
printConstant(ps, f, INDEXK(c));
}
break;
case OP_SETTABLE:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_DIV:
case OP_POW:
case OP_EQ:
case OP_LT:
case OP_LE:
if (ISK(b) || ISK(c)) {
printConstant(ps, f, bx);
break;
case OP_GETUPVAL:
case OP_SETUPVAL:
ps.print(" ; ");
if (b < f.upvalues.length) {
printUpvalue(ps, f.upvalues[b]);
} else {
ps.print("UNKNOWN_UPVALUE_" + b);
}
break;
case OP_GETTABUP:
ps.print(" ; ");
if (b < f.upvalues.length) {
printUpvalue(ps, f.upvalues[b]);
} else {
ps.print("UNKNOWN_UPVALUE_" + b);
}
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
break;
case OP_SETTABUP:
ps.print(" ; ");
if (a < f.upvalues.length) {
printUpvalue(ps, f.upvalues[a]);
} else {
ps.print("UNKNOWN_UPVALUE_" + a);
}
ps.print(" ");
if (ISK(b))
printConstant(ps, f, INDEXK(b));
else
@@ -270,28 +263,62 @@ public class Print extends Lua {
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
break;
case OP_GETTABLE:
case OP_SELF:
if (ISK(c)) {
ps.print(" ; ");
printConstant(ps, f, INDEXK(c));
}
break;
case OP_SETTABLE:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_DIV:
case OP_POW:
case OP_EQ:
case OP_LT:
case OP_LE:
if (ISK(b) || ISK(c)) {
ps.print(" ; ");
if (ISK(b))
printConstant(ps, f, INDEXK(b));
else
ps.print("-");
ps.print(" ");
if (ISK(c))
printConstant(ps, f, INDEXK(c));
else
ps.print("-");
}
break;
case OP_JMP:
case OP_FORLOOP:
case OP_FORPREP:
ps.print(" ; to " + (sbx + pc + 2));
break;
case OP_CLOSURE:
if (bx < f.p.length) {
ps.print(" ; " + f.p[bx].getClass().getName());
} else {
ps.print(" ; UNKNOWN_PROTYPE_" + bx);
}
break;
case OP_SETLIST:
if (c == 0)
ps.print(" ; " + ((int) code[++pc]) + " (stored in the next OP)");
else
ps.print(" ; " + ((int) c));
break;
case OP_VARARG:
ps.print( " ; is_vararg="+ f.is_vararg );
break;
default:
break;
}
break;
case OP_JMP:
case OP_FORLOOP:
case OP_FORPREP:
ps.print(" ; to " + (sbx + pc + 2));
break;
case OP_CLOSURE:
ps.print(" ; " + f.p[bx].getClass().getName());
break;
case OP_SETLIST:
if (c == 0)
ps.print(" ; " + ((int) code[++pc]));
else
ps.print(" ; " + ((int) c));
break;
case OP_VARARG:
ps.print( " ; is_vararg="+ f.is_vararg );
break;
default:
break;
}
return pc;
}
private static int getline(Prototype f, int pc) {

View File

@@ -24,16 +24,16 @@ package org.luaj.vm2;
/**
* Class to encapsulate varargs values, either as part of a variable argument list, or multiple return values.
* <p>
* To construct varargs, use one of the static methods such as
* To construct varargs, use one of the static methods such as
* {@code LuaValue.varargsOf(LuaValue,LuaValue)}
* <p>
* <p>
* Any LuaValue can be used as a stand-in for Varargs, for both calls and return values.
* When doing so, nargs() will return 1 and arg1() or arg(1) will return this.
* This simplifies the case when calling or implementing varargs functions with only
* 1 argument or 1 return value.
* Any LuaValue can be used as a stand-in for Varargs, for both calls and return values.
* When doing so, nargs() will return 1 and arg1() or arg(1) will return this.
* This simplifies the case when calling or implementing varargs functions with only
* 1 argument or 1 return value.
* <p>
* Varargs can also be derived from other varargs by appending to the front with a call
* Varargs can also be derived from other varargs by appending to the front with a call
* such as {@code LuaValue.varargsOf(LuaValue,Varargs)}
* or by taking a portion of the args using {@code Varargs.subargs(int start)}
* <p>
@@ -57,22 +57,22 @@ public abstract class Varargs {
abstract public LuaValue arg( int i );
/**
* Get the number of arguments, or 0 if there are none.
* @return number of arguments.
* Get the number of arguments, or 0 if there are none.
* @return number of arguments.
*/
abstract public int narg();
/**
* Get the first argument in the list.
* Get the first argument in the list.
* @return LuaValue which is first in the list, or LuaValue.NIL if there are no values.
* @see Varargs#arg(int)
* @see LuaValue#NIL
*/
abstract public LuaValue arg1();
/**
/**
* Evaluate any pending tail call and return result.
* @return the evaluated tail call result
* @return the evaluated tail call result
*/
public Varargs eval() { return this; }
@@ -88,7 +88,7 @@ public abstract class Varargs {
// utilities to get specific arguments and type-check them.
// -----------------------------------------------------------------------
/** Gets the type of argument {@code i}
/** Gets the type of argument {@code i}
* @param i the index of the argument to convert, 1 is the first argument
* @return int value corresponding to one of the LuaValue integer type values
* @see LuaValue#TNIL
@@ -117,20 +117,20 @@ public abstract class Varargs {
public boolean isfunction(int i) { return arg(i).isfunction(); }
/** Tests if argument i is a number.
* Since anywhere a number is required, a string can be used that
* is a number, this will return true for both numbers and
* strings that can be interpreted as numbers.
* Since anywhere a number is required, a string can be used that
* is a number, this will return true for both numbers and
* strings that can be interpreted as numbers.
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists and is a number or
* @return true if the argument exists and is a number or
* string that can be interpreted as a number, false otherwise
* @see LuaValue#TNUMBER
* @see LuaValue#TSTRING
* */
public boolean isnumber(int i) { return arg(i).isnumber(); }
/** Tests if argument i is a string.
* Since all lua numbers can be used where strings are used,
* this will return true for both strings and numbers.
/** Tests if argument i is a string.
* Since all lua numbers can be used where strings are used,
* this will return true for both strings and numbers.
* @param i the index of the argument to test, 1 is the first argument
* @return true if the argument exists and is a string or number, false otherwise
* @see LuaValue#TNUMBER
@@ -167,7 +167,7 @@ public abstract class Varargs {
/** Return argument i as a boolean value, {@code defval} if nil, or throw a LuaError if any other type.
* @param i the index of the argument to test, 1 is the first argument
* @return true if argument i is boolean true, false if it is false, or defval if not supplied or nil
* @return true if argument i is boolean true, false if it is false, or defval if not supplied or nil
* @exception LuaError if the argument is not a lua boolean
* */
public boolean optboolean(int i, boolean defval) { return arg(i).optboolean(defval); }
@@ -256,7 +256,7 @@ public abstract class Varargs {
* */
public Object optuserdata(int i, Object defval) { return arg(i).optuserdata(defval); }
/** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass,
/** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass,
* {@code defval} if nil, or throw a LuaError if any other type.
* @param i the index of the argument to test, 1 is the first argument
* @param c the class to which the userdata instance must be assignable
@@ -291,7 +291,7 @@ public abstract class Varargs {
* @return java double value if argument i is a number or string that converts to a number
* @exception LuaError if the argument is not a number
* */
public double checkdouble(int i) { return arg(i).checknumber().todouble(); }
public double checkdouble(int i) { return arg(i).checkdouble(); }
/** Return argument i as a function, or throw an error if an incompatible type.
* @param i the index of the argument to test, 1 is the first argument
@@ -300,12 +300,12 @@ public abstract class Varargs {
* */
public LuaFunction checkfunction(int i) { return arg(i).checkfunction(); }
/** Return argument i as a java int value, discarding any fractional part, or throw an error if not a number.
/** Return argument i as a java int value, or throw an error if it cannot be converted to one.
* @param i the index of the argument to test, 1 is the first argument
* @return int value with fraction discarded and truncated if necessary if argument i is number
* @exception LuaError if the argument is not a number
* @return int value if argument i is a number or string that converts to a number
* @exception LuaError if the argument cannot be represented by a java int value
* */
public int checkint(int i) { return arg(i).checknumber().toint(); }
public int checkint(int i) { return arg(i).checkint(); }
/** Return argument i as a java int value, or throw an error if not a number or is not representable by a java int.
* @param i the index of the argument to test, 1 is the first argument
@@ -314,12 +314,12 @@ public abstract class Varargs {
* */
public LuaInteger checkinteger(int i) { return arg(i).checkinteger(); }
/** Return argument i as a java long value, discarding any fractional part, or throw an error if not a number.
/** Return argument i as a java long value, or throw an error if it cannot be converted to one.
* @param i the index of the argument to test, 1 is the first argument
* @return long value with fraction discarded and truncated if necessary if argument i is number
* @exception LuaError if the argument is not a number
* @return long value if argument i is a number or string that converts to a number
* @exception LuaError if the argument cannot be represented by a java long value
* */
public long checklong(int i) { return arg(i).checknumber().tolong(); }
public long checklong(int i) { return arg(i).checklong(); }
/** Return argument i as a LuaNumber, or throw an error if not a number or string that can be converted to a number.
* @param i the index of the argument to test, 1 is the first argument
@@ -363,7 +363,7 @@ public abstract class Varargs {
* */
public Object checkuserdata(int i) { return arg(i).checkuserdata(); }
/** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass,
/** Return argument i as a java Object if it is a userdata whose instance Class c or a subclass,
* or throw an error if any other type.
* @param i the index of the argument to test, 1 is the first argument
* @param c the class to which the userdata instance must be assignable
@@ -387,7 +387,7 @@ public abstract class Varargs {
public LuaValue checknotnil(int i) { return arg(i).checknotnil(); }
/** Performs test on argument i as a LuaValue when a user-supplied assertion passes, or throw an error.
* Returns normally if the value of {@code test} is {@code true}, otherwise throws and argument error with
* Returns normally if the value of {@code test} is {@code true}, otherwise throws and argument error with
* the supplied message, {@code msg}.
* @param test user supplied assertion to test against
* @param i the index to report in any error message
@@ -404,20 +404,20 @@ public abstract class Varargs {
return i>narg() || arg(i).isnil();
}
/** Convert argument {@code i} to java boolean based on lua rules for boolean evaluation.
/** Convert argument {@code i} to java boolean based on lua rules for boolean evaluation.
* @param i the index of the argument to convert, 1 is the first argument
* @return {@code false} if argument i is nil or false, otherwise {@code true}
* */
public boolean toboolean(int i) { return arg(i).toboolean(); }
/** Return argument i as a java byte value, discarding any fractional part and truncating,
/** Return argument i as a java byte value, discarding any fractional part and truncating,
* or 0 if not a number.
* @param i the index of the argument to convert, 1 is the first argument
* @return byte value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
* */
public byte tobyte(int i) { return arg(i).tobyte(); }
/** Return argument i as a java char value, discarding any fractional part and truncating,
/** Return argument i as a java char value, discarding any fractional part and truncating,
* or 0 if not a number.
* @param i the index of the argument to convert, 1 is the first argument
* @return char value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
@@ -430,21 +430,21 @@ public abstract class Varargs {
* */
public double todouble(int i) { return arg(i).todouble(); }
/** Return argument i as a java float value, discarding excess fractional part and truncating,
/** Return argument i as a java float value, discarding excess fractional part and truncating,
* or 0 if not a number.
* @param i the index of the argument to convert, 1 is the first argument
* @return float value with excess fraction discarded and truncated if necessary if argument i is number, otherwise 0
* */
public float tofloat(int i) { return arg(i).tofloat(); }
/** Return argument i as a java int value, discarding any fractional part and truncating,
/** Return argument i as a java int value, discarding any fractional part and truncating,
* or 0 if not a number.
* @param i the index of the argument to convert, 1 is the first argument
* @return int value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
* */
public int toint(int i) { return arg(i).toint(); }
/** Return argument i as a java long value, discarding any fractional part and truncating,
/** Return argument i as a java long value, discarding any fractional part and truncating,
* or 0 if not a number.
* @param i the index of the argument to convert, 1 is the first argument
* @return long value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
@@ -457,7 +457,7 @@ public abstract class Varargs {
* */
public String tojstring(int i) { return arg(i).tojstring(); }
/** Return argument i as a java short value, discarding any fractional part and truncating,
/** Return argument i as a java short value, discarding any fractional part and truncating,
* or 0 if not a number.
* @param i the index of the argument to convert, 1 is the first argument
* @return short value with fraction discarded and truncated if necessary if argument i is number, otherwise 0
@@ -477,8 +477,8 @@ public abstract class Varargs {
* */
public Object touserdata(int i,Class c) { return arg(i).touserdata(c); }
/** Convert the list of varargs values to a human readable java String.
* @return String value in human readable form such as {1,2}.
/** Convert the list of varargs values to a human readable java String.
* @return String value in human readable form such as {1,2}.
*/
public String tojstring() {
Buffer sb = new Buffer();
@@ -491,8 +491,8 @@ public abstract class Varargs {
return sb.tojstring();
}
/** Convert the value or values to a java String using Varargs.tojstring()
* @return String value in human readable form.
/** Convert the value or values to a java String using Varargs.tojstring()
* @return String value in human readable form.
* @see Varargs#tojstring()
*/
public String toString() { return tojstring(); }
@@ -544,21 +544,21 @@ public abstract class Varargs {
}
}
/** Varargs implemenation backed by two values.
/** Varargs implemenation backed by two values.
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static method on LuaValue.
*
* Instead use the corresponding static method on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue, Varargs)
*/
static final class PairVarargs extends Varargs {
private final LuaValue v1;
private final Varargs v2;
/** Construct a Varargs from an two LuaValue.
/** Construct a Varargs from an two LuaValue.
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static method on LuaValue.
*
* Instead use the corresponding static method on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue, Varargs)
*/
PairVarargs(LuaValue v1, Varargs v2) {
@@ -571,8 +571,8 @@ public abstract class Varargs {
public int narg() {
return 1+v2.narg();
}
public LuaValue arg1() {
return v1;
public LuaValue arg1() {
return v1;
}
public Varargs subargs(final int start) {
if (start == 1)
@@ -585,22 +585,22 @@ public abstract class Varargs {
}
}
/** Varargs implemenation backed by an array of LuaValues
/** Varargs implemenation backed by an array of LuaValues
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static methods on LuaValue.
*
* Instead use the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[])
* @see LuaValue#varargsOf(LuaValue[], Varargs)
*/
static final class ArrayVarargs extends Varargs {
private final LuaValue[] v;
private final Varargs r;
/** Construct a Varargs from an array of LuaValue.
/** Construct a Varargs from an array of LuaValue.
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static methods on LuaValue.
*
* Instead use the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[])
* @see LuaValue#varargsOf(LuaValue[], Varargs)
*/
@@ -631,11 +631,11 @@ public abstract class Varargs {
}
}
/** Varargs implemenation backed by an array of LuaValues
/** Varargs implemenation backed by an array of LuaValues
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static methods on LuaValue.
*
* Instead use the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[], int, int)
* @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
*/
@@ -644,11 +644,11 @@ public abstract class Varargs {
private final LuaValue[] v;
private final int length;
private final Varargs more;
/** Construct a Varargs from an array of LuaValue.
/** Construct a Varargs from an array of LuaValue.
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static methods on LuaValue.
*
* Instead use the corresponding static methods on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[], int, int)
*/
ArrayPartVarargs(LuaValue[] v, int offset, int length) {
@@ -657,11 +657,11 @@ public abstract class Varargs {
this.length = length;
this.more = LuaValue.NONE;
}
/** Construct a Varargs from an array of LuaValue and additional arguments.
/** Construct a Varargs from an array of LuaValue and additional arguments.
* <p>
* This is an internal class not intended to be used directly.
* Instead use the corresponding static method on LuaValue.
*
* Instead use the corresponding static method on LuaValue.
*
* @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
*/
public ArrayPartVarargs(LuaValue[] v, int offset, int length, Varargs more) {
@@ -676,8 +676,8 @@ public abstract class Varargs {
public int narg() {
return length + more.narg();
}
public LuaValue arg1() {
return length>0? v[offset]: more.arg1();
public LuaValue arg1() {
return length>0? v[offset]: more.arg1();
}
public Varargs subargs(int start) {
if (start <= 0)
@@ -707,8 +707,10 @@ public abstract class Varargs {
/** Return Varargs that cannot be using a shared array for the storage, and is flattened.
* Internal utility method not intended to be called directly from user code.
* @return Varargs containing same values, but flattened and with a new array if needed.
* @exclude
* @hide
*/
Varargs dealias() {
public Varargs dealias() {
int n = narg();
switch (n) {
case 0: return LuaValue.NONE;

View File

@@ -46,20 +46,20 @@ public class Constants extends Lua {
/* OpMode - basic instruction format */
static final int
static final int
iABC = 0,
iABx = 1,
iAsBx = 2;
/* OpArgMask */
static final int
static final int
OpArgN = 0, /* argument is not used */
OpArgU = 1, /* argument is used */
OpArgR = 2, /* argument is a register or a jump offset */
OpArgK = 3; /* argument is a constant or register/constant */
protected static void _assert(boolean b) {
protected static void _assert(boolean b) {
if (!b)
throw new LuaError("compiler assert failed");
}
@@ -104,6 +104,11 @@ public class Constants extends Lua {
((a << POS_A) & MASK_A) |
((bc << POS_Bx) & MASK_Bx) ;
}
static int CREATE_Ax(int o, int a) {
return ((o << POS_OP) & MASK_OP) |
((a << POS_Ax) & MASK_Ax) ;
}
// vector reallocation
@@ -150,7 +155,7 @@ public class Constants extends Lua {
}
static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) {
return v == null ? new LexState.Labeldesc[2] : v.length < min_n ? realloc(v, v.length*2) : v;
return v == null ? new LexState.Labeldesc[2] : v.length < min_n ? realloc(v, v.length*2) : v;
}
static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) {

View File

@@ -28,14 +28,14 @@ import java.io.OutputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LocVars;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
/** Class to dump a {@link Prototype} into an output stream, as part of compiling.
* <p>
* Generally, this class is not used directly, but rather indirectly via a command
* Generally, this class is not used directly, but rather indirectly via a command
* line interface tool such as {@link luac}.
* <p>
* A lua binary file is created via {@link DumpState#dump}:
@@ -85,7 +85,7 @@ public class DumpState {
public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES;
// header fields
private boolean IS_LITTLE_ENDIAN = false;
private boolean IS_LITTLE_ENDIAN = true;
private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_NUMBER = 8;
private static final int SIZEOF_INT = 4;
@@ -190,7 +190,7 @@ public class DumpState {
dumpString((LuaString)o);
break;
default:
throw new IllegalArgumentException("bad type for " + o);
throw new IllegalArgumentException("bad type for " + o);
}
}
n = f.p.length;

View File

@@ -50,7 +50,6 @@ public class FuncState extends Constants {
Hashtable h; /* table to find (and reuse) elements in `k' */
FuncState prev; /* enclosing function */
LexState ls; /* lexical state */
LuaC.CompileState L; /* compiler being invoked */
BlockCnt bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
@@ -113,8 +112,8 @@ public class FuncState extends Constants {
void errorlimit (int limit, String what) {
// TODO: report message logic.
String msg = (f.linedefined == 0) ?
L.pushfstring("main function has more than "+limit+" "+what) :
L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what);
ls.L.pushfstring("main function has more than "+limit+" "+what) :
ls.L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what);
ls.lexerror(msg, 0);
}
@@ -296,7 +295,7 @@ public class FuncState extends Constants {
}
} /* else go through */
}
this.codeABC(OP_LOADNIL, from, n - 1, 0);
this.codeABC(OP_LOADNIL, from, n - 1, 0);
}
@@ -478,9 +477,9 @@ public class FuncState extends Constants {
this.h = new Hashtable();
} else if (this.h.containsKey(v)) {
return ((Integer) h.get(v)).intValue();
}
}
final int idx = this.nk;
this.h.put(v, new Integer(idx));
this.h.put(v, idx);
final Prototype f = this.f;
if (f.k == null || nk + 1 >= f.k.length)
f.k = realloc( f.k, nk*2 + 1 );
@@ -496,7 +495,7 @@ public class FuncState extends Constants {
if ( r instanceof LuaDouble ) {
double d = r.todouble();
int i = (int) d;
if ( d == (double) i )
if ( d == (double) i )
r = LuaInteger.valueOf(i);
}
return this.addk(r);
@@ -581,11 +580,11 @@ public class FuncState extends Constants {
break;
}
case LexState.VK: {
this.codeABx(OP_LOADK, reg, e.u.info);
this.codeK(reg, e.u.info);
break;
}
case LexState.VKNUM: {
this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval()));
this.codeK(reg, this.numberK(e.u.nval()));
break;
}
case LexState.VRELOCABLE: {
@@ -688,7 +687,7 @@ public class FuncState extends Constants {
case LexState.VKNUM: {
e.u.info = this.numberK(e.u.nval());
e.k = LexState.VK;
/* go through */
/* go through */
}
case LexState.VK: {
if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */
@@ -1117,6 +1116,20 @@ public class FuncState extends Constants {
return this.code(CREATE_ABx(o, a, bc), this.ls.lastline);
}
int codeextraarg(int a) {
_assert(a <= MAXARG_Ax);
return this.code(CREATE_Ax(OP_EXTRAARG, a), this.ls.lastline);
}
int codeK(int reg, int k) {
if (k <= MAXARG_Bx)
return codeABx(OP_LOADK, reg, k);
else {
int p = codeABx(OP_LOADKX, reg, 0);
codeextraarg(k);
return p;
}
}
void setlist(int base, int nelems, int tostore) {
int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1;

View File

@@ -62,14 +62,14 @@ public class LexState extends Constants {
private static final int EOZ = (-1);
private static final int MAX_INT = Integer.MAX_VALUE-2;
private static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX?
private static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX?
private static final int LUAI_MAXCCALLS = 200;
private static final String LUA_QS(String s) { return "'"+s+"'"; }
private static final String LUA_QL(Object o) { return LUA_QS(String.valueOf(o)); }
private static final int LUA_COMPAT_LSTR = 1; // 1 for compatibility, 2 for old behavior
private static final boolean LUA_COMPAT_VARARG = true;
private static final boolean LUA_COMPAT_VARARG = true;
public static boolean isReservedKeyword(String varName) {
return RESERVED_LOCAL_VAR_KEYWORDS_TABLE.containsKey(varName);
@@ -84,7 +84,7 @@ public class LexState extends Constants {
/*
** grep "ORDER OPR" if you change these enums
*/
static final int
static final int
OPR_ADD=0, OPR_SUB=1, OPR_MUL=2, OPR_DIV=3, OPR_MOD=4, OPR_POW=5,
OPR_CONCAT=6,
OPR_NE=7, OPR_EQ=8,
@@ -92,11 +92,11 @@ public class LexState extends Constants {
OPR_AND=13, OPR_OR=14,
OPR_NOBINOPR=15;
static final int
static final int
OPR_MINUS=0, OPR_NOT=1, OPR_LEN=2, OPR_NOUNOPR=3;
/* exp kind */
static final int
static final int
VVOID = 0, /* no value */
VNIL = 1,
VTRUE = 2,
@@ -153,14 +153,14 @@ public class LexState extends Constants {
"::", "<eos>", "<number>", "<name>", "<string>", "<eof>",
};
final static int
final static int
/* terminal symbols denoted by reserved words */
TK_AND=257, TK_BREAK=258, TK_DO=259, TK_ELSE=260, TK_ELSEIF=261,
TK_END=262, TK_FALSE=263, TK_FOR=264, TK_FUNCTION=265, TK_GOTO=266, TK_IF=267,
TK_AND=257, TK_BREAK=258, TK_DO=259, TK_ELSE=260, TK_ELSEIF=261,
TK_END=262, TK_FALSE=263, TK_FOR=264, TK_FUNCTION=265, TK_GOTO=266, TK_IF=267,
TK_IN=268, TK_LOCAL=269, TK_NIL=270, TK_NOT=271, TK_OR=272, TK_REPEAT=273,
TK_RETURN=274, TK_THEN=275, TK_TRUE=276, TK_UNTIL=277, TK_WHILE=278,
/* other terminal symbols */
TK_CONCAT=279, TK_DOTS=280, TK_EQ=281, TK_GE=282, TK_LE=283, TK_NE=284,
TK_CONCAT=279, TK_DOTS=280, TK_EQ=281, TK_GE=282, TK_LE=283, TK_NE=284,
TK_DBCOLON=285, TK_EOS=286, TK_NUMBER=287, TK_NAME=288, TK_STRING=289;
final static int FIRST_RESERVED = TK_AND;
@@ -170,12 +170,12 @@ public class LexState extends Constants {
static {
for ( int i=0; i<NUM_RESERVED; i++ ) {
LuaString ts = (LuaString) LuaValue.valueOf( luaX_tokens[i] );
RESERVED.put(ts, new Integer(FIRST_RESERVED+i));
RESERVED.put(ts, FIRST_RESERVED+i);
}
}
private boolean isalnum(int c) {
return (c >= '0' && c <= '9')
return (c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c == '_');
@@ -188,17 +188,17 @@ public class LexState extends Constants {
}
private boolean isdigit(int c) {
return (c >= '0' && c <= '9');
return (c >= '0' && c <= '9');
}
private boolean isxdigit(int c) {
return (c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f')
|| (c >= 'A' && c <= 'F');
|| (c >= 'A' && c <= 'F');
}
private boolean isspace(int c) {
return (c <= ' ');
return (c >= 0 && c <= ' ');
}
@@ -235,7 +235,7 @@ public class LexState extends Constants {
String token2str( int token ) {
if ( token < FIRST_RESERVED ) {
return iscntrl(token)?
return iscntrl(token)?
L.pushfstring( "char("+((int)token)+")" ):
L.pushfstring( String.valueOf( (char) token ) );
} else {
@@ -388,8 +388,13 @@ public class LexState extends Constants {
seminfo.r = LuaValue.ZERO;
else if (str.indexOf('x')>=0 || str.indexOf('X')>=0)
seminfo.r = strx2number(str, seminfo);
else
seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim()));
else {
try {
seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim()));
} catch (NumberFormatException e) {
lexerror("malformed number (" + e.getMessage() + ")", TK_NUMBER);
}
}
return true;
}
@@ -408,7 +413,6 @@ public class LexState extends Constants {
else
break;
}
save('\0');
String str = new String(buff, 0, nbuff);
str2d(str, seminfo);
}
@@ -585,6 +589,13 @@ public class LexState extends Constants {
inclinenumber();
continue;
}
case ' ':
case '\f':
case '\t':
case 0x0B: /* \v */ {
nextChar();
continue;
}
case '-': {
nextChar();
if (current != '-')
@@ -688,19 +699,12 @@ public class LexState extends Constants {
return TK_EOS;
}
default: {
if (isspace(current)) {
_assert (!currIsNewline());
nextChar();
continue;
} else if (isdigit(current)) {
read_numeral(seminfo);
return TK_NUMBER;
} else if (isalpha(current) || current == '_') {
if (isalpha(current) || current == '_') {
/* identifier or reserved word */
LuaString ts;
do {
save_and_next();
} while (isalnum(current) || current == '_');
} while (isalnum(current));
ts = newstring(buff, 0, nbuff);
if ( RESERVED.containsKey(ts) )
return ((Integer)RESERVED.get(ts)).intValue();
@@ -821,7 +825,7 @@ public class LexState extends Constants {
/* dynamic structures used by the parser */
static class Dyndata {
Vardesc[] actvar; /* list of active local variables */
Vardesc[] actvar; /* list of active local variables */
int n_actvar = 0;
Labeldesc[] gt; /* list of pending gotos */
int n_gt = 0;
@@ -1610,7 +1614,7 @@ public class LexState extends Constants {
switch (t.token) {
case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_EOS:
return true;
case TK_UNTIL:
case TK_UNTIL:
return withuntil;
default: return false;
}
@@ -1634,7 +1638,7 @@ public class LexState extends Constants {
static class LHS_assign {
LHS_assign prev;
/* variable (global, local, upvalue, or indexed) */
expdesc v = new expdesc();
expdesc v = new expdesc();
};
@@ -1744,7 +1748,7 @@ public class LexState extends Constants {
fs.checkrepeated(dyd.label, dyd.n_label, label); /* check for repeated labels */
checknext(TK_DBCOLON); /* skip double colon */
/* create new entry for this label */
l = newlabelentry(dyd.label=grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.pc);
l = newlabelentry(dyd.label=grow(dyd.label, dyd.n_label+1), dyd.n_label++, label, line, fs.getlabel());
skipnoopstat(); /* skip other no-op statements */
if (block_follow(false)) { /* label is last no-op statement in the block? */
/* assume that locals are already out of scope */
@@ -1845,7 +1849,7 @@ public class LexState extends Constants {
if (this.testnext(','))
this.exp1(); /* optional step */
else { /* default step = 1 */
fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1)));
fs.codeK(fs.freereg, fs.numberK(LuaInteger.valueOf(1)));
fs.reserveregs(1);
}
this.forbody(base, line, 1, true);

View File

@@ -117,7 +117,7 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
protected CompileState() {}
/** Parse the input */
private Prototype luaY_parser(InputStream z, String name) throws IOException{
Prototype luaY_parser(InputStream z, String name) throws IOException{
LexState lexstate = new LexState(this, z);
FuncState funcstate = new FuncState();
// lexstate.buff = buff;

View File

@@ -1,483 +1,485 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.IOException;
import java.io.InputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua basic library functions.
* <p>
* This contains all library functions listed as "basic functions" in the lua documentation for JME.
* The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files.
* Since JME has no file system by default, {@link BaseLib} implements
* {@link ResourceFinder} using {@link Class#getResource(String)},
* which is the closest equivalent on JME.
* The default loader chain in {@link PackageLib} will use these as well.
* <p>
* To use basic library functions that include a {@link ResourceFinder} based on
* directory lookup, use {@link org.luaj.vm2.lib.jse.JseBaseLib} instead.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* <p>
* For special cases where the smallest possible footprint is desired,
* a minimal set of libraries could be loaded
* directly via {@link Globals#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* <p>
* This is a direct port of the corresponding library in C.
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see ResourceFinder
* @see Globals#finder
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a>
*/
public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals;
/** Perform one-time initialization on the library by adding base functions
* to the supplied environment, and returning it as the return value.
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
globals.finder = this;
globals.baselib = this;
env.set( "_G", env );
env.set( "_VERSION", Lua._VERSION );
env.set("assert", new _assert());
env.set("collectgarbage", new collectgarbage());
env.set("dofile", new dofile());
env.set("error", new error());
env.set("getmetatable", new getmetatable());
env.set("load", new load());
env.set("loadfile", new loadfile());
env.set("pcall", new pcall());
env.set("print", new print(this));
env.set("rawequal", new rawequal());
env.set("rawget", new rawget());
env.set("rawlen", new rawlen());
env.set("rawset", new rawset());
env.set("select", new select());
env.set("setmetatable", new setmetatable());
env.set("tonumber", new tonumber());
env.set("tostring", new tostring());
env.set("type", new type());
env.set("xpcall", new xpcall());
next next;
env.set("next", next = new next());
env.set("pairs", new pairs(next));
env.set("ipairs", new ipairs());
return env;
}
/** ResourceFinder implementation
*
* Tries to open the file as a resource, which can work for JSE and JME.
*/
public InputStream findResource(String filename) {
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename);
}
// "assert", // ( v [,message] ) -> v, message | ERR
static final class _assert extends VarArgFunction {
public Varargs invoke(Varargs args) {
if ( !args.arg1().toboolean() )
error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" );
return args;
}
}
// "collectgarbage", // ( opt [,arg] ) -> value
static final class collectgarbage extends VarArgFunction {
public Varargs invoke(Varargs args) {
String s = args.optjstring(1, "collect");
if ( "collect".equals(s) ) {
System.gc();
return ZERO;
} else if ( "count".equals(s) ) {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
return varargsOf(valueOf(used/1024.), valueOf(used%1024));
} else if ( "step".equals(s) ) {
System.gc();
return LuaValue.TRUE;
} else {
this.argerror("gc op");
}
return NIL;
}
}
// "dofile", // ( filename ) -> result1, ...
final class dofile extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
Varargs v = filename == null?
loadStream( globals.STDIN, "=stdin", "bt", globals ):
loadFile( args.checkjstring(1), "bt", globals );
return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
}
}
// "error", // ( message [,level] ) -> ERR
static final class error extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
throw arg1.isnil()? new LuaError(null, arg2.optint(1)):
arg1.isstring()? new LuaError(arg1.tojstring(), arg2.optint(1)):
new LuaError(arg1);
}
}
// "getmetatable", // ( object ) -> table
static final class getmetatable extends LibFunction {
public LuaValue call() {
return argerror(1, "value");
}
public LuaValue call(LuaValue arg) {
LuaValue mt = arg.getmetatable();
return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL;
}
}
// "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg
final class load extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue ld = args.arg1();
args.argcheck(ld.isstring() || ld.isfunction(), 1, "ld must be string or function");
String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)");
String mode = args.optjstring(3, "bt");
LuaValue env = args.optvalue(4, globals);
return loadStream(ld.isstring()? ld.strvalue().toInputStream():
new StringInputStream(ld.checkfunction()), source, mode, env);
}
}
// "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg
final class loadfile extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
String mode = args.optjstring(2, "bt");
LuaValue env = args.optvalue(3, globals);
return filename == null?
loadStream( globals.STDIN, "=stdin", mode, env ):
loadFile( filename, mode, env );
}
}
// "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkvalue(1);
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, func.invoke(args.subargs(2)));
} catch ( LuaError le ) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
}
// "print", // (...) -> void
final class print extends VarArgFunction {
final BaseLib baselib;
print(BaseLib baselib) {
this.baselib = baselib;
}
public Varargs invoke(Varargs args) {
LuaValue tostring = globals.get("tostring");
for ( int i=1, n=args.narg(); i<=n; i++ ) {
if ( i>1 ) globals.STDOUT.print( '\t' );
LuaString s = tostring.call( args.arg(i) ).strvalue();
globals.STDOUT.print(s.tojstring());
}
globals.STDOUT.println();
return NONE;
}
}
// "rawequal", // (v1, v2) -> boolean
static final class rawequal extends LibFunction {
public LuaValue call() {
return argerror(1, "value");
}
public LuaValue call(LuaValue arg) {
return argerror(2, "value");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return valueOf(arg1.raweq(arg2));
}
}
// "rawget", // (table, index) -> value
static final class rawget extends LibFunction {
public LuaValue call() {
return argerror(1, "value");
}
public LuaValue call(LuaValue arg) {
return argerror(2, "value");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return arg1.checktable().rawget(arg2);
}
}
// "rawlen", // (v) -> value
static final class rawlen extends LibFunction {
public LuaValue call(LuaValue arg) {
return valueOf(arg.rawlen());
}
}
// "rawset", // (table, index, value) -> table
static final class rawset extends LibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"value");
}
public LuaValue call(LuaValue table, LuaValue index) {
return argerror(3,"value");
}
public LuaValue call(LuaValue table, LuaValue index, LuaValue value) {
LuaTable t = table.checktable();
t.rawset(index.checknotnil(), value);
return t;
}
}
// "select", // (f, ...) -> value1, ...
static final class select extends VarArgFunction {
public Varargs invoke(Varargs args) {
int n = args.narg()-1;
if ( args.arg1().equals(valueOf("#")) )
return valueOf(n);
int i = args.checkint(1);
if ( i == 0 || i < -n )
argerror(1,"index out of range");
return args.subargs(i<0? n+i+2: i+1);
}
}
// "setmetatable", // (table, metatable) -> table
static final class setmetatable extends LibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"value");
}
public LuaValue call(LuaValue table, LuaValue metatable) {
final LuaValue mt0 = table.checktable().getmetatable();
if ( mt0!=null && !mt0.rawget(METATABLE).isnil() )
error("cannot change a protected metatable");
return table.setmetatable(metatable.isnil()? null: metatable.checktable());
}
}
// "tonumber", // (e [,base]) -> value
static final class tonumber extends LibFunction {
public LuaValue call(LuaValue e) {
return e.tonumber();
}
public LuaValue call(LuaValue e, LuaValue base) {
if (base.isnil())
return e.tonumber();
final int b = base.checkint();
if ( b < 2 || b > 36 )
argerror(2, "base out of range");
return e.checkstring().tonumber(b);
}
}
// "tostring", // (e) -> value
static final class tostring extends LibFunction {
public LuaValue call(LuaValue arg) {
LuaValue h = arg.metatag(TOSTRING);
if ( ! h.isnil() )
return h.call(arg);
LuaValue v = arg.tostring();
if ( ! v.isnil() )
return v;
return valueOf(arg.tojstring());
}
}
// "type", // (v) -> value
static final class type extends LibFunction {
public LuaValue call(LuaValue arg) {
return valueOf(arg.typename());
}
}
// "xpcall", // (f, err) -> result1, ...
final class xpcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
final LuaThread t = globals.running;
final LuaValue preverror = t.errorfunc;
t.errorfunc = args.checkvalue(2);
try {
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, args.arg1().invoke(args.subargs(3)));
} catch ( LuaError le ) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
} finally {
t.errorfunc = preverror;
}
}
}
// "pairs" (t) -> iter-func, t, nil
static final class pairs extends VarArgFunction {
final next next;
pairs(next next) {
this.next = next;
}
public Varargs invoke(Varargs args) {
return varargsOf( next, args.checktable(1), NIL );
}
}
// // "ipairs", // (t) -> iter-func, t, 0
static final class ipairs extends VarArgFunction {
inext inext = new inext();
public Varargs invoke(Varargs args) {
return varargsOf( inext, args.checktable(1), ZERO );
}
}
// "next" ( table, [index] ) -> next-index, next-value
static final class next extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).next(args.arg(2));
}
}
// "inext" ( table, [int-index] ) -> next-index, next-value
static final class inext extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).inext(args.arg(2));
}
}
/**
* Load from a named file, returning the chunk or nil,error of can't load
* @param env
* @param mode
* @return Varargs containing chunk, or NIL,error-text on error
*/
public Varargs loadFile(String filename, String mode, LuaValue env) {
InputStream is = globals.finder.findResource(filename);
if ( is == null )
return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory"));
try {
return loadStream(is, "@"+filename, mode, env);
} finally {
try {
is.close();
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) {
try {
if ( is == null )
return varargsOf(NIL, valueOf("not found: "+chunkname));
return globals.load(is, chunkname, mode, env);
} catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
private static class StringInputStream extends InputStream {
final LuaValue func;
byte[] bytes;
int offset, remaining = 0;
StringInputStream(LuaValue func) {
this.func = func;
}
public int read() throws IOException {
if ( remaining <= 0 ) {
LuaValue s = func.call();
if ( s.isnil() )
return -1;
LuaString ls = s.strvalue();
bytes = ls.m_bytes;
offset = ls.m_offset;
remaining = ls.m_length;
if (remaining <= 0)
return -1;
}
--remaining;
return bytes[offset++];
}
}
}
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.IOException;
import java.io.InputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua basic library functions.
* <p>
* This contains all library functions listed as "basic functions" in the lua documentation for JME.
* The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files.
* Since JME has no file system by default, {@link BaseLib} implements
* {@link ResourceFinder} using {@link Class#getResource(String)},
* which is the closest equivalent on JME.
* The default loader chain in {@link PackageLib} will use these as well.
* <p>
* To use basic library functions that include a {@link ResourceFinder} based on
* directory lookup, use {@link org.luaj.vm2.lib.jse.JseBaseLib} instead.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* <p>
* For special cases where the smallest possible footprint is desired,
* a minimal set of libraries could be loaded
* directly via {@link Globals#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* <p>
* This is a direct port of the corresponding library in C.
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see ResourceFinder
* @see Globals#finder
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a>
*/
public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals;
/** Perform one-time initialization on the library by adding base functions
* to the supplied environment, and returning it as the return value.
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
globals.finder = this;
globals.baselib = this;
env.set( "_G", env );
env.set( "_VERSION", Lua._VERSION );
env.set("assert", new _assert());
env.set("collectgarbage", new collectgarbage());
env.set("dofile", new dofile());
env.set("error", new error());
env.set("getmetatable", new getmetatable());
env.set("load", new load());
env.set("loadfile", new loadfile());
env.set("pcall", new pcall());
env.set("print", new print(this));
env.set("rawequal", new rawequal());
env.set("rawget", new rawget());
env.set("rawlen", new rawlen());
env.set("rawset", new rawset());
env.set("select", new select());
env.set("setmetatable", new setmetatable());
env.set("tonumber", new tonumber());
env.set("tostring", new tostring());
env.set("type", new type());
env.set("xpcall", new xpcall());
next next;
env.set("next", next = new next());
env.set("pairs", new pairs(next));
env.set("ipairs", new ipairs());
return env;
}
/** ResourceFinder implementation
*
* Tries to open the file as a resource, which can work for JSE and JME.
*/
public InputStream findResource(String filename) {
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename);
}
// "assert", // ( v [,message] ) -> v, message | ERR
static final class _assert extends VarArgFunction {
public Varargs invoke(Varargs args) {
if ( !args.arg1().toboolean() )
error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" );
return args;
}
}
// "collectgarbage", // ( opt [,arg] ) -> value
static final class collectgarbage extends VarArgFunction {
public Varargs invoke(Varargs args) {
String s = args.optjstring(1, "collect");
if ( "collect".equals(s) ) {
System.gc();
return ZERO;
} else if ( "count".equals(s) ) {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
return varargsOf(valueOf(used/1024.), valueOf(used%1024));
} else if ( "step".equals(s) ) {
System.gc();
return LuaValue.TRUE;
} else {
argerror(1, "invalid option '" + s + "'");
}
return NIL;
}
}
// "dofile", // ( filename ) -> result1, ...
final class dofile extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
Varargs v = filename == null?
loadStream( globals.STDIN, "=stdin", "bt", globals ):
loadFile( args.checkjstring(1), "bt", globals );
return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
}
}
// "error", // ( message [,level] ) -> ERR
static final class error extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
if (arg1.isnil()) throw new LuaError(NIL);
if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1);
throw new LuaError(arg1.tojstring(), arg2.optint(1));
}
}
// "getmetatable", // ( object ) -> table
static final class getmetatable extends LibFunction {
public LuaValue call() {
return argerror(1, "value expected");
}
public LuaValue call(LuaValue arg) {
LuaValue mt = arg.getmetatable();
return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL;
}
}
// "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg
final class load extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue ld = args.arg1();
if (!ld.isstring() && !ld.isfunction()) {
throw new LuaError("bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")");
}
String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)");
String mode = args.optjstring(3, "bt");
LuaValue env = args.optvalue(4, globals);
return loadStream(ld.isstring()? ld.strvalue().toInputStream():
new StringInputStream(ld.checkfunction()), source, mode, env);
}
}
// "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg
final class loadfile extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
String mode = args.optjstring(2, "bt");
LuaValue env = args.optvalue(3, globals);
return filename == null?
loadStream( globals.STDIN, "=stdin", mode, env ):
loadFile( filename, mode, env );
}
}
// "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkvalue(1);
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, func.invoke(args.subargs(2)));
} catch ( LuaError le ) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
}
// "print", // (...) -> void
final class print extends VarArgFunction {
final BaseLib baselib;
print(BaseLib baselib) {
this.baselib = baselib;
}
public Varargs invoke(Varargs args) {
LuaValue tostring = globals.get("tostring");
for ( int i=1, n=args.narg(); i<=n; i++ ) {
if ( i>1 ) globals.STDOUT.print( '\t' );
LuaString s = tostring.call( args.arg(i) ).strvalue();
globals.STDOUT.print(s.tojstring());
}
globals.STDOUT.print('\n');
return NONE;
}
}
// "rawequal", // (v1, v2) -> boolean
static final class rawequal extends LibFunction {
public LuaValue call() {
return argerror(1, "value expected");
}
public LuaValue call(LuaValue arg) {
return argerror(2, "value expected");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return valueOf(arg1.raweq(arg2));
}
}
// "rawget", // (table, index) -> value
static final class rawget extends TableLibFunction {
public LuaValue call(LuaValue arg) {
return argerror(2, "value expected");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return arg1.checktable().rawget(arg2);
}
}
// "rawlen", // (v) -> value
static final class rawlen extends LibFunction {
public LuaValue call(LuaValue arg) {
return valueOf(arg.rawlen());
}
}
// "rawset", // (table, index, value) -> table
static final class rawset extends TableLibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"value expected");
}
public LuaValue call(LuaValue table, LuaValue index) {
return argerror(3,"value expected");
}
public LuaValue call(LuaValue table, LuaValue index, LuaValue value) {
LuaTable t = table.checktable();
if (!index.isvalidkey()) argerror(2, "table index is nil");
t.rawset(index, value);
return t;
}
}
// "select", // (f, ...) -> value1, ...
static final class select extends VarArgFunction {
public Varargs invoke(Varargs args) {
int n = args.narg()-1;
if ( args.arg1().equals(valueOf("#")) )
return valueOf(n);
int i = args.checkint(1);
if ( i == 0 || i < -n )
argerror(1,"index out of range");
return args.subargs(i<0? n+i+2: i+1);
}
}
// "setmetatable", // (table, metatable) -> table
static final class setmetatable extends TableLibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"nil or table expected");
}
public LuaValue call(LuaValue table, LuaValue metatable) {
final LuaValue mt0 = table.checktable().getmetatable();
if ( mt0!=null && !mt0.rawget(METATABLE).isnil() )
error("cannot change a protected metatable");
return table.setmetatable(metatable.isnil()? null: metatable.checktable());
}
}
// "tonumber", // (e [,base]) -> value
static final class tonumber extends LibFunction {
public LuaValue call(LuaValue e) {
return e.tonumber();
}
public LuaValue call(LuaValue e, LuaValue base) {
if (base.isnil())
return e.tonumber();
final int b = base.checkint();
if ( b < 2 || b > 36 )
argerror(2, "base out of range");
return e.checkstring().tonumber(b);
}
}
// "tostring", // (e) -> value
static final class tostring extends LibFunction {
public LuaValue call(LuaValue arg) {
LuaValue h = arg.metatag(TOSTRING);
if ( ! h.isnil() )
return h.call(arg);
LuaValue v = arg.tostring();
if ( ! v.isnil() )
return v;
return valueOf(arg.tojstring());
}
}
// "type", // (v) -> value
static final class type extends LibFunction {
public LuaValue call(LuaValue arg) {
return valueOf(arg.typename());
}
}
// "xpcall", // (f, err) -> result1, ...
final class xpcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
final LuaThread t = globals.running;
final LuaValue preverror = t.errorfunc;
t.errorfunc = args.checkvalue(2);
try {
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, args.arg1().invoke(args.subargs(3)));
} catch ( LuaError le ) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
} finally {
t.errorfunc = preverror;
}
}
}
// "pairs" (t) -> iter-func, t, nil
static final class pairs extends VarArgFunction {
final next next;
pairs(next next) {
this.next = next;
}
public Varargs invoke(Varargs args) {
return varargsOf( next, args.checktable(1), NIL );
}
}
// // "ipairs", // (t) -> iter-func, t, 0
static final class ipairs extends VarArgFunction {
inext inext = new inext();
public Varargs invoke(Varargs args) {
return varargsOf( inext, args.checktable(1), ZERO );
}
}
// "next" ( table, [index] ) -> next-index, next-value
static final class next extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).next(args.arg(2));
}
}
// "inext" ( table, [int-index] ) -> next-index, next-value
static final class inext extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).inext(args.arg(2));
}
}
/**
* Load from a named file, returning the chunk or nil,error of can't load
* @param env
* @param mode
* @return Varargs containing chunk, or NIL,error-text on error
*/
public Varargs loadFile(String filename, String mode, LuaValue env) {
InputStream is = globals.finder.findResource(filename);
if ( is == null )
return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory"));
try {
return loadStream(is, "@"+filename, mode, env);
} finally {
try {
is.close();
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) {
try {
if ( is == null )
return varargsOf(NIL, valueOf("not found: "+chunkname));
return globals.load(is, chunkname, mode, env);
} catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
private static class StringInputStream extends InputStream {
final LuaValue func;
byte[] bytes;
int offset, remaining = 0;
StringInputStream(LuaValue func) {
this.func = func;
}
public int read() throws IOException {
if ( remaining < 0 )
return -1;
if ( remaining == 0 ) {
LuaValue s = func.call();
if ( s.isnil() )
return remaining = -1;
LuaString ls = s.strvalue();
bytes = ls.m_bytes;
offset = ls.m_offset;
remaining = ls.m_length;
if (remaining <= 0)
return -1;
}
--remaining;
return 0xFF&bytes[offset++];
}
}
}

View File

@@ -27,15 +27,15 @@ import org.luaj.vm2.Varargs;
/**
* Subclass of LibFunction that implements the Lua standard {@code bit32} library.
* <p>
* Typically, this library is included as part of a call to either
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -71,7 +71,7 @@ public class Bit32Lib extends TwoArgFunction {
"arshift", "lrotate", "lshift", "rrotate", "rshift"
});
env.set("bit32", t);
env.get("package").get("loaded").set("bit32", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("bit32", t);
return t;
}
@@ -219,6 +219,6 @@ public class Bit32Lib extends TwoArgFunction {
}
private static LuaValue bitsToValue( int x ) {
return ( x < 0 ) ? valueOf((double) ((long) x & 0xFFFFFFFFL)) : valueOf(x);
return ( x < 0 ) ? valueOf((double) ((long) x & 0xFFFFFFFFL)) : valueOf(x);
}
}

View File

@@ -27,25 +27,25 @@ import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code coroutine}
* library.
* <p>
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code coroutine}
* library.
* <p>
* The coroutine library in luaj has the same behavior as the
* coroutine library in C, but is implemented using Java Threads to maintain
* the call state between invocations. Therefore it can be yielded from anywhere,
* coroutine library in C, but is implemented using Java Threads to maintain
* the call state between invocations. Therefore it can be yielded from anywhere,
* similar to the "Coco" yield-from-anywhere patch available for C-based lua.
* However, coroutines that are yielded but never resumed to complete their execution
* may not be collected by the garbage collector.
* <p>
* Typically, this library is included as part of a call to either
* may not be collected by the garbage collector.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("coroutine").get("running").call() );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -79,10 +79,10 @@ public class CoroutineLib extends TwoArgFunction {
coroutine.set("resume", new resume());
coroutine.set("running", new running());
coroutine.set("status", new status());
coroutine.set("yield", new yield());
coroutine.set("yield", new Yield());
coroutine.set("wrap", new wrap());
env.set("coroutine", coroutine);
env.get("package").get("loaded").set("coroutine", coroutine);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("coroutine", coroutine);
return coroutine;
}
@@ -92,7 +92,7 @@ public class CoroutineLib extends TwoArgFunction {
}
}
final class resume extends VarArgFunction {
static final class resume extends VarArgFunction {
public Varargs invoke(Varargs args) {
final LuaThread t = args.checkthread(1);
return t.resume( args.subargs(2) );
@@ -113,7 +113,7 @@ public class CoroutineLib extends TwoArgFunction {
}
}
final class yield extends VarArgFunction {
private final class Yield extends VarArgFunction {
public Varargs invoke(Varargs args) {
return globals.yield( args );
}
@@ -127,7 +127,7 @@ public class CoroutineLib extends TwoArgFunction {
}
}
final class wrapper extends VarArgFunction {
static final class wrapper extends VarArgFunction {
final LuaThread luathread;
wrapper(LuaThread luathread) {
this.luathread = luathread;

View File

@@ -21,6 +21,9 @@
******************************************************************************/
package org.luaj.vm2.lib;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaBoolean;
@@ -38,26 +41,26 @@ import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code debug}
* library.
* <p>
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code debug}
* library.
* <p>
* The debug library in luaj tries to emulate the behavior of the corresponding C-based lua library.
* To do this, it must maintain a separate stack of calls to {@link LuaClosure} and {@link LibFunction}
* instances.
* To do this, it must maintain a separate stack of calls to {@link LuaClosure} and {@link LibFunction}
* instances.
* Especially when lua-to-java bytecode compiling is being used
* via a {@link org.luaj.vm2.Globals.Compiler} such as {@link org.luaj.vm2.luajc.LuaJC},
* this cannot be done in all cases.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#debugGlobals()} or
* via a {@link org.luaj.vm2.Globals.Compiler} such as {@link org.luaj.vm2.luajc.LuaJC},
* this cannot be done in all cases.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#debugGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#debugGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.debugGlobals();
* System.out.println( globals.get("debug").get("traceback").call() );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -84,27 +87,27 @@ public class DebugLib extends TwoArgFunction {
try { TRACE = (null != System.getProperty("TRACE")); } catch (Exception e) {}
}
private static final LuaString LUA = valueOf("Lua");
private static final LuaString QMARK = valueOf("?");
private static final LuaString CALL = valueOf("call");
private static final LuaString LINE = valueOf("line");
private static final LuaString COUNT = valueOf("count");
static final LuaString LUA = valueOf("Lua");
private static final LuaString QMARK = valueOf("?");
private static final LuaString CALL = valueOf("call");
private static final LuaString LINE = valueOf("line");
private static final LuaString COUNT = valueOf("count");
private static final LuaString RETURN = valueOf("return");
private static final LuaString FUNC = valueOf("func");
private static final LuaString ISTAILCALL = valueOf("istailcall");
private static final LuaString ISVARARG = valueOf("isvararg");
private static final LuaString NUPS = valueOf("nups");
private static final LuaString NPARAMS = valueOf("nparams");
private static final LuaString NAME = valueOf("name");
private static final LuaString NAMEWHAT = valueOf("namewhat");
private static final LuaString WHAT = valueOf("what");
private static final LuaString SOURCE = valueOf("source");
private static final LuaString SHORT_SRC = valueOf("short_src");
private static final LuaString LINEDEFINED = valueOf("linedefined");
private static final LuaString LASTLINEDEFINED = valueOf("lastlinedefined");
private static final LuaString CURRENTLINE = valueOf("currentline");
private static final LuaString ACTIVELINES = valueOf("activelines");
static final LuaString FUNC = valueOf("func");
static final LuaString ISTAILCALL = valueOf("istailcall");
static final LuaString ISVARARG = valueOf("isvararg");
static final LuaString NUPS = valueOf("nups");
static final LuaString NPARAMS = valueOf("nparams");
static final LuaString NAME = valueOf("name");
static final LuaString NAMEWHAT = valueOf("namewhat");
static final LuaString WHAT = valueOf("what");
static final LuaString SOURCE = valueOf("source");
static final LuaString SHORT_SRC = valueOf("short_src");
static final LuaString LINEDEFINED = valueOf("linedefined");
static final LuaString LASTLINEDEFINED = valueOf("lastlinedefined");
static final LuaString CURRENTLINE = valueOf("currentline");
static final LuaString ACTIVELINES = valueOf("activelines");
Globals globals;
@@ -135,19 +138,19 @@ public class DebugLib extends TwoArgFunction {
debug.set("upvalueid", new upvalueid());
debug.set("upvaluejoin", new upvaluejoin());
env.set("debug", debug);
env.get("package").get("loaded").set("debug", debug);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("debug", debug);
return debug;
}
// debug.debug()
static final class debug extends ZeroArgFunction {
static final class debug extends ZeroArgFunction {
public LuaValue call() {
return NONE;
}
}
// debug.gethook ([thread])
final class gethook extends VarArgFunction {
final class gethook extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaThread t = args.narg() > 0 ? args.checkthread(1): globals.running;
LuaThread.State s = t.state;
@@ -159,10 +162,10 @@ public class DebugLib extends TwoArgFunction {
}
// debug.getinfo ([thread,] f [, what])
final class getinfo extends VarArgFunction {
final class getinfo extends VarArgFunction {
public Varargs invoke(Varargs args) {
int a=1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
LuaValue func = args.arg(a++);
String what = args.optjstring(a++, "flnStu");
DebugLib.CallStack callstack = callstack(thread);
@@ -222,10 +225,10 @@ public class DebugLib extends TwoArgFunction {
}
// debug.getlocal ([thread,] f, local)
final class getlocal extends VarArgFunction {
final class getlocal extends VarArgFunction {
public Varargs invoke(Varargs args) {
int a=1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
int level = args.checkint(a++);
int local = args.checkint(a++);
CallFrame f = callstack(thread).getCallFrame(level);
@@ -234,7 +237,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.getmetatable (value)
final class getmetatable extends LibFunction {
static final class getmetatable extends LibFunction {
public LuaValue call(LuaValue v) {
LuaValue mt = v.getmetatable();
return mt != null? mt: NIL;
@@ -249,7 +252,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.getupvalue (f, up)
static final class getupvalue extends VarArgFunction {
static final class getupvalue extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1);
int up = args.checkint(2);
@@ -265,7 +268,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.getuservalue (u)
static final class getuservalue extends LibFunction {
static final class getuservalue extends LibFunction {
public LuaValue call(LuaValue u) {
return u.isuserdata()? u: NIL;
}
@@ -273,10 +276,10 @@ public class DebugLib extends TwoArgFunction {
// debug.sethook ([thread,] hook, mask [, count])
final class sethook extends VarArgFunction {
final class sethook extends VarArgFunction {
public Varargs invoke(Varargs args) {
int a=1;
LuaThread t = args.isthread(a)? args.checkthread(a++): globals.running;
LuaThread t = args.isthread(a)? args.checkthread(a++): globals.running;
LuaValue func = args.optfunction(a++, null);
String str = args.optjstring(a++,"");
int count = args.optint(a++,0);
@@ -298,20 +301,20 @@ public class DebugLib extends TwoArgFunction {
}
// debug.setlocal ([thread,] level, local, value)
final class setlocal extends VarArgFunction {
final class setlocal extends VarArgFunction {
public Varargs invoke(Varargs args) {
int a=1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
int level = args.checkint(a++);
int local = args.checkint(a++);
LuaValue value = args.arg(a++);
CallFrame f = callstack(thread).getCallFrame(level);
CallFrame f = callstack(thread).getCallFrame(level);
return f != null? f.setLocal(local, value): NONE;
}
}
// debug.setmetatable (value, table)
final class setmetatable extends TwoArgFunction {
static final class setmetatable extends TwoArgFunction {
public LuaValue call(LuaValue value, LuaValue table) {
LuaValue mt = table.opttable(null);
switch ( value.type() ) {
@@ -328,7 +331,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.setupvalue (f, up, value)
final class setupvalue extends VarArgFunction {
static final class setupvalue extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1);
int up = args.checkint(2);
@@ -346,7 +349,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.setuservalue (udata, value)
final class setuservalue extends VarArgFunction {
static final class setuservalue extends VarArgFunction {
public Varargs invoke(Varargs args) {
Object o = args.checkuserdata(1);
LuaValue v = args.checkvalue(2);
@@ -358,10 +361,10 @@ public class DebugLib extends TwoArgFunction {
}
// debug.traceback ([thread,] [message [, level]])
final class traceback extends VarArgFunction {
final class traceback extends VarArgFunction {
public Varargs invoke(Varargs args) {
int a=1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
String message = args.optjstring(a++, null);
int level = args.optint(a++,1);
String tb = callstack(thread).traceback(level);
@@ -370,7 +373,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.upvalueid (f, n)
final class upvalueid extends VarArgFunction {
static final class upvalueid extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1);
int up = args.checkint(2);
@@ -385,7 +388,7 @@ public class DebugLib extends TwoArgFunction {
}
// debug.upvaluejoin (f1, n1, f2, n2)
final class upvaluejoin extends VarArgFunction {
static final class upvaluejoin extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaClosure f1 = args.checkclosure(1);
int n1 = args.checkint(2);
@@ -442,6 +445,10 @@ public class DebugLib extends TwoArgFunction {
return callstack().traceback(level);
}
public CallFrame getCallFrame(int level) {
return callstack().getCallFrame(level);
}
void callHook(LuaThread.State s, LuaValue type, LuaValue arg) {
if (s.inhook || s.hookfunc == null) return;
s.inhook = true;
@@ -503,43 +510,75 @@ public class DebugLib extends TwoArgFunction {
final static CallFrame[] EMPTY = {};
CallFrame[] frame = EMPTY;
int calls = 0;
Lock lock = new ReentrantLock();
CallStack() {}
synchronized int currentline() {
return calls > 0? frame[calls-1].currentline(): -1;
}
private synchronized CallFrame pushcall() {
if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2);
CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i)
f[i] = new CallFrame();
frame = f;
for (int i = 1; i < n; ++i)
f[i].previous = f[i-1];
int currentline() {
lock.lock();
try {
return calls > 0? frame[calls-1].currentline(): -1;
} finally {
lock.unlock();
}
return frame[calls++];
}
final synchronized void onCall(LuaFunction function) {
pushcall().set(function);
}
final synchronized void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
pushcall().set(function, varargs, stack);
private CallFrame pushcall() {
lock.lock();
try {
if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2);
CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i)
f[i] = new CallFrame();
frame = f;
for (int i = 1; i < n; ++i)
f[i].previous = f[i-1];
}
return frame[calls++];
} finally {
lock.unlock();
}
}
final synchronized void onReturn() {
if (calls > 0)
frame[--calls].reset();
final void onCall(LuaFunction function) {
lock.lock();
try {
pushcall().set(function);
} finally {
lock.unlock();
}
}
final void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
lock.lock();
try {
pushcall().set(function, varargs, stack);
} finally {
lock.unlock();
}
}
final synchronized void onInstruction(int pc, Varargs v, int top) {
if (calls > 0)
frame[calls-1].instr(pc, v, top);
final void onReturn() {
lock.lock();
try {
if (calls > 0)
frame[--calls].reset();
} finally {
lock.unlock();
}
}
final void onInstruction(int pc, Varargs v, int top) {
lock.lock();
try {
if (calls > 0)
frame[calls-1].instr(pc, v, top);
} finally {
lock.unlock();
}
}
/**
@@ -547,101 +586,125 @@ public class DebugLib extends TwoArgFunction {
* @param level
* @return String containing the traceback.
*/
synchronized String traceback(int level) {
StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" );
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
sb.append("\n\t");
sb.append( c.shortsource() );
sb.append( ':' );
if (c.currentline() > 0)
sb.append( c.currentline()+":" );
sb.append( " in " );
DebugInfo ar = auxgetinfo("n", c.f, c);
if (c.linedefined() == 0)
sb.append("main chunk");
else if ( ar.name != null ) {
sb.append( "function '" );
sb.append( ar.name );
sb.append( '\'' );
} else {
sb.append( "function <"+c.shortsource()+":"+c.linedefined()+">" );
String traceback(int level) {
lock.lock();
try {
StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" );
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
sb.append("\n\t");
sb.append( c.shortsource() );
sb.append( ':' );
if (c.currentline() > 0)
sb.append( c.currentline()+":" );
sb.append( " in " );
DebugInfo ar = auxgetinfo("n", c.f, c);
if (c.linedefined() == 0)
sb.append("main chunk");
else if ( ar.name != null ) {
sb.append( "function '" );
sb.append( ar.name );
sb.append( '\'' );
} else {
sb.append( "function <" );
sb.append( c.shortsource() );
sb.append( ':' );
sb.append( c.linedefined() );
sb.append( '>' );
}
}
sb.append("\n\t[Java]: in ?");
return sb.toString();
} finally {
lock.unlock();
}
sb.append("\n\t[Java]: in ?");
return sb.toString();
}
synchronized DebugLib.CallFrame getCallFrame(int level) {
if (level < 1 || level > calls)
DebugLib.CallFrame getCallFrame(int level) {
lock.lock();
try {
if (level < 1 || level > calls)
return null;
return frame[calls-level];
} finally {
lock.unlock();
}
}
DebugLib.CallFrame findCallFrame(LuaValue func) {
lock.lock();
try {
for (int i = 1; i <= calls; ++i)
if (frame[calls-i].f == func)
return frame[i];
return null;
return frame[calls-level];
} finally {
lock.unlock();
}
}
synchronized DebugLib.CallFrame findCallFrame(LuaValue func) {
for (int i = 1; i <= calls; ++i)
if (frame[calls-i].f == func)
return frame[i];
return null;
}
synchronized DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
DebugInfo ar = new DebugInfo();
for (int i = 0, n = what.length(); i < n; ++i) {
switch (what.charAt(i)) {
case 'S':
ar.funcinfo(f);
break;
case 'l':
ar.currentline = ci != null && ci.f.isclosure()? ci.currentline(): -1;
break;
case 'u':
if (f != null && f.isclosure()) {
Prototype p = f.checkclosure().p;
ar.nups = (short) p.upvalues.length;
ar.nparams = (short) p.numparams;
ar.isvararg = p.is_vararg != 0;
} else {
ar.nups = 0;
ar.isvararg = true;
ar.nparams = 0;
}
break;
case 't':
ar.istailcall = false;
break;
case 'n': {
/* calling function is a known Lua function? */
if (ci != null && ci.previous != null) {
if (ci.previous.f.isclosure()) {
NameWhat nw = getfuncname(ci.previous);
if (nw != null) {
ar.name = nw.name;
ar.namewhat = nw.namewhat;
DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
lock.lock();
try {
DebugInfo ar = new DebugInfo();
for (int i = 0, n = what.length(); i < n; ++i) {
switch (what.charAt(i)) {
case 'S':
ar.funcinfo(f);
break;
case 'l':
ar.currentline = ci != null && ci.f.isclosure()? ci.currentline(): -1;
break;
case 'u':
if (f != null && f.isclosure()) {
Prototype p = f.checkclosure().p;
ar.nups = (short) p.upvalues.length;
ar.nparams = (short) p.numparams;
ar.isvararg = p.is_vararg != 0;
} else {
ar.nups = 0;
ar.isvararg = true;
ar.nparams = 0;
}
break;
case 't':
ar.istailcall = false;
break;
case 'n': {
/* calling function is a known Lua function? */
if (ci != null && ci.previous != null) {
if (ci.previous.f.isclosure()) {
NameWhat nw = getfuncname(ci.previous);
if (nw != null) {
ar.name = nw.name;
ar.namewhat = nw.namewhat;
}
}
}
}
if (ar.namewhat == null) {
ar.namewhat = ""; /* not found */
ar.name = null;
}
break;
}
case 'L':
case 'f':
break;
default:
// TODO: return bad status.
break;
}
if (ar.namewhat == null) {
ar.namewhat = ""; /* not found */
ar.name = null;
}
break;
}
case 'L':
case 'f':
break;
default:
// TODO: return bad status.
break;
}
}
return ar;
} finally {
lock.unlock();
}
return ar;
}
}
}
static class CallFrame {
public static class CallFrame {
LuaFunction f;
int pc;
int top;
@@ -673,30 +736,30 @@ public class DebugLib extends TwoArgFunction {
}
Varargs getLocal(int i) {
LuaString name = getlocalname(i);
if ( name != null )
return varargsOf( name, stack[i-1] );
if ( i >= 1 && i <= stack.length && stack[i-1] != null )
return varargsOf( name == null ? NIL : name, stack[i-1] );
else
return NIL;
}
Varargs setLocal(int i, LuaValue value) {
LuaString name = getlocalname(i);
if ( name != null ) {
if ( i >= 1 && i <= stack.length && stack[i-1] != null ) {
stack[i-1] = value;
return name;
return name == null ? NIL : name;
} else {
return NIL;
}
}
int currentline() {
public int currentline() {
if ( !f.isclosure() ) return -1;
int[] li = f.checkclosure().p.lineinfo;
return li==null || pc<0 || pc>=li.length? -1: li[pc];
return li==null || pc<0 || pc>=li.length? -1: li[pc];
}
String sourceline() {
if ( !f.isclosure() ) return f.tojstring();
return f.checkclosure().p.shortsource() + ":" + currentline();
}
private int linedefined() {
int linedefined() {
return f.isclosure()? f.checkclosure().p.linedefined: -1;
}
LuaString getlocalname(int index) {
@@ -717,7 +780,7 @@ public class DebugLib extends TwoArgFunction {
static void lua_assert(boolean x) {
if (!x) throw new RuntimeException("lua_assert failed");
}
}
static class NameWhat {
final String name;
@@ -792,13 +855,13 @@ public class DebugLib extends TwoArgFunction {
LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE) /* name of indexed variable */
? p.getlocalname(t + 1, pc)
: (t < p.upvalues.length ? p.upvalues[t].name : QMARK);
name = kname(p, k);
return new NameWhat( name.tojstring(), vn != null && vn.eq_b(ENV)? "global": "field" );
String jname = kname(p, pc, k);
return new NameWhat( jname, vn != null && vn.eq_b(ENV)? "global": "field" );
}
case Lua.OP_GETUPVAL: {
int u = Lua.GETARG_B(i); /* upvalue index */
name = u < p.upvalues.length ? p.upvalues[u].name : QMARK;
return new NameWhat( name.tojstring(), "upvalue" );
return name == null ? null : new NameWhat( name.tojstring(), "upvalue" );
}
case Lua.OP_LOADK:
case Lua.OP_LOADKX: {
@@ -812,8 +875,8 @@ public class DebugLib extends TwoArgFunction {
}
case Lua.OP_SELF: {
int k = Lua.GETARG_C(i); /* key index */
name = kname(p, k);
return new NameWhat( name.tojstring(), "method" );
String jname = kname(p, pc, k);
return new NameWhat( jname, "method" );
}
default:
break;
@@ -822,11 +885,20 @@ public class DebugLib extends TwoArgFunction {
return null; /* no useful name found */
}
static LuaString kname(Prototype p, int c) {
if (Lua.ISK(c) && p.k[Lua.INDEXK(c)].isstring())
return p.k[Lua.INDEXK(c)].strvalue();
else
return QMARK;
static String kname(Prototype p, int pc, int c) {
if (Lua.ISK(c)) { /* is 'c' a constant? */
LuaValue k = p.k[Lua.INDEXK(c)];
if (k.isstring()) { /* literal constant? */
return k.tojstring(); /* it is its own name */
} /* else no reasonable name found */
} else { /* 'c' is a register */
NameWhat what = getobjname(p, pc, c); /* search for 'c' */
if (what != null && "constant".equals(what.namewhat)) { /* found a constant name? */
return what.name; /* 'name' already filled */
}
/* else no reasonable name found */
}
return "?"; /* no reasonable name found */
}
/*
@@ -867,6 +939,10 @@ public class DebugLib extends TwoArgFunction {
if (reg == a) setreg = pc; /* jumped code can change 'a' */
break;
}
case Lua.OP_SETLIST: { // Lua.testAMode(Lua.OP_SETLIST) == false
if ( ((i>>14)&0x1ff) == 0 ) pc++; // if c == 0 then c stored in next op -> skip
break;
}
default:
if (Lua.testAMode(op) && reg == a) /* any instruction that set A */
setreg = pc;

View File

@@ -31,31 +31,31 @@ import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class extending {@link LibFunction} which implements the
* core of the lua standard {@code io} library.
* <p>
/**
* Abstract base class extending {@link LibFunction} which implements the
* core of the lua standard {@code io} library.
* <p>
* It contains the implementation of the io library support that is common to
* the JSE and JME platforms.
* In practice on of the concrete IOLib subclasses is chosen:
* {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* the JSE and JME platforms.
* In practice on of the concrete IOLib subclasses is chosen:
* {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* {@link org.luaj.vm2.lib.jme.JmeIoLib} for the JME platform.
* <p>
* The JSE implementation conforms almost completely to the C-based lua library,
* while the JME implementation follows closely except in the area of random-access files,
* which are difficult to support properly on JME.
* <p>
* Typically, this library is included as part of a call to either
* The JSE implementation conforms almost completely to the C-based lua library,
* while the JME implementation follows closely except in the area of random-access files,
* which are difficult to support properly on JME.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre>
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseIoLib} library will be loaded, which will include
* the base functionality provided by this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load the
* the base functionality provided by this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load the
* {@link org.luaj.vm2.lib.jse.JseIoLib}.
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -73,10 +73,10 @@ import org.luaj.vm2.Varargs;
* @see org.luaj.vm2.lib.jme.JmeIoLib
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a>
*/
abstract
abstract
public class IoLib extends TwoArgFunction {
abstract
abstract
protected class File extends LuaValue{
abstract public void write( LuaString string ) throws IOException;
abstract public void flush() throws IOException;
@@ -85,16 +85,22 @@ public class IoLib extends TwoArgFunction {
abstract public boolean isclosed();
// returns new position
abstract public int seek(String option, int bytecount) throws IOException;
abstract public void setvbuf(String mode, int size);
abstract public void setvbuf(String mode, int size);
// get length remaining to read
abstract public int remaining() throws IOException;
abstract public int remaining() throws IOException;
// peek ahead one character
abstract public int peek() throws IOException, EOFException;
// return char if read, -1 if eof, throw IOException on other exception
abstract public int peek() throws IOException, EOFException;
// return char if read, -1 if eof, throw IOException on other exception
abstract public int read() throws IOException, EOFException;
// return number of bytes read if positive, false if eof, throw IOException on other exception
abstract public int read(byte[] bytes, int offset, int length) throws IOException;
public boolean eof() throws IOException {
try {
return peek() < 0;
} catch (EOFException e) { return true; }
}
// delegate method access to file methods table
public LuaValue get( LuaValue key ) {
return filemethods.get(key);
@@ -112,6 +118,18 @@ public class IoLib extends TwoArgFunction {
public String tojstring() {
return "file: " + Integer.toHexString(hashCode());
}
public void finalize() throws Throwable {
try {
if (!isclosed()) {
try {
close();
} catch (IOException ignore) {}
}
} finally {
super.finalize();
}
}
}
/** Enumerated value representing stdin */
@@ -123,29 +141,29 @@ public class IoLib extends TwoArgFunction {
/** Enumerated value representing a file type for a named file */
protected static final int FTYPE_NAMED = 3;
/**
* Wrap the standard input.
* @return File
/**
* Wrap the standard input.
* @return File
* @throws IOException
*/
abstract protected File wrapStdin() throws IOException;
/**
* Wrap the standard output.
* @return File
/**
* Wrap the standard output.
* @return File
* @throws IOException
*/
abstract protected File wrapStdout() throws IOException;
/**
* Wrap the standard error output.
* @return File
/**
* Wrap the standard error output.
* @return File
* @throws IOException
*/
abstract protected File wrapStderr() throws IOException;
/**
* Open a file in a particular mode.
* Open a file in a particular mode.
* @param filename
* @param readMode true if opening in read mode
* @param appendMode true if opening in append mode
@@ -157,7 +175,7 @@ public class IoLib extends TwoArgFunction {
abstract protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException;
/**
* Open a temporary file.
* Open a temporary file.
* @return File object if successful
* @throws IOException if could not be opened
*/
@@ -167,7 +185,7 @@ public class IoLib extends TwoArgFunction {
* Start a new process and return a file for input or output
* @param prog the program to execute
* @param mode "r" to read, "w" to write
* @return File to read to or write from
* @return File to read to or write from
* @throws IOException if an i/o exception occurs
*/
abstract protected File openProgram(String prog, String mode) throws IOException;
@@ -178,7 +196,7 @@ public class IoLib extends TwoArgFunction {
private static final LuaValue STDIN = valueOf("stdin");
private static final LuaValue STDOUT = valueOf("stdout");
private static final LuaValue STDERR = valueOf("stderr");
private static final LuaValue STDERR = valueOf("stderr");
private static final LuaValue FILE = valueOf("file");
private static final LuaValue CLOSED_FILE = valueOf("closed file");
@@ -256,7 +274,7 @@ public class IoLib extends TwoArgFunction {
// return the table
env.set("io", t);
env.get("package").get("loaded").set("io", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("io", t);
return t;
}
@@ -269,8 +287,15 @@ public class IoLib extends TwoArgFunction {
static final class IoLibV extends VarArgFunction {
private File f;
public IoLib iolib;
private boolean toclose;
private Varargs args;
public IoLibV() {
}
public IoLibV(File f, String name, int opcode, IoLib iolib, boolean toclose, Varargs args) {
this(f, name, opcode, iolib);
this.toclose = toclose;
this.args = args.dealias();
}
public IoLibV(File f, String name, int opcode, IoLib iolib) {
super();
this.f = f;
@@ -290,22 +315,26 @@ public class IoLib extends TwoArgFunction {
case IO_TYPE: return iolib._io_type(args.arg1());
case IO_POPEN: return iolib._io_popen(args.checkjstring(1),args.optjstring(2,"r"));
case IO_OPEN: return iolib._io_open(args.checkjstring(1), args.optjstring(2,"r"));
case IO_LINES: return iolib._io_lines(args.isvalue(1)? args.checkjstring(1): null);
case IO_LINES: return iolib._io_lines(args);
case IO_READ: return iolib._io_read(args);
case IO_WRITE: return iolib._io_write(args);
case FILE_CLOSE: return iolib._file_close(args.arg1());
case FILE_FLUSH: return iolib._file_flush(args.arg1());
case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,1024));
case FILE_LINES: return iolib._file_lines(args.arg1());
case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,8192));
case FILE_LINES: return iolib._file_lines(args);
case FILE_READ: return iolib._file_read(args.arg1(),args.subargs(2));
case FILE_SEEK: return iolib._file_seek(args.arg1(),args.optjstring(2,"cur"),args.optint(3,0));
case FILE_WRITE: return iolib._file_write(args.arg1(),args.subargs(2));
case IO_INDEX: return iolib._io_index(args.arg(2));
case LINES_ITER: return iolib._lines_iter(f);
case LINES_ITER: return iolib._lines_iter(f, toclose, this.args);
}
} catch ( IOException ioe ) {
if (opcode == LINES_ITER) {
String s = ioe.getMessage();
error(s != null ? s : ioe.toString());
}
return errorresult(ioe);
}
return NONE;
@@ -316,7 +345,7 @@ public class IoLib extends TwoArgFunction {
return infile!=null? infile: (infile=ioopenfile(FTYPE_STDIN, "-","r"));
}
// io.flush() -> bool
// io.flush() -> bool
public Varargs _io_flush() throws IOException {
checkopen(output());
outfile.flush();
@@ -337,7 +366,7 @@ public class IoLib extends TwoArgFunction {
// io.input([file]) -> file
public Varargs _io_input(LuaValue file) {
infile = file.isnil()? input():
infile = file.isnil()? input():
file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(),"r"):
checkfile(file);
return infile;
@@ -345,7 +374,7 @@ public class IoLib extends TwoArgFunction {
// io.output(filename) -> file
public Varargs _io_output(LuaValue filename) {
outfile = filename.isnil()? output():
outfile = filename.isnil()? output():
filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(),"w"):
checkfile(filename);
return outfile;
@@ -361,6 +390,7 @@ public class IoLib extends TwoArgFunction {
// io.popen(prog, [mode]) -> file
public Varargs _io_popen(String prog, String mode) throws IOException {
if (!"r".equals(mode) && !"w".equals(mode)) argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'");
return openProgram(prog, mode);
}
@@ -369,11 +399,12 @@ public class IoLib extends TwoArgFunction {
return rawopenfile(FTYPE_NAMED, filename, mode);
}
// io.lines(filename) -> iterator
public Varargs _io_lines(String filename) {
infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r");
// io.lines(filename, ...) -> iterator
public Varargs _io_lines(Varargs args) {
String filename = args.optjstring(1, null);
File infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r");
checkopen(infile);
return lines(infile);
return lines(infile, filename != null, args.subargs(2));
}
// io.read(...) -> (...)
@@ -401,13 +432,19 @@ public class IoLib extends TwoArgFunction {
// file:setvbuf(mode,[size]) -> void
public Varargs _file_setvbuf(LuaValue file, String mode, int size) {
if ("no".equals(mode)) {
} else if ("full".equals(mode)) {
} else if ("line".equals(mode)) {
} else {
argerror(1, "invalid value: '" + mode + "'; must be one of 'no', 'full' or 'line'");
}
checkfile(file).setvbuf(mode,size);
return LuaValue.TRUE;
}
// file:lines() -> iterator
public Varargs _file_lines(LuaValue file) {
return lines(checkfile(file));
// file:lines(...) -> iterator
public Varargs _file_lines(Varargs args) {
return lines(checkfile(args.arg1()), false, args.subargs(2));
}
// file:read(...) -> (...)
@@ -417,10 +454,16 @@ public class IoLib extends TwoArgFunction {
// file:seek([whence][,offset]) -> pos | nil,error
public Varargs _file_seek(LuaValue file, String whence, int offset) throws IOException {
if ("set".equals(whence)) {
} else if ("end".equals(whence)) {
} else if ("cur".equals(whence)) {
} else {
argerror(1, "invalid value: '" + whence + "'; must be one of 'set', 'cur' or 'end'");
}
return valueOf( checkfile(file).seek(whence,offset) );
}
// file:write(...) -> void
// file:write(...) -> void
public Varargs _file_write(LuaValue file, Varargs subargs) throws IOException {
return iowrite(checkfile(file),subargs);
}
@@ -433,8 +476,13 @@ public class IoLib extends TwoArgFunction {
}
// lines iterator(s,var) -> var'
public Varargs _lines_iter(LuaValue file) throws IOException {
return freadline(checkfile(file));
public Varargs _lines_iter(LuaValue file, boolean toclose, Varargs args) throws IOException {
File f = optfile(file);
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();
return ret;
}
private File output() {
@@ -467,8 +515,8 @@ public class IoLib extends TwoArgFunction {
return LuaValue.TRUE;
}
private static Varargs errorresult(Exception ioe) {
String s = ioe.getMessage();
static Varargs errorresult(Exception ioe) {
String s = ioe.getMessage();
return errorresult("io error: "+(s!=null? s: ioe.toString()));
}
@@ -476,9 +524,9 @@ public class IoLib extends TwoArgFunction {
return varargsOf(NIL, valueOf(errortext));
}
private Varargs lines(final File f) {
private Varargs lines(final File f, boolean toclose, Varargs args) {
try {
return new IoLibV(f,"lnext",LINES_ITER,this);
return new IoLibV(f,"lnext",LINES_ITER,this,toclose,args);
} catch ( Exception e ) {
return error("lines: "+e);
}
@@ -492,6 +540,7 @@ public class IoLib extends TwoArgFunction {
private Varargs ioread(File f, Varargs args) throws IOException {
int i,n=args.narg();
if (n == 0) return freadline(f,false);
LuaValue[] v = new LuaValue[n];
LuaValue ai,vi;
LuaString fmt;
@@ -502,15 +551,16 @@ public class IoLib extends TwoArgFunction {
break item;
case LuaValue.TSTRING:
fmt = ai.checkstring();
if ( fmt.m_length == 2 && fmt.m_bytes[fmt.m_offset] == '*' ) {
if ( fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*' ) {
switch ( fmt.m_bytes[fmt.m_offset+1] ) {
case 'n': vi = freadnumber(f); break item;
case 'l': vi = freadline(f); break item;
case 'l': vi = freadline(f,false); break item;
case 'L': vi = freadline(f,true); break item;
case 'a': vi = freadall(f); break item;
}
}
default:
return argerror( i+1, "(invalid format)" );
default:
return argerror( i+1, "(invalid format)" );
}
if ( (v[i++] = vi).isnil() )
break;
@@ -537,6 +587,17 @@ public class IoLib extends TwoArgFunction {
}
private File rawopenfile(int filetype, String filename, String mode) throws IOException {
int len = mode.length();
for (int i = 0; i < len; i++) { // [rwa][+]?b*
char ch = mode.charAt(i);
if (i == 0 && "rwa".indexOf(ch) >= 0) continue;
if (i == 1 && ch == '+') continue;
if (i >= 1 && ch == 'b') continue;
len = -1;
break;
}
if (len <= 0) argerror(2, "invalid mode: '" + mode + "'");
switch (filetype) {
case FTYPE_STDIN: return wrapStdin();
case FTYPE_STDOUT: return wrapStdout();
@@ -544,7 +605,7 @@ public class IoLib extends TwoArgFunction {
}
boolean isreadmode = mode.startsWith("r");
boolean isappend = mode.startsWith("a");
boolean isupdate = mode.indexOf("+") > 0;
boolean isupdate = mode.indexOf('+') > 0;
boolean isbinary = mode.endsWith("b");
return openFile( filename, isreadmode, isappend, isupdate, isbinary );
}
@@ -553,44 +614,45 @@ public class IoLib extends TwoArgFunction {
// ------------- file reading utilitied ------------------
public static LuaValue freadbytes(File f, int count) throws IOException {
if (count == 0) return f.eof() ? NIL : EMPTYSTRING;
byte[] b = new byte[count];
int r;
if ( ( r = f.read(b,0,b.length) ) < 0 )
return NIL;
return LuaString.valueUsing(b, 0, r);
}
public static LuaValue freaduntil(File f,boolean lineonly) throws IOException {
public static LuaValue freaduntil(File f,boolean lineonly,boolean withend) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
try {
if ( lineonly ) {
loop: while ( (c = f.read()) > 0 ) {
loop: while ( (c = f.read()) >= 0 ) {
switch ( c ) {
case '\r': break;
case '\n': break loop;
case '\r': if (withend) baos.write(c); break;
case '\n': if (withend) baos.write(c); break loop;
default: baos.write(c); break;
}
}
} else {
while ( (c = f.read()) > 0 )
while ( (c = f.read()) >= 0 )
baos.write(c);
}
} catch ( EOFException e ) {
c = -1;
}
return ( c < 0 && baos.size() == 0 )?
return ( c < 0 && baos.size() == 0 )?
(LuaValue) NIL:
(LuaValue) LuaString.valueUsing(baos.toByteArray());
}
public static LuaValue freadline(File f) throws IOException {
return freaduntil(f,true);
public static LuaValue freadline(File f,boolean withend) throws IOException {
return freaduntil(f,true,withend);
}
public static LuaValue freadall(File f) throws IOException {
int n = f.remaining();
if ( n >= 0 ) {
return freadbytes(f, n);
return n == 0 ? EMPTYSTRING : freadbytes(f, n);
} else {
return freaduntil(f,false);
return freaduntil(f,false,false);
}
}
public static LuaValue freadnumber(File f) throws IOException {
@@ -619,7 +681,7 @@ public class IoLib extends TwoArgFunction {
if ( baos != null )
baos.write( c );
}
}
}

View File

@@ -27,21 +27,21 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LuaFunction} common to Java functions exposed to lua.
* Subclass of {@link LuaFunction} common to Java functions exposed to lua.
* <p>
* To provide for common implementations in JME and JSE,
* To provide for common implementations in JME and JSE,
* library functions are typically grouped on one or more library classes
* and an opcode per library function is defined and used to key the switch
* to the correct function within the library.
* to the correct function within the library.
* <p>
* Since lua functions can be called with too few or too many arguments,
* and there are overloaded {@link LuaValue#call()} functions with varying
* Since lua functions can be called with too few or too many arguments,
* and there are overloaded {@link LuaValue#call()} functions with varying
* number of arguments, a Java function exposed in lua needs to handle the
* argument fixup when a function is called with a number of arguments
* differs from that expected.
* argument fixup when a function is called with a number of arguments
* differs from that expected.
* <p>
* To simplify the creation of library functions,
* there are 5 direct subclasses to handle common cases based on number of
* To simplify the creation of library functions,
* there are 5 direct subclasses to handle common cases based on number of
* argument values and number of return return values.
* <ul>
* <li>{@link ZeroArgFunction}</li>
@@ -51,13 +51,13 @@ import org.luaj.vm2.Varargs;
* <li>{@link VarArgFunction}</li>
* </ul>
* <p>
* To be a Java library that can be loaded via {@code require}, it should have
* a public constructor that returns a {@link LuaValue} that, when executed,
* initializes the library.
* <p>
* For example, the following code will implement a library called "hyperbolic"
* To be a Java library that can be loaded via {@code require}, it should have
* a public constructor that returns a {@link LuaValue} that, when executed,
* initializes the library.
* <p>
* For example, the following code will implement a library called "hyperbolic"
* with two functions, "sinh", and "cosh":
<pre> {@code
<pre> {@code
* import org.luaj.vm2.LuaValue;
* import org.luaj.vm2.lib.*;
*
@@ -86,13 +86,13 @@ import org.luaj.vm2.Varargs;
* }
*}
*}</pre>
* The default constructor is used to instantiate the library
* The default constructor is used to instantiate the library
* in response to {@code require 'hyperbolic'} statement,
* provided it is on Java&quot;s class path.
* This instance is then invoked with 2 arguments: the name supplied to require(),
* and the environment for this function. The library may ignore these, or use
* them to leave side effects in the global environment, for example.
* In the previous example, two functions are created, 'sinh', and 'cosh', and placed
* provided it is on Java&quot;s class path.
* This instance is then invoked with 2 arguments: the name supplied to require(),
* and the environment for this function. The library may ignore these, or use
* them to leave side effects in the global environment, for example.
* In the previous example, two functions are created, 'sinh', and 'cosh', and placed
* into a global table called 'hyperbolic' using the supplied 'env' argument.
* <p>
* To test it, a script such as this can be used:
@@ -105,7 +105,7 @@ import org.luaj.vm2.Varargs;
* end
* print( 'sinh(.5)', hyperbolic.sinh(.5) )
* print( 'cosh(.5)', hyperbolic.cosh(.5) )
* }</pre>
* }</pre>
* <p>
* It should produce something like:
* <pre> {@code
@@ -115,16 +115,16 @@ import org.luaj.vm2.Varargs;
* k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953
* cosh(.5) 1.127626
* }</pre>
* <p>
* See the source code in any of the library functions
* such as {@link BaseLib} or {@link TableLib} for other examples.
* }</pre>
* <p>
* See the source code in any of the library functions
* such as {@link BaseLib} or {@link TableLib} for other examples.
*/
abstract public class LibFunction extends LuaFunction {
/** User-defined opcode to differentiate between instances of the library function class.
/** User-defined opcode to differentiate between instances of the library function class.
* <p>
* Subclass will typicall switch on this value to provide the specific behavior for each function.
* Subclass will typicall switch on this value to provide the specific behavior for each function.
*/
protected int opcode;
@@ -135,37 +135,37 @@ abstract public class LibFunction extends LuaFunction {
protected String name;
/** Default constructor for use by subclasses */
protected LibFunction() {
protected LibFunction() {
}
public String tojstring() {
return name != null? name: super.tojstring();
return name != null ? "function: " + name : super.tojstring();
}
/**
* Bind a set of library functions.
/**
* Bind a set of library functions.
* <p>
* An array of names is provided, and the first name is bound
* with opcode = 0, second with 1, etc.
* @param env The environment to apply to each bound function
* An array of names is provided, and the first name is bound
* with opcode = 0, second with 1, etc.
* @param env The environment to apply to each bound function
* @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function.
* @see #bind(LuaValue, Class, String[], int)
* @see #bind(LuaValue, Class, String[], int)
*/
protected void bind(LuaValue env, Class factory, String[] names ) {
bind( env, factory, names, 0 );
}
/**
* Bind a set of library functions, with an offset
/**
* Bind a set of library functions, with an offset
* <p>
* An array of names is provided, and the first name is bound
* with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc.
* @param env The environment to apply to each bound function
* An array of names is provided, and the first name is bound
* with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc.
* @param env The environment to apply to each bound function
* @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function.
* @param firstopcode the first opcode to use
* @see #bind(LuaValue, Class, String[])
* @param firstopcode the first opcode to use
* @see #bind(LuaValue, Class, String[])
*/
protected void bind(LuaValue env, Class factory, String[] names, int firstopcode ) {
try {
@@ -178,7 +178,7 @@ abstract public class LibFunction extends LuaFunction {
} catch ( Exception e ) {
throw new LuaError( "bind failed: "+e );
}
}
}
/** Java code generation utility to allocate storage for upvalue, leave it empty */
protected static LuaValue[] newupe() {
@@ -196,7 +196,7 @@ abstract public class LibFunction extends LuaFunction {
}
public LuaValue call() {
return argerror(1,"value");
return argerror(1,"value expected");
}
public LuaValue call(LuaValue a) {
return call();
@@ -219,4 +219,4 @@ abstract public class LibFunction extends LuaFunction {
default: return call(args.arg1(),args.arg(2),args.arg(3),args.arg(4));
}
}
}
}

View File

@@ -28,13 +28,13 @@ import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code math}
* library.
* <p>
* It contains only the math library support that is possible on JME.
* For a more complete implementation based on math functions specific to JSE
* use {@link org.luaj.vm2.lib.jse.JseMathLib}.
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code math}
* library.
* <p>
* It contains only the math library support that is possible on JME.
* For a more complete implementation based on math functions specific to JSE
* use {@link org.luaj.vm2.lib.jse.JseMathLib}.
* In Particular the following math functions are <b>not</b> implemented by this library:
* <ul>
* <li>acos</li>
@@ -47,21 +47,21 @@ import org.luaj.vm2.Varargs;
* <li>atan2</li>
* </ul>
* <p>
* The implementations of {@code exp()} and {@code pow()} are constructed by
* The implementations of {@code exp()} and {@code pow()} are constructed by
* hand for JME, so will be slower and less accurate than when executed on the JSE platform.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
* } </pre>
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example,
* the subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example,
* the subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will
* be included, which also includes this base functionality.
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -70,8 +70,8 @@ import org.luaj.vm2.Varargs;
* globals.load(new MathLib());
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
* @see LibFunction
@@ -87,8 +87,8 @@ public class MathLib extends TwoArgFunction {
*/
public static MathLib MATHLIB = null;
/** Construct a MathLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using
/** Construct a MathLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using
* {@link #call(LuaValue, LuaValue)}. */
public MathLib() {
MATHLIB = this;
@@ -125,7 +125,7 @@ public class MathLib extends TwoArgFunction {
math.set("sqrt", new sqrt());
math.set("tan", new tan());
env.set("math", math);
env.get("package").get("loaded").set("math", math);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("math", math);
return math;
}
@@ -158,15 +158,17 @@ public class MathLib extends TwoArgFunction {
exp(MathLib mathlib) {
this.mathlib = mathlib;
}
protected double call(double d) {
return mathlib.dpow_lib(Math.E,d);
}
protected double call(double d) {
return mathlib.dpow_lib(Math.E,d);
}
}
static final class fmod extends BinaryOp {
protected double call(double x, double y) {
double q = x/y;
return x - y * (q>=0? Math.floor(q): Math.ceil(q));
static final class fmod extends TwoArgFunction {
public LuaValue call(LuaValue xv, LuaValue yv) {
if (xv.islong() && yv.islong()) {
return valueOf(xv.tolong() % yv.tolong());
}
return valueOf(xv.checkdouble() % yv.checkdouble());
}
}
static final class ldexp extends BinaryOp {
@@ -194,27 +196,36 @@ public class MathLib extends TwoArgFunction {
static class max extends VarArgFunction {
public Varargs invoke(Varargs args) {
double m = args.checkdouble(1);
for ( int i=2,n=args.narg(); i<=n; ++i )
m = Math.max(m,args.checkdouble(i));
return valueOf(m);
LuaValue m = args.checkvalue(1);
for ( int i=2,n=args.narg(); i<=n; ++i ) {
LuaValue v = args.checkvalue(i);
if (m.lt_b(v)) m = v;
}
return m;
}
}
static class min extends VarArgFunction {
public Varargs invoke(Varargs args) {
double m = args.checkdouble(1);
for ( int i=2,n=args.narg(); i<=n; ++i )
m = Math.min(m,args.checkdouble(i));
return valueOf(m);
LuaValue m = args.checkvalue(1);
for ( int i=2,n=args.narg(); i<=n; ++i ) {
LuaValue v = args.checkvalue(i);
if (v.lt_b(m)) m = v;
}
return m;
}
}
static class modf extends VarArgFunction {
public Varargs invoke(Varargs args) {
double x = args.checkdouble(1);
LuaValue n = args.arg1();
/* number is its own integer part, no fractional part */
if (n.islong()) return varargsOf(n, valueOf(0.0));
double x = n.checkdouble();
/* integer part (rounds toward zero) */
double intPart = ( x > 0 ) ? Math.floor( x ) : Math.ceil( x );
double fracPart = x - intPart;
/* fractional part (test needed for inf/-inf) */
double fracPart = x == intPart ? 0.0 : x - intPart;
return varargsOf( valueOf(intPart), valueOf(fracPart) );
}
}
@@ -252,26 +263,26 @@ public class MathLib extends TwoArgFunction {
/** compute power using installed math library, or default if there is no math library installed */
public static LuaValue dpow(double a, double b) {
return LuaDouble.valueOf(
return LuaDouble.valueOf(
MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b) );
}
public static double dpow_d(double a, double b) {
return MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
return MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b);
}
/**
* Hook to override default dpow behavior with faster implementation.
/**
* Hook to override default dpow behavior with faster implementation.
*/
public double dpow_lib(double a, double b) {
return dpow_default(a,b);
}
/**
* Default JME version computes using longhand heuristics.
/**
* Default JME version computes using longhand heuristics.
*/
protected static double dpow_default(double a, double b) {
if ( b < 0 )

View File

@@ -35,17 +35,17 @@ import org.luaj.vm2.Varargs;
* Subclass of {@link LibFunction} which implements the standard lua {@code os} library.
* <p>
* It is a usable base with simplified stub functions
* for library functions that cannot be implemented uniformly
* on Jse and Jme.
* for library functions that cannot be implemented uniformly
* on Jse and Jme.
* <p>
* This can be installed as-is on either platform, or extended
* This can be installed as-is on either platform, or extended
* and refined to be used in a complete Jse implementation.
* <p>
* Because the nature of the {@code os} library is to encapsulate
* os-specific features, the behavior of these functions varies considerably
* from their counterparts in the C platform.
* Because the nature of the {@code os} library is to encapsulate
* os-specific features, the behavior of these functions varies considerably
* from their counterparts in the C platform.
* <p>
* The following functions have limited implementations of features
* The following functions have limited implementations of features
* that are not supported well on Jme:
* <ul>
* <li>{@code execute()}</li>
@@ -54,7 +54,7 @@ import org.luaj.vm2.Varargs;
* <li>{@code tmpname()}</li>
* </ul>
* <p>
* Typically, this library is included as part of a call to either
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
@@ -63,7 +63,7 @@ import org.luaj.vm2.Varargs;
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseOsLib} library will be loaded, which will include
* the base functionality provided by this class.
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -80,8 +80,8 @@ import org.luaj.vm2.Varargs;
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a>
*/
public class OsLib extends TwoArgFunction {
public static String TMP_PREFIX = ".luaj";
public static String TMP_SUFFIX = "tmp";
public static final String TMP_PREFIX = ".luaj";
public static final String TMP_SUFFIX = "tmp";
private static final int CLOCK = 0;
private static final int DATE = 1;
@@ -114,8 +114,8 @@ public class OsLib extends TwoArgFunction {
protected Globals globals;
/**
* Create and OsLib instance.
/**
* Create and OsLib instance.
*/
public OsLib() {
}
@@ -132,7 +132,7 @@ public class OsLib extends TwoArgFunction {
for (int i = 0; i < NAMES.length; ++i)
os.set(NAMES[i], new OsLibFunc(i, NAMES[i]));
env.set("os", os);
env.get("package").get("loaded").set("os", os);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("os", os);
return os;
}
@@ -200,8 +200,8 @@ public class OsLib extends TwoArgFunction {
}
/**
* @return an approximation of the amount in seconds of CPU time used by
* the program. For luaj this simple returns the elapsed time since the
* @return an approximation of the amount in seconds of CPU time used by
* the program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded.
*/
protected double clock() {
@@ -209,7 +209,7 @@ public class OsLib extends TwoArgFunction {
}
/**
* Returns the number of seconds from time t1 to time t2.
* Returns the number of seconds from time t1 to time t2.
* In POSIX, Windows, and some other systems, this value is exactly t2-t1.
* @param t2
* @param t1
@@ -220,21 +220,21 @@ public class OsLib extends TwoArgFunction {
}
/**
* If the time argument is present, this is the time to be formatted
* (see the os.time function for a description of this value).
* If the time argument is present, this is the time to be formatted
* (see the os.time function for a description of this value).
* Otherwise, date formats the current time.
*
* Date returns the date as a string,
* Date returns the date as a string,
* formatted according to the same rules as ANSII strftime, but without
* support for %g, %G, or %V.
*
* When called without arguments, date returns a reasonable date and
* time representation that depends on the host system and on the
* When called without arguments, date returns a reasonable date and
* time representation that depends on the host system and on the
* current locale (that is, os.date() is equivalent to os.date("%c")).
*
* @param format
*
* @param format
* @param time time since epoch, or -1 if not supplied
* @return a LString or a LTable containing date and time,
* @return a LString or a LTable containing date and time,
* formatted according to the given string format.
*/
public String date(String format, double time) {
@@ -277,8 +277,8 @@ public class OsLib extends TwoArgFunction {
break;
case 'B':
result.append(MonthName[d.get(Calendar.MONTH)]);
break;
case 'c':
break;
case 'c':
result.append(date("%a %b %d %H:%M:%S %Y", time));
break;
case 'd':
@@ -314,7 +314,7 @@ public class OsLib extends TwoArgFunction {
case 'w':
result.append(String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
break;
case 'W':
case 'W':
result.append(String.valueOf(weekNumber(d, 1)));
break;
case 'x':
@@ -372,7 +372,7 @@ public class OsLib extends TwoArgFunction {
}
private int timeZoneOffset(Calendar d) {
int localStandarTimeMillis = (
int localStandarTimeMillis = (
d.get(Calendar.HOUR_OF_DAY) * 3600 +
d.get(Calendar.MINUTE) * 60 +
d.get(Calendar.SECOND)) * 1000;
@@ -381,28 +381,28 @@ public class OsLib extends TwoArgFunction {
d.get(Calendar.YEAR),
d.get(Calendar.MONTH),
d.get(Calendar.DAY_OF_MONTH),
d.get(Calendar.DAY_OF_WEEK),
d.get(Calendar.DAY_OF_WEEK),
localStandarTimeMillis) / 1000;
}
private boolean isDaylightSavingsTime(Calendar d) {
return timeZoneOffset(d) != d.getTimeZone().getRawOffset() / 1000;
return timeZoneOffset(d) != d.getTimeZone().getRawOffset() / 1000;
}
/**
* This function is equivalent to the C function system.
* It passes command to be executed by an operating system shell.
* It returns a status code, which is system-dependent.
* If command is absent, then it returns nonzero if a shell
/**
* This function is equivalent to the C function system.
* It passes command to be executed by an operating system shell.
* It returns a status code, which is system-dependent.
* If command is absent, then it returns nonzero if a shell
* is available and zero otherwise.
* @param command command to pass to the system
*/
*/
protected Varargs execute(String command) {
return varargsOf(NIL, valueOf("exit"), ONE);
}
/**
* Calls the C function exit, with an optional code, to terminate the host program.
* Calls the C function exit, with an optional code, to terminate the host program.
* @param code
*/
protected void exit(int code) {
@@ -415,13 +415,13 @@ public class OsLib extends TwoArgFunction {
* or null if the variable is not defined in either environment.
*
* The default implementation, which is used by the JmePlatform,
* only queryies System.getProperty().
* only queryies System.getProperty().
*
* The JsePlatform overrides this behavior and returns the
* environment variable value using System.getenv() if it exists,
* environment variable value using System.getenv() if it exists,
* or the System property value if it does not.
*
* A SecurityException may be thrown if access is not allowed
* A SecurityException may be thrown if access is not allowed
* for 'varname'.
* @param varname
* @return String value, or null if not defined
@@ -431,10 +431,10 @@ public class OsLib extends TwoArgFunction {
}
/**
* Deletes the file or directory with the given name.
* Directories must be empty to be removed.
* Deletes the file or directory with the given name.
* Directories must be empty to be removed.
* If this function fails, it throws and IOException
*
*
* @param filename
* @throws IOException if it fails
*/
@@ -443,9 +443,9 @@ public class OsLib extends TwoArgFunction {
}
/**
* Renames file or directory named oldname to newname.
* Renames file or directory named oldname to newname.
* If this function fails,it throws and IOException
*
*
* @param oldname old file name
* @param newname new file name
* @throws IOException if it fails
@@ -455,21 +455,21 @@ public class OsLib extends TwoArgFunction {
}
/**
* Sets the current locale of the program. locale is a string specifying
* a locale; category is an optional string describing which category to change:
* "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category
* is "all".
* Sets the current locale of the program. locale is a string specifying
* a locale; category is an optional string describing which category to change:
* "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category
* is "all".
*
* If locale is the empty string, the current locale is set to an implementation-
* defined native locale. If locale is the string "C", the current locale is set
* defined native locale. If locale is the string "C", the current locale is set
* to the standard C locale.
*
* When called with null as the first argument, this function only returns the
* When called with null as the first argument, this function only returns the
* name of the current locale for the given category.
*
*
* @param locale
* @param category
* @return the name of the new locale, or null if the request
* @return the name of the new locale, or null if the request
* cannot be honored.
*/
protected String setlocale(String locale, String category) {
@@ -477,10 +477,10 @@ public class OsLib extends TwoArgFunction {
}
/**
* Returns the current time when called without arguments,
* or a time representing the date and time specified by the given table.
* This table must have fields year, month, and day,
* and may have fields hour, min, sec, and isdst
* Returns the current time when called without arguments,
* or a time representing the date and time specified by the given table.
* This table must have fields year, month, and day,
* and may have fields hour, min, sec, and isdst
* (for a description of these fields, see the os.date function).
* @param table
* @return long value for the time
@@ -504,15 +504,15 @@ public class OsLib extends TwoArgFunction {
}
/**
* Returns a string with a file name that can be used for a temporary file.
* The file must be explicitly opened before its use and explicitly removed
* Returns a string with a file name that can be used for a temporary file.
* The file must be explicitly opened before its use and explicitly removed
* when no longer needed.
*
* On some systems (POSIX), this function also creates a file with that name,
* to avoid security risks. (Someone else might create the file with wrong
* permissions in the time between getting the name and creating the file.)
* You still have to open the file to use it and to remove it (even if you
* do not use it).
* On some systems (POSIX), this function also creates a file with that name,
* to avoid security risks. (Someone else might create the file with wrong
* permissions in the time between getting the name and creating the file.)
* You still have to open the file to use it and to remove it (even if you
* do not use it).
*
* @return String filename to use
*/

View File

@@ -30,9 +30,9 @@ import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard package and module
* library functions.
/**
* Subclass of {@link LibFunction} which implements the lua standard package and module
* library functions.
*
* <h3>Lua Environment Variables</h3>
* The following variables are available to lua scrips when this library has been loaded:
@@ -50,14 +50,14 @@ import org.luaj.vm2.Varargs;
* </ul>
*
* <h3>Loading</h3>
* Typically, this library is included as part of a call to either
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("require").call"foo") );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -67,8 +67,8 @@ import org.luaj.vm2.Varargs;
* } </pre>
* <h3>Limitations</h3>
* This library has been implemented to match as closely as possible the behavior in the corresponding library in C.
* However, the default filesystem search semantics are different and delegated to the bas library
* as outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib} documentation.
* However, the default filesystem search semantics are different and delegated to the bas library
* as outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib} documentation.
* <p>
* @see LibFunction
* @see BaseLib
@@ -81,23 +81,26 @@ public class PackageLib extends TwoArgFunction {
/** The default value to use for package.path. This can be set with the system property
* <code>"luaj.package.path"</code>, and is <code>"?.lua"</code> by default. */
public static String DEFAULT_LUA_PATH;
public static final String DEFAULT_LUA_PATH;
static {
String path = null;
try {
DEFAULT_LUA_PATH = System.getProperty("luaj.package.path");
path = System.getProperty("luaj.package.path");
} catch (Exception e) {
System.out.println(e.toString());
}
if (DEFAULT_LUA_PATH == null)
DEFAULT_LUA_PATH = "?.lua";
if (path == null) {
path = "?.lua";
}
DEFAULT_LUA_PATH = path;
}
private static final LuaString _LOADED = valueOf("loaded");
static final LuaString _LOADED = valueOf("loaded");
private static final LuaString _LOADLIB = valueOf("loadlib");
private static final LuaString _PRELOAD = valueOf("preload");
private static final LuaString _PATH = valueOf("path");
private static final LuaString _SEARCHPATH = valueOf("searchpath");
private static final LuaString _SEARCHERS = valueOf("searchers");
static final LuaString _PRELOAD = valueOf("preload");
static final LuaString _PATH = valueOf("path");
static final LuaString _SEARCHPATH = valueOf("searchpath");
static final LuaString _SEARCHERS = valueOf("searchers");
/** The globals that were used to load this library. */
Globals globals;
@@ -141,6 +144,7 @@ public class PackageLib extends TwoArgFunction {
searchers.set(2, lua_searcher = new lua_searcher());
searchers.set(3, java_searcher = new java_searcher());
package_.set(_SEARCHERS, searchers);
package_.set("config", FILE_SEP + "\n;\n?\n!\n-\n");
package_.get(_LOADED).set("package", package_);
env.set("package", package_);
globals.package_ = this;
@@ -153,7 +157,7 @@ public class PackageLib extends TwoArgFunction {
}
/** Set the lua path used by this library instance to a new value.
/** Set the lua path used by this library instance to a new value.
* Merely sets the value of {@link path} to be used in subsequent searches. */
public void setLuaPath( String newLuaPath ) {
package_.set(_PATH, LuaValue.valueOf(newLuaPath));
@@ -165,33 +169,33 @@ public class PackageLib extends TwoArgFunction {
// ======================== Package loading =============================
/**
/**
* require (modname)
*
* Loads the given module. The function starts by looking into the package.loaded table
* to determine whether modname is already loaded. If it is, then require returns the value
* Loads the given module. The function starts by looking into the package.loaded table
* to determine whether modname is already loaded. If it is, then require returns the value
* stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.
*
* To find a loader, require is guided by the package.searchers sequence.
* By changing this sequence, we can change how require looks for a module.
* To find a loader, require is guided by the package.searchers sequence.
* By changing this sequence, we can change how require looks for a module.
* The following explanation is based on the default configuration for package.searchers.
*
* First require queries package.preload[modname]. If it has a value, this value
* (which should be a function) is the loader. Otherwise require searches for a Lua loader using
* the path stored in package.path. If that also fails, it searches for a Java loader using
* First require queries package.preload[modname]. If it has a value, this value
* (which should be a function) is the loader. Otherwise require searches for a Lua loader using
* the path stored in package.path. If that also fails, it searches for a Java loader using
* the classpath, using the public default constructor, and casting the instance to LuaFunction.
*
* Once a loader is found, require calls the loader with two arguments: modname and an extra value
* Once a loader is found, require calls the loader with two arguments: modname and an extra value
* dependent on how it got the loader. If the loader came from a file, this extra value is the file name.
* If the loader is a Java instance of LuaFunction, this extra value is the environment.
* If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname].
* If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname],
* If the loader is a Java instance of LuaFunction, this extra value is the environment.
* If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname].
* If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname],
* then require assigns true to this entry.
* In any case, require returns the final value of package.loaded[modname].
*
*
* If there is any error loading or running the module, or if it cannot find any loader for the module,
* then require raises an error.
*/
*/
public class require extends OneArgFunction {
public LuaValue call( LuaValue arg ) {
LuaString name = arg.checkstring();
@@ -210,7 +214,7 @@ public class PackageLib extends TwoArgFunction {
for ( int i=1; true; i++ ) {
LuaValue searcher = tbl.get(i);
if ( searcher.isnil() ) {
error( "module '"+name+"' not found: "+name+sb );
error( "module '"+name+"' not found: "+name+sb );
}
/* call loader with module name as argument */
@@ -226,14 +230,14 @@ public class PackageLib extends TwoArgFunction {
result = loader.arg1().call(name, loader.arg(2));
if ( ! result.isnil() )
loaded.set( name, result );
else if ( (result = loaded.get(name)) == _SENTINEL )
else if ( (result = loaded.get(name)) == _SENTINEL )
loaded.set( name, result = LuaValue.TRUE );
return result;
}
}
public static class loadlib extends VarArgFunction {
public Varargs loadlib( Varargs args ) {
public Varargs invoke( Varargs args ) {
args.checkstring(1);
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
}
@@ -243,7 +247,7 @@ public class PackageLib extends TwoArgFunction {
public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1);
LuaValue val = package_.get(_PRELOAD).get(name);
return val.isnil()?
return val.isnil()?
valueOf("\n\tno field package.preload['"+name+"']"):
val;
}
@@ -252,11 +256,10 @@ public class PackageLib extends TwoArgFunction {
public class lua_searcher extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1);
InputStream is = null;
// get package path
LuaValue path = package_.get(_PATH);
if ( ! path.isstring() )
if ( ! path.isstring() )
return valueOf("package.path is not a string");
// get the searchpath function.
@@ -268,7 +271,7 @@ public class PackageLib extends TwoArgFunction {
LuaString filename = v.arg1().strvalue();
// Try to load the file.
v = globals.loadfile(filename.tojstring());
v = globals.loadfile(filename.tojstring());
if ( v.arg1().isfunction() )
return LuaValue.varargsOf(v.arg1(), filename);
@@ -353,9 +356,9 @@ public class PackageLib extends TwoArgFunction {
StringBuffer sb = new StringBuffer(j);
for ( int i=0; i<j; i++ ) {
c = filename.charAt(i);
sb.append(
sb.append(
(isClassnamePart(c))? c:
((c=='/') || (c=='\\'))? '.': '_' );
((c=='/') || (c=='\\'))? '.': '_' );
}
return sb.toString();
}
@@ -374,5 +377,5 @@ public class PackageLib extends TwoArgFunction {
default:
return false;
}
}
}
}

View File

@@ -24,32 +24,32 @@ package org.luaj.vm2.lib;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.Buffer;
import org.luaj.vm2.LuaClosure;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.compiler.DumpState;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code string}
* library.
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code string}
* library.
* <p>
* Typically, this library is included as part of a call to either
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("string").get("upper").call( LuaValue.valueOf("abcde") ) );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new StringLib());
* globals.load(new JseStringLib());
* System.out.println( globals.get("string").get("upper").call( LuaValue.valueOf("abcde") ) );
* } </pre>
* <p>
@@ -61,8 +61,8 @@ import org.luaj.vm2.compiler.DumpState;
*/
public class StringLib extends TwoArgFunction {
/** Construct a StringLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using
/** Construct a StringLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using
* {@link #call(LuaValue, LuaValue)}. */
public StringLib() {
}
@@ -75,16 +75,16 @@ public class StringLib extends TwoArgFunction {
* If the shared strings metatable instance is null, will set the metatable as
* the global shared metatable for strings.
* <P>
* All tables and metatables are read-write by default so if this will be used in
* a server environment, sandboxing should be used. In particular, the
* All tables and metatables are read-write by default so if this will be used in
* a server environment, sandboxing should be used. In particular, the
* {@link LuaString#s_metatable} table should probably be made read-only.
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable string = new LuaTable();
string.set("byte", new byte_());
string.set("char", new char_());
string.set("byte", new _byte());
string.set("char", new _char());
string.set("dump", new dump());
string.set("find", new find());
string.set("format", new format());
@@ -97,17 +97,17 @@ public class StringLib extends TwoArgFunction {
string.set("reverse", new reverse());
string.set("sub", new sub());
string.set("upper", new upper());
LuaTable mt = LuaValue.tableOf(
new LuaValue[] { INDEX, string });
env.set("string", string);
env.get("package").get("loaded").set("string", string);
if (LuaString.s_metatable == null)
LuaString.s_metatable = mt;
if (!env.get("package").isnil()) env.get("package").get("loaded").set("string", string);
if (LuaString.s_metatable == null) {
LuaString.s_metatable = LuaValue.tableOf(new LuaValue[] { INDEX, string });
}
return string;
}
/**
* string.byte (s [, i [, j]])
* string.byte (s [, i [, j]])
*
* Returns the internal numerical codes of the
* characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the
@@ -117,7 +117,7 @@ public class StringLib extends TwoArgFunction {
*
* @param args the calling args
*/
static final class byte_ extends VarArgFunction {
static final class _byte extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaString s = args.checkstring(1);
int l = s.m_length;
@@ -137,45 +137,47 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.char (...)
*
* Receives zero or more integers. Returns a string with length equal
* to the number of arguments, in which each character has the internal
* numerical code equal to its corresponding argument.
* Receives zero or more integers. Returns a string with length equal
* to the number of arguments, in which each character has the internal
* numerical code equal to its corresponding argument.
*
* Note that numerical codes are not necessarily portable across platforms.
*
* @param args the calling VM
*/
static final class char_ extends VarArgFunction {
static final class _char extends VarArgFunction {
public Varargs invoke(Varargs args) {
int n = args.narg();
byte[] bytes = new byte[n];
for ( int i=0, a=1; i<n; i++, a++ ) {
int c = args.checkint(a);
if (c<0 || c>=256) argerror(a, "invalid value");
if (c<0 || c>=256) argerror(a, "invalid value for string.char [0; 255]: " + c);
bytes[i] = (byte) c;
}
return LuaString.valueUsing( bytes );
}
}
/**
* string.dump (function)
/**
* string.dump (function[, stripDebug])
*
* Returns a string containing a binary representation of the given function,
* so that a later loadstring on this string returns a copy of the function.
* Returns a string containing a binary representation of the given function,
* so that a later loadstring on this string returns a copy of the function.
* function must be a Lua function without upvalues.
*
* Boolean param stripDebug - true to strip debugging info, false otherwise.
* The default value for stripDebug is true.
*
* TODO: port dumping code as optional add-on
*/
static final class dump extends OneArgFunction {
public LuaValue call(LuaValue arg) {
LuaValue f = arg.checkfunction();
static final class dump extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue f = args.checkfunction(1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
DumpState.dump( ((LuaClosure)f).p, baos, true );
DumpState.dump( ((LuaClosure)f).p, baos, args.optboolean(2, true) );
return LuaString.valueUsing(baos.toByteArray());
} catch (IOException e) {
return error( e.getMessage() );
@@ -183,20 +185,20 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.find (s, pattern [, init [, plain]])
*
* Looks for the first match of pattern in the string s.
* If it finds a match, then find returns the indices of s
* where this occurrence starts and ends; otherwise, it returns nil.
* A third, optional numerical argument init specifies where to start the search;
* its default value is 1 and may be negative. A value of true as a fourth,
* optional argument plain turns off the pattern matching facilities,
* so the function does a plain "find substring" operation,
* with no characters in pattern being considered "magic".
* Looks for the first match of pattern in the string s.
* If it finds a match, then find returns the indices of s
* where this occurrence starts and ends; otherwise, it returns nil.
* A third, optional numerical argument init specifies where to start the search;
* its default value is 1 and may be negative. A value of true as a fourth,
* optional argument plain turns off the pattern matching facilities,
* so the function does a plain "find substring" operation,
* with no characters in pattern being considered "magic".
* Note that if plain is given, then init must be given as well.
*
* If the pattern has captures, then in a successful match the captured values
* If the pattern has captures, then in a successful match the captured values
* are also returned, after the two indices.
*/
static final class find extends VarArgFunction {
@@ -205,30 +207,30 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.format (formatstring, ...)
*
* Returns a formatted version of its variable number of arguments following
* the description given in its first argument (which must be a string).
* The format string follows the same rules as the printf family of standard C functions.
* The only differences are that the options/modifiers *, l, L, n, p, and h are not supported
* and that there is an extra option, q. The q option formats a string in a form suitable
* to be safely read back by the Lua interpreter: the string is written between double quotes,
* and all double quotes, newlines, embedded zeros, and backslashes in the string are correctly
* Returns a formatted version of its variable number of arguments following
* the description given in its first argument (which must be a string).
* The format string follows the same rules as the printf family of standard C functions.
* The only differences are that the options/modifiers *, l, L, n, p, and h are not supported
* and that there is an extra option, q. The q option formats a string in a form suitable
* to be safely read back by the Lua interpreter: the string is written between double quotes,
* and all double quotes, newlines, embedded zeros, and backslashes in the string are correctly
* escaped when written. For instance, the call
* string.format('%q', 'a string with "quotes" and \n new line')
*
* will produce the string:
* "a string with \"quotes\" and \
* new line"
*
* The options c, d, E, e, f, g, G, i, o, u, X, and x all expect a number as argument,
* whereas q and s expect a string.
*
* This function does not accept string values containing embedded zeros,
* except as arguments to the q option.
* The options c, d, E, e, f, g, G, i, o, u, X, and x all expect a number as argument,
* whereas q and s expect a string.
*
* This function does not accept string values containing embedded zeros,
* except as arguments to the q option.
*/
static final class format extends VarArgFunction {
final class format extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaString fmt = args.checkstring( 1 );
final int n = fmt.length();
@@ -259,7 +261,7 @@ public class StringLib extends TwoArgFunction {
break;
case 'i':
case 'd':
fdsc.format( result, args.checkint( arg ) );
fdsc.format( result, args.checklong( arg ) );
break;
case 'o':
case 'u':
@@ -298,7 +300,7 @@ public class StringLib extends TwoArgFunction {
}
}
private static void addquoted(Buffer buf, LuaString s) {
static void addquoted(Buffer buf, LuaString s) {
int c;
buf.append( (byte) '"' );
for ( int i = 0, n = s.length(); i < n; i++ ) {
@@ -328,7 +330,7 @@ public class StringLib extends TwoArgFunction {
private static final String FLAGS = "-+ #0";
static class FormatDesc {
class FormatDesc {
private boolean leftAdjust;
private boolean zeroPad;
@@ -338,11 +340,13 @@ public class StringLib extends TwoArgFunction {
private static final int MAX_FLAGS = 5;
private int width;
private int precision;
int precision;
public final int conversion;
public final int length;
public final String src;
public FormatDesc(Varargs args, LuaString strfrmt, final int start) {
int p = start, n = strfrmt.length();
int c = 0;
@@ -390,6 +394,7 @@ public class StringLib extends TwoArgFunction {
zeroPad &= !leftAdjust; // '-' overrides '0'
conversion = c;
length = p - start;
src = strfrmt.substring(start - 1, p).tojstring();
}
public void format(Buffer buf, byte c) {
@@ -465,8 +470,7 @@ public class StringLib extends TwoArgFunction {
}
public void format(Buffer buf, double x) {
// TODO
buf.append( String.valueOf( x ) );
buf.append( StringLib.this.format(src, x) );
}
public void format(Buffer buf, LuaString s) {
@@ -476,27 +480,31 @@ public class StringLib extends TwoArgFunction {
buf.append(s);
}
public static final void pad(Buffer buf, char c, int n) {
public final void pad(Buffer buf, char c, int n) {
byte b = (byte)c;
while ( n-- > 0 )
buf.append(b);
}
}
/**
protected String format(String src, double x) {
return String.valueOf(x);
}
/**
* string.gmatch (s, pattern)
*
* Returns an iterator function that, each time it is called, returns the next captures
* from pattern over string s. If pattern specifies no captures, then the
* whole match is produced in each call.
* Returns an iterator function that, each time it is called, returns the next captures
* from pattern over string s. If pattern specifies no captures, then the
* whole match is produced in each call.
*
* As an example, the following loop
* s = "hello world from Lua"
* for w in string.gmatch(s, "%a+") do
* print(w)
* end
*
* will iterate over all the words from string s, printing one per line.
*
* will iterate over all the words from string s, printing one per line.
* The next example collects all pairs key=value from the given string into a table:
* t = {}
* s = "from=world, to=Lua"
@@ -504,7 +512,7 @@ public class StringLib extends TwoArgFunction {
* t[k] = v
* end
*
* For this function, a '^' at the start of a pattern does not work as an anchor,
* For this function, a '^' at the start of a pattern does not work as an anchor,
* as this would prevent the iteration.
*/
static final class gmatch extends VarArgFunction {
@@ -519,18 +527,20 @@ public class StringLib extends TwoArgFunction {
private final int srclen;
private final MatchState ms;
private int soffset;
private int lastmatch;
public GMatchAux(Varargs args, LuaString src, LuaString pat) {
this.srclen = src.length();
this.ms = new MatchState(args, src, pat);
this.soffset = 0;
this.lastmatch = -1;
}
public Varargs invoke(Varargs args) {
for ( ; soffset<srclen; soffset++ ) {
for ( ; soffset<=srclen; soffset++ ) {
ms.reset();
int res = ms.match(soffset, 0);
if ( res >=0 ) {
if ( res >=0 && res != lastmatch ) {
int soff = soffset;
soffset = res;
lastmatch = soffset = res;
return ms.push_captures( true, soff, res );
}
}
@@ -539,28 +549,28 @@ public class StringLib extends TwoArgFunction {
}
/**
/**
* string.gsub (s, pattern, repl [, n])
* Returns a copy of s in which all (or the first n, if given) occurrences of the
* pattern have been replaced by a replacement string specified by repl, which
* may be a string, a table, or a function. gsub also returns, as its second value,
* Returns a copy of s in which all (or the first n, if given) occurrences of the
* pattern have been replaced by a replacement string specified by repl, which
* may be a string, a table, or a function. gsub also returns, as its second value,
* the total number of matches that occurred.
*
* If repl is a string, then its value is used for replacement.
* The character % works as an escape character: any sequence in repl of the form %n,
* with n between 1 and 9, stands for the value of the n-th captured substring (see below).
* The sequence %0 stands for the whole match. The sequence %% stands for a single %.
*
* If repl is a table, then the table is queried for every match, using the first capture
* as the key; if the pattern specifies no captures, then the whole match is used as the key.
* If repl is a string, then its value is used for replacement.
* The character % works as an escape character: any sequence in repl of the form %n,
* with n between 1 and 9, stands for the value of the n-th captured substring (see below).
* The sequence %0 stands for the whole match. The sequence %% stands for a single %.
*
* If repl is a function, then this function is called every time a match occurs,
* with all captured substrings passed as arguments, in order; if the pattern specifies
* no captures, then the whole match is passed as a sole argument.
* If repl is a table, then the table is queried for every match, using the first capture
* as the key; if the pattern specifies no captures, then the whole match is used as the key.
*
* If the value returned by the table query or by the function call is a string or a number,
* then it is used as the replacement string; otherwise, if it is false or nil,
* then there is no replacement (that is, the original match is kept in the string).
* If repl is a function, then this function is called every time a match occurs,
* with all captured substrings passed as arguments, in order; if the pattern specifies
* no captures, then the whole match is passed as a sole argument.
*
* If the value returned by the table query or by the function call is a string or a number,
* then it is used as the replacement string; otherwise, if it is false or nil,
* then there is no replacement (that is, the original match is kept in the string).
*
* Here are some examples:
* x = string.gsub("hello world", "(%w+)", "%1 %1")
@@ -589,6 +599,7 @@ public class StringLib extends TwoArgFunction {
LuaString src = args.checkstring( 1 );
final int srclen = src.length();
LuaString p = args.checkstring( 2 );
int lastmatch = -1; /* end of last match */
LuaValue repl = args.arg( 3 );
int max_s = args.optint( 4, srclen + 1 );
final boolean anchor = p.length() > 0 && p.charAt( 0 ) == '^';
@@ -601,29 +612,26 @@ public class StringLib extends TwoArgFunction {
while ( n < max_s ) {
ms.reset();
int res = ms.match( soffset, anchor ? 1 : 0 );
if ( res != -1 ) {
if ( res != -1 && res != lastmatch ) { /* match? */
n++;
ms.add_value( lbuf, soffset, res, repl );
ms.add_value( lbuf, soffset, res, repl ); /* add replacement to buffer */
soffset = lastmatch = res;
}
if ( res != -1 && res > soffset )
soffset = res;
else if ( soffset < srclen )
else if ( soffset < srclen ) /* otherwise, skip one character */
lbuf.append( (byte) src.luaByte( soffset++ ) );
else
break;
if ( anchor )
break;
else break; /* end of subject */
if ( anchor ) break;
}
lbuf.append( src.substring( soffset, srclen ) );
return varargsOf(lbuf.tostring(), valueOf(n));
}
}
/**
/**
* string.len (s)
*
* Receives a string and returns its length. The empty string "" has length 0.
* Embedded zeros are counted, so "a\000bc\000" has length 5.
* Receives a string and returns its length. The empty string "" has length 0.
* Embedded zeros are counted, so "a\000bc\000" has length 5.
*/
static final class len extends OneArgFunction {
public LuaValue call(LuaValue arg) {
@@ -631,11 +639,11 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.lower (s)
*
* Receives a string and returns a copy of this string with all uppercase letters
* changed to lowercase. All other characters are left unchanged.
* Receives a string and returns a copy of this string with all uppercase letters
* changed to lowercase. All other characters are left unchanged.
* The definition of what an uppercase letter is depends on the current locale.
*/
static final class lower extends OneArgFunction {
@@ -662,7 +670,7 @@ public class StringLib extends TwoArgFunction {
/**
* string.rep (s, n)
*
* Returns a string that is the concatenation of n copies of the string s.
* Returns a string that is the concatenation of n copies of the string s.
*/
static final class rep extends VarArgFunction {
public Varargs invoke(Varargs args) {
@@ -677,10 +685,10 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.reverse (s)
*
* Returns a string that is the string s reversed.
* Returns a string that is the string s reversed.
*/
static final class reverse extends OneArgFunction {
public LuaValue call(LuaValue arg) {
@@ -693,15 +701,15 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.sub (s, i [, j])
*
* Returns the substring of s that starts at i and continues until j;
* i and j may be negative. If j is absent, then it is assumed to be equal to -1
* (which is the same as the string length). In particular, the call
* string.sub(s,1,j)
* returns a prefix of s with length j, and
* string.sub(s, -i)
* Returns the substring of s that starts at i and continues until j;
* i and j may be negative. If j is absent, then it is assumed to be equal to -1
* (which is the same as the string length). In particular, the call
* string.sub(s,1,j)
* returns a prefix of s with length j, and
* string.sub(s, -i)
* returns a suffix of s with length i.
*/
static final class sub extends VarArgFunction {
@@ -725,12 +733,12 @@ public class StringLib extends TwoArgFunction {
}
}
/**
/**
* string.upper (s)
*
* Receives a string and returns a copy of this string with all lowercase letters
* changed to uppercase. All other characters are left unchanged.
* The definition of what a lowercase letter is depends on the current locale.
* Receives a string and returns a copy of this string with all lowercase letters
* changed to uppercase. All other characters are left unchanged.
* The definition of what a lowercase letter is depends on the current locale.
*/
static final class upper extends OneArgFunction {
public LuaValue call(LuaValue arg) {
@@ -764,7 +772,7 @@ public class StringLib extends TwoArgFunction {
boolean anchor = false;
int poff = 0;
if ( pat.luaByte( 0 ) == '^' ) {
if ( pat.length() > 0 && pat.luaByte( 0 ) == '^' ) {
anchor = true;
poff = 1;
}
@@ -785,7 +793,7 @@ public class StringLib extends TwoArgFunction {
return NIL;
}
private static int posrelat( int pos, int len ) {
static int posrelat( int pos, int len ) {
return ( pos >= 0 ) ? pos : len + pos + 1;
}
@@ -795,6 +803,8 @@ public class StringLib extends TwoArgFunction {
private static final LuaString SPECIALS = valueOf("^$*+?.([%-");
private static final int MAX_CAPTURES = 32;
private static final int MAXCCALLS = 200;
private static final int CAP_UNFINISHED = -1;
private static final int CAP_POSITION = -2;
@@ -807,12 +817,12 @@ public class StringLib extends TwoArgFunction {
private static final byte MASK_CONTROL = 0x40;
private static final byte MASK_HEXDIGIT = (byte)0x80;
private static final byte[] CHAR_TABLE;
static final byte[] CHAR_TABLE;
static {
CHAR_TABLE = new byte[256];
for ( int i = 0; i < 256; ++i ) {
for ( int i = 0; i < 128; ++i ) {
final char c = (char) i;
CHAR_TABLE[i] = (byte)( ( Character.isDigit( c ) ? MASK_DIGIT : 0 ) |
( Character.isLowerCase( c ) ? MASK_LOWERCASE : 0 ) |
@@ -821,7 +831,7 @@ public class StringLib extends TwoArgFunction {
if ( ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) || ( c >= '0' && c <= '9' ) ) {
CHAR_TABLE[i] |= MASK_HEXDIGIT;
}
if ( ( c >= '!' && c <= '/' ) || ( c >= ':' && c <= '@' ) ) {
if ( ( c >= '!' && c <= '/' ) || ( c >= ':' && c <= '@' ) || ( c >= '[' && c <= '`' ) || ( c >= '{' && c <= '~' ) ) {
CHAR_TABLE[i] |= MASK_PUNCT;
}
if ( ( CHAR_TABLE[i] & ( MASK_LOWERCASE | MASK_UPPERCASE ) ) != 0 ) {
@@ -833,11 +843,12 @@ public class StringLib extends TwoArgFunction {
CHAR_TABLE['\r'] |= MASK_SPACE;
CHAR_TABLE['\n'] |= MASK_SPACE;
CHAR_TABLE['\t'] |= MASK_SPACE;
CHAR_TABLE[0x0C /* '\v' */ ] |= MASK_SPACE;
CHAR_TABLE[0x0B /* '\v' */ ] |= MASK_SPACE;
CHAR_TABLE['\f'] |= MASK_SPACE;
};
static class MatchState {
int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
final LuaString s;
final LuaString p;
final Varargs args;
@@ -852,10 +863,12 @@ public class StringLib extends TwoArgFunction {
this.level = 0;
this.cinit = new int[ MAX_CAPTURES ];
this.clen = new int[ MAX_CAPTURES ];
this.matchdepth = MAXCCALLS;
}
void reset() {
level = 0;
this.matchdepth = MAXCCALLS;
}
private void add_s( Buffer lbuf, LuaString news, int soff, int e ) {
@@ -866,8 +879,14 @@ public class StringLib extends TwoArgFunction {
lbuf.append( (byte) b );
} else {
++i; // skip ESC
b = (byte) news.luaByte( i );
b = (byte)(i < l ? news.luaByte( i ) : 0);
if ( !Character.isDigit( (char) b ) ) {
if (b != L_ESC) error( "invalid use of '" + (char)L_ESC +
"' in replacement string: after '" + (char)L_ESC +
"' must be '0'-'9' or '" + (char)L_ESC +
"', but found " + (i < l ? "symbol '" + (char)b + "' with code " + b +
" at pos " + (i + 1) :
"end of string"));
lbuf.append( b );
} else if ( b == '0' ) {
lbuf.append( s.substring( soff, e ) );
@@ -924,7 +943,7 @@ public class StringLib extends TwoArgFunction {
if ( i == 0 ) {
return s.substring( soff, end );
} else {
return error( "invalid capture index" );
return error( "invalid capture index %" + (i + 1) );
}
} else {
int l = clen[i];
@@ -943,7 +962,7 @@ public class StringLib extends TwoArgFunction {
private int check_capture( int l ) {
l -= '1';
if ( l < 0 || l >= level || this.clen[l] == CAP_UNFINISHED ) {
error("invalid capture index");
error("invalid capture index %" + (l + 1));
}
return l;
}
@@ -961,19 +980,19 @@ public class StringLib extends TwoArgFunction {
switch ( p.luaByte( poffset++ ) ) {
case L_ESC:
if ( poffset == p.length() ) {
error( "malformed pattern (ends with %)" );
error( "malformed pattern (ends with '%')" );
}
return poffset + 1;
case '[':
if ( p.luaByte( poffset ) == '^' ) poffset++;
if ( poffset != p.length() && p.luaByte( poffset ) == '^' ) poffset++;
do {
if ( poffset == p.length() ) {
error( "malformed pattern (missing ])" );
error( "malformed pattern (missing ']')" );
}
if ( p.luaByte( poffset++ ) == L_ESC && poffset != p.length() )
poffset++;
} while ( p.luaByte( poffset ) != ']' );
if ( p.luaByte( poffset++ ) == L_ESC && poffset < p.length() )
poffset++; /* skip escapes (e.g. '%]') */
} while ( poffset == p.length() || p.luaByte( poffset ) != ']' );
return poffset + 1;
default:
return poffset;
@@ -993,9 +1012,10 @@ public class StringLib extends TwoArgFunction {
case 'c': res = ( cdata & MASK_CONTROL ) != 0; break;
case 'p': res = ( cdata & MASK_PUNCT ) != 0; break;
case 's': res = ( cdata & MASK_SPACE ) != 0; break;
case 'g': res = ( cdata & ( MASK_ALPHA | MASK_DIGIT | MASK_PUNCT ) ) != 0; break;
case 'w': res = ( cdata & ( MASK_ALPHA | MASK_DIGIT ) ) != 0; break;
case 'x': res = ( cdata & MASK_HEXDIGIT ) != 0; break;
case 'z': res = ( c == 0 ); break;
case 'z': res = ( c == 0 ); break; /* deprecated option */
default: return cl == c;
}
return ( lcl == cl ) ? res : !res;
@@ -1037,80 +1057,86 @@ public class StringLib extends TwoArgFunction {
* where match ends, otherwise returns -1.
*/
int match( int soffset, int poffset ) {
while ( true ) {
// Check if we are at the end of the pattern -
// equivalent to the '\0' case in the C version, but our pattern
// string is not NUL-terminated.
if ( poffset == p.length() )
return soffset;
switch ( p.luaByte( poffset ) ) {
case '(':
if ( ++poffset < p.length() && p.luaByte( poffset ) == ')' )
return start_capture( soffset, poffset + 1, CAP_POSITION );
else
return start_capture( soffset, poffset, CAP_UNFINISHED );
case ')':
return end_capture( soffset, poffset + 1 );
case L_ESC:
if ( poffset + 1 == p.length() )
error("malformed pattern (ends with '%')");
switch ( p.luaByte( poffset + 1 ) ) {
case 'b':
soffset = matchbalance( soffset, poffset + 2 );
if ( soffset == -1 ) return -1;
poffset += 4;
continue;
case 'f': {
poffset += 2;
if ( p.luaByte( poffset ) != '[' ) {
error("Missing [ after %f in pattern");
if (matchdepth-- == 0) error("pattern too complex");
try {
while ( true ) {
// Check if we are at the end of the pattern -
// equivalent to the '\0' case in the C version, but our pattern
// string is not NUL-terminated.
if ( poffset == p.length() )
return soffset;
switch ( p.luaByte( poffset ) ) {
case '(':
if ( ++poffset < p.length() && p.luaByte( poffset ) == ')' )
return start_capture( soffset, poffset + 1, CAP_POSITION );
else
return start_capture( soffset, poffset, CAP_UNFINISHED );
case ')':
return end_capture( soffset, poffset + 1 );
case L_ESC:
if ( poffset + 1 == p.length() )
error("malformed pattern (ends with '%')");
switch ( p.luaByte( poffset + 1 ) ) {
case 'b':
soffset = matchbalance( soffset, poffset + 2 );
if ( soffset == -1 ) return -1;
poffset += 4;
continue;
case 'f': {
poffset += 2;
if ( poffset == p.length() || p.luaByte( poffset ) != '[' ) {
error("missing '[' after '%f' in pattern");
}
int ep = classend( poffset );
int previous = ( soffset == 0 ) ? '\0' : s.luaByte( soffset - 1 );
int next = ( soffset == s.length() ) ? '\0' : s.luaByte( soffset );
if ( matchbracketclass( previous, poffset, ep - 1 ) ||
!matchbracketclass( next, poffset, ep - 1 ) )
return -1;
poffset = ep;
continue;
}
int ep = classend( poffset );
int previous = ( soffset == 0 ) ? -1 : s.luaByte( soffset - 1 );
if ( matchbracketclass( previous, poffset, ep - 1 ) ||
matchbracketclass( s.luaByte( soffset ), poffset, ep - 1 ) )
default: {
int c = p.luaByte( poffset + 1 );
if ( Character.isDigit( (char) c ) ) {
soffset = match_capture( soffset, c );
if ( soffset == -1 )
return -1;
return match( soffset, poffset + 2 );
}
}
}
case '$':
if ( poffset + 1 == p.length() )
return ( soffset == s.length() ) ? soffset : -1;
}
int ep = classend( poffset );
boolean m = soffset < s.length() && singlematch( s.luaByte( soffset ), poffset, ep );
int pc = ( ep < p.length() ) ? p.luaByte( ep ) : '\0';
switch ( pc ) {
case '?':
int res;
if ( m && ( ( res = match( soffset + 1, ep + 1 ) ) != -1 ) )
return res;
poffset = ep + 1;
continue;
case '*':
return max_expand( soffset, poffset, ep );
case '+':
return ( m ? max_expand( soffset + 1, poffset, ep ) : -1 );
case '-':
return min_expand( soffset, poffset, ep );
default:
if ( !m )
return -1;
soffset++;
poffset = ep;
continue;
}
default: {
int c = p.luaByte( poffset + 1 );
if ( Character.isDigit( (char) c ) ) {
soffset = match_capture( soffset, c );
if ( soffset == -1 )
return -1;
return match( soffset, poffset + 2 );
}
}
}
case '$':
if ( poffset + 1 == p.length() )
return ( soffset == s.length() ) ? soffset : -1;
}
int ep = classend( poffset );
boolean m = soffset < s.length() && singlematch( s.luaByte( soffset ), poffset, ep );
int pc = ( ep < p.length() ) ? p.luaByte( ep ) : '\0';
switch ( pc ) {
case '?':
int res;
if ( m && ( ( res = match( soffset + 1, ep + 1 ) ) != -1 ) )
return res;
poffset = ep + 1;
continue;
case '*':
return max_expand( soffset, poffset, ep );
case '+':
return ( m ? max_expand( soffset + 1, poffset, ep ) : -1 );
case '-':
return min_expand( soffset, poffset, ep );
default:
if ( !m )
return -1;
soffset++;
poffset = ep;
continue;
}
} finally {
matchdepth++;
}
}
@@ -1175,7 +1201,7 @@ public class StringLib extends TwoArgFunction {
int matchbalance( int soff, int poff ) {
final int plen = p.length();
if ( poff == plen || poff + 1 == plen ) {
error( "unbalanced pattern" );
error( "malformed pattern (missing arguments to '%b')" );
}
final int slen = s.length();
if ( soff >= slen )

View File

@@ -25,19 +25,19 @@ import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code table}
* library.
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code table}
* library.
*
* <p>
* Typically, this library is included as part of a call to either
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
@@ -70,15 +70,9 @@ public class TableLib extends TwoArgFunction {
table.set("sort", new sort());
table.set("unpack", new unpack());
env.set("table", table);
env.get("package").get("loaded").set("table", table);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("table", table);
return NIL;
}
static class TableLibFunction extends LibFunction {
public LuaValue call() {
return argerror(1, "table expected, got no value");
}
}
// "concat" (table [, sep [, i [, j]]]) -> string
static class concat extends TableLibFunction {
@@ -100,18 +94,22 @@ public class TableLib extends TwoArgFunction {
static class insert extends VarArgFunction {
public Varargs invoke(Varargs args) {
switch (args.narg()) {
case 0: case 1: {
return argerror(2, "value expected");
}
case 2: {
LuaTable table = args.arg1().checktable();
LuaTable table = args.checktable(1);
table.insert(table.length()+1,args.arg(2));
return NONE;
}
default: {
args.arg1().checktable().insert(args.checkint(2),args.arg(3));
case 3: {
LuaTable table = args.checktable(1);
int pos = args.checkint(2);
int max = table.length() + 1;
if (pos < 1 || pos > max) argerror(2, "position out of bounds: " + pos + " not between 1 and " + max);
table.insert(pos, args.arg(3));
return NONE;
}
default: {
return error("wrong number of arguments to 'table.insert': " + args.narg() + " (must be 2 or 3)");
}
}
}
}
@@ -128,15 +126,21 @@ public class TableLib extends TwoArgFunction {
// "remove" (table [, pos]) -> removed-ele
static class remove extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.arg1().checktable().remove(args.optint(2, 0));
LuaTable table = args.checktable(1);
int size = table.length();
int pos = args.optint(2, size);
if (pos != size && (pos < 1 || pos > size + 1)) {
argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size + 1));
}
return table.remove(pos);
}
}
// "sort" (table [, comp])
static class sort extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.arg1().checktable().sort(
args.arg(2).isnil()? NIL: args.arg(2).checkfunction());
args.checktable(1).sort(
args.isnil(2)? NIL: args.checkfunction(2));
return NONE;
}
}
@@ -146,11 +150,9 @@ public class TableLib extends TwoArgFunction {
static class unpack extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaTable t = args.checktable(1);
switch (args.narg()) {
case 1: return t.unpack();
case 2: return t.unpack(args.checkint(2));
default: return t.unpack(args.checkint(2), args.checkint(3));
}
// do not waste resource for calc rawlen if arg3 is not nil
int len = args.arg(3).isnil() ? t.length() : 0;
return t.unpack(args.optint(2, 1), args.optint(3, len));
}
}
}

View File

@@ -0,0 +1,9 @@
package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
class TableLibFunction extends LibFunction {
public LuaValue call() {
return argerror(1, "table expected, got no value");
}
}

View File

@@ -101,7 +101,7 @@ abstract public class Visitor {
visitExps(args.exps);
}
public void visit(TableField field) {
if ( field.name != null );
if ( field.name != null )
visit( field.name );
if ( field.index != null )
field.index.accept(this);

View File

@@ -21,6 +21,7 @@
******************************************************************************/
package org.luaj.vm2.lib.jse;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -133,7 +134,7 @@ public class CoerceJavaToLua {
}
static final Map COERCIONS = new HashMap();
static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
static {
Coercion boolCoercion = new BoolCoercion() ;

View File

@@ -174,13 +174,13 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
switch ( targetType ) {
case TARGET_TYPE_BYTE: return new Byte( (byte) value.toint() );
case TARGET_TYPE_CHAR: return new Character( (char) value.toint() );
case TARGET_TYPE_SHORT: return new Short( (short) value.toint() );
case TARGET_TYPE_INT: return new Integer( (int) value.toint() );
case TARGET_TYPE_LONG: return new Long( (long) value.todouble() );
case TARGET_TYPE_FLOAT: return new Float( (float) value.todouble() );
case TARGET_TYPE_DOUBLE: return new Double( (double) value.todouble() );
case TARGET_TYPE_BYTE: return (byte) value.toint() ;
case TARGET_TYPE_CHAR: return (char) value.toint() ;
case TARGET_TYPE_SHORT: return (short) value.toint();
case TARGET_TYPE_INT: return value.toint();
case TARGET_TYPE_LONG: return (long) value.todouble();
case TARGET_TYPE_FLOAT: return (float) value.todouble();
case TARGET_TYPE_DOUBLE: return (double) value.todouble();
default: return null;
}
}
@@ -308,7 +308,7 @@ public class CoerceLuaToJava {
public Object coerce(LuaValue value) {
switch ( value.type() ) {
case LuaValue.TNUMBER:
return value.isint()? (Object)new Integer(value.toint()): (Object)new Double(value.todouble());
return value.isint()? value.toint() : value.todouble();
case LuaValue.TBOOLEAN:
return value.toboolean()? Boolean.TRUE: Boolean.FALSE;
case LuaValue.TSTRING:

View File

@@ -21,6 +21,7 @@
******************************************************************************/
package org.luaj.vm2.lib.jse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -106,9 +107,10 @@ public class JseBaseLib extends org.luaj.vm2.lib.BaseLib {
if ( ! f.exists() )
return super.findResource(filename);
try {
return new FileInputStream(f);
return new BufferedInputStream(new FileInputStream(f));
} catch ( IOException ioe ) {
return null;
}
}
}

View File

@@ -36,20 +36,20 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.IoLib;
import org.luaj.vm2.lib.LibFunction;
/**
* Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io}
* library for the JSE platform.
* <p>
* It uses RandomAccessFile to implement seek on files.
/**
* Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io}
* library for the JSE platform.
* <p>
* Typically, this library is included as part of a call to
* It uses RandomAccessFile to implement seek on files.
* <p>
* Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre>
* <p>
* For special cases where the smallest possible footprint is desired,
* For special cases where the smallest possible footprint is desired,
* a minimal set of libraries could be loaded
* directly via {@link Globals#load(LuaValue)} using code such as:
* <pre> {@code
@@ -96,9 +96,9 @@ public class JseIoLib extends IoLib {
protected File openProgram(String prog, String mode) throws IOException {
final Process p = Runtime.getRuntime().exec(prog);
return "w".equals(mode)?
new FileImpl( p.getOutputStream() ):
new FileImpl( p.getInputStream() );
return "w".equals(mode)?
new FileImpl( p.getOutputStream() ):
new FileImpl( p.getInputStream() );
}
protected File tmpFile() throws IOException {
@@ -133,7 +133,7 @@ public class JseIoLib extends IoLib {
this( null, null, o );
}
public String tojstring() {
return "file ("+this.hashCode()+")";
return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")";
}
public boolean isstdfile() {
return file == null;
@@ -199,11 +199,11 @@ public class JseIoLib extends IoLib {
}
notimplemented();
return 0;
}
}
// return char if read, -1 if eof, throw IOException on other exception
// return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException {
if ( is != null )
if ( is != null )
return is.read();
else if ( file != null ) {
return file.read();
@@ -321,7 +321,7 @@ public class JseIoLib extends IoLib {
}
public int remaining() throws IOException {
return 0;
return -1;
}
public int peek() throws IOException, EOFException {

View File

@@ -24,6 +24,7 @@ package org.luaj.vm2.lib.jse;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code math}
@@ -76,8 +77,9 @@ public class JseMathLib extends org.luaj.vm2.lib.MathLib {
LuaValue math = env.get("math");
math.set("acos", new acos());
math.set("asin", new asin());
math.set("atan", new atan());
math.set("atan2", new atan2());
LuaValue atan = new atan2();
math.set("atan", atan);
math.set("atan2", atan);
math.set("cosh", new cosh());
math.set("exp", new exp());
math.set("log", new log());
@@ -89,11 +91,21 @@ public class JseMathLib extends org.luaj.vm2.lib.MathLib {
static final class acos extends UnaryOp { protected double call(double d) { return Math.acos(d); } }
static final class asin extends UnaryOp { protected double call(double d) { return Math.asin(d); } }
static final class atan extends UnaryOp { protected double call(double d) { return Math.atan(d); } }
static final class atan2 extends BinaryOp { protected double call(double y, double x) { return Math.atan2(y, x); } }
static final class atan2 extends TwoArgFunction {
public LuaValue call(LuaValue x, LuaValue y) {
return valueOf(Math.atan2(x.checkdouble(), y.optdouble(1)));
}
}
static final class cosh extends UnaryOp { protected double call(double d) { return Math.cosh(d); } }
static final class exp extends UnaryOp { protected double call(double d) { return Math.exp(d); } }
static final class log extends UnaryOp { protected double call(double d) { return Math.log(d); } }
static final class log extends TwoArgFunction {
public LuaValue call(LuaValue x, LuaValue base) {
double nat = Math.log(x.checkdouble());
double b = base.optdouble(Math.E);
if (b != Math.E) nat /= Math.log(b);
return valueOf(nat);
}
}
static final class pow extends BinaryOp { protected double call(double x, double y) { return Math.pow(x, y); } }
static final class sinh extends UnaryOp { protected double call(double d) { return Math.sinh(d); } }
static final class tanh extends UnaryOp { protected double call(double d) { return Math.tanh(d); } }
@@ -105,3 +117,4 @@ public class JseMathLib extends org.luaj.vm2.lib.MathLib {
}

View File

@@ -33,8 +33,8 @@ import org.luaj.vm2.lib.OsLib;
/**
* Subclass of {@link LibFunction} which implements the standard lua {@code os} library.
* <p>
* This contains more complete implementations of the following functions
* using features that are specific to JSE:
* This contains more complete implementations of the following functions
* using features that are specific to JSE:
* <ul>
* <li>{@code execute()}</li>
* <li>{@code remove()}</li>
@@ -42,18 +42,18 @@ import org.luaj.vm2.lib.OsLib;
* <li>{@code tmpname()}</li>
* </ul>
* <p>
* Because the nature of the {@code os} library is to encapsulate
* os-specific features, the behavior of these functions varies considerably
* from their counterparts in the C platform.
* Because the nature of the {@code os} library is to encapsulate
* os-specific features, the behavior of these functions varies considerably
* from their counterparts in the C platform.
* <p>
* Typically, this library is included as part of a call to
* Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("os").get("time").call() );
* } </pre>
* <p>
* For special cases where the smallest possible footprint is desired,
* For special cases where the smallest possible footprint is desired,
* a minimal set of libraries could be loaded
* directly via {@link Globals#load(LuaValue)} using code such as:
* <pre> {@code
@@ -74,13 +74,13 @@ import org.luaj.vm2.lib.OsLib;
public class JseOsLib extends org.luaj.vm2.lib.OsLib {
/** return code indicating the execute() threw an I/O exception */
public static int EXEC_IOEXCEPTION = 1;
public static final int EXEC_IOEXCEPTION = 1;
/** return code indicating the execute() was interrupted */
public static int EXEC_INTERRUPTED = -2;
public static final int EXEC_INTERRUPTED = -2;
/** return code indicating the execute() threw an unknown exception */
public static int EXEC_ERROR = -3;
public static final int EXEC_ERROR = -3;
/** public constructor */
public JseOsLib() {
@@ -120,13 +120,13 @@ public class JseOsLib extends org.luaj.vm2.lib.OsLib {
if ( ! f.exists() )
throw new IOException("No such file or directory");
if ( ! f.renameTo(new File(newname)) )
throw new IOException("Failed to delete");
throw new IOException("Failed to rename");
}
protected String tmpname() {
try {
java.io.File f = java.io.File.createTempFile(TMP_PREFIX ,TMP_SUFFIX);
return f.getName();
return f.getAbsolutePath();
} catch ( IOException ioe ) {
return super.tmpname();
}

View File

@@ -23,8 +23,8 @@ package org.luaj.vm2.lib.jse;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.Bit32Lib;
import org.luaj.vm2.lib.CoroutineLib;
@@ -34,10 +34,10 @@ import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.TableLib;
/** The {@link org.luaj.vm2.lib.jse.JsePlatform} class is a convenience class to standardize
* how globals tables are initialized for the JSE platform.
/** The {@link org.luaj.vm2.lib.jse.JsePlatform} class is a convenience class to standardize
* how globals tables are initialized for the JSE platform.
* <p>
* It is used to allocate either a set of standard globals using
* It is used to allocate either a set of standard globals using
* {@link #standardGlobals()} or debug globals using {@link #debugGlobals()}
* <p>
* A simple example of initializing globals and using them from Java is:
@@ -51,7 +51,7 @@ import org.luaj.vm2.lib.TableLib;
* globals.load( new FileInputStream("main.lua"), "main.lua" ).call();
* } </pre>
* <p>
* although {@code require} could also be used:
* although {@code require} could also be used:
* <pre> {@code
* globals.get("require").call(LuaValue.valueOf("main"));
* } </pre>
@@ -72,8 +72,8 @@ import org.luaj.vm2.lib.TableLib;
* <li>{@link org.luaj.vm2.lib.jse.JseOsLib}</li>
* <li>{@link org.luaj.vm2.lib.jse.LuajavaLib}</li>
* </ul>
* In addition, the {@link LuaC} compiler is installed so lua files may be loaded in their source form.
* <p>
* In addition, the {@link LuaC} compiler is installed so lua files may be loaded in their source form.
* <p>
* The debug globals are simply the standard globals plus the {@code debug} library {@link DebugLib}.
* <p>
* The class ensures that initialization is done in the correct order.
@@ -97,7 +97,7 @@ public class JsePlatform {
globals.load(new PackageLib());
globals.load(new Bit32Lib());
globals.load(new TableLib());
globals.load(new StringLib());
globals.load(new JseStringLib());
globals.load(new CoroutineLib());
globals.load(new JseMathLib());
globals.load(new JseIoLib());
@@ -105,7 +105,7 @@ public class JsePlatform {
globals.load(new LuajavaLib());
LoadState.install(globals);
LuaC.install(globals);
return globals;
return globals;
}
/** Create standard globals including the {@link DebugLib} library.
@@ -123,11 +123,12 @@ public class JsePlatform {
}
/** Simple wrapper for invoking a lua function with command line arguments.
* The supplied function is first given a new Globals object,
/** Simple wrapper for invoking a lua function with command line arguments.
* The supplied function is first given a new Globals object as its environment
* then the program is run with arguments.
* @return {@link Varargs} containing any values returned by mainChunk.
*/
public static void luaMain(LuaValue mainChunk, String[] args) {
public static Varargs luaMain(LuaValue mainChunk, String[] args) {
Globals g = standardGlobals();
int n = args.length;
LuaValue[] vargs = new LuaValue[args.length];
@@ -137,6 +138,6 @@ public class JsePlatform {
arg.set("n", n);
g.set("arg", arg);
mainChunk.initupvalue1(g);
mainChunk.invoke(LuaValue.varargsOf(vargs));
return mainChunk.invoke(LuaValue.varargsOf(vargs));
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib.jse;
public class JseStringLib extends org.luaj.vm2.lib.StringLib {
/** public constructor */
public JseStringLib() {
}
protected String format(String src, double x) {
String out;
try {
out = String.format(src, new Object[] {Double.valueOf(x)});
} catch (Throwable e) {
out = super.format(src, x);
}
return out;
}
}

View File

@@ -37,38 +37,38 @@ import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.VarArgFunction;
/**
* Subclass of {@link LibFunction} which implements the features of the luajava package.
* <p>
* Luajava is an approach to mixing lua and java using simple functions that bind
* java classes and methods to lua dynamically. The API is documented on the
/**
* Subclass of {@link LibFunction} which implements the features of the luajava package.
* <p>
* Luajava is an approach to mixing lua and java using simple functions that bind
* java classes and methods to lua dynamically. The API is documented on the
* <a href="http://www.keplerproject.org/luajava/">luajava</a> documentation pages.
*
* <p>
* Typically, this library is included as part of a call to
* Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("luajava").get("bindClass").call( LuaValue.valueOf("java.lang.System") ).invokeMethod("currentTimeMillis") );
* } </pre>
* <p>
* To instantiate and use it directly,
* To instantiate and use it directly,
* link it into your globals table via {@link Globals#load} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new LuajavaLib());
* globals.load(
* globals.load(
* "sys = luajava.bindClass('java.lang.System')\n"+
* "print ( sys:currentTimeMillis() )\n", "main.lua" ).call();
* "print ( sys:currentTimeMillis() )\n", "main.lua" ).call();
* } </pre>
* <p>
*
* The {@code luajava} library is available
* The {@code luajava} library is available
* on all JSE platforms via the call to {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* and the luajava api's are simply invoked from lua.
* Because it makes extensive use of Java's reflection API, it is not available
* and the luajava api's are simply invoked from lua.
* Because it makes extensive use of Java's reflection API, it is not available
* on JME, but can be used in Android applications.
* <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
@@ -91,10 +91,10 @@ public class LuajavaLib extends VarArgFunction {
static final int LOADLIB = 5;
static final String[] NAMES = {
"bindClass",
"newInstance",
"new",
"createProxy",
"bindClass",
"newInstance",
"new",
"createProxy",
"loadLib",
};
@@ -112,7 +112,7 @@ public class LuajavaLib extends VarArgFunction {
LuaTable t = new LuaTable();
bind( t, this.getClass(), NAMES, BINDCLASS );
env.set("luajava", t);
env.get("package").get("loaded").set("luajava", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("luajava", t);
return t;
}
case BINDCLASS: {
@@ -122,13 +122,13 @@ public class LuajavaLib extends VarArgFunction {
case NEWINSTANCE:
case NEW: {
// get constructor
final LuaValue c = args.checkvalue(1);
final LuaValue c = args.checkvalue(1);
final Class clazz = (opcode==NEWINSTANCE? classForName(c.tojstring()): (Class) c.checkuserdata(Class.class));
final Varargs consargs = args.subargs(2);
return JavaClass.forClass(clazz).getConstructor().invoke(consargs);
}
case CREATEPROXY: {
case CREATEPROXY: {
final int niface = args.narg()-1;
if ( niface <= 0 )
throw new LuaError("no interfaces");
@@ -136,7 +136,7 @@ public class LuajavaLib extends VarArgFunction {
// get the interfaces
final Class[] ifaces = new Class[niface];
for ( int i=0; i<niface; i++ )
for ( int i=0; i<niface; i++ )
ifaces[i] = classForName(args.checkjstring(i+1));
// create the invocation handler
@@ -191,16 +191,16 @@ public class LuajavaLib extends VarArgFunction {
if ( func.isnil() )
return null;
boolean isvarargs = ((method.getModifiers() & METHOD_MODIFIERS_VARARGS) != 0);
int n = args!=null? args.length: 0;
int n = args!=null? args.length: 0;
LuaValue[] v;
if ( isvarargs ) {
if ( isvarargs ) {
Object o = args[--n];
int m = Array.getLength( o );
v = new LuaValue[n+m];
for ( int i=0; i<n; i++ )
v[i] = CoerceJavaToLua.coerce(args[i]);
for ( int i=0; i<m; i++ )
v[i+n] = CoerceJavaToLua.coerce(Array.get(o,i));
v[i+n] = CoerceJavaToLua.coerce(Array.get(o,i));
} else {
v = new LuaValue[n];
for ( int i=0; i<n; i++ )

View File

@@ -244,8 +244,8 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
case LuaValue.TSTRING: return luajValue.tojstring();
case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class);
case LuaValue.TNUMBER: return luajValue.isinttype()?
(Object) new Integer(luajValue.toint()):
(Object) new Double(luajValue.todouble());
luajValue.toint():
luajValue.todouble();
default: return luajValue;
}
}

View File

@@ -32,6 +32,7 @@ import org.luaj.vm2.compiler.DumpLoadEndianIntTest;
import org.luaj.vm2.compiler.LuaParserTests;
import org.luaj.vm2.compiler.RegressionTests;
import org.luaj.vm2.compiler.SimpleTests;
import org.luaj.vm2.lib.jse.JsePlatformTest;
import org.luaj.vm2.lib.jse.LuaJavaCoercionTest;
import org.luaj.vm2.lib.jse.LuajavaAccessibleMembersTest;
import org.luaj.vm2.lib.jse.LuajavaClassMembersTest;
@@ -85,6 +86,7 @@ public class AllTests {
// library tests
TestSuite lib = new TestSuite("Library Tests");
lib.addTestSuite(JsePlatformTest.class);
lib.addTestSuite(LuajavaAccessibleMembersTest.class);
lib.addTestSuite(LuajavaClassMembersTest.class);
lib.addTestSuite(LuaJavaCoercionTest.class);

View File

@@ -0,0 +1,21 @@
package org.luaj.vm2.lib.jse;
import junit.framework.TestCase;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
public class JsePlatformTest extends TestCase {
public void testLuaMainPassesArguments() {
Globals globals = JsePlatform.standardGlobals();
LuaValue chunk = globals.load("return #arg, arg.n, arg[2], arg[1]");
Varargs results = JsePlatform.luaMain(chunk, new String[] { "aaa", "bbb" });
assertEquals(results.narg(), 4);
assertEquals(results.arg(1), LuaValue.valueOf(2));
assertEquals(results.arg(2), LuaValue.valueOf(2));
assertEquals(results.arg(3), LuaValue.valueOf("bbb"));
assertEquals(results.arg(4), LuaValue.valueOf("aaa"));
}
}

View File

@@ -1 +1 @@
version: 3.0.1
version: 3.1.0