@@ -15,6 +15,10 @@ def compile
15
15
16
16
opt = args [ 1 ..-1 ] . select { |a | a . first == :optarg }
17
17
18
+ @kwargs = args [ 1 ..-1 ] . select do |arg |
19
+ [ :kwarg , :kwoptarg , :kwrestarg ] . include? arg . first
20
+ end
21
+
18
22
argc = args . length - 1
19
23
20
24
# block name (&block)
@@ -35,7 +39,7 @@ def compile
35
39
end
36
40
37
41
if compiler . arity_check?
38
- arity_code = arity_check ( args , opt , uses_splat , block_name , mid )
42
+ arity_code = arity_check ( args , opt , uses_splat , @kwargs , block_name , mid )
39
43
end
40
44
41
45
in_scope do
@@ -64,6 +68,8 @@ def compile
64
68
line "}"
65
69
end
66
70
71
+ compile_keyword_args
72
+
67
73
# must do this after opt args incase opt arg uses yield
68
74
scope_name = scope . identity
69
75
@@ -112,6 +118,39 @@ def compile
112
118
wrap '(' , ", nil) && '#{ mid } '" if expr?
113
119
end
114
120
121
+ def compile_keyword_args
122
+ return if @kwargs . empty?
123
+ helper :hash2
124
+
125
+ line "if ($kwargs == null) {"
126
+ line " $kwargs = $hash2([], {});"
127
+ line "}"
128
+ line "if (!$kwargs.$$is_hash) {"
129
+ line " throw new Error('expecting keyword args');"
130
+ line "}"
131
+
132
+ @kwargs . each do |kwarg |
133
+ case kwarg . first
134
+ when :kwoptarg
135
+ arg_name = kwarg [ 1 ]
136
+ var_name = variable ( arg_name . to_s )
137
+ line "if ((#{ var_name } = $kwargs.smap['#{ arg_name } ']) == null) {"
138
+ line " #{ var_name } = " , expr ( kwarg [ 2 ] )
139
+ line "}"
140
+ when :kwarg
141
+ arg_name = kwarg [ 1 ]
142
+ var_name = variable ( arg_name . to_s )
143
+ line "if ((#{ var_name } = $kwargs.smap['#{ arg_name } ']) == null) {"
144
+ line " throw new Error('expecting keyword arg: #{ arg_name } ')"
145
+ line "}"
146
+ when :kwrestarg
147
+ nil
148
+ else
149
+ raise "unknown kwarg type #{ kwarg . first } "
150
+ end
151
+ end
152
+ end
153
+
115
154
# Simple helper to check whether this method should be defined through
116
155
# `Opal.defn()` runtime helper.
117
156
#
@@ -129,14 +168,18 @@ def uses_defn?(scope)
129
168
end
130
169
131
170
# Returns code used in debug mode to check arity of method call
132
- def arity_check ( args , opt , splat , block_name , mid )
171
+ def arity_check ( args , opt , splat , kwargs , block_name , mid )
133
172
meth = mid . to_s . inspect
134
173
135
174
arity = args . size - 1
136
175
arity -= ( opt . size )
176
+
137
177
arity -= 1 if splat
178
+
179
+ arity -= ( kwargs . size )
180
+
138
181
arity -= 1 if block_name
139
- arity = -arity - 1 if !opt . empty? or splat
182
+ arity = -arity - 1 if !opt . empty? or ! kwargs . empty? or splat
140
183
141
184
# $arity will point to our received arguments count
142
185
aritycode = "var $arity = arguments.length;"
@@ -154,15 +197,26 @@ class ArgsNode < Base
154
197
handle :args
155
198
156
199
def compile
200
+ done_kwargs = false
157
201
children . each_with_index do |child , idx |
158
202
next if :blockarg == child . first
159
203
next if :restarg == child . first and child [ 1 ] . nil?
160
204
161
- child = child [ 1 ] . to_sym
162
- push ', ' unless idx == 0
163
- child = variable ( child )
164
- scope . add_arg child . to_sym
165
- push child . to_s
205
+ case child . first
206
+ when :kwarg , :kwoptarg , :kwrestarg
207
+ unless done_kwargs
208
+ done_kwargs = true
209
+ push ', ' unless idx == 0
210
+ scope . add_arg '$kwargs'
211
+ push '$kwargs'
212
+ end
213
+ else
214
+ child = child [ 1 ] . to_sym
215
+ push ', ' unless idx == 0
216
+ child = variable ( child )
217
+ scope . add_arg child . to_sym
218
+ push child . to_s
219
+ end
166
220
end
167
221
end
168
222
end
0 commit comments