Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
see/see/apis/see
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
891 lines (792 sloc)
28.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function traceback() | |
| local errors = { } | |
| local ok, err, name | |
| local i = 2 | |
| while true do | |
| ok, err = pcall(_G.error, '', i) | |
| name = err:match("^[^:]+") | |
| if name ~= "bios" and name ~= "see" and name ~= "xpcall" and name ~= "pcall" then break end | |
| i = i + 1 | |
| end | |
| while true do | |
| ok, err = pcall(_G.error, '', i) | |
| local name = err:match("^[^:]+") | |
| if (name == "bios" or name == "see" or name == "xpcall" or name == "pcall" or name == "shell") or not err then break end | |
| table.insert(errors, err) | |
| i = i + 1 | |
| end | |
| return errors | |
| end | |
| function formatTraceback(err, errors, level) | |
| level = level < #errors and levels or #errors | |
| if not err then | |
| err = "" | |
| else | |
| --[[local i = err:find(":") | |
| if i then | |
| local j = err:find(":", i + 1) | |
| if j then | |
| err = err:sub(j + 2, -1) | |
| end | |
| end]] | |
| end | |
| local f = err | |
| for i = 1, level do | |
| err = errors[i] | |
| local ls = err:find(":") | |
| if ls then ls = ls + 1 end | |
| local le = err:find(":", ls) | |
| if le then le = le - 1 end | |
| local line = ls and le and err:sub(ls, le) | |
| local location = ls and err:sub(1, ls - 2) or err | |
| f = f .. "\n@ line " .. (le and line or "?") .. " in " .. location | |
| end | |
| return f .. (#errors - level > 0 and "\n... and " .. (#errors - level) .. " more." or "") | |
| end | |
| local function error(err, level, b) | |
| --print(err) | |
| --while true do end | |
| --[[if term.isColor() then | |
| term.setTextColor(colors.red) | |
| end | |
| print(formatTraceback(err, traceback(), 10)) | |
| if term.isColor() then | |
| term.setTextColor(colors.white) | |
| end | |
| _G.error()]] | |
| end | |
| function errorHandler(err) | |
| --print(err) | |
| if term.isColor() then | |
| term.setTextColor(colors.red) | |
| end | |
| print(formatTraceback(err, traceback(), 10)) | |
| if term.isColor() then | |
| term.setTextColor(colors.white) | |
| end | |
| end | |
| StandardGlobals = { } | |
| --[[ | |
| Creates a new anonymous class. This class' global table will attempt to index from the parent environment. | |
| @param SeeRT:rt The VM to run this function for. | |
| @param string:name The name to use inside the definition. | |
| @param table:annotations The table of annotations to be executed for the class. | |
| @param function:def The function used to define this class. | |
| @return Class The newly created anonymous class. | |
| ]] | |
| function StandardGlobals.class(rt, name, annotations, def) | |
| local g = getfenv(def) | |
| local useG = setmetatable({ }, { __index = g }) | |
| local anonName = "__anon[" .. tostring(rt.nAnonClasses) .. "]" | |
| rt.nAnonClasses = rt.nAnonClasses + 1 | |
| return rt:loadClass(def, annotations, anonName, name, useG) | |
| end | |
| --[[ | |
| Gets the type of a given value. | |
| @param SeeRT:rt The VM to run this function for. | |
| @param value:any The value to get the type of. | |
| @return string|table The type identifier if value if primitive, or the class of value if value is an object. | |
| ]] | |
| function StandardGlobals.typeof(rt, value) | |
| --print(type(value)) | |
| if type(value) == "table" then | |
| if getmetatable(value) == rt.classMT then | |
| return rawget(value, "__class") | |
| end | |
| return "table" | |
| end | |
| return type(value) | |
| end | |
| --[[ | |
| Checks if an object is a primitive lua object or a SEE object | |
| @param SeeRT the SEE rt | |
| @param any the object to check | |
| ]] | |
| function StandardGlobals.isprimitive(rt, value) | |
| if type(value) == "table" then | |
| return getmetatable(value) ~= rt.classMT | |
| end | |
| return true | |
| end | |
| --[[ | |
| Casts a value to a particular class. | |
| @param SeeRT:rt The VM to run this function for. | |
| @param any:value The value to cast. | |
| @param string|table:castType The type to cast to. | |
| ]] | |
| function StandardGlobals.cast(rt, value, castType) | |
| if StandardGlobals.typeof(rt, value) == castType then | |
| return value | |
| end | |
| if type(castType) == "string" then | |
| -- Casting to primitive. | |
| if castType == "string" then | |
| if StandardGlobals.isprimitive(rt, value) then | |
| return tostring(value) | |
| else | |
| return value:toString():lstr() | |
| end | |
| elseif castType == "number" then | |
| if StandardGlobals.isprimitive(rt, value) then | |
| return tonumber(value) | |
| else | |
| return value:toString():toNumber() | |
| end | |
| else | |
| -- TODO | |
| end | |
| elseif type(castType) == "table" then | |
| if castType.__cast then | |
| return castType.__cast(value) | |
| else | |
| StandardGlobals.throw(rt, rt:loadClassFromAny("see.rt.CastException"):new(StandardGlobals.typeof(rt, value), castType)) | |
| end | |
| -- Casting to Object. | |
| -- TODO | |
| else | |
| --StandardGlobals.throw(rt, rt:loadClassFromAny("see.rt.CastException"):new()) | |
| end | |
| end | |
| --[[ | |
| Casts an object to a different class by setting its metatable. | |
| @param SeeRT:rt The VM to run this function for. | |
| @param see.base.Object value | |
| ]] | |
| function StandardGlobals.reinterpret_cast(rt, value, castType) | |
| if type(value) ~= "table" or not value.__type then | |
| local RuntimeException = self:loadClassFromAny("see.rt.RuntimeException") | |
| self:throw(RuntimeException:new("Cannot use reinterpret_cast to cast a primitive type.")) | |
| --error("Cannot use reinterpret_cast to cast a primitive type.") | |
| end | |
| setmetatable(value, castType.__meta) | |
| end | |
| --[[ | |
| Throws an exception. | |
| @param SeeRT:rt The VM to run this function for. | |
| @param see.base.Exception The exception to throw. | |
| ]] | |
| function StandardGlobals.throw(rt, exception) | |
| if exception then | |
| rt:throw(exception) | |
| else | |
| rt:throw(rt:loadClassFromAny("see.rt.RuntimeException"):new("Invalid argument to throw.")) | |
| end | |
| end | |
| --[[ | |
| Tries to execute code until an error occurs. Calls catch code if an error occurs. | |
| @param SeeRT:rt The VM to run this function for. | |
| @param function:tryFunc Function to run and catch errors from. | |
| @param function:catchFunc(see.base.Exception) Function to run on error. | |
| ]] | |
| function StandardGlobals.try(rt, tryFunc, catchFunc) | |
| local suc, err = pcall(tryFunc) | |
| if suc then return end | |
| if err then rt.lastException = rt:loadClassFromAny("see.rt.RuntimeException"):new(err) end | |
| catchFunc(rt.lastException) | |
| end | |
| --[[ | |
| Converts a Lua string to a see.base.String. | |
| @param see.base.String... Strings to concatenate. | |
| ]] | |
| function StandardGlobals.STR(rt, ...) | |
| return rt.base.String:new(...) | |
| end | |
| --[[ | |
| Create a copy of a table | |
| ]] | |
| local function copy(t) | |
| local r = { } | |
| for k, v in pairs(t) do | |
| r[k] = v | |
| end | |
| return r | |
| end | |
| SeeRT = { } | |
| SeeRT.__index = SeeRT | |
| --[[ | |
| Creates a new SeeRT. | |
| ]] | |
| function SeeRT.new(natives, seePath) | |
| local self = { } | |
| setmetatable(self, SeeRT) | |
| self.seePath = seePath | |
| self.maxThreadID = 0 | |
| self.threads = { } | |
| self.objThreads = { } | |
| self.threadFilters = { } | |
| self.threadsToIDs = { } | |
| self.idsToThreads = { } | |
| self.nAnonClasses = 0 | |
| self.standardGlobals = { } | |
| for k, v in pairs(StandardGlobals) do | |
| self.standardGlobals[k] = function(...) return StandardGlobals[k](self, ...) end | |
| end | |
| self.base = { } | |
| self.rt = { } | |
| self.event = { } | |
| self.classes = { } | |
| self.classLookup = { } | |
| self.classTables = { } | |
| self.natives = natives | |
| self.natives.error = error | |
| self.archives = { } | |
| self.classPaths = { } | |
| local rt = self | |
| function self.index(t, k) | |
| -- Class access. | |
| if rt.classLookup[t] then | |
| --print(rt.classTables[t].name .. "[" .. k .. "]") | |
| local v = rt.classTables[rt.rt.Class][k] | |
| if v then | |
| return v | |
| end | |
| v = rt.classTables[rt.base.Object][k] | |
| if v then | |
| return v | |
| end | |
| return rt.classTables[t][k] | |
| end | |
| -- Instance access. | |
| if k == "__class" then | |
| return nil | |
| end | |
| local oi | |
| local class = rawget(t, "__class") | |
| --print("[" .. k .. "]") | |
| repeat | |
| --print(rt.classTables[class].__name .. ", super: " .. (self.classTables[class].__super and self.classTables[self.classTables[class].__super].__name or "")) | |
| local v = rt.classTables[class][k] | |
| if v then | |
| return v | |
| end | |
| if not oi then | |
| oi = rt.classTables[class].opIndex | |
| end | |
| class = self.classTables[class].__super | |
| until not class | |
| if oi then | |
| return oi(t, k) | |
| end | |
| end | |
| function self.newindex(t, k, v) | |
| if rt.classTables[t] then | |
| rt.classTables[t][k] = v | |
| else | |
| local oni = rawget(t, "opNewIndex") | |
| if not oni or not oni(t, k, v) then | |
| rawset(t, k, v) | |
| end | |
| end | |
| end | |
| --metamethods mapped to easier method names | |
| self.metaMethodMap = { | |
| opAdd = "__add", | |
| opSub = "__sub", | |
| opMul = "__mul", | |
| opDiv = "__div", | |
| opMod = "__mod", | |
| opPow = "__pow", | |
| opNeg = "__unm", | |
| opConcat = "__concat", | |
| opEq = "__eq", | |
| opLT = "__lt", | |
| opLE = "__le", | |
| opCall = "__call" | |
| } | |
| self.opMap = { } | |
| self.classMT = { __index = self.index, __newindex = self.newindex } | |
| for k, v in pairs(self.metaMethodMap) do | |
| self.opMap[v] = k | |
| self.classMT[v] = function(t, ...) | |
| return t[k](t, ...) | |
| end | |
| end | |
| --temporary loadClass function to load see.base.Object, see.rt.Class and a DefaultClassLoader | |
| self.loadClass = function(def, annotations, name, refName, env) | |
| local className = getPackageName(name) | |
| if self.classes[name] then | |
| return self.classes[name] | |
| end | |
| if not refName then | |
| env = copy(self.standardGlobals) | |
| end | |
| local anon = refName ~= nil | |
| refName = refName or className | |
| local class = { } | |
| local fdtnl = false | |
| if name == "see.base.Object" then | |
| self.base.Object = class | |
| fdtnl = true | |
| end | |
| if name == "see.rt.Class" then | |
| self.rt.Class = class | |
| fdtnl = true | |
| end | |
| self.classes[name] = class | |
| self.classLookup[class] = true | |
| self.classTables[class] = { __name = name } | |
| setmetatable(class, self.classMT) | |
| if not fdtnl then | |
| -- Import base classes. | |
| for k, class in pairs(self.base) do | |
| env[k] = class | |
| end | |
| -- Import Events class for convenience. | |
| env["Events"] = self.event.Events | |
| end | |
| -- Setup class environment. | |
| env[refName] = class | |
| setfenv(def, env) | |
| xpcall(def, errorHandler) | |
| local abool = true | |
| for k, v in pairs(annotations) do | |
| local ret | |
| if anon then | |
| ret = self:executeTableStyleAnnotation(env, class, k, v) | |
| else | |
| ret = self:executeAnnotation(env, class, v) | |
| end | |
| if ret then | |
| abool = false | |
| end | |
| end | |
| -- Extend Object by default. | |
| if abool and self.base.Object and name ~= "see.base.Object" then | |
| --rawset(class, "super", self.base.Object) | |
| self.classTables[class].__super = self.base.Object | |
| end | |
| if not rawget(self.classTables[class], "init") then | |
| rawset(self.classTables[class], "init", function() end) | |
| end | |
| local static = rawget(self.classTables[class], "__static") | |
| if static then | |
| static() | |
| end | |
| return class | |
| end | |
| --use loadClass to load see.base.Object and see.rt.Class | |
| --then set up a real ClassLoader | |
| self:loadClassFromAny("see.base.Object") | |
| self:loadClassFromAny("see.rt.Class") | |
| --start with a class loader to load other classes and class loaders (File and Archive) | |
| local DefaultClassLoader = self:loadClassFromAny("see.rt.DefaultClassLoader") | |
| self.classLoader = DefaultClassLoader:new() | |
| --doesnt technically matter.. but it may confuse a user to call getClassLoader on a class and get nil.. so prevent it | |
| for className, class in pairs(self.classes) do | |
| if not self.classTables[class].__classLoader then | |
| self.classTables[class].__classLoader = self.classLoader | |
| end | |
| end | |
| --start using a real ClassLoader ASAP | |
| --again, not technically needed, but loadClass was intended to be a temporary thing ONLY for setting up a ClassLoader | |
| self.loadClass = nil | |
| --start using a FileClassLoader and an ArchiveClassLoader | |
| local FileClassLoader = self:loadClassFromAny("see.rt.FileClassLoader") | |
| local ArchiveClassLoader = self:loadClassFromAny("see.rt.ArchiveClassLoader") | |
| self.fileClassLoader = FileClassLoader:new() | |
| self.archiveClassLoader = ArchiveClassLoader:new() | |
| --load some base classes | |
| self.base.Exception = self:loadClassFromAny("see.base.Exception") | |
| self.base.System = self:loadClassFromAny("see.base.System") | |
| self.base.Iterators = self:loadClassFromAny("see.base.Iterators") | |
| self.base.Array = self:loadClassFromAny("see.base.Array") | |
| self.base.String = self:loadClassFromAny("see.base.String") | |
| self.event.Events = self:loadClassFromAny("see.event.Events") | |
| self.event.UnknownEvent = self:loadClassFromAny("see.event.impl.UnknownEvent") | |
| self.rt.RuntimeException = self:loadClassFromAny("see.rt.RuntimeException") | |
| local sys = self.base.System | |
| sys.setProperty("see.home", self.seePath) | |
| sys.setProperty("see.version", "420") --todo versions | |
| sys.setProperty("file.separator", "/") | |
| sys.setProperty("line.separator", "\n") | |
| sys.setProperty("path.separator", ":") | |
| sys.setProperty("os.arch", "cc64") | |
| sys.setProperty("os.name", "CraftOS") --todo automate | |
| sys.setProperty("os.version", "1.6") --todo automate | |
| return self | |
| end | |
| function SeeRT:throw(exception, level) | |
| self.lastException = exception | |
| _G.error("Uncaught exception " .. exception:getClass():getName():lstr() .. ': "' .. exception.message:lstr() .. '"', level) | |
| end | |
| --[[ | |
| Gets a value out of a table using a valid package string. | |
| @param table:t The table to search. | |
| @param string:package The package to look for. | |
| @param string:del The delimiter to use for the package. default='.' | |
| @return any The value found. | |
| ]] | |
| function getByPackage(t, package, del) | |
| if not del then del = "%." end | |
| local f = package:find(del) | |
| if f then | |
| return getByPackage(t[package:sub(1, f - 1)], package:sub(f + 1), del) | |
| else | |
| return t[package] | |
| end | |
| end | |
| --[[ | |
| Get package end. | |
| @param string:package The package to compute the name for. | |
| @return string:name Name of the package, or nil if invalid or empty package. | |
| ]] | |
| function getPackageName(package, del) | |
| if not del then del = "." end | |
| for i = #package, 0, -1 do | |
| if i == 0 or package:sub(i, i) == del then | |
| return package:sub(i + 1) | |
| end | |
| end | |
| end | |
| --[[ | |
| Get the components of a package. | |
| @param string:package The package to extract components from. | |
| @return table[string] The components of the given package. | |
| ]] | |
| function getPackageComponents(package) | |
| local components = { } | |
| for component in package:gmatch("%w[%w%d]*") do | |
| table.insert(components, component) | |
| end | |
| return components | |
| end | |
| --[[ | |
| Finds all annotations in the given code string. | |
| @param string:code The code to search for annotations in. | |
| @return table List of annotation strings. | |
| ]] | |
| function getAnnotations(code) | |
| local annotations = { } | |
| for annotation in code:gmatch("%-%-@[^\n]*") do | |
| table.insert(annotations, annotation:sub(4)) | |
| end | |
| return annotations | |
| end | |
| --[[ | |
| Spawn a thread | |
| ]] | |
| function SeeRT:spawnThread(func, obj) | |
| local thread = coroutine.create(function() xpcall(func, errorHandler) end) | |
| self.objThreads[thread] = obj | |
| table.insert(self.threads, thread) | |
| self.maxThreadID = self.maxThreadID + 1 | |
| obj.id = self.maxThreadID | |
| self.threadsToIDs[thread] = self.maxThreadID | |
| self.idsToThreads[self.maxThreadID] = thread | |
| self.threadFilters[#self.threads] = -1 | |
| return thread | |
| end | |
| --[[ | |
| Interrupt a thread | |
| ]] | |
| function SeeRT:interruptThread(thread) | |
| os.queueEvent("__thread_interrupt", thread.id) | |
| end | |
| --[[ | |
| Start the SEE runtime | |
| ]] | |
| function SeeRT:start(mainClass, ...) | |
| local args = { ... } | |
| xpcall(function() (function(mainClass, ...) | |
| local Events = self.event.Events | |
| local UnknownEvent = self.event.UnknownEvent | |
| local args = self.base.Array:new(...) | |
| local threads = self.threads | |
| local f = function() mainClass.main(args) end | |
| threads[1] = coroutine.create(function() xpcall(f, errorHandler) end) | |
| local t = self:loadClassFromAny("see.concurrent.Thread"):new(f) | |
| t.co = threads[1] | |
| t.id = 1 | |
| self.objThreads[threads[1]] = t | |
| self.maxThreadID = 1 | |
| self.threadsToIDs[threads[1]] = 1 | |
| self.idsToThreads[1] = threads[1] | |
| local threadFilters = self.threadFilters | |
| local event | |
| local isNative | |
| threadFilters[1] = { coroutine.resume(threads[1], args) } | |
| while true do | |
| local threadDeaths = { } | |
| for i = 1, #threads do | |
| if threadFilters[i] == -1 then | |
| threadFilters[i] = { coroutine.resume(threads[i], event) } | |
| if coroutine.status(threads[i]) == "dead" then | |
| table.insert(threadDeaths, i) | |
| self.objThreads[threads[i]] = nil | |
| end | |
| elseif coroutine.status(threads[i]) == "dead" then | |
| table.insert(threadDeaths, i) | |
| self.objThreads[threads[i]] = nil | |
| elseif event then | |
| local passEvent = false | |
| if event[1] ~= "__thread_interrupt" then | |
| if event.ident == "terminate" then | |
| self:throw(self.rt.RuntimeException:new("terminated")) | |
| end | |
| if #threadFilters[i] == 1 then | |
| passEvent = true | |
| else | |
| for j = 2, #threadFilters[i] do | |
| if isNative then | |
| if threadFilters[i][j] == event[1] then | |
| passEvent = true | |
| break | |
| end | |
| elseif threadFilters[i][j] == event.ident then | |
| passEvent = true | |
| break | |
| end | |
| end | |
| end | |
| else | |
| if self.idsToThreads[event[2]] == threads[i] then | |
| passEvent = true | |
| event = event[1] | |
| end | |
| end | |
| if passEvent then | |
| local threadData | |
| if isNative then | |
| threadData = { coroutine.resume(threads[i], unpack(event)) } | |
| else | |
| threadData = { coroutine.resume(threads[i], event) } | |
| end | |
| if not threadData[1] then | |
| error(threadData[2], 1) | |
| table.insert(threadDeaths, i) | |
| self.objThreads[threads[i]] = nil | |
| end | |
| threadFilters[i] = threadData | |
| end | |
| if coroutine.status(threads[i]) == "dead" then | |
| table.insert(threadDeaths, i) | |
| self.objThreads[threads[i]] = nil | |
| end | |
| end | |
| end | |
| local death = false | |
| for i = #threadDeaths, 1, -1 do | |
| table.remove(threads, threadDeaths[i]) | |
| table.remove(threadFilters, threadDeaths[i]) | |
| death = true | |
| end | |
| if death then | |
| local foundNonDaemon = false | |
| for i = 1, #self.objThreads do | |
| if not self.objThreads[i]:isDaemon() then | |
| foundNonDaemon = true | |
| end | |
| end | |
| if not foundNonDaemon then | |
| return | |
| end | |
| end | |
| if #threads == 0 then break end | |
| local eventData = { coroutine.yield() } | |
| local eventParams = { } | |
| for i = 2, #eventData do | |
| eventParams[i - 1] = eventData[i] | |
| end | |
| if Events.isNativeEvent(eventData[1]) then | |
| isNative = true | |
| event = eventData | |
| else | |
| isNative = false | |
| if type(eventData[2]) == "table" and eventData[2].__type then | |
| event = eventData[2] | |
| else | |
| if eventData[1] == "__thread_interrupt" then | |
| event = { "__thread_interrupt", eventData[2] } | |
| else | |
| local EventClass = Events.getEventClass(eventData[1]) | |
| if EventClass == UnknownEvent then | |
| xpcall(function() event = EventClass:new(unpack(eventData)) end, errorHandler) | |
| else | |
| xpcall(function() event = EventClass:new(unpack(eventParams)) end, errorHandler) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end)(mainClass, unpack(args)) end, errorHandler) | |
| end | |
| --[[ | |
| Loads a class by trying the standard library first, then from class paths. | |
| @param string:name | |
| @return table The class loaded. | |
| ]] | |
| function SeeRT:loadClassFromAny(name) | |
| local class = self.classes[name] | |
| if class then return class end | |
| -- Load from the standard library. | |
| local err | |
| local p = name:gsub("%.", "/") | |
| class, err = self:loadClassFromFile(fs.combine(fs.combine(self.seePath, "/lib/"), p) .. ".lua", name) | |
| local lerr = err | |
| if class then return class end | |
| -- Load from custom class paths. | |
| for _, classPath in pairs(self.classPaths) do | |
| if fs.isDir(classPath) then | |
| class, err = self:loadClassFromFile(fs.combine(classPath, name:gsub("%.", "/") .. ".lua"), name) | |
| else | |
| class, err = self:loadClassFromArchive(self.archives[classPath], name:gsub("%.", "/") .. ".lua", name) | |
| end | |
| if class then return class end | |
| end | |
| if not lerr:find("Could not read file.") then | |
| err = lerr | |
| end | |
| if not class then | |
| local RuntimeException = self:loadClassFromAny("see.rt.RuntimeException") | |
| self:throw(RuntimeException:new("Could not load class " .. name .. ". " .. err)) | |
| --error("Could not load class " .. name .. ". " .. err) | |
| end | |
| end | |
| --[[ | |
| Executes annotations onto an environment. | |
| @param table:env The function environment. | |
| @param table:annotation Annotation to execute. | |
| ]] | |
| function SeeRT:executeAnnotation(env, class, annotation) | |
| local pindex = annotation:find("%s") | |
| if not pindex then | |
| local RuntimeException = self:loadClassFromAny("see.rt.RuntimeException") | |
| self:throw(RuntimeException:new("Failed to execute annotation for class " .. class.name .. ".")) | |
| --error("Failed to execute annotation for class " .. class.name .. ".") | |
| end | |
| local aname = annotation:sub(1, pindex - 1) | |
| if aname == "import" then | |
| local name = annotation:sub(pindex + 1):gsub("%s", "") | |
| local class = self:loadClassFromAny(name) | |
| if not class then | |
| local RuntimeException = self:loadClassFromAny("see.rt.RuntimeException") | |
| self:throw(RuntimeException:new("Failed to import class.")) | |
| --error("Failed to import class.") | |
| end | |
| env[getPackageName(name)] = class | |
| elseif aname == "native" then | |
| local name = annotation:sub(pindex + 1):gsub("%s", "") | |
| if name == "__rt" then | |
| env.__rt = self | |
| return | |
| end | |
| local keys = getPackageComponents(name) | |
| local lk | |
| local v = self.natives | |
| local w = env | |
| local nativeValue = "" | |
| for _, key in pairs(keys) do | |
| nativeValue = nativeValue .. key .. "." | |
| if not v[key] then | |
| local RuntimeException = self:loadClassFromAny("see.rt.RuntimeException") | |
| self:throw(RuntimeException:new("Could not find native value " .. nativeValue)) | |
| --error("Could not find native value " .. nativeValue) | |
| end | |
| v = v[key] | |
| if not w[key] or type(w) ~= "table" then | |
| w[key] = { } | |
| end | |
| lw = w | |
| w = w[key] | |
| lk = key | |
| end | |
| lw[lk] = v | |
| elseif aname == "abstract" then | |
| local name = annotation:sub(pindex + 1):gsub("%s", "") | |
| self.classTables[class][name] = function() | |
| self:throw(self:loadClassFromAny("see.rt.UnimplementedMethodException"):new("Method \"" .. name .. "\" is not implemented!")) | |
| end | |
| elseif aname == "extends" then | |
| local name = annotation:sub(pindex + 1):gsub("%s", "") | |
| local super = env[getPackageName(name)] | |
| self.classTables[class].__super = super | |
| return true | |
| end | |
| return false | |
| end | |
| --[[ | |
| Executes table-style annotations for anonymous classes. | |
| @param table:env The function environment. | |
| @param table:annotation Annotation to execute. | |
| @param string:key The annotation type. | |
| @param any:value The value of the annotation. | |
| ]] | |
| function SeeRT:executeTableStyleAnnotation(env, class, key, value) | |
| if key == "import" or key == "native" or key == "abstract" then | |
| for i = 1, #value do | |
| self:executeAnnotation(env, class, key .. " " .. value[i]) | |
| end | |
| elseif key == "extends" then | |
| if type(value) == "string" then | |
| self:executeAnnotation(env, class, "extends " .. value) | |
| elseif type(value) == "table" then | |
| --rawset(self.classTables[class], "__super", value) | |
| self.classTables[class].__super = value | |
| return true | |
| else | |
| -- TODO: throw error | |
| end | |
| end | |
| return false | |
| end | |
| --[[ | |
| Load a class from a file. | |
| @param string:path The path to the class to load. | |
| @param string:name The full name of the class to load. | |
| @return table The loaded class. | |
| @return string Error message, or nil if successful. | |
| ]] | |
| function SeeRT:loadClassFromFile(path, name) | |
| if self.fileClassLoader then | |
| return self.fileClassLoader:loadClass(path, name) | |
| else | |
| local fileHandle = fs.open(path, "r") | |
| if not fileHandle then | |
| return nil, "Could not read file." | |
| end | |
| local code = fileHandle.readAll() | |
| fileHandle.close() | |
| -- Setup class execution environment | |
| local def, err = loadstring(code, name) | |
| if not def then | |
| return nil, err | |
| end | |
| if self.classLoader then | |
| return self.classLoader:loadClass(def, getAnnotations(code), name) | |
| else | |
| return self.loadClass(def, getAnnotations(code), name) | |
| end | |
| end | |
| end | |
| -- TODO: Make file offset table. Currently, it takes O(N) time to find each file where N is its position in the file. | |
| --[[ | |
| Load a class from a SEE archive. | |
| @param string:archiveBytes The loaded archive. | |
| @param string:path The path to the class to load. | |
| @param string:name The full name of the class to load. | |
| @return table The loaded class. | |
| @return string Error message, or nil if successful. | |
| ]] | |
| function SeeRT:loadClassFromArchive(archiveBytes, path, name) | |
| if self.archiveClassLoader then | |
| self.archiveClassLoader:loadClass(archiveBytes, path, name) | |
| elseif self.classLoader then | |
| local loc = archiveBytes:find("F" .. path .. "\0", 1, true) | |
| if not loc then | |
| return nil, "Could not find class " .. name .. " in archive path " .. path .. "." | |
| end | |
| loc = loc + #path + 2 | |
| local fin = archiveBytes:find("\0", loc) - 1 | |
| local code = archiveBytes:sub(loc, fin) | |
| -- Setup class execution environment | |
| local def, err = loadstring(code, name) | |
| if not def then | |
| return nil, err | |
| end | |
| if self.classLoader then | |
| return self.classLoader:loadClass(def, getAnnotations(code), name) | |
| else | |
| return self.loadClass(def, getAnnotations(code), name) | |
| end | |
| end | |
| end |