Skip to content

Commit 568e93e

Browse files
committedNov 13, 2013
Merge TargetScope into BaseScopeNode
1 parent 7207a93 commit 568e93e

File tree

5 files changed

+283
-314
lines changed

5 files changed

+283
-314
lines changed
 

‎lib/opal/compiler.rb

+3-23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
require 'set'
22
require 'opal/parser'
3-
require 'opal/target_scope'
4-
require 'opal/version'
53
require 'opal/fragment'
64
require 'opal/nodes'
75

@@ -70,9 +68,9 @@ def compile(source, options = {})
7068
@source = source
7169
@options.update options
7270

73-
@sexp = Parser.new.parse(@source, self.file)
71+
@sexp = Parser.new.parse(@source, self.file) || s(:nil)
7472

75-
top_node = Nodes::TopNode.new(@sexp, :expr, self)
73+
top_node = Nodes::TopNode.new(s(:top, @sexp), :expr, self)
7674
@fragments = top_node.compile_to_fragments.flatten
7775

7876
@result = @fragments.map(&:code).join('')
@@ -127,25 +125,7 @@ def helper(name)
127125
@helpers << name
128126
end
129127

130-
# Every time the parser enters a new scope, this is called with
131-
# the scope type as an argument. Valid types are `:top` for the
132-
# top level/file scope; `:class`, `:module` and `:sclass` for the
133-
# obvious ruby classes/modules; `:def` and `:iter` for methods
134-
# and blocks respectively.
135-
#
136-
# This method just pushes a new instance of `Opal::Scope` onto the
137-
# stack, sets the new scope as the `@scope` variable, and yields
138-
# the given block. Once the block returns, the old scope is put
139-
# back on top of the stack.
140-
def in_scope(type)
141-
return unless block_given?
142-
143-
parent = @scope
144-
@scope = TargetScope.new(type, self).tap { |s| s.parent = parent }
145-
yield @scope
146-
147-
@scope = parent
148-
end
128+
attr_accessor :scope
149129

150130
# To keep code blocks nicely indented, this will yield a block after
151131
# adding an extra layer of indent, and then returning the resulting

‎lib/opal/nodes/base.rb

+5-8
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,15 @@ def self.children(*names)
2323
end
2424
end
2525

26-
attr_reader :compiler
26+
attr_reader :compiler, :type
2727

2828
def initialize(sexp, level, compiler)
2929
@sexp = sexp
30+
@type = sexp.type
3031
@level = level
3132
@compiler = compiler
3233
end
3334

34-
def type
35-
@sexp.type
36-
end
37-
3835
def children
3936
@sexp[1..-1]
4037
end
@@ -119,15 +116,15 @@ def expr_or_nil(sexp)
119116
end
120117

121118
def add_local(name)
122-
scope.add_local name.to_sym
119+
scope.add_scope_local name.to_sym
123120
end
124121

125122
def add_ivar(name)
126-
scope.add_ivar name
123+
scope.add_scope_ivar name
127124
end
128125

129126
def add_temp(temp)
130-
scope.add_temp temp
127+
scope.add_scope_temp temp
131128
end
132129

133130
def helper(name)

‎lib/opal/nodes/base_scope.rb

