@@ -859,6 +859,19 @@ function compile_call(ctx::JSCompilationContext, expr::Expr)
859859 return " "
860860 end
861861
862+ # Type conversions: Int32(x) → (x)|0, Float64(x) → +(x), etc.
863+ if resolved_fn isa Type && length (call_args) == 1
864+ if resolved_fn === Int32 || resolved_fn === Int64 || resolved_fn === UInt32
865+ return " ($(call_args[1 ]) )|0"
866+ elseif resolved_fn === Float64 || resolved_fn === Float32
867+ return " +($(call_args[1 ]) )"
868+ elseif resolved_fn === Bool
869+ return " !!($(call_args[1 ]) )"
870+ elseif resolved_fn === String
871+ return " String($(call_args[1 ]) )"
872+ end
873+ end
874+
862875 # Check package registry for positional calls (functions AND type constructors)
863876 if resolved_fn isa Function || resolved_fn isa Type
864877 fn_mod = parentmodule (resolved_fn)
@@ -1018,6 +1031,26 @@ function compile_call(ctx::JSCompilationContext, expr::Expr)
10181031 return " ($(call_args[1 ]) / $(call_args[2 ]) )"
10191032 end
10201033
1034+ # Comparison operators (unoptimized IR keeps these as calls)
1035+ if resolved_fn === Base.:(== ) && length (call_args) == 2
1036+ return " $(call_args[1 ]) === $(call_args[2 ]) "
1037+ end
1038+ if resolved_fn === Base.:(!= ) && length (call_args) == 2
1039+ return " $(call_args[1 ]) !== $(call_args[2 ]) "
1040+ end
1041+ if resolved_fn === Base.:(< ) && length (call_args) == 2
1042+ return " $(call_args[1 ]) < $(call_args[2 ]) "
1043+ end
1044+ if resolved_fn === Base.:(<= ) && length (call_args) == 2
1045+ return " $(call_args[1 ]) <= $(call_args[2 ]) "
1046+ end
1047+ if resolved_fn === Base.:(> ) && length (call_args) == 2
1048+ return " $(call_args[1 ]) > $(call_args[2 ]) "
1049+ end
1050+ if resolved_fn === Base.:(>= ) && length (call_args) == 2
1051+ return " $(call_args[1 ]) >= $(call_args[2 ]) "
1052+ end
1053+
10211054 # js() escape hatch
10221055 if fn_name == " js"
10231056 template_str = nothing
@@ -1203,13 +1236,20 @@ function compile_call(ctx::JSCompilationContext, expr::Expr)
12031236 arr_js = pos_args[1 ]
12041237 by_js = get (kwargs, :by , nothing )
12051238 rev = get (kwargs, :rev , nothing )
1206- is_rev = rev != = nothing && rev != " false"
1239+ # Determine if rev is a static literal or a dynamic expression
1240+ is_static_true = rev == " true"
1241+ is_static_false = rev === nothing || rev == " false"
12071242 if by_js != = nothing
1208- cmp = is_rev ?
1209- " (function(a,b){var _a=$(by_js) (a),_b=$(by_js) (b);return _a<_b?1:_a>_b?-1:0})" :
1210- " (function(a,b){var _a=$(by_js) (a),_b=$(by_js) (b);return _a<_b?-1:_a>_b?1:0})"
1243+ if is_static_true
1244+ cmp = " (function(a,b){var _a=$(by_js) (a),_b=$(by_js) (b);return _a<_b?1:_a>_b?-1:0})"
1245+ elseif is_static_false
1246+ cmp = " (function(a,b){var _a=$(by_js) (a),_b=$(by_js) (b);return _a<_b?-1:_a>_b?1:0})"
1247+ else
1248+ # Dynamic rev: emit comparator that checks rev at runtime
1249+ cmp = " (function(a,b){var _a=$(by_js) (a),_b=$(by_js) (b),_d=_a<_b?-1:_a>_b?1:0;return $(rev) ?-_d:_d})"
1250+ end
12111251 return " $(arr_js) .slice().sort($(cmp) )"
1212- elseif is_rev
1252+ elseif is_static_true || ( ! is_static_false && rev != = nothing )
12131253 return " $(arr_js) .slice().sort().reverse()"
12141254 else
12151255 return " $(arr_js) .slice().sort()"
@@ -1409,9 +1449,23 @@ function compile_call(ctx::JSCompilationContext, expr::Expr)
14091449 try getfield (type_arg. mod, type_arg. name) catch ; nothing end
14101450 elseif type_arg isa Type || type_arg isa DataType
14111451 type_arg
1452+ elseif type_arg isa Core. SSAValue
1453+ # In optimize=false IR, types may be SSAValues (e.g. apply_type results)
1454+ stype = try ctx. code_info. ssavaluetypes[type_arg. id] catch ; nothing end
1455+ if stype isa Core. Const
1456+ stype. val
1457+ elseif stype isa DataType
1458+ stype
1459+ else
1460+ nothing
1461+ end
14121462 else
14131463 nothing
14141464 end
1465+ # Unresolvable type: Julia already type-checks at compile time, so isa is always true
1466+ if T === nothing
1467+ return " true"
1468+ end
14151469 if T === Nothing
14161470 return " $(x_val) === null"
14171471 elseif T === Int32 || T === Int64 || T === UInt32 || T === UInt64 || T === Float64 || T === Float32
@@ -1597,6 +1651,47 @@ function compile_call(ctx::JSCompilationContext, expr::Expr)
15971651 end
15981652 end
15991653
1654+ # Handle Base arithmetic and comparison operators (unoptimized IR)
1655+ if callee isa GlobalRef && callee. mod === Base && length (args) >= 3
1656+ op = callee. name
1657+ a_js = compile_value (ctx, args[2 ])
1658+ b_js = compile_value (ctx, args[3 ])
1659+ if op === :(== )
1660+ return " $(a_js) === $(b_js) "
1661+ elseif op === :(!= )
1662+ return " $(a_js) !== $(b_js) "
1663+ elseif op === :(< )
1664+ return " $(a_js) < $(b_js) "
1665+ elseif op === :(<= )
1666+ return " $(a_js) <= $(b_js) "
1667+ elseif op === :(> )
1668+ return " $(a_js) > $(b_js) "
1669+ elseif op === :(>= )
1670+ return " $(a_js) >= $(b_js) "
1671+ elseif op === :(+ )
1672+ return " ($(a_js) + $(b_js) )"
1673+ elseif op === :(- )
1674+ return " ($(a_js) - $(b_js) )"
1675+ elseif op === :(* )
1676+ return " ($(a_js) * $(b_js) )"
1677+ elseif op === :(/ )
1678+ return " ($(a_js) / $(b_js) )"
1679+ elseif op === :(% ) || op === :rem
1680+ return " ($(a_js) % $(b_js) )"
1681+ end
1682+ end
1683+
1684+ # Handle unary negation: Base.:(-)(x) → -(x)
1685+ if callee isa GlobalRef && callee. mod === Base && callee. name === :(- ) && length (args) == 2
1686+ a_js = compile_value (ctx, args[2 ])
1687+ return " (-($(a_js) ))"
1688+ end
1689+
1690+ # Suppress _typeof_captured_variable — IR artifact, no-op in JS
1691+ if callee isa GlobalRef && callee. name === :_typeof_captured_variable
1692+ return " undefined"
1693+ end
1694+
16001695 # Generic call — will be expanded later
16011696 callee_name = compile_value (ctx, callee)
16021697 call_args = [compile_value (ctx, a) for a in args[2 : end ]]
@@ -2001,11 +2096,22 @@ function compile_new_expr(ctx::JSCompilationContext, expr::Expr)
20012096 T_ref = expr. args[1 ]
20022097 field_args = expr. args[2 : end ]
20032098
2004- # Resolve type: could be a DataType, GlobalRef, or Argument
2099+ # Resolve type: could be a DataType, GlobalRef, SSAValue, or Argument
20052100 T = if T_ref isa DataType
20062101 T_ref
20072102 elseif T_ref isa GlobalRef
20082103 try getfield (T_ref. mod, T_ref. name) catch ; nothing end
2104+ elseif T_ref isa Core. SSAValue
2105+ # In optimize=false IR, closure types may be SSAValues
2106+ # e.g. %5 = Main.:(var"#fn##0#fn##1")::Core.Const(ClosureType)
2107+ ssa_type = try ctx. code_info. ssavaluetypes[T_ref. id] catch ; nothing end
2108+ if ssa_type isa Core. Const
2109+ ssa_type. val
2110+ elseif ssa_type isa DataType
2111+ ssa_type
2112+ else
2113+ nothing
2114+ end
20092115 elseif T_ref isa Core. Argument
20102116 # In constructors, Argument(1) is the type — get from arg_types context
20112117 nothing # Can't resolve at compile time from within the constructor
@@ -2059,8 +2165,9 @@ function compile_closure_creation(ctx::JSCompilationContext, T::DataType, captur
20592165 # Get the non-self parameter types from the method signature
20602166 param_types = m. sig. parameters[2 : end ]
20612167
2062- # Get typed IR (use code_typed_by_type since code_typed(Method, ...) may return empty)
2063- ci, rt = Base. code_typed_by_type (Tuple{T, param_types... }; optimize= true )[1 ]
2168+ # Get typed IR — use optimize=false to preserve high-level operations
2169+ # (e.g. lowercase → .toLowerCase() instead of broken .map(lowercase))
2170+ ci, rt = Base. code_typed_by_type (Tuple{T, param_types... }; optimize= false )[1 ]
20642171
20652172 # Build argument names for the closure (skip #self# at slot 1)
20662173 nargs = length (param_types)
0 commit comments