-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathnamespace.rb
75 lines (68 loc) · 2.29 KB
/
namespace.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# Instances of this class keep track of both classes and modules that we have
# seen before, as well as the methods and properties that are defined in each.
#
# Use cases this enables:
#
# * detection of "open" classes and modules, i.e., redefining a class or
# module that was previously declared in order to add or modify methods
# or properties.
#
# * knowing when to prefix method or property access with `this.` and
# when to add `.bind(this)` for methods and properties that were defined
# outside of this class.
#
module Ruby2JS
class Namespace
def initialize
@active = [] # current scope
@seen = {} # history of all definitions seen previously
end
# convert an AST name which is represented as a set of nested
# s(:const, # ...) into an array of symbols that represent
# the relative path.
def resolve(token, result = [])
return [] unless token&.type == :const
resolve(token.children.first, result)
result.push(token.children.last)
end
# return the active scope as a flat array of symbols
def active
@active.flatten.compact
end
# enter a new scope, which may be a nested subscope. Mark the new scope
# as seen, and return any previous definition that may have been seen
# before.
def enter(name)
@active.push resolve(name)
previous = @seen[active]
@seen[active] ||= {}
previous
end
# return the set of known properties (and methods) for either the current
# scope or a named subscope.
def getOwnProps(name = nil)
@seen[active + resolve(name)]&.dup || {}
end
# add new props (and methods) to the current scope.
def defineProps(props, namespace=active)
@seen[namespace] ||= {}
@seen[namespace].merge! props || {}
end
# find a named scope which may be relative to any point in the ancestry of
# the current scope. Return the properties for that scope.
def find(name)
name = resolve(name)
prefix = active
while prefix.pop
result = @seen[prefix + name]
return result if result
end
{}
end
# leave a given scope. Note that the scope may be compound (e.g., M::N),
# and if so, it will pop the entire resolved name.
def leave()
@active.pop
end
end
end