diff --git a/nattlua/parser/node.lua b/nattlua/parser/node.lua index 38cf5771..2f60afb1 100644 --- a/nattlua/parser/node.lua +++ b/nattlua/parser/node.lua @@ -1034,31 +1034,37 @@ local NAN_TYPE = {} local ANY_TYPE = {} function META:AssociateType(obj) - self.inferred_types_done = self.inferred_types_done or {} - self.inferred_types = self.inferred_types or {} - - do - local t = obj.Type - local hash = obj - - if hash.Type == "symbol" then - hash = obj.Data - elseif obj.Type == "string" then - hash = obj.Data or STRING_TYPE - elseif obj.Type == "number" then - hash = obj.Data or NUMBER_TYPE - - if hash ~= hash then hash = NAN_TYPE end - elseif obj.Type == "any" then - hash = ANY_TYPE - end + local done = self.inferred_types_done + local types = self.inferred_types + + if not done then + done = {} + self.inferred_types_done = done + end + + if not types then + types = {} + self.inferred_types = types + end - if self.inferred_types_done[hash] then return end + local obj_type = obj.Type + local hash = obj - self.inferred_types_done[hash] = true + if obj_type == "symbol" then + hash = obj.Data + elseif obj_type == "string" then + hash = obj.Data or STRING_TYPE + elseif obj_type == "number" then + hash = obj.Data or NUMBER_TYPE + if hash ~= hash then hash = NAN_TYPE end + elseif obj_type == "any" then + hash = ANY_TYPE end - self.inferred_types[#self.inferred_types + 1] = obj + if done[hash] then return end + + done[hash] = true + types[#types + 1] = obj end function META:GetAssociatedTypes() diff --git a/nattlua/types/number.lua b/nattlua/types/number.lua index 4a9f04aa..e68fd50f 100644 --- a/nattlua/types/number.lua +++ b/nattlua/types/number.lua @@ -92,21 +92,7 @@ end function META.Equal(a--[[#: TNumber]], b--[[#: TBaseType]]) if a.Type ~= b.Type then return false, "types differ" end - do - return a.Hash == b.Hash - end - - if a.Data == b.Data then return true, "max values are equal" end - - if not a.Data and not b.Data then - return true, "no literal data in either value" - else - if a:IsNan() and b:IsNan() then return true, "both values are nan" end - - return a.Data == b.Data, "literal values are equal" - end - - return false, "values are not equal" + return a.Hash == b.Hash, "hash values are equal" end function META:IsLiteral() diff --git a/nattlua/types/table.lua b/nattlua/types/table.lua index 27a5f0aa..4245775d 100644 --- a/nattlua/types/table.lua +++ b/nattlua/types/table.lua @@ -224,7 +224,11 @@ function META:__tostring() local contract = self:GetContract() if contract and contract.Type == "table" and contract ~= self then - for i, keyval in ipairs(contract:GetData()) do + local contract_data = contract:GetData() + local contract_len = #contract_data + + for i = 1, contract_len do + local keyval = contract_data[i] local table_kv = self:FindKeyValExact(keyval.key) local key = tostring(table_kv and table_kv.key or "nil") local val = tostring(table_kv and table_kv.val or "nil") @@ -244,7 +248,11 @@ function META:__tostring() end end else - for i, keyval in ipairs(self.Data) do + local data = self.Data + local len = #data + + for i = 1, len do + local keyval = data[i] local key, val = tostring(keyval.key), tostring(keyval.val) s[i] = indent .. "[" .. key .. "]" .. " = " .. val end @@ -264,8 +272,11 @@ function META:GetArrayLength() if contract and contract ~= self then return contract:GetArrayLength() end local len = 0 + local data = self.Data + local data_len = #data - for _, kv in ipairs(self.Data) do + for i = 1, data_len do + local kv = data[i] if kv.key:IsNumeric() then if kv.key:IsLiteral() then -- TODO: not very accurate @@ -335,7 +346,11 @@ function META:FollowsContract(contract--[[#: TTable]]) end end - for _, keyval in ipairs(self.Data) do + local data = self.Data + local len = #data + + for i = 1, len do + local keyval = data[i] if not keyval.val:IsNil() then local res, err = contract:FindKeyValExact(keyval.key) @@ -356,8 +371,11 @@ function META:FollowsContract(contract--[[#: TTable]]) end function META:CanBeEmpty() - for _, keyval in ipairs(self.Data) do - if not keyval.val:CanBeNil() then return false end + local data = self.Data + local len = #data + + for i = 1, len do + if not data[i].val:CanBeNil() then return false end end return true @@ -675,8 +693,11 @@ function META:FindKeyValWide(key--[[#: TBaseType]], reverse--[[#: boolean | nil] if keyval then return keyval end local reasons = {} + local data = self.Data + local len = #data - for i, keyval in ipairs(self.Data) do + for i = 1, len do + local keyval = data[i] if key:Equal(keyval.key) then return keyval end local ok, reason diff --git a/nattlua/types/tuple.lua b/nattlua/types/tuple.lua index 32e92e18..826c97c6 100644 --- a/nattlua/types/tuple.lua +++ b/nattlua/types/tuple.lua @@ -51,9 +51,11 @@ function META:GetHash(visited) visited[self] = "*circular*" local types = {} + local data = self.Data + local len = #data - for i, v in ipairs(self.Data) do - types[i] = v:GetHash(visited) + for i = 1, len do + types[i] = data[i]:GetHash(visited) end visited[self] = table.concat(types, ",") @@ -65,9 +67,11 @@ function META:__tostring() self.suppress = true local strings--[[#: List<|string|>]] = {} + local data = self.Data + local len = #data - for i, v in ipairs(self.Data) do - strings[i] = tostring(v) + for i = 1, len do + strings[i] = tostring(data[i]) end if self.Remainder then table.insert(strings, tostring(self.Remainder)) end @@ -125,9 +129,13 @@ function META:Copy(map--[[#: Map<|any, any|> | nil]], copy_tables) local copy = META.New({}) map[self] = copy + + local data = self.Data + local copy_data = copy.Data + local len = #data - for i, v in ipairs(self.Data) do - copy.Data[i] = copy_val(v, map, copy_tables) + for i = 1, len do + copy_data[i] = copy_val(data[i], map, copy_tables) end copy.Repeat = self.Repeat diff --git a/nattlua/types/union.lua b/nattlua/types/union.lua index 89758414..19269b93 100644 --- a/nattlua/types/union.lua +++ b/nattlua/types/union.lua @@ -81,8 +81,9 @@ function META.Equal( end function META:GetHash(visited)--[[#: string]] - if self:GetCardinality() == 1 then - return (self.Data[1]--[[# as any]]):GetHash() + local data = self.Data + if #data == 1 then + return (data[1]--[[# as any]]):GetHash() end visited = visited or {} @@ -91,13 +92,14 @@ function META:GetHash(visited)--[[#: string]] visited[self] = "*circular*" local types = {} + local len = #data - for i, v in ipairs(self.Data) do - types[i] = v:GetHash(visited) + for i = 1, len do + types[i] = data[i]:GetHash(visited) end table_sort(types) - visited[self] = table.concat(types, "|") + visited[self] = table_concat(types, "|") return visited[self]--[[# as string]] end @@ -110,9 +112,12 @@ function META:__tostring() local s = {} self.suppress = true + + local data = self.Data + local len = #data - for i, v in ipairs(self.Data) do - s[i] = tostring(v) + for i = 1, len do + s[i] = tostring(data[i]) end if not s[1] then @@ -122,7 +127,7 @@ function META:__tostring() self.suppress = false - if #s == 1 then return (s[1]--[[# as string]]) .. "|" end + if len == 1 then return (s[1]--[[# as string]]) .. "|" end table_sort(s, sort) return table_concat(s, " | ") @@ -155,11 +160,16 @@ local function remove(self--[[#: TUnion]], index--[[#: number]]) end local function find_index(self--[[#: TUnion]], obj--[[#: any]]) - for i = 1, #self.Data do - local v = self.Data[i]--[[# as any]] + local data = self.Data + local len = #data + local obj_type = obj.Type - if v:Equal(obj) then - if v.Type ~= "function" or v:GetFunctionBodyNode() == obj:GetFunctionBodyNode() then + for i = 1, len do + local v = data[i]--[[# as any]] + + -- Check type first before expensive Equal call + if v.Type == obj_type and v:Equal(obj) then + if obj_type ~= "function" or v:GetFunctionBodyNode() == obj:GetFunctionBodyNode() then return i end end