Skip to content
Permalink
Browse files
Added argument checking utility. Added Math class. Fixed stack overfl…
…ows when casting to a String.
  • Loading branch information
James King committed Aug 6, 2013
1 parent 48372af commit f900646
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 77 deletions.
@@ -31,6 +31,10 @@ function StandardGlobals.typeof(vm, value)
return type(value)
end

function StandardGlobals.isprimitive(vm, t)
return type(t) == "string"
end

--[[
Casts a value to a particular class.
@param SeeVM:vm The VM to run this function for.
@@ -76,7 +80,11 @@ end
@param see.base.Exception The exception to throw.
]]
function StandardGlobals.throw(vm, exception)
vm.lastException = exception
if exception then
vm.lastException = exception
else
vm.lastException = vm:loadClassFromAny("see.rt.RuntimeException").new("Invalid argument to throw.")
end
error()
end

@@ -117,10 +125,12 @@ function SeeVM.new(natives)
end

self.base = { }
self.rt = { }
self.classes = { }
self.natives = natives
self.classPaths = { }
self.base.Object = self:loadClassFromAny("see.base.Object")
self.rt = self:loadClassFromAny("see.rt.Class")
self.base.Exception = self:loadClassFromAny("see.base.Exception")
self.base.System = self:loadClassFromAny("see.base.System")
self.base.Array = self:loadClassFromAny("see.base.Array")
@@ -203,7 +213,7 @@ function SeeVM:loadClassFromAny(name)

-- Load from custom class paths
for _, classPath in pairs(self.classPaths) do
class, err = self:loadClassFromFile(fs.combine(classPath, name:gsub("%.", "/") .. ".lua"), name)
class = self:loadClassFromFile(fs.combine(classPath, name:gsub("%.", "/") .. ".lua"), name)
if class then return class end
end

@@ -223,8 +233,9 @@ function SeeVM:executeAnnotation(env, class, annotation)

if aname == "import" then
local name = annotation:sub(pindex + 1):gsub("%s", "")

env[getPackageName(name)] = self:loadClassFromAny(name)
local class = self:loadClassFromAny(name)
if not class then error("Failed to import class.") end
env[getPackageName(name)] = class
elseif aname == "native" then
local name = annotation:sub(pindex + 1):gsub("%s", "")
local keys = getPackageComponents(name)
@@ -301,6 +312,12 @@ function SeeVM:loadClass(def, annotations, name)
class.__meta = { __index = class }
class.__name = name

if not self.rt.Class then
class.__type = class
else
class.__type = self.rt.Class
end

function class.new(...)
if not class.init then error("Could not instantiate class " .. name .. ". No init method.") end
local self = { }
@@ -1,4 +1,5 @@
--@import see.base.String
--@import see.base.System

--[[
Constructs a new Exception.
@@ -7,4 +8,8 @@
function Exception:init(message)
message = cast(message, String)
self.message = message
end

function Exception:toString()
return "[" .. self:getClass():getName() .. "]" .. self.message
end
@@ -13,26 +13,39 @@
]]
function String.__cast(value)
local type = typeof(value)
if typeof(type) == "table" then
if typeof(type) == "table" and value.__type then
return value:toString()
end
return String.new(tostring(value))

local str = tostring(value)
local ret = String.new()

for i = 1, #str do
ret.charArray[i] = str:sub(i, i):byte()
end

return ret
end

--[[
Constructs a new String which copies the given string.
@param string:str The string to be copied.
@param strings:str... The strings to be copied.
]]
function String:init(str)
function String:init(...)
local args = {...}
self.charArray = Array.new()
if not str then return end
if typeof(str) == "string" then
for i = 1, #str do
self.charArray[i] = str:sub(i, i):byte()
end
elseif typeof(str) == String then
for i = 1, str:length() do
self.charArray[i] = str.charArray[i]

for i = 1, #args do
local str = cast(args[i], String)
if not str then return end
if typeof(str) == "string" then
for i = 1, #str do
self.charArray:add(str:sub(i, i):byte())
end
elseif typeof(str) == String then
for i = 1, str:length() do
self.charArray:add(str.charArray[i])
end
end
end
end
@@ -57,11 +70,13 @@ function String:length()
return self.charArray.length
end

function String:concat(str)
local ret = String.new(self)
local len = self:length()
for i = 1, str:length() do
ret.charArray[i + len] = str.charArray[i]
function String.concat(a, b)
a = cast(a, String)
b = cast(b, String)
local ret = String.new(a)
local len = a:length()
for i = 1, b:length() do
ret.charArray[i + len] = b.charArray[i]
end
return ret
end
@@ -1,3 +1,7 @@
function Class:getName()
return self.__name
return String.new(self.__name)
end