+270-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,277 @@
33
module Opal
44
module Nodes
55
class BaseScopeNode < Base
6+
7+
# Every scope can have a parent scope
8+
attr_accessor :parent
9+
10+
# The class or module name if this scope is a class scope
11+
attr_accessor :name
12+
13+
# The given block name for a def scope
14+
attr_accessor :block_name
15+
16+
attr_reader :scope_name
17+
attr_reader :ivars
18+
19+
attr_accessor :mid
20+
21+
# true if singleton def, false otherwise
22+
attr_accessor :defs
23+
24+
# used by modules to know what methods to donate to includees
25+
attr_reader :methods
26+
27+
# uses parents super method
28+
attr_accessor :uses_super
29+
attr_accessor :uses_zuper
30+
31+
attr_accessor :catch_return
32+
33+
def initialize(*)
34+
super
35+
36+
@locals = []
37+
@temps = []
38+
@args = []
39+
@ivars = []
40+
@parent = nil
41+
@queue = []
42+
@unique = 'a'
43+
@while_stack = []
44+
45+
@methods = []
46+
47+
@uses_block = false
48+
49+
# used by classes to store all ivars used in direct def methods
50+
@proto_ivars = []
51+
end
52+
653
def in_scope(type, &block)
7-
indent { compiler.in_scope(type, &block) }
54+
indent do
55+
@parent = compiler.scope
56+
compiler.scope = self
57+
block.call self
58+
compiler.scope = @parent
59+
end
60+
end
61+
62+
# Returns true if this scope is a class/module body scope
63+
def class_scope?
64+
@type == :class or @type == :module
65+
end
66+
67+
# Returns true if this is strictly a class scope
68+
def class?
69+
@type == :class
70+
end
71+
72+
# True if this is a module scope
73+
def module?
74+
@type == :module
75+
end
76+
77+
def sclass?
78+
@type == :sclass
79+
end
80+
81+
# Returns true if this is a top scope (main file body)
82+
def top?
83+
@type == :top
84+
end
85+
86+
# True if a block/iter scope
87+
def iter?
88+
@type == :iter
89+
end
90+
91+
def def?
92+
@type == :def
93+
end
94+
95+
# Is this a normal def method directly inside a class? This is
96+
# used for optimizing ivars as we can set them to nil in the
97+
# class body
98+
def def_in_class?
99+
!@defs && @type == :def && @parent && @parent.class?
100+
end
101+
102+
# Inside a class or module scope, the javascript variable name returned
103+
# by this function points to the classes' prototype. This is the target
104+
# to where methods are actually added inside a class body.
105+
def proto
106+
"def"
107+
end
108+
109+
# A scope donates its methods if it is a module, or the core Object
110+
# class. Modules donate their methods to classes or objects they are
111+
# included in. Object donates methods to bridged classes whose native
112+
# prototypes do not actually inherit from Opal.Object.prototype.
113+
def should_donate?
114+
@type == :module
115+
end
116+
117+
##
118+
# Vars to use inside each scope
119+
def to_vars
120+
vars = @temps.dup
121+
vars.push(*@locals.map { |l| "#{l} = nil" })
122+
123+
iv = ivars.map do |ivar|
124+
"if (self#{ivar} == null) self#{ivar} = nil;\n"
125+
end
126+
127+
indent = @compiler.parser_indent
128+
res = vars.empty? ? '' : "var #{vars.join ', '};"
129+
str = ivars.empty? ? res : "#{res}\n#{indent}#{iv.join indent}"
130+
131+
if class? and !@proto_ivars.empty?
132+
#raise "FIXME to_vars"
133+
pvars = @proto_ivars.map { |i| "#{proto}#{i}"}.join(' = ')
134+
result = "%s\n%s%s = nil;" % [str, indent, pvars]
135+
else
136+
result = str
137+
end
138+
139+
fragment(result)
140+
end
141+
142+
# Generates code for this module to donate methods
143+
def to_donate_methods
144+
if should_donate? and !@methods.empty?
145+
fragment("%s;$opal.donate(self, [%s]);" % [@compiler.parser_indent, @methods.map(&:inspect).join(', ')])
146+
else
147+
fragment("")
148+
end
149+
end
150+
151+
def add_scope_ivar(ivar)
152+
if def_in_class?
153+
@parent.add_proto_ivar ivar
154+
else
155+
@ivars << ivar unless @ivars.include? ivar
156+
end
157+
end
158+
159+
def add_proto_ivar(ivar)
160+
@proto_ivars << ivar unless @proto_ivars.include? ivar
161+
end
162+
163+
def add_arg(arg)
164+
@args << arg unless @args.include? arg
165+
arg
166+
end
167+
168+
def add_scope_local(local)
169+
return if has_local? local
170+
171+
@locals << local
172+
end
173+
174+
def has_local?(local)
175+
return true if @locals.include? local or @args.include? local
176+
return @parent.has_local?(local) if @parent and @type == :iter
177+
178+
false
179+
end
180+
181+
def add_scope_temp(*tmps)
182+
@temps.push(*tmps)
183+
end
184+
185+
def has_temp?(tmp)
186+
@temps.include? tmp
187+
end
188+
189+
def new_temp
190+
return @queue.pop unless @queue.empty?
191+
192+
tmp = next_temp
193+
@temps << tmp
194+
tmp
195+
end
196+
197+
def next_temp
198+
tmp = "$#{@unique}"
199+
@unique = @unique.succ
200+
tmp
201+
end
202+
203+
def queue_temp(name)
204+
@queue << name
205+
end
206+
207+
def push_while
208+
info = {}
209+
@while_stack.push info
210+
info
211+
end
212+
213+
def pop_while
214+
@while_stack.pop
215+
end
216+
217+
def in_while?
218+
!@while_stack.empty?
219+
end
220+
221+
def uses_block!
222+
if @type == :iter && @parent
223+
@parent.uses_block!
224+
else
225+
@uses_block = true
226+
identify!
227+
end
228+
end
229+
230+
def identify!
231+
return @identity if @identity
232+
233+
@identity = @compiler.unique_temp
234+
@parent.add_scope_temp @identity if @parent
235+
236+
@identity
237+
end
238+
239+
def identity
240+
@identity
241+
end
242+
243+
def find_parent_def
244+
scope = self
245+
while scope = scope.parent
246+
if scope.def?
247+
return scope
248+
end
249+
end
250+
251+
nil
252+
end
253+
254+
def get_super_chain
255+
chain, scope, defn, mid = [], self, 'null', 'null'
256+
257+
while scope
258+
if scope.type == :iter
259+
chain << scope.identify!
260+
scope = scope.parent if scope.parent
261+
262+
elsif scope.type == :def
263+
defn = scope.identify!
264+
mid = "'#{scope.mid}'"
265+
break
266+
267+
else
268+
break
269+
end
270+
end
271+
272+
[chain, defn, mid]
273+
end
274+
275+
def uses_block?
276+
@uses_block
8277
end
9278
end
10279
end

‎lib/opal/nodes/top.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
require 'opal/version'
12
require 'opal/nodes/base_scope'
23

34
module Opal
45
module Nodes
56
# Generates code for an entire file, i.e. the base sexp
67
class TopNode < BaseScopeNode
8+
9+
children :body
10+
711
def compile
812
push version_comment
913

@@ -30,7 +34,7 @@ def compile
3034
end
3135

3236
def stmts
33-
compiler.returns(@sexp || s(:nil))
37+
compiler.returns(body)
3438
end
3539

3640
def compile_irb_vars

‎lib/opal/target_scope.rb

-281
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.