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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/lib/opal.rb +0 -5
  4. data/lib/opal/compiler.rb +24 -44
  5. data/lib/opal/nodes/base.rb +5 -8
  6. data/lib/opal/nodes/call.rb +4 -0
  7. data/lib/opal/nodes/class.rb +6 -7
  8. data/lib/opal/nodes/def.rb +4 -4
  9. data/lib/opal/nodes/definitions.rb +0 -14
  10. data/lib/opal/nodes/iter.rb +51 -38
  11. data/lib/opal/nodes/literal.rb +21 -24
  12. data/lib/opal/nodes/module.rb +4 -4
  13. data/lib/opal/nodes/runtime_helpers.rb +45 -0
  14. data/lib/opal/nodes/scope.rb +280 -0
  15. data/lib/opal/nodes/singleton_class.rb +4 -5
  16. data/lib/opal/nodes/super.rb +1 -1
  17. data/lib/opal/nodes/top.rb +9 -7
  18. data/lib/opal/nodes/yield.rb +14 -3
  19. data/lib/opal/parser.rb +4 -18
  20. data/lib/opal/parser/grammar.rb +3745 -3667
  21. data/lib/opal/parser/grammar.y +1692 -1778
  22. data/lib/opal/parser/keywords.rb +35 -35
  23. data/lib/opal/parser/lexer.rb +356 -325
  24. data/lib/opal/parser/sexp.rb +1 -1
  25. data/lib/opal/version.rb +1 -1
  26. data/opal.gemspec +1 -0
  27. data/opal/core/array.rb +320 -81
  28. data/opal/core/enumerable.rb +46 -5
  29. data/opal/core/hash.rb +6 -64
  30. data/opal/core/helpers.rb +67 -0
  31. data/opal/core/method.rb +1 -1
  32. data/opal/core/module.rb +4 -4
  33. data/opal/core/range.rb +1 -12
  34. data/opal/core/regexp.rb +2 -8
  35. data/opal/core/runtime.js +74 -3
  36. data/opal/core/string.rb +99 -74
  37. data/opal/opal.rb +3 -72
  38. data/spec/filters/bugs/array.rb +2 -30
  39. data/spec/filters/bugs/basic_object.rb +0 -1
  40. data/spec/filters/bugs/string.rb +26 -21
  41. data/spec/filters/unsupported/enumerator.rb +3 -0
  42. data/spec/filters/unsupported/float.rb +1 -0
  43. data/spec/filters/unsupported/immutable_strings.rb +15 -0
  44. data/spec/filters/unsupported/tainted.rb +58 -30
  45. data/spec/filters/unsupported/trusted.rb +35 -15
  46. data/spec/opal/parser/class_spec.rb +4 -4
  47. data/spec/opal/parser/def_spec.rb +4 -4
  48. data/spec/opal/parser/lvar_spec.rb +6 -6
  49. data/spec/opal/parser/module_spec.rb +4 -4
  50. data/spec/opal/parser/sclass_spec.rb +2 -2
  51. data/spec/stdlib/native/exposure_spec.rb +33 -0
  52. data/stdlib/buffer.rb +1 -1
  53. data/stdlib/buffer/view.rb +1 -1
  54. data/stdlib/native.rb +193 -174
  55. data/stdlib/opal-parser.rb +0 -6
  56. data/stdlib/pp.rb +9 -0
  57. data/tasks/mspec.rake +3 -1
  58. metadata +9 -9
  59. data/lib/opal/nodes/base_scope.rb +0 -11
  60. data/lib/opal/target_scope.rb +0 -281
  61. data/spec/filters/20.rb +0 -4
  62. data/spec/filters/unsupported/array_subclasses.rb +0 -37
@@ -1,8 +1,8 @@
1
- require 'opal/nodes/base_scope'
1
+ require 'opal/nodes/scope'
2
2
 
3
3
  module Opal
4
4
  module Nodes
5
- class ModuleNode < BaseScopeNode
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(:module) do
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/base_scope'
1
+ require 'opal/nodes/scope'
2
2
 
3
3
  module Opal
4
4
  module Nodes
5
- class SingletonClassNode < BaseScopeNode
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(:sclass) do
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
@@ -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 || 'self._klass._proto'
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}, "
@@ -1,15 +1,20 @@
1
- require 'opal/nodes/base_scope'
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 < BaseScopeNode
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(:top) do
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
- sexp = @sexp || s(:nil)
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
@@ -4,7 +4,10 @@ module Opal
4
4
  module Nodes
5
5
  class BaseYieldNode < Base
6
6
  def compile_call(children, level)
7
- scope.uses_block!
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 block_name
24
- scope.block_name || '$yield'
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)