local error = logger.error local pairs = pairs local ipairs = ipairs local select = select local concat = table.concat local format = string.format local getinfo = debug.getinfo local setmetatable = setmetatable local warn = warn or print local assert = assert local HyperLua = require("HyperLua") local Log = HyperLua.Log local IsObject = HyperLua.IsObject local IsObjectType = HyperLua.IsObjectType local IsDerivedFrom = HyperLua.IsDerivedFrom local IsInstance = HyperLua.IsInstance local NewObjectType = HyperLua.NewObjectType local RenewObjectType = HyperLua.RenewObjectType local ReloadObjectType = HyperLua.ReloadObjectType local IsReloadingType = HyperLua.IsReloadingType local CommitObjectType = HyperLua.CommitObjectType local IsCommitedType = HyperLua.IsCommitedType local HasField = HyperLua.HasField local AddField = HyperLua.AddField local AddMethod = HyperLua.AddMethod local ReplaceMethod = HyperLua.ReplaceMethod local HotAddMethod = HyperLua.HotAddMethod local HotReplaceMethod = HyperLua.HotReplaceMethod local SetObjectAsConst = HyperLua.SetObjectAsConst local IsConstObject = HyperLua.IsConstObject local GetObjectID = HyperLua.GetObjectID local GetObjectSize = HyperLua.GetObjectSize local GetObjectCount = HyperLua.GetObjectCount local GetMethodInfo = HyperLua.GetMethodInfo local GetConstructor = HyperLua.GetConstructor local GetTypeName = HyperLua.GetTypeName local GetBaseTypeName = HyperLua.GetBaseTypeName local TryGet = HyperLua.TryGet local TrySet = HyperLua.TrySet local LuaType = HyperLua.LuaType local LuaPairs = HyperLua.Pairs local LuaObjects = HyperLua.Objects local LuaToString = HyperLua.ToString local DumpEnv = HyperLua.DumpEnv local AddExternalTypeSys = HyperLua.AddExternalTypeSystem local IsExternalType = HyperLua.IsExternalType local IsExternalInstance = HyperLua.IsExternalInstance local LogLevel_Info = 0 local LogLevel_Warn = 1 local LogLevel_Error = 2 local ProfilerWrapper = _G.g_ProfilerWrapper local ASSETS_PATH = _G.g_AssetsPath or "." local IS_BUILD_SHIPPING = _G.g_IsBuildShipping local PLACEHOLDER = "placeholder" local OPTIONAL = "optional" local EMPTY_TABLE = {} local HL = {} HL.Boolean = "boolean" HL.Number = "number" HL.Int = "int" HL.String = "string" HL.Table = "table" HL.Userdata = "userdata" HL.Pointer = "pointer" HL.Function = "function" HL.Varlist = "varlist" HL.Thread = "thread" HL.Any = "any" HL.Opt = function(...) return OPTIONAL, ... end local HyperLuaObject local HyperLuaReloading = true local HyperLuaClasses = {} local HyperLuaFwdClasses = {} local HyperLuaStrCache = {} local AnonymousTypeID = 1 local MT_CTOR = 1 local MT_STATIC = 2 local MT_INSTANCE = 3 local MT_VIRTUAL = 4 local MT_OVERRIDE = 5 local MT_FINAL = 6 local HyperLuaBuiltinTypes = { ["boolean"] = true, ["number"] = true, ["int"] = true, ["pointer"] = true, ["string"] = true, ["table"] = true, ["userdata"] = true, ["function"] = true, ["thread"] = true, ["any"] = true, } local HyperLuaInitValueTypes = { ["boolean"] = { ["boolean"] = true, }, ["number"] = { ["number"] = true, }, ["int"] = { ["number"] = true, }, ["pointer"] = { ["nil"] = true, ["pointer"] = true, }, ["string"] = { ["string"] = true, }, ["table"] = { ["nil"] = true, ["function"] = true, }, ["userdata"] = { ["nil"] = true, ["function"] = true, }, ["function"] = { ["nil"] = true, ["function"] = true, }, ["thread"] = { ["nil"] = true, ["function"] = true, }, } local HyperLuaParamValueTypes = { ["boolean"] = { ["boolean"] = true, }, ["number"] = { ["number"] = true, }, ["int"] = { ["number"] = true, }, ["pointer"] = { ["nil"] = true, ["pointer"] = true, }, ["string"] = { ["string"] = true, }, ["table"] = { ["nil"] = true, ["table"] = true, }, ["userdata"] = { ["nil"] = true, ["userdata"] = true, }, ["function"] = { ["nil"] = true, ["function"] = true, }, ["thread"] = { ["nil"] = true, ["thread"] = true, }, } local function SaveOption(name, value) value = tostring(value) local path = string.format("%s/%s.flag", ASSETS_PATH, name) local file = io.open(path, "wb") if file then file:write(value) file:close() end return value end local function LoadOption(name, default) local path = string.format("%s/%s.flag", ASSETS_PATH, name) local file = io.open(path, "rb") if file then local ret = file:read("*a") file:close() return ret end return default end local RuntimeTypeCheckLevel_None = 0 local RuntimeTypeCheckLevel_Log = 1 local RuntimeTypeCheckLevel_Error = 2 local RuntimeTypeCheckLevel = RuntimeTypeCheckLevel_Error local RuntimeTypeCheck = true HL.RuntimeTypeCheckLevel_None = RuntimeTypeCheckLevel_None HL.RuntimeTypeCheckLevel_Log = RuntimeTypeCheckLevel_Log HL.RuntimeTypeCheckLevel_Error = RuntimeTypeCheckLevel_Error do function HL.SetRuntimeTypeCheckLevel(level) assert(level >= RuntimeTypeCheckLevel_None and level <= RuntimeTypeCheckLevel_Error) SaveOption("rttc", level) return level end function HL.GetRuntimeTypeCheckLevel() return RuntimeTypeCheckLevel end local DefaultRuntimeTypeCheckLevel, RuntimeTypeCheckFlag if IS_BUILD_SHIPPING then DefaultRuntimeTypeCheckLevel = RuntimeTypeCheckLevel_None RuntimeTypeCheckFlag = RuntimeTypeCheckLevel_None else DefaultRuntimeTypeCheckLevel = _G.g_OverrideDefaultTypeCheckFlag or RuntimeTypeCheckLevel_Error RuntimeTypeCheckFlag = _G.g_OverrideTypeCheckFlag or LoadOption("rttc", DefaultRuntimeTypeCheckLevel) end RuntimeTypeCheckFlag = tonumber(RuntimeTypeCheckFlag) or DefaultRuntimeTypeCheckLevel RuntimeTypeCheckLevel = RuntimeTypeCheckFlag RuntimeTypeCheck = RuntimeTypeCheckLevel ~= RuntimeTypeCheckLevel_None HyperLua.SetRuntimeTypeCheckLevel(RuntimeTypeCheckLevel) end local RuntimeObjectTrack = false do function HL.SetRuntimeObjectTrackEnabled(enabled) if enabled == nil then enabled = not RuntimeObjectTrack end SaveOption("rtot", enabled and "1" or "0") return enabled end function HL.GetRuntimeObjectTrackEnabled() return RuntimeObjectTrack end local RuntimeObjectTrackFlag = LoadOption("rtot", "0") RuntimeObjectTrack = RuntimeObjectTrackFlag == "1" HyperLua.SetRuntimeObjectTrack(RuntimeObjectTrack) end local function TypeCheckError(str, ...) local msg = select('#', ...) > 0 and format(str, ...) or str if RuntimeTypeCheckLevel == RuntimeTypeCheckLevel_Error then error(msg) else Log(LogLevel_Error, msg) end end local function Error(str, ...) local msg = select('#', ...) > 0 and format(str, ...) or str error(msg) end local function GetType(object) local typeName = GetTypeName(object) return typeName and HyperLuaClasses[typeName] or nil end local function GetBaseType(objectOrType) local baseTypeName = GetBaseTypeName(objectOrType) return baseTypeName and HyperLuaClasses[baseTypeName] or nil end local function IsBuiltinType(typeName) return HyperLuaBuiltinTypes[typeName] ~= nil end local function CheckFieldType(fieldType) if not IsBuiltinType(fieldType) and not IsObjectType(fieldType) and not IsExternalType(fieldType) then Error("invalid field type '%s'", tostring(fieldType)) end end local function CheckFieldName(objectType, fieldName) if HasField(objectType, fieldName) then Error("member with name '%s' already exists in '%s'", fieldName, GetTypeName(objectType)) end end local function CheckFieldInitValue(objectType, fieldName, fieldType, storeInObject, initValue) if fieldType == "any" then return end local initValueType = LuaType(initValue) local expectedTypes = HyperLuaInitValueTypes[fieldType] if not expectedTypes then if storeInObject then if initValueType ~= "function" and initValueType ~= "nil" then Error("%s @ %s: '%s' or '%s' expected, got '%s'", fieldName, GetTypeName(objectType), "function", "nil", initValueType) end else if initValueType ~= "userdata" and initValueType ~= "function" and initValueType ~= "nil" then Error("%s @ %s: '%s' or '%s' or '%s' expected, got '%s'", fieldName, GetTypeName(objectType), "userdata", "function", "nil", initValueType) end if initValueType == "userdata" then if not IsInstance(initValue, fieldType) then Error("%s @ %s: '%s' expected", fieldName, GetTypeName(objectType), GetTypeName(fieldType)) end end end else if not expectedTypes[initValueType] then if storeInObject then local types = {} for k, _ in pairs(expectedTypes) do types[#types + 1] = "'" .. k .. "'" end Error("%s @ %s: %s expected, got '%s'", fieldName, GetTypeName(objectType), concat(types, " or "), initValueType) else if fieldType ~= initValueType then local types = { fieldType } for k, _ in pairs(expectedTypes) do types[#types + 1] = "'" .. k .. "'" end Error("%s @ %s: %s expected, got '%s'", fieldName, GetTypeName(objectType), concat(types, " or "), initValueType) end end end end end local function CheckMethodInitValue(methodBody) local methodBodyType = LuaType(methodBody) if methodBodyType ~= "function" then Error("'%s' expected, got '%s'", "function", methodBodyType) end end local function CheckMethodParamTypes(paramTypes, baseParamTypes, isReturn) local name = isReturn and "return value" or "param" if paramTypes and baseParamTypes then if #paramTypes ~= #baseParamTypes then TypeCheckError("%s type count mismatch ('%d' expected, got '%d')", name, #baseParamTypes, #paramTypes) return false end for ith, paramType in ipairs(paramTypes) do local baseParamType = baseParamTypes[ith] if paramType ~= baseParamType then if IsBuiltinType(paramType) or IsBuiltinType(baseParamType) or not IsDerivedFrom(paramType, baseParamType) then local paramTypeName = IsBuiltinType(paramType) and paramType or GetTypeName(paramType) local baseParamTypeName = IsBuiltinType(baseParamType) and baseParamType or GetTypeName(baseParamType) TypeCheckError("bad %s type #%d ('%s' expected, got '%s')", name, ith, baseParamTypeName, paramTypeName) return false end end end elseif paramTypes then TypeCheckError("%s type count mismatch ('%d' expected, got '%d')", name, 0, #paramTypes) return false elseif baseParamTypes then TypeCheckError("%s type count mismatch ('%d' expected, got '%d')", name, #baseParamTypes, 0) return false end return true end local function CheckOverrideMethod(objectType, baseObjectType, methodName, inParams, retParams) local methodInfo = GetMethodInfo(baseObjectType, methodName) if not methodInfo then TypeCheckError("'%s' override method '%s' not exists in '%s'", GetTypeName(objectType), methodName, GetTypeName(baseObjectType)) return false end local baseMethodType, baseInParams, baseRetParams = methodInfo[1], methodInfo[2], methodInfo[3] if baseMethodType == MT_VIRTUAL or baseMethodType == MT_OVERRIDE then CheckMethodParamTypes(inParams, baseInParams) CheckMethodParamTypes(retParams, baseRetParams) elseif baseMethodType == MT_FINAL then TypeCheckError("'%s' can not override final method '%s' in '%s'", GetTypeName(objectType), methodName, GetTypeName(baseObjectType)) return false elseif baseMethodType == MT_STATIC then TypeCheckError("'%s' can not override static method '%s' in '%s'", GetTypeName(objectType), methodName, GetTypeName(baseObjectType)) return false else TypeCheckError("'%s' can not override non-virtual method '%s' in '%s'", GetTypeName(objectType), methodName, GetTypeName(baseObjectType)) return false end return true end local function CreateMethodParams(isReturn, ...) local hasVarList = false local curParams = {} local requireCnt = nil for _, paramType in ipairs({ ... }) do if hasVarList then if isReturn then TypeCheckError("'varlist' must be the last return value type") else TypeCheckError("'varlist' must be the last param type") end else if paramType == "varlist" then hasVarList = true curParams[#curParams + 1] = paramType elseif paramType == PLACEHOLDER then curParams[#curParams + 1] = paramType elseif paramType == OPTIONAL then if requireCnt ~= nil then TypeCheckError("'Opt' should be the last group or parameters.") end requireCnt = #curParams elseif IsBuiltinType(paramType) then curParams[#curParams + 1] = paramType elseif IsObjectType(paramType) then curParams[#curParams + 1] = paramType elseif IsExternalType(paramType) then curParams[#curParams + 1] = paramType else if isReturn then TypeCheckError("invalid return value type") else TypeCheckError("invalid param type") end end end end if isReturn and #curParams == 0 then TypeCheckError("no return value type found.") end if requireCnt == nil then requireCnt = #curParams end return #curParams > 0 and curParams or nil, requireCnt end local function CheckMethodParamValues(debugInfo, paramTypes, requireCnt, isReturn, ...) if requireCnt == nil then requireCnt = 0 end local name = isReturn and "return value" or "argument" for ith, paramType in ipairs(paramTypes) do if paramType == "varlist" then return select(1, ...) elseif paramType ~= "any" then local paramValue = select(ith, ...) local gotType = LuaType(paramValue) local expectedTypes = HyperLuaParamValueTypes[paramType] if expectedTypes then if not expectedTypes[gotType] then if ith <= requireCnt or gotType ~= "nil" then local types = {} for k, _ in pairs(expectedTypes) do types[#types + 1] = "'" .. k .. "'" end TypeCheckError("%s: bad %s #%d (%s expected, got '%s')", debugInfo, name, ith, concat(types, ' or '), gotType) end end elseif paramValue then local ok, errParam = IsInstance(paramValue, paramType) if not ok then local externalTypeName = IsExternalType(paramType) if externalTypeName then if not IsExternalInstance(paramValue, paramType) then TypeCheckError("%s: bad %s #%d (external '%s' expected, got '%s')", debugInfo, name, ith, externalTypeName, LuaType(paramValue)) end else if errParam == 1 then TypeCheckError("%s: bad %s #%d (HL 'object' expected, got '%s')", debugInfo, name, ith, gotType) elseif errParam == 2 then TypeCheckError("%s: bad %s #%d (HL 'class' expected)", debugInfo, name, ith) else TypeCheckError("%s: bad %s #%d ('%s' expected, got '%s)", debugInfo, name, ith, GetTypeName(paramType), GetTypeName(paramValue)) end end end end end end local expectedMinCount = requireCnt local expectedMaxCount = #paramTypes local gotCount = select('#', ...) if expectedMinCount == expectedMaxCount then if expectedMinCount ~= gotCount then TypeCheckError("%s: %s count mismatch (expected '%d', got '%d')", debugInfo, name, expectedMinCount, gotCount) end else if gotCount < expectedMinCount then TypeCheckError("%s: %s count mismatch (expected '%d' at least, got '%d')", debugInfo, name, expectedMinCount, gotCount) elseif gotCount > expectedMaxCount then TypeCheckError("%s: %s count mismatch (expected '%d' at most, got '%d')", debugInfo, name, expectedMaxCount, gotCount) end end return select(1, ...) end local function CreateMethodWrapper(methodName, methodBody, inParams, inRequireCnt, retParams, retRequireCnt) local info = getinfo(methodBody, "S") local debugInfo = format("%s:%d:%s", info.source, info.linedefined, methodName) if inParams and retParams then return function(...) return CheckMethodParamValues(debugInfo, retParams, retRequireCnt, true, methodBody(CheckMethodParamValues(debugInfo, inParams, inRequireCnt, false, ...))) end elseif inParams then return function(...) return methodBody(CheckMethodParamValues(debugInfo, inParams, inRequireCnt, false, ...)) end elseif retParams then return function(...) return CheckMethodParamValues(debugInfo, retParams, retRequireCnt, true, methodBody(...)) end else return methodBody end end local function CreateProxy(metatable) return setmetatable({}, metatable) end local function AddStringToCache(str) HyperLuaStrCache[str] = true end local function FieldInternal(objectType, fieldType, storeInObject, constant, fieldName, initValue) if IsReloadingType(objectType) then return end CheckFieldType(fieldType) CheckFieldName(objectType, fieldName) CheckFieldInitValue(objectType, fieldName, fieldType, storeInObject, initValue) local internalFieldType if IsObjectType(fieldType) then internalFieldType = "object" elseif IsExternalType(fieldType) then internalFieldType = "external" else internalFieldType = fieldType end if not AddField(objectType, fieldName, internalFieldType, storeInObject, constant, initValue, fieldType) then Error("add '%s' field to '%s' failed", fieldName, GetTypeName(objectType)) end AddStringToCache(fieldName) end local function MethodInternal(objectType, methodType, inParams, inRequireCnt, retParams, retRequireCnt, methodName, methodBody) local methodInfo if RuntimeTypeCheck then methodInfo = { methodType, inParams, retParams } end local typeName = GetTypeName(objectType) local isOverrideOrFinal = methodType == MT_OVERRIDE or methodType == MT_FINAL local methodWrapper = methodBody if RuntimeTypeCheck then local isConstructor = methodType == MT_CTOR if isConstructor then if retParams then TypeCheckError("return value is not allowed in constructor '%s'", methodName) end end CheckMethodInitValue(methodBody) methodWrapper = CreateMethodWrapper(methodName, methodBody, inParams, inRequireCnt, retParams, retRequireCnt) end if ProfilerWrapper then local info = getinfo(methodBody, "S") local funcName = format("%s.%s %s:%d", typeName, methodName, info.source, info.linedefined) methodWrapper = ProfilerWrapper(funcName, methodWrapper) end if IsReloadingType(objectType) then local bHasMethodField = HasField(objectType, methodName) local subTypes = {} do local insert = table.insert for _, t in pairs(HyperLuaClasses) do if t ~= objectType and IsDerivedFrom(t, objectType) then local bValidInSubType = true if RuntimeTypeCheck and false then if (not bHasMethodField) and HasField(t, methodName) then if not CheckOverrideMethod(objectType, GetBaseType(objectType) or HyperLuaObject, methodName, inParams, retParams) then Log(LogLevel_Error, "hot replace method '%s' in '%s' failed, method with the same name already exists in sub-type '%s'", methodName, GetTypeName(objectType), GetTypeName(t)) bValidInSubType = false end end end if bValidInSubType then insert(subTypes, t) end end end end if bHasMethodField then if isOverrideOrFinal then if RuntimeTypeCheck then CheckOverrideMethod(objectType, GetBaseType(objectType) or HyperLuaObject, methodName, inParams, retParams) end end if not HotReplaceMethod(objectType, methodName, methodType, methodWrapper, methodInfo, subTypes) then Error("hot replace method '%s' in '%s' failed", methodName, GetTypeName(objectType)) end else if not HotAddMethod(objectType, methodName, methodType, methodWrapper, methodInfo, subTypes) then Error("hot add new method '%s' in '%s' failed", methodName, GetTypeName(objectType)) end end else if isOverrideOrFinal then if RuntimeTypeCheck then CheckOverrideMethod(objectType, GetBaseType(objectType) or HyperLuaObject, methodName, inParams, retParams) end if not ReplaceMethod(objectType, methodName, methodType, methodWrapper, methodInfo) then Error("replace method '%s' in '%s' failed", methodName, GetTypeName(objectType)) end else CheckFieldName(objectType, methodName) if not AddMethod(objectType, methodName, methodType, methodWrapper, methodInfo) then Error("add method '%s' to '%s' failed", methodName, GetTypeName(objectType)) end end end AddStringToCache(methodName) end function HL.Class(classNameOrNil, baseObjectType) local className = classNameOrNil if not className then className = format("HL.Anonymous.%d", AnonymousTypeID) AnonymousTypeID = AnonymousTypeID + 1 end local objectType = HyperLuaClasses[className] local bReloading = false if objectType then if not HyperLuaReloading then Error("class with name '%s' already exists", className) end bReloading = true end if baseObjectType and not IsCommitedType(baseObjectType) then Error("base type '%s' is not commited", GetTypeName(baseObjectType)) end baseObjectType = baseObjectType or HyperLuaObject if bReloading then if GetBaseType(objectType) ~= baseObjectType then Error("bad base type, '%s' expected, got '%s'", GetTypeName(GetBaseType(objectType)), GetTypeName(baseObjectType)) end ReloadObjectType(objectType) return objectType end local fwdObjectType = HyperLuaFwdClasses[className] if fwdObjectType then HyperLuaFwdClasses[className] = nil objectType = RenewObjectType(fwdObjectType, baseObjectType) else objectType = NewObjectType(className, baseObjectType) end HyperLuaClasses[className] = objectType return objectType end do local fieldStoreInObject = false local fieldIsConstant = false local fieldValueType local fieldInitValue local fieldProxy = nil fieldProxy = CreateProxy({ __shl = function(_, defaultValue) fieldInitValue = defaultValue return fieldProxy end, }) local function MakeFieldProxy(storeInObject, isConstant) return function(valueType, initValue) fieldStoreInObject = storeInObject fieldIsConstant = isConstant fieldValueType = valueType fieldInitValue = initValue return fieldProxy end end HL.Field = MakeFieldProxy(true, false) HL.Const = MakeFieldProxy(false, true) HL.StaticField = MakeFieldProxy(false, false) local methodType local methodInParams, methodInRequireCnt local methodRetParams, methodRetRequireCnt local methodBody local methodProxy = nil methodProxy = CreateProxy({ __shl = function(_, func) methodBody = func return methodProxy end, __index = { Return = function(...) if RuntimeTypeCheck then methodRetParams, methodRetRequireCnt = CreateMethodParams(true, ...) end return methodProxy end, Assign = function(func) methodBody = func return methodBody end, }, }) local function MakeMethodProxy(inMethodType) return function(...) if RuntimeTypeCheck then methodType = nil methodInParams = nil methodInRequireCnt = nil methodRetParams = nil methodRetRequireCnt = nil methodBody = nil end methodType = inMethodType if methodType ~= MT_STATIC then if RuntimeTypeCheck then methodInParams, methodInRequireCnt = CreateMethodParams(false, PLACEHOLDER, ...) end else if RuntimeTypeCheck then methodInParams, methodInRequireCnt = CreateMethodParams(false, ...) end end return methodProxy end end HL.Constructor = MakeMethodProxy(MT_CTOR) HL.Method = MakeMethodProxy(MT_INSTANCE) HL.Virtual = MakeMethodProxy(MT_VIRTUAL) HL.Override = MakeMethodProxy(MT_OVERRIDE) HL.Final = MakeMethodProxy(MT_FINAL) HL.StaticMethod = MakeMethodProxy(MT_STATIC) local function DeclareMemberToObjectType(objectType, memberName, memberProxy) if memberProxy == methodProxy then if RuntimeTypeCheck then if methodType ~= MT_STATIC then methodInParams[1] = objectType end if methodType == MT_CTOR then assert(methodRetParams == nil) elseif methodRetParams == nil then methodRetParams = EMPTY_TABLE end end MethodInternal(objectType, methodType, methodInParams, methodInRequireCnt, methodRetParams, methodRetRequireCnt, memberName, methodBody) if RuntimeTypeCheck then methodType = nil methodInParams = nil methodInRequireCnt = nil methodRetParams = nil methodRetRequireCnt = nil methodBody = nil end return end if memberProxy == fieldProxy then FieldInternal(objectType, fieldValueType, fieldStoreInObject, fieldIsConstant, memberName, fieldInitValue) fieldValueType = nil fieldStoreInObject = false fieldIsConstant = false fieldInitValue = nil return end Error("unknown declaration '%s' with member name '%s' to object type '%s'", tostring(memberProxy), memberName, GetTypeName(objectType)) end HyperLua.SetObjectTypeNewIndexBeforeCommit(DeclareMemberToObjectType) end function HL.Commit(objectType) local isCommited = IsCommitedType(objectType) if isCommited then Error("repeated Commit to class '%s'", GetTypeName(objectType)) end CommitObjectType(objectType) return objectType end function HL.Forward(className) local objectType = HyperLuaClasses[className] or HyperLuaFwdClasses[className] if not objectType then objectType = NewObjectType(className) HyperLuaFwdClasses[className] = objectType end return objectType end do local ObjectTypeFallbackAccessors = { Super = function(objectType) return HL.GetBaseType(objectType) end, SuperConstructor = function(objectType) return HL.Super(objectType) end, } local function AccessFallbackFieldOfObjectType(objectType, key) local f = ObjectTypeFallbackAccessors[key] return f and f(objectType, key) end HyperLua.SetObjectTypeIndexFallback(AccessFallbackFieldOfObjectType) end function HL.Super(objectType) local baseType = GetBaseType(objectType) return baseType and GetConstructor(baseType) or nil end local function DumpObject(obj, onlyHyperLua, usePrint) if type(obj) ~= "userdata" or not HL.Is(obj, HL.Object) then Error("bad argument #1, instance of HyperLua Object expected, got '%s'", type(obj)) end local lines = {} local insert = table.insert insert(lines, format("%s = {", LuaToString(obj))) for k, v in LuaPairs(obj) do if IsObject(v) then insert(lines, format(" %s = %s", k, LuaToString(v))) else if not onlyHyperLua then insert(lines, format(" %s = %s", k, v)) end end end insert(lines, "}") local ret = table.concat(lines, "\n") if usePrint == nil or usePrint == true then print(ret) return end return ret end local function DumpObjects(onlyHyperLua) collectgarbage("stop") for obj, t in LuaObjects() do DumpObject(obj, onlyHyperLua) end collectgarbage("restart") end local function DumpObjectCount(outputFunc) if outputFunc == nil then outputFunc = warn end local nameLength = 0 local countTotal = 0 local countArray = {} for typeName, objectType in pairs(HyperLuaClasses) do local objectCount = GetObjectCount(objectType) if objectCount > 0 then countTotal = countTotal + objectCount countArray[#countArray + 1] = { typeName, objectCount } if #typeName > nameLength then nameLength = #typeName end end end table.sort(countArray, function(e1, e2) return e1[2] > e2[2] end) local dumpFmt = format("%%%ds: %%d", nameLength) outputFunc(format("HL object count: %d", countTotal)) for k, v in pairs(countArray) do outputFunc(format(dumpFmt, v[1], v[2])) end end HL.DumpObject = DumpObject HL.DumpObjects = DumpObjects HL.DumpObjectCount = DumpObjectCount HL.DumpUncommited = function() warn("Uncommitted HL Classes:") for objectType, v in pairs(HyperLuaClasses) do if not IsCommitedType(objectType) then warn(GetTypeName(objectType)) end end end HyperLuaObject = HL.Class("HyperLua.Object") do HL.Commit(HyperLuaObject) end HL.Object = HyperLuaObject HL.GetType = GetType HL.GetTypeName = GetTypeName HL.GetBaseType = GetBaseType HL.GetBaseTypeName = GetBaseTypeName HL.GetObjectID = GetObjectID HL.GetObjectSize = GetObjectSize HL.GetObjectCount = GetObjectCount HL.SetObjectAsConst= SetObjectAsConst HL.IsConstObject = IsConstObject HL.As = function(object, objectType) return IsInstance(object, objectType) and object or nil end HL.Is = IsInstance HL.TryGet = TryGet HL.TrySet = TrySet HL.DumpEnv = function(objectType, reason) DumpEnv(objectType, reason or "unknown") end HL.SetReloading = function(bReload) HyperLuaReloading = bReload == true end HL.IsReloading = function() return HyperLuaReloading end local HLExternalTypeSystem = HL.Class("HLExternalTypeSystem") do HLExternalTypeSystem.IsExternalType = HL.Virtual(HL.Any).Return(HL.Opt(HL.String)) << function(self, typeToTest) return nil end HLExternalTypeSystem.IsExternalInstance = HL.Virtual(HL.Userdata, HL.Any).Return(HL.Boolean) << function(self, instanceToTest, type) return false end HLExternalTypeSystem.Register = HL.Method() << function(self) AddExternalTypeSys(self) end HL.Commit(HLExternalTypeSystem) end HL.ExternalTypeSystem = HLExternalTypeSystem return HL