function Class:toString()
return self:getName()
end
@@ -0,0 +1,13 @@
--@import see.rt.RuntimeException
--@extends see.rt.RuntimeException

function InvalidArgumentException:init(n, expected, got)
if not isprimitive(expected) then
expected = exepected:getName()
end
if not isprimitive(got) then
got = got:getName()
end

RuntimeException.init(self, String.new("Invalid argument #", n, ". Expected ", expected, ", got ", got, "."))
end
@@ -5,6 +5,5 @@
@param string:message The error message.
]]
function RuntimeException:init(message)
message = cast(message, String)
Exception.init(self, message)
end
@@ -0,0 +1,14 @@
--@import see.rt.InvalidArgumentException

--[[
Ensures an argument is of the correct type.
@param number:n The argument being checked.
@param any:value The value being checked.
@param see.rt.Class|see.base.String:type The type to validate for.
]]
function ArgumentUtils.check(n, value, type)
local actualType = typeof(value)
if actualType ~= type then
throw(InvalidArgumentException.new(n, type, actualType))
end
end
@@ -1,11 +1,158 @@
function Math.sin(x)
-- body
--@native math
--@import see.util.ArgumentUtils

Math.HUGE = math.huge
Math.PI = math.pi

function Math.abs(x)
ArgumentUtils.check(1, x, "number")
return math.abs(x)
end

function Math.acos(x)
ArgumentUtils.check(1, x, "number")
return math.acos(x)
end

function Math.asin(x)
ArgumentUtils.check(1, x, "number")
return math.asin(x)
end

function Math.atan(x)
ArgumentUtils.check(1, x, "number")
return math.atan(x)
end

function Math.atan2(y, x)
ArgumentUtils.check(1, y, "number")
ArgumentUtils.check(2, x, "number")
return math.atan2(y, x)
end

function Math.ceil(x)
ArgumentUtils.check(1, x, "number")
return math.ceil(x)
end

function Math.cos(x)
-- body
ArgumentUtils.check(1, x, "number")
return math.cos(x)
end

function Math.cosh(x)
ArgumentUtils.check(1, x, "number")
return math.cosh(x)
end

function Math.deg(x)
ArgumentUtils.check(1, x, "number")
return math.deg(x)
end

function Math.exp(x)
ArgumentUtils.check(1, x, "number")
return math.exp(x)
end

function Math.floor(x)
ArgumentUtils.check(1, x, "number")
return math.floor(x)
end

function Math.frexp(x)
ArgumentUtils.check(1, x, "number")
return math.frexp(x)
end

function Math.ldexp(m, e)
ArgumentUtils.check(1, m, "number")
ArgumentUtils.check(2, e, "number")
return math.ldexp(m, e)
end

function Math.log(x)
ArgumentUtils.check(1, x, "number")
return math.log(x)
end

function Math.log10(x)
ArgumentUtils.check(1, x, "number")
return math.log10(x)
end

function Math.max(x, ...)
local args = {...}
ArgumentUtils.check(1, x, "number")
for i = 1, #args do
ArgumentUtils.check(i + 1, args[i], "number")
end
return math.max(x, ...)
end

function Math.min(x, ...)
local args = {...}
ArgumentUtils.check(1, x, "number")
for i = 1, #args do
ArgumentUtils.check(i + 1, args[i], "number")
end
return math.min(x, ...)
end

function Math.modf(x)
ArgumentUtils.check(1, x, "number")
return math.modf(x)
end

function Math.pow(x, y)
ArgumentUtils.check(1, x, "number")
ArgumentUtils.check(2, y, "number")
return math.pow(x, y)
end

function Math.rad(x)
ArgumentUtils.check(1, x, "number")
return math.rad(x)
end

function Math.random(m, n)
if m then
ArgumentUtils.check(1, m, "number")
if n then
ArgumentUtils.check(2, n, "number")
return math.random(m, n)
end
return math.random(m)
end
return math.random()
end

function Math.randomseed(x)
ArgumentUtils.check(1, x, "number")
return math.randomseed(x)
end

function Math.sin(x)
ArgumentUtils.check(1, x, "number")
return math.sin(x)
end

function Math.sinh(x)
ArgumentUtils.check(1, x, "number")
return math.sinh(x)
end

function Math.sqrt(x)
ArgumentUtils.check(1, x, "number")
return math.sqrt(x)
end

function Math.tan(x)
ArgumentUtils.check(1, x, "number")
return math.tan(x)
end

function Math.tan( ... )
-- body
function Math.tanh(x)
ArgumentUtils.check(1, x, "number")
return math.tanh(x)
end

0 comments on commit f900646

Please sign in to comment.