Skip to content

Commit d4d4d23

Browse files
committed
[js] Turn native assign_* ops on lexicalrefs into binds
1 parent 933e0cb commit d4d4d23

File tree

2 files changed

+54
-14
lines changed

2 files changed

+54
-14
lines changed

src/vm/js/Compiler.nqp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,24 @@ class QAST::CompilerJS does DWIMYNameMangling does SerializeOnce {
272272

273273
}
274274
}
275+
276+
method try_get_bind_scope($var) {
277+
if nqp::istype($var, QAST::Var) && $var.scope eq 'lexicalref' {
278+
# Make sure we've got the lexical itself in scope to bind to.
279+
my $info := self;
280+
while $info {
281+
return NQPMu if $info.qast && $info.qast.ann('DYN_COMP_WRAPPER');
282+
if nqp::defined($info.lexicalref_type($var)) {
283+
return NQPMu;
284+
}
285+
if $info.has_own_variable($var.name) {
286+
return 'lexical';
287+
}
288+
$info := $info.outer;
289+
}
290+
}
291+
return NQPMu;
292+
}
275293
}
276294

277295
method is_dynamic_var(BlockInfo $info, QAST::Var $var) {

src/vm/js/Operations.nqp

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -160,32 +160,54 @@ class QAST::OperationsJS {
160160

161161
add_simple_op('setcontspec', $T_OBJ, [$T_OBJ, $T_STR, $T_OBJ], :side_effects, :decont(0));
162162

163+
sub do_assign($comp, $node, $op_name, $value_kind) {
164+
my $cont := $comp.as_js($node[0], :want($T_OBJ));
165+
my $value := $comp.as_js($node[1], :want($value_kind));
166+
167+
my $deconted := $value_kind == $T_OBJ ?? $comp.await("{$value.expr}.\$\$decont($*CTX)") !! $value.expr;
168+
Chunk.new($T_OBJ, $cont.expr, [
169+
$cont,
170+
$value,
171+
$comp.await ~ $cont.expr ~ '.$$' ~ $op_name ~ '(' ~ $*CTX ~ ', ' ~ $deconted ~ ");\n"
172+
]);
173+
}
174+
163175
sub add_assign_op($op_name, $value_kind) {
164176
# TODO If possible lower it to a bind instead just like on the moarvm backend
165177
# POTENTIAL OPTIMALIZATION
166178

167179
add_op($op_name, sub ($comp, $node, :$want) {
168-
my $cont := $comp.as_js($node[0], :want($T_OBJ));
169-
my $value := $comp.as_js($node[1], :want($value_kind));
170-
171-
my $deconted := $value_kind == $T_OBJ ?? $comp.await("{$value.expr}.\$\$decont($*CTX)") !! $value.expr;
172-
Chunk.new($T_OBJ, $cont.expr, [
173-
$cont,
174-
$value,
175-
$comp.await ~ $cont.expr ~ '.$$' ~ $op_name ~ '(' ~ $*CTX ~ ', ' ~ $deconted ~ ");\n"
176-
]);
180+
do_assign($comp, $node, $op_name, $value_kind);
181+
});
182+
}
183+
184+
sub add_native_assign_op($op_name, $value_kind) {
185+
add_op($op_name, sub ($comp, $node, :$want) {
186+
unless +$node.list == 2 {
187+
nqp::die("The '$op_name' op needs 2 arguments, got " ~ +$node.list);
188+
}
189+
if $*BLOCK.try_get_bind_scope($node[0]) -> $bind_scope {
190+
# Can lower it to a bind instead.
191+
my $target := nqp::clone($node[0]);
192+
$target.scope($bind_scope);
193+
$comp.as_js(QAST::Op.new(:op('bind'), $target, $node[1]), :$want);
194+
}
195+
else {
196+
do_assign($comp, $node, $op_name, $value_kind);
197+
}
177198
});
178199
}
179200

180201
add_assign_op('assignunchecked', $T_OBJ);
181202
add_assign_op('assign', $T_OBJ);
182203

183-
add_assign_op('assign_i', $T_INT);
184-
add_assign_op('assign_n', $T_NUM);
185-
add_assign_op('assign_s', $T_STR);
204+
add_native_assign_op('assign_i', $T_INT);
205+
add_native_assign_op('assign_n', $T_NUM);
206+
add_native_assign_op('assign_s', $T_STR);
207+
208+
add_native_assign_op('assign_i64', $T_INT64);
209+
add_native_assign_op('assign_u64', $T_UINT64);
186210

187-
add_assign_op('assign_i64', $T_INT64);
188-
add_assign_op('assign_u64', $T_UINT64);
189211

190212
add_simple_op('decont', $T_OBJ, [$T_OBJ], :method_call, :ctx, :await);
191213

0 commit comments

Comments
 (0)