mirror of
https://github.com/Reuh/anselme.git
synced 2025-10-27 16:49:31 +00:00
Cache variable lookup
About 10% performance improvement.
This commit is contained in:
parent
ba1824a904
commit
4c76ef5f96
1 changed files with 37 additions and 18 deletions
|
|
@ -71,12 +71,20 @@ local Environment = ast.abstract.Runtime {
|
||||||
undefine = nil, -- { [name string] = true, ... } - variable present here will not be looked up in parent env
|
undefine = nil, -- { [name string] = true, ... } - variable present here will not be looked up in parent env
|
||||||
export = nil, -- bool
|
export = nil, -- bool
|
||||||
|
|
||||||
|
_lookup_cache = nil, -- { [name string] = variable metadata, ... }
|
||||||
|
_lookup_cache_current = nil, -- { [name string] = true, ... }
|
||||||
|
|
||||||
init = function(self, state, parent, partial_names, is_export)
|
init = function(self, state, parent, partial_names, is_export)
|
||||||
self.variables = Table:new(state)
|
self.variables = Table:new(state)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.partial = partial_names
|
self.partial = partial_names
|
||||||
self.export = is_export
|
self.export = is_export
|
||||||
self.undefine = {}
|
self.undefine = {}
|
||||||
|
-- TODO: something defined in a parent scope after caching here will not be reflected
|
||||||
|
-- 99% of the time a scope is not reused after popping it off the stack, except when captured in closures
|
||||||
|
-- so we might want to make accessing variable defined after environment capture illegal or precache all upvalues
|
||||||
|
self._lookup_cache = {}
|
||||||
|
self._lookup_cache_current = {}
|
||||||
end,
|
end,
|
||||||
|
|
||||||
traverse = function(self, fn, ...)
|
traverse = function(self, fn, ...)
|
||||||
|
|
@ -102,7 +110,10 @@ local Environment = ast.abstract.Runtime {
|
||||||
if symbol.undefine then
|
if symbol.undefine then
|
||||||
self.undefine[name] = true
|
self.undefine[name] = true
|
||||||
else
|
else
|
||||||
self.variables:set(state, symbol:to_identifier(), VariableMetadata:new(state, symbol, exp))
|
local variable = VariableMetadata:new(state, symbol, exp)
|
||||||
|
self.variables:set(state, symbol:to_identifier(), variable)
|
||||||
|
self._lookup_cache[name] = variable
|
||||||
|
self._lookup_cache_current[name] = true
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
-- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes
|
-- define or redefine new overloadable variable in current environment, inheriting existing overload variants from (parent) scopes
|
||||||
|
|
@ -134,27 +145,39 @@ local Environment = ast.abstract.Runtime {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
-- lookup variable in current or parent scope, cache the result
|
||||||
|
_lookup = function(self, state, identifier)
|
||||||
|
local name = identifier.name
|
||||||
|
local _cache = self._lookup_cache
|
||||||
|
if _cache[name] == nil then
|
||||||
|
if self.variables:has(state, identifier) then
|
||||||
|
_cache[name] = self.variables:get(state, identifier)
|
||||||
|
elseif self.parent and not self.undefine[identifier.name] then
|
||||||
|
_cache[name] = self.parent:_lookup(state, identifier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return _cache[name]
|
||||||
|
end,
|
||||||
|
|
||||||
-- returns bool if variable defined in current or parent environment
|
-- returns bool if variable defined in current or parent environment
|
||||||
defined = function(self, state, identifier)
|
defined = function(self, state, identifier)
|
||||||
if self.variables:has(state, identifier) then
|
return self:_lookup(state, identifier) ~= nil
|
||||||
return true
|
|
||||||
elseif self.parent and not self.undefine[identifier.name] then
|
|
||||||
return self.parent:defined(state, identifier)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end,
|
end,
|
||||||
-- returns bool if variable defined in current environment layer
|
-- returns bool if variable defined in current environment layer
|
||||||
-- (note: by current layer, we mean the closest one where the variable is able to exist - if it is exported, the closest export layer, etc.)
|
-- (note: by current layer, we mean the closest one where the variable is able to exist - if it is exported, the closest export layer, etc.)
|
||||||
defined_in_current = function(self, state, symbol)
|
defined_in_current = function(self, state, symbol)
|
||||||
local name = symbol.string
|
local name = symbol.string
|
||||||
if self.variables:has(state, symbol:to_identifier()) then
|
local _cache = self._lookup_cache_current
|
||||||
return true
|
if _cache[name] == nil then
|
||||||
elseif self.parent and not self.undefine[name] then
|
if self.variables:has(state, symbol:to_identifier()) then
|
||||||
if (self.partial and not self.partial[name]) or (self.export ~= symbol.exported) then
|
_cache[name] = true
|
||||||
return self.parent:defined_in_current(state, symbol)
|
elseif self.parent and not self.undefine[name] then
|
||||||
|
if (self.partial and not self.partial[name]) or (self.export ~= symbol.exported) then
|
||||||
|
_cache[name] = self.parent:defined_in_current(state, symbol)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return _cache[name]
|
||||||
end,
|
end,
|
||||||
-- return bool if variable is defined in the current environment only - won't search in parent env for exported & partial names
|
-- return bool if variable is defined in the current environment only - won't search in parent env for exported & partial names
|
||||||
defined_in_current_strict = function(self, state, identifier)
|
defined_in_current_strict = function(self, state, identifier)
|
||||||
|
|
@ -164,11 +187,7 @@ local Environment = ast.abstract.Runtime {
|
||||||
-- get variable in current or parent scope, with metadata
|
-- get variable in current or parent scope, with metadata
|
||||||
_get_variable = function(self, state, identifier)
|
_get_variable = function(self, state, identifier)
|
||||||
if self:defined(state, identifier) then
|
if self:defined(state, identifier) then
|
||||||
if self.variables:has(state, identifier) then
|
return self:_lookup(state, identifier)
|
||||||
return self.variables:get(state, identifier)
|
|
||||||
elseif self.parent and not self.undefine[identifier.name] then
|
|
||||||
return self.parent:_get_variable(state, identifier)
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
error(("identifier %q is undefined in branch %s"):format(identifier.name, state.branch_id), 0)
|
error(("identifier %q is undefined in branch %s"):format(identifier.name, state.branch_id), 0)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue