opal 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/lib/opal.rb +0 -5
- data/lib/opal/compiler.rb +24 -44
- data/lib/opal/nodes/base.rb +5 -8
- data/lib/opal/nodes/call.rb +4 -0
- data/lib/opal/nodes/class.rb +6 -7
- data/lib/opal/nodes/def.rb +4 -4
- data/lib/opal/nodes/definitions.rb +0 -14
- data/lib/opal/nodes/iter.rb +51 -38
- data/lib/opal/nodes/literal.rb +21 -24
- data/lib/opal/nodes/module.rb +4 -4
- data/lib/opal/nodes/runtime_helpers.rb +45 -0
- data/lib/opal/nodes/scope.rb +280 -0
- data/lib/opal/nodes/singleton_class.rb +4 -5
- data/lib/opal/nodes/super.rb +1 -1
- data/lib/opal/nodes/top.rb +9 -7
- data/lib/opal/nodes/yield.rb +14 -3
- data/lib/opal/parser.rb +4 -18
- data/lib/opal/parser/grammar.rb +3745 -3667
- data/lib/opal/parser/grammar.y +1692 -1778
- data/lib/opal/parser/keywords.rb +35 -35
- data/lib/opal/parser/lexer.rb +356 -325
- data/lib/opal/parser/sexp.rb +1 -1
- data/lib/opal/version.rb +1 -1
- data/opal.gemspec +1 -0
- data/opal/core/array.rb +320 -81
- data/opal/core/enumerable.rb +46 -5
- data/opal/core/hash.rb +6 -64
- data/opal/core/helpers.rb +67 -0
- data/opal/core/method.rb +1 -1
- data/opal/core/module.rb +4 -4
- data/opal/core/range.rb +1 -12
- data/opal/core/regexp.rb +2 -8
- data/opal/core/runtime.js +74 -3
- data/opal/core/string.rb +99 -74
- data/opal/opal.rb +3 -72
- data/spec/filters/bugs/array.rb +2 -30
- data/spec/filters/bugs/basic_object.rb +0 -1
- data/spec/filters/bugs/string.rb +26 -21
- data/spec/filters/unsupported/enumerator.rb +3 -0
- data/spec/filters/unsupported/float.rb +1 -0
- data/spec/filters/unsupported/immutable_strings.rb +15 -0
- data/spec/filters/unsupported/tainted.rb +58 -30
- data/spec/filters/unsupported/trusted.rb +35 -15
- data/spec/opal/parser/class_spec.rb +4 -4
- data/spec/opal/parser/def_spec.rb +4 -4
- data/spec/opal/parser/lvar_spec.rb +6 -6
- data/spec/opal/parser/module_spec.rb +4 -4
- data/spec/opal/parser/sclass_spec.rb +2 -2
- data/spec/stdlib/native/exposure_spec.rb +33 -0
- data/stdlib/buffer.rb +1 -1
- data/stdlib/buffer/view.rb +1 -1
- data/stdlib/native.rb +193 -174
- data/stdlib/opal-parser.rb +0 -6
- data/stdlib/pp.rb +9 -0
- data/tasks/mspec.rake +3 -1
- metadata +9 -9
- data/lib/opal/nodes/base_scope.rb +0 -11
- data/lib/opal/target_scope.rb +0 -281
- data/spec/filters/20.rb +0 -4
- data/spec/filters/unsupported/array_subclasses.rb +0 -37
data/lib/opal/nodes/module.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'opal/nodes/
|
1
|
+
require 'opal/nodes/scope'
|
2
2
|
|
3
3
|
module Opal
|
4
4
|
module Nodes
|
5
|
-
class ModuleNode <
|
5
|
+
class ModuleNode < ScopeNode
|
6
6
|
handle :module
|
7
7
|
|
8
8
|
children :cid, :body
|
@@ -14,12 +14,12 @@ module Opal
|
|
14
14
|
push "(function($base) {"
|
15
15
|
line " var self = $module($base, '#{name}');"
|
16
16
|
|
17
|
-
in_scope
|
17
|
+
in_scope do
|
18
18
|
scope.name = name
|
19
19
|
add_temp "#{scope.proto} = self._proto"
|
20
20
|
add_temp '$scope = self._scope'
|
21
21
|
|
22
|
-
body_code = stmt(body)
|
22
|
+
body_code = stmt(body || s(:nil))
|
23
23
|
empty_line
|
24
24
|
|
25
25
|
line scope.to_vars
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'opal/nodes/base'
|
3
|
+
|
4
|
+
module Opal
|
5
|
+
module Nodes
|
6
|
+
class RuntimeHelpers < Base
|
7
|
+
HELPERS = Set.new
|
8
|
+
|
9
|
+
children :recvr, :meth, :arglist
|
10
|
+
|
11
|
+
def self.compatible?(recvr, meth, arglist)
|
12
|
+
recvr == [:const, :Opal] and HELPERS.include?(meth.to_sym)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.helper(name, &block)
|
16
|
+
HELPERS << name
|
17
|
+
define_method("compile_#{name}", &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def compile
|
21
|
+
if HELPERS.include?(meth.to_sym)
|
22
|
+
__send__("compile_#{meth}")
|
23
|
+
else
|
24
|
+
raise "Helper not supported: #{meth}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
helper :truthy? do
|
29
|
+
unless sexp = arglist[1]
|
30
|
+
raise "truthy? requires an object"
|
31
|
+
end
|
32
|
+
|
33
|
+
js_truthy(sexp)
|
34
|
+
end
|
35
|
+
|
36
|
+
helper :falsy? do
|
37
|
+
unless sexp = arglist[1]
|
38
|
+
raise "falsy? requires an object"
|
39
|
+
end
|
40
|
+
|
41
|
+
js_falsy(sexp)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'opal/nodes/base'
|
2
|
+
|
3
|
+
module Opal
|
4
|
+
module Nodes
|
5
|
+
class ScopeNode < 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
|
+
|
53
|
+
def in_scope(&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
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'opal/nodes/
|
1
|
+
require 'opal/nodes/scope'
|
2
2
|
|
3
3
|
module Opal
|
4
4
|
module Nodes
|
5
|
-
class SingletonClassNode <
|
5
|
+
class SingletonClassNode < ScopeNode
|
6
6
|
handle :sclass
|
7
7
|
|
8
8
|
children :object, :body
|
@@ -10,17 +10,16 @@ module Opal
|
|
10
10
|
def compile
|
11
11
|
push "(function(self) {"
|
12
12
|
|
13
|
-
in_scope
|
13
|
+
in_scope do
|
14
14
|
add_temp '$scope = self._scope'
|
15
15
|
add_temp 'def = self._proto'
|
16
16
|
|
17
17
|
line scope.to_vars
|
18
|
-
line stmt(body)
|
18
|
+
line stmt(compiler.returns(body))
|
19
19
|
end
|
20
20
|
|
21
21
|
line "})(", recv(object), ".$singleton_class())"
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
24
|
end
|
26
25
|
end
|
data/lib/opal/nodes/super.rb
CHANGED
@@ -18,7 +18,7 @@ module Opal
|
|
18
18
|
if scope.def?
|
19
19
|
scope.uses_block!
|
20
20
|
scope_name = scope.identify!
|
21
|
-
class_name = scope.parent.name
|
21
|
+
class_name = scope.parent.name ? "$#{scope.parent.name}" : 'self._klass._proto'
|
22
22
|
|
23
23
|
if scope.defs
|
24
24
|
push "$opal.find_super_dispatcher(self, '#{scope.mid.to_s}', #{scope_name}, "
|
data/lib/opal/nodes/top.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
-
require 'opal/
|
1
|
+
require 'opal/version'
|
2
|
+
require 'opal/nodes/scope'
|
2
3
|
|
3
4
|
module Opal
|
4
5
|
module Nodes
|
5
6
|
# Generates code for an entire file, i.e. the base sexp
|
6
|
-
class TopNode <
|
7
|
+
class TopNode < ScopeNode
|
8
|
+
handle :top
|
9
|
+
|
10
|
+
children :body
|
11
|
+
|
7
12
|
def compile
|
8
13
|
push version_comment
|
9
14
|
|
10
15
|
line "(function($opal) {"
|
11
16
|
|
12
|
-
in_scope
|
17
|
+
in_scope do
|
13
18
|
body_code = stmt(stmts)
|
14
19
|
body_code = [body_code] unless body_code.is_a?(Array)
|
15
20
|
|
@@ -30,10 +35,7 @@ module Opal
|
|
30
35
|
end
|
31
36
|
|
32
37
|
def stmts
|
33
|
-
|
34
|
-
scope = s(:scope, sexp)
|
35
|
-
scope.line = sexp.line
|
36
|
-
scope
|
38
|
+
compiler.returns(body)
|
37
39
|
end
|
38
40
|
|
39
41
|
def compile_irb_vars
|
data/lib/opal/nodes/yield.rb
CHANGED
@@ -4,7 +4,10 @@ module Opal
|
|
4
4
|
module Nodes
|
5
5
|
class BaseYieldNode < Base
|
6
6
|
def compile_call(children, level)
|
7
|
-
|
7
|
+
yielding_scope = find_yielding_scope
|
8
|
+
|
9
|
+
yielding_scope.uses_block!
|
10
|
+
block_name = yielding_scope.block_name || '$yield'
|
8
11
|
|
9
12
|
if yields_single_arg?(children)
|
10
13
|
push expr(children.first)
|
@@ -20,8 +23,16 @@ module Opal
|
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
|
26
|
+
def find_yielding_scope
|
27
|
+
working = scope
|
28
|
+
while working
|
29
|
+
if working.block_name or working.def?
|
30
|
+
break
|
31
|
+
end
|
32
|
+
working = working.parent
|
33
|
+
end
|
34
|
+
|
35
|
+
working
|
25
36
|
end
|
26
37
|
|
27
38
|
def yields_single_arg?(children)
|