Skip to content

Commit 3333c31

Browse files
committed
Support @new @variadic
Signed-off-by: Yuta Sato <cannorin@users.noreply.github.com>
1 parent 4396ad4 commit 3333c31

9 files changed

+98
-18
lines changed

Changes.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
**Compiler**
66

7+
- #5364 support `@new @variadic`
8+
79
**Syntax**
810

911
**Playground**

jscomp/core/lam_compile_external_call.ml

+26-12
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@
2626

2727
module E = Js_exp_make
2828

29-
let splice_fn_apply fn args =
29+
let splice_apply fn args =
3030
E.runtime_call Js_runtime_modules.caml_splice_call "spliceApply"
3131
[ fn; E.array Immutable args ]
3232

33-
let splice_obj_fn_apply obj name args =
33+
let splice_new_apply fn args =
34+
E.runtime_call Js_runtime_modules.caml_splice_call "spliceNewApply"
35+
[ fn; E.array Immutable args ]
36+
37+
let splice_obj_apply obj name args =
3438
E.runtime_call Js_runtime_modules.caml_splice_call "spliceObjApply"
3539
[ obj; E.str name; E.array Immutable args ]
3640

@@ -253,7 +257,7 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
253257
if splice then
254258
let args, eff, dynamic = assemble_args_has_splice arg_types args in
255259
add_eff eff
256-
(if dynamic then splice_fn_apply fn args
260+
(if dynamic then splice_apply fn args
257261
else E.call ~info:{ arity = Full; call_info = Call_na } fn args)
258262
else
259263
let args, eff = assemble_args_no_splice arg_types args in
@@ -265,13 +269,13 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
265269
let args, eff, dynamic = assemble_args_has_splice arg_types args in
266270
(* TODO: fix in rest calling convention *)
267271
add_eff eff
268-
(if dynamic then splice_fn_apply fn args
272+
(if dynamic then splice_apply fn args
269273
else E.call ~info:{ arity = Full; call_info = Call_na } fn args)
270274
else
271275
let args, eff = assemble_args_no_splice arg_types args in
272276
(* TODO: fix in rest calling convention *)
273277
add_eff eff (E.call ~info:{ arity = Full; call_info = Call_na } fn args)
274-
| Js_new { external_module_name = module_name; name = fn; scopes } ->
278+
| Js_new { external_module_name = module_name; name = fn; splice; scopes } ->
275279
(* handle [@@new]*)
276280
(* This has some side effect, it will
277281
mark its identifier (If it has) as an object,
@@ -281,15 +285,25 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
281285
TODO: we should propagate this property
282286
as much as we can(in alias table)
283287
*)
284-
let args, eff = assemble_args_no_splice arg_types args in
285-
let fn = translate_scoped_module_val module_name fn scopes in
286-
add_eff eff
287-
((match cxt.continuation with
288+
let mark () =
289+
match cxt.continuation with
288290
| Declare (_, id) | Assign id ->
289291
(* Format.fprintf Format.err_formatter "%a@."Ident.print id; *)
290292
Ext_ident.make_js_object id
291-
| EffectCall _ | NeedValue _ -> ());
292-
E.new_ fn args)
293+
| EffectCall _ | NeedValue _ -> ()
294+
in
295+
if splice then
296+
let args, eff, dynamic = assemble_args_has_splice arg_types args in
297+
let fn = translate_scoped_module_val module_name fn scopes in
298+
add_eff eff
299+
(mark ();
300+
if dynamic then splice_new_apply fn args
301+
else E.new_ fn args)
302+
else
303+
let args, eff = assemble_args_no_splice arg_types args in
304+
let fn = translate_scoped_module_val module_name fn scopes in
305+
add_eff eff
306+
(mark (); E.new_ fn args)
293307
| Js_send { splice; name; js_send_scopes } -> (
294308
match args with
295309
| self :: args ->
@@ -300,7 +314,7 @@ let translate_ffi (cxt : Lam_compile_context.t) arg_types
300314
let args, eff, dynamic = assemble_args_has_splice arg_types args in
301315
add_eff eff
302316
(let self = translate_scoped_access js_send_scopes self in
303-
if dynamic then splice_obj_fn_apply self name args
317+
if dynamic then splice_obj_apply self name args
304318
else
305319
E.call
306320
~info:{ arity = Full; call_info = Call_na }

jscomp/frontend/ast_external_process.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -800,12 +800,12 @@ let external_desc_of_non_obj (loc : Location.t) (st : external_desc)
800800
val_send = None;
801801
set_name = None;
802802
get_name = None;
803-
splice = false;
803+
splice;
804804
scopes;
805805
mk_obj = _;
806806
return_wrapper = _;
807807
} ->
808-
Js_new { name; external_module_name; scopes }
808+
Js_new { name; external_module_name; splice; scopes }
809809
| { new_name = Some _; _ } ->
810810
Bs_syntaxerr.err loc
811811
(Conflict_ffi_attribute "Attribute found that conflicts with %@new")

jscomp/frontend/external_ffi_types.ml

+4-3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ type external_spec =
7272
| Js_new of {
7373
name : string ;
7474
external_module_name : external_module_name option;
75+
splice : bool ;
7576
scopes : string list;
7677
}
7778
| Js_set of {
@@ -224,7 +225,7 @@ let check_ffi ?loc ffi : bool =
224225
->
225226
upgrade (is_package_relative_path external_module_name.bundle);
226227
check_external_module_name external_module_name
227-
| Js_new {external_module_name ; name; scopes = _}
228+
| Js_new {external_module_name ; name; splice = _; scopes = _}
228229
| Js_call {external_module_name ; name ; splice = _; scopes = _ }
229230
->
230231
Ext_option.iter external_module_name (fun external_module_name ->
@@ -266,10 +267,10 @@ let is_bs_primitive s =
266267

267268
let () = Oprint.map_primitive_name :=
268269

269-
# 272 "frontend/external_ffi_types.pp.ml"
270+
# 273 "frontend/external_ffi_types.pp.ml"
270271
String.escaped
271272

272-
# 275 "frontend/external_ffi_types.pp.ml"
273+
# 276 "frontend/external_ffi_types.pp.ml"
273274
(* TODO: better error message when version mismatch *)
274275
let from_string s : t =
275276
if is_bs_primitive s then

jscomp/frontend/external_ffi_types.mli

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type external_spec =
5858
| Js_new of {
5959
name : string;
6060
external_module_name : external_module_name option;
61+
splice : bool;
6162
scopes : string list;
6263
}
6364
| Js_set of { js_set_name : string; js_set_scopes : string list }

jscomp/frontend/external_ffi_types.pp.ml

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type external_spec =
7171
| Js_new of {
7272
name : string ;
7373
external_module_name : external_module_name option;
74+
splice : bool ;
7475
scopes : string list;
7576
}
7677
| Js_set of {
@@ -223,7 +224,7 @@ let check_ffi ?loc ffi : bool =
223224
->
224225
upgrade (is_package_relative_path external_module_name.bundle);
225226
check_external_module_name external_module_name
226-
| Js_new {external_module_name ; name; scopes = _}
227+
| Js_new {external_module_name ; name; splice = _; scopes = _}
227228
| Js_call {external_module_name ; name ; splice = _; scopes = _ }
228229
->
229230
Ext_option.iter external_module_name (fun external_module_name ->

jscomp/runtime/caml_splice_call.ml

+15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ let spliceApply : obj -> obj -> obj = [%raw{|function(fn,args){
3838
return fn.apply(null,applied)
3939
}|}]
4040

41+
let spliceNewApply : obj -> obj -> obj = [%raw{|function (ctor,args){
42+
var i, argLen;
43+
argLen = args.length
44+
var applied = [null] // Function.prototype.bind.apply(fn, args) requires the first element in `args` to be `null`
45+
for(i = 0; i < argLen - 1; ++i){
46+
applied.push(args[i])
47+
}
48+
var lastOne = args[argLen - 1]
49+
for(i = 0; i < lastOne.length; ++i ){
50+
applied.push(lastOne[i])
51+
}
52+
var C = Function.prototype.bind.apply(ctor, applied)
53+
return new C()
54+
}|}]
55+
4156
let spliceObjApply : obj -> obj -> obj -> obj = [%raw{|function(obj,name,args){
4257
var i, argLen;
4358
argLen = args.length

jscomp/runtime/caml_splice_call.mli

+2
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ type obj = Obj.t
2727

2828
val spliceApply : obj -> obj -> obj
2929

30+
val spliceNewApply : obj -> obj -> obj
31+
3032
val spliceObjApply : obj -> obj -> obj -> obj

jscomp/test/splice_test.ml

+44
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,50 @@ let dynamic arr =
3737
;; dynamic [||]
3838
;; dynamic [|1;1;3|]
3939

40+
(* Array constructor with a single parameter `x`
41+
just makes an array with its length set to `x`,
42+
so at least two parameters are needed
43+
*)
44+
external newArr : int -> int -> int array -> int array = "Array"
45+
[@@bs.splice] [@@bs.new]
46+
47+
let () =
48+
let a = newArr 1 2 [|3;4|] in
49+
eq __LOC__ a [|1;2;3;4|]
50+
51+
let dynamicNew arr =
52+
let a = newArr 1 2 arr in
53+
eq __LOC__ a (Array.concat [[|1; 2|]; arr])
54+
55+
;; dynamicNew [|3;4|]
56+
;; dynamicNew [||]
57+
;; dynamicNew [|1;3|]
58+
59+
[%%raw{|
60+
class Foo {
61+
constructor(...names) {
62+
this.names = names;
63+
}
64+
}
65+
|}]
66+
67+
type foo
68+
69+
external newFoo : string array -> foo = "Foo" [@@bs.splice] [@@bs.new]
70+
external fooNames : foo -> string array = "names" [@@get]
71+
72+
let () =
73+
let f = newFoo [|"a";"b";"c"|] in
74+
eq __LOC__ (fooNames f) [|"a";"b";"c"|]
75+
76+
let dynamicFoo arr =
77+
let f = newFoo arr in
78+
eq __LOC__ (fooNames f) arr
79+
80+
;; dynamicFoo [||]
81+
;; dynamicFoo [|"a"|]
82+
;; dynamicFoo [|"a";"b";"c"|]
83+
4084
module Pipe = struct
4185
external push : int array -> int -> int array -> unit =
4286
"push" [@@send] [@@bs.splice]

0 commit comments

Comments
 (0)