opal 0.3.19 → 0.3.20
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.
- data/.gitignore +3 -1
- data/Gemfile +3 -2
- data/README.md +304 -48
- data/Rakefile +1 -2
- data/core/alpha.rb +2 -1
- data/core/array.rb +92 -96
- data/core/basic_object.rb +1 -10
- data/core/boolean.rb +6 -18
- data/core/class.rb +9 -10
- data/core/comparable.rb +1 -1
- data/core/enumerable.rb +11 -11
- data/core/enumerator.rb +2 -10
- data/core/error.rb +16 -31
- data/core/hash.rb +32 -36
- data/core/json.rb +50 -0
- data/core/kernel.rb +48 -57
- data/core/load_order +3 -5
- data/core/module.rb +37 -35
- data/core/nil_class.rb +4 -0
- data/core/numeric.rb +10 -30
- data/core/proc.rb +1 -1
- data/core/range.rb +3 -4
- data/core/regexp.rb +21 -6
- data/core/runtime.js +278 -370
- data/core/string.rb +21 -37
- data/core/struct.rb +11 -3
- data/core/time.rb +44 -37
- data/lib/opal.rb +3 -3
- data/lib/opal/builder.rb +48 -27
- data/lib/opal/builder_task.rb +3 -20
- data/lib/opal/grammar.rb +18 -13
- data/lib/opal/grammar.y +7 -4
- data/lib/opal/parser.rb +290 -199
- data/lib/opal/scope.rb +187 -176
- data/lib/opal/version.rb +1 -1
- data/test/core/kernel/define_singleton_method_spec.rb +21 -0
- data/test/core/time/at_spec.rb +7 -0
- data/test/core/time/day_spec.rb +5 -0
- data/test/core/time/friday_spec.rb +9 -0
- data/test/core/time/hour_spec.rb +5 -0
- data/test/core/time/min_spec.rb +5 -0
- data/test/core/time/monday_spec.rb +9 -0
- data/test/core/time/month_spec.rb +5 -0
- data/test/core/time/now_spec.rb +5 -0
- data/test/core/time/saturday_spec.rb +9 -0
- data/test/index.html +2 -1
- data/test/language/singleton_class_spec.rb +0 -16
- data/test/opal/array/to_json_spec.rb +7 -0
- data/test/opal/boolean/singleton_class_spec.rb +9 -0
- data/test/opal/boolean/to_json_spec.rb +9 -0
- data/test/opal/hash/to_json_spec.rb +9 -0
- data/test/opal/json/parse_spec.rb +31 -0
- data/test/opal/kernel/to_json_spec.rb +5 -0
- data/test/opal/nil/to_json_spec.rb +5 -0
- data/test/opal/numeric/to_json_spec.rb +6 -0
- data/test/opal/runtime/call_spec.rb +16 -0
- data/test/opal/runtime/defined_spec.rb +11 -0
- data/test/opal/runtime/super_spec.rb +16 -0
- data/test/opal/string/to_json_spec.rb +6 -0
- data/test/spec_helper.rb +1 -3
- metadata +48 -15
- data/core/dir.rb +0 -89
- data/core/file.rb +0 -85
- data/core/match_data.rb +0 -35
- data/core/rational.rb +0 -16
- data/test/core/file/expand_path_spec.rb +0 -20
data/lib/opal/scope.rb
CHANGED
@@ -1,225 +1,236 @@
|
|
1
|
-
module Opal
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Opal
|
2
|
+
class Parser
|
3
|
+
class Scope
|
4
|
+
attr_reader :locals
|
5
|
+
attr_reader :temps
|
6
|
+
attr_accessor :parent
|
6
7
|
|
7
|
-
|
8
|
+
attr_accessor :name
|
8
9
|
|
9
|
-
|
10
|
+
attr_accessor :block_name
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
attr_reader :scope_name
|
13
|
+
attr_reader :ivars
|
13
14
|
|
14
|
-
|
15
|
+
attr_accessor :donates_methods
|
15
16
|
|
16
|
-
|
17
|
+
attr_reader :type
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
attr_accessor :defines_defn
|
20
|
+
attr_accessor :defines_defs
|
20
21
|
|
21
|
-
|
22
|
-
attr_reader :methods
|
22
|
+
attr_accessor :mid
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
@type = type
|
27
|
-
@locals = []
|
28
|
-
@temps = []
|
29
|
-
@args = []
|
30
|
-
@ivars = []
|
31
|
-
@parent = nil
|
32
|
-
@queue = []
|
33
|
-
@unique = "a"
|
34
|
-
@while_stack = []
|
24
|
+
# true if singleton def, false otherwise
|
25
|
+
attr_accessor :defs
|
35
26
|
|
36
|
-
|
37
|
-
|
27
|
+
# used by modules to know what methods to donate to includees
|
28
|
+
attr_reader :methods
|
38
29
|
|
39
|
-
|
30
|
+
# singleton methods defined on classes/modules
|
31
|
+
attr_reader :smethods
|
40
32
|
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
def initialize(type, parser)
|
34
|
+
@parser = parser
|
35
|
+
@type = type
|
36
|
+
@locals = []
|
37
|
+
@temps = []
|
38
|
+
@args = []
|
39
|
+
@ivars = []
|
40
|
+
@parent = nil
|
41
|
+
@queue = []
|
42
|
+
@unique = "a"
|
43
|
+
@while_stack = []
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
@type == :class or @type == :module
|
48
|
-
end
|
45
|
+
@defines_defs = false
|
46
|
+
@defines_defn = false
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
def to_vars
|
53
|
-
vars = []
|
54
|
-
|
55
|
-
if @type == :class
|
56
|
-
vars << '__class = this'
|
57
|
-
vars << '__scope = this._scope'
|
58
|
-
vars << 'def = this._proto'
|
59
|
-
elsif @type == :module
|
60
|
-
vars << '__class = this'
|
61
|
-
vars << '__scope = this._scope'
|
62
|
-
vars << 'def = this._proto'
|
63
|
-
elsif @type == :sclass
|
64
|
-
vars << '__scope = this._scope'
|
65
|
-
elsif @type == :iter
|
66
|
-
vars << 'def = (this._isObject ? this._klass._proto : this._proto)' if @defines_defn
|
67
|
-
else
|
68
|
-
vars << 'def = (this._isObject ? this._klass._proto : this._proto)' if @defines_defn
|
69
|
-
end
|
70
|
-
|
71
|
-
locals.each { |l| vars << "#{l} = nil" }
|
72
|
-
temps.each { |t| vars << t }
|
73
|
-
|
74
|
-
iv = ivars.map do |ivar|
|
75
|
-
"if (this#{ivar} == null) this#{ivar} = nil;\n"
|
76
|
-
end
|
77
|
-
|
78
|
-
indent = @parser.parser_indent
|
79
|
-
res = vars.empty? ? '' : "var #{vars.join ', '}; "
|
80
|
-
ivars.empty? ? res : "#{res}\n#{indent}#{iv.join indent}"
|
81
|
-
end
|
48
|
+
@methods = []
|
49
|
+
@smethods = []
|
82
50
|
|
83
|
-
|
84
|
-
def to_donate_methods
|
85
|
-
"#{@parser.parser_indent};__donate(this, [#{@methods.map { |m| m.inspect }.join ', '}]);"
|
86
|
-
end
|
51
|
+
@uses_block = false
|
87
52
|
|
88
|
-
|
89
|
-
|
90
|
-
|
53
|
+
# used by classes to store all ivars used in direct def methods
|
54
|
+
@proto_ivars = []
|
55
|
+
end
|
91
56
|
|
92
|
-
|
93
|
-
|
94
|
-
|
57
|
+
# Returns true if this scope is a class/module body scope
|
58
|
+
def class_scope?
|
59
|
+
@type == :class or @type == :module
|
60
|
+
end
|
95
61
|
|
96
|
-
|
97
|
-
|
62
|
+
def class?
|
63
|
+
@type == :class
|
64
|
+
end
|
98
65
|
|
99
|
-
|
100
|
-
|
66
|
+
def module?
|
67
|
+
@type == :module
|
68
|
+
end
|
101
69
|
|
102
|
-
|
103
|
-
|
104
|
-
|
70
|
+
def top?
|
71
|
+
@type == :top
|
72
|
+
end
|
105
73
|
|
106
|
-
|
107
|
-
|
74
|
+
def iter?
|
75
|
+
@type == :iter
|
76
|
+
end
|
108
77
|
|
109
|
-
|
110
|
-
|
111
|
-
|
78
|
+
# Is this a normal def method directly inside a class? This is
|
79
|
+
# used for optimizing ivars as we can set them to nil in the
|
80
|
+
# class body
|
81
|
+
def def_in_class?
|
82
|
+
@type == :def && @parent && @parent.class?
|
83
|
+
end
|
112
84
|
|
113
|
-
|
114
|
-
|
85
|
+
def proto
|
86
|
+
"#{ @name }_prototype"
|
87
|
+
end
|
115
88
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
89
|
+
##
|
90
|
+
# Vars to use inside each scope
|
91
|
+
def to_vars
|
92
|
+
vars = locals.map { |l| "#{l} = nil" }
|
93
|
+
vars.push *temps
|
121
94
|
|
122
|
-
|
123
|
-
|
124
|
-
|
95
|
+
iv = ivars.map do |ivar|
|
96
|
+
"if (this#{ivar} == null) this#{ivar} = nil;\n"
|
97
|
+
end
|
125
98
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
info
|
130
|
-
end
|
99
|
+
indent = @parser.parser_indent
|
100
|
+
res = vars.empty? ? '' : "var #{vars.join ', '};"
|
101
|
+
str = ivars.empty? ? res : "#{res}\n#{indent}#{iv.join indent}"
|
131
102
|
|
132
|
-
|
133
|
-
|
134
|
-
|
103
|
+
if class? and !@proto_ivars.empty?
|
104
|
+
pvars = @proto_ivars.map { |i| "#{proto}#{i}"}.join(' = ')
|
105
|
+
"%s\n%s%s = nil;" % [str, indent, pvars]
|
106
|
+
else
|
107
|
+
str
|
108
|
+
end
|
109
|
+
end
|
135
110
|
|
136
|
-
|
137
|
-
|
138
|
-
|
111
|
+
# Generates code for this module to donate methods
|
112
|
+
def to_donate_methods
|
113
|
+
out = ""
|
139
114
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
115
|
+
unless @methods.empty?
|
116
|
+
out += "%s;#{@name}._donate([%s]);" %
|
117
|
+
[@parser.parser_indent, @methods.map(&:inspect).join(', ')]
|
118
|
+
end
|
119
|
+
|
120
|
+
unless @smethods.empty?
|
121
|
+
out += "%s;#{@name}._sdonate([%s]);" %
|
122
|
+
[@parser.parser_indent, @smethods.map(&:inspect).join(', ')]
|
123
|
+
end
|
124
|
+
|
125
|
+
out
|
146
126
|
end
|
147
|
-
end
|
148
127
|
|
149
|
-
|
150
|
-
|
151
|
-
|
128
|
+
def add_ivar(ivar)
|
129
|
+
if def_in_class?
|
130
|
+
@parent.add_proto_ivar ivar
|
131
|
+
else
|
132
|
+
@ivars << ivar unless @ivars.include? ivar
|
133
|
+
end
|
134
|
+
end
|
152
135
|
|
153
|
-
|
154
|
-
|
155
|
-
|
136
|
+
def add_proto_ivar(ivar)
|
137
|
+
@proto_ivars << ivar unless @proto_ivars.include? ivar
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_arg(arg)
|
141
|
+
@args << arg unless @args.include? arg
|
142
|
+
end
|
143
|
+
|
144
|
+
def add_local(local)
|
145
|
+
return if has_local? local
|
146
|
+
|
147
|
+
@locals << local
|
148
|
+
end
|
149
|
+
|
150
|
+
def has_local?(local)
|
151
|
+
return true if @locals.include? local or @args.include? local
|
152
|
+
return @parent.has_local?(local) if @parent and @type == :iter
|
153
|
+
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
def add_temp(*tmps)
|
158
|
+
@temps.push *tmps
|
159
|
+
end
|
160
|
+
|
161
|
+
def new_temp
|
162
|
+
return @queue.pop unless @queue.empty?
|
163
|
+
|
164
|
+
tmp = "__#{@unique}"
|
165
|
+
@unique = @unique.succ
|
166
|
+
@temps << tmp
|
167
|
+
tmp
|
168
|
+
end
|
169
|
+
|
170
|
+
def queue_temp(name)
|
171
|
+
@queue << name
|
172
|
+
end
|
156
173
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
174
|
+
def push_while
|
175
|
+
info = {}
|
176
|
+
@while_stack.push info
|
177
|
+
info
|
178
|
+
end
|
162
179
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
scope = scope.parent if scope.parent
|
180
|
+
def pop_while
|
181
|
+
@while_stack.pop
|
182
|
+
end
|
167
183
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
break
|
184
|
+
def in_while?
|
185
|
+
!@while_stack.empty?
|
186
|
+
end
|
172
187
|
|
188
|
+
def uses_block!
|
189
|
+
if @type == :iter && @parent
|
190
|
+
@parent.uses_block!
|
173
191
|
else
|
174
|
-
|
192
|
+
@uses_block = true
|
193
|
+
identify!
|
175
194
|
end
|
176
195
|
end
|
177
196
|
|
178
|
-
|
179
|
-
|
197
|
+
def identify!
|
198
|
+
return @identity if @identity
|
180
199
|
|
181
|
-
|
182
|
-
|
183
|
-
identify!
|
184
|
-
@parent.identify_def
|
185
|
-
elsif @type == :def
|
186
|
-
identify!
|
187
|
-
end
|
188
|
-
end
|
200
|
+
@identity = @parser.unique_temp
|
201
|
+
@parent.add_temp @identity
|
189
202
|
|
190
|
-
|
191
|
-
|
192
|
-
end
|
203
|
+
@identity
|
204
|
+
end
|
193
205
|
|
194
|
-
|
195
|
-
|
196
|
-
@parent.catches_break!
|
197
|
-
else
|
198
|
-
@catches_break = true
|
206
|
+
def identity
|
207
|
+
@identity
|
199
208
|
end
|
200
|
-
end
|
201
209
|
|
202
|
-
|
203
|
-
|
204
|
-
end
|
210
|
+
def get_super_chain
|
211
|
+
chain, scope, defn, mid = [], self, 'null', 'null'
|
205
212
|
|
206
|
-
|
207
|
-
|
208
|
-
|
213
|
+
while scope
|
214
|
+
if scope.type == :iter
|
215
|
+
chain << scope.identify!
|
216
|
+
scope = scope.parent if scope.parent
|
217
|
+
|
218
|
+
elsif scope.type == :def
|
219
|
+
defn = scope.identify!
|
220
|
+
mid = "'#{scope.mid}'"
|
221
|
+
break
|
222
|
+
|
223
|
+
else
|
224
|
+
break
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
[chain, defn, mid]
|
229
|
+
end
|
209
230
|
|
210
|
-
|
211
|
-
|
212
|
-
# parent scope will be checked. As a fallback, nil is returned. This is
|
213
|
-
# used by super() to find out what method super() should be sent to.
|
214
|
-
def mid
|
215
|
-
if @type == :def
|
216
|
-
@mid
|
217
|
-
elsif @type == :iter && @parent
|
218
|
-
@parent.mid
|
219
|
-
else
|
220
|
-
nil
|
231
|
+
def uses_block?
|
232
|
+
@uses_block
|
221
233
|
end
|
222
234
|
end
|
223
235
|
end
|
224
|
-
|
225
|
-
end; end
|
236
|
+
end
|
data/lib/opal/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
class DefineMethodByProcScope
|
2
|
+
in_scope = true
|
3
|
+
method_proc = proc { in_scope }
|
4
|
+
|
5
|
+
define_singleton_method(:proc_test, &method_proc)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Kernel#define_singleton_method" do
|
9
|
+
it "defines a new singleton method for objects" do
|
10
|
+
s = Object.new
|
11
|
+
s.define_singleton_method(:test) { "world!" }
|
12
|
+
s.test.should == "world!"
|
13
|
+
lambda {
|
14
|
+
Object.new.test
|
15
|
+
}.should raise_error(NoMethodError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "maintains the Proc's scope" do
|
19
|
+
DefineMethodByProcScope.proc_test.should == true
|
20
|
+
end
|
21
|
+
end
|