Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 83 additions & 70 deletions compiler/lib/driver.ml
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,6 @@ let link' ~export_runtime ~standalone ~link (js : Javascript.statement_list) :
let check_missing = standalone in
let t = Timer.make () in
if times () then Format.eprintf "Start Linking...@.";
let traverse = new Js_traverse.free in
let js = traverse#program js in
let js =
if mark_start_of_generated_code ()
then
Expand All @@ -311,23 +309,25 @@ let link' ~export_runtime ~standalone ~link (js : Javascript.statement_list) :
| `All_from from -> Linker.list_all ~from ()
| `No -> StringSet.empty
| `Needed ->
let free = traverse#get_free in
let free : StringSet.t =
Javascript.IdentSet.fold
(fun x acc ->
match x with
| V _ ->
(* This is an error. We don't complain here as we want
to be able to name other variable to make it
easier to spot the problematic ones *)
acc
| S { name = Utf8 x; _ } -> StringSet.add x acc)
free
StringSet.empty
in
let prim = Primitive.get_external () in
let all_external = StringSet.union prim all_provided in
StringSet.inter free all_external
(* We should ideally use free variables here, but it's much
faster to use an over-approximation looking at all variables. *)
let used = ref StringSet.empty in
let o =
object
inherit Js_traverse.iter

method ident =
function
| V _ -> ()
| S { name = Utf8 name; _ } ->
if StringSet.mem name all_external
then used := StringSet.add name !used
end
in
o#program js;
!used
in
let linkinfos =
let from =
Expand Down Expand Up @@ -493,6 +493,40 @@ let output formatter ~source_map () js =
if times () then Format.eprintf " write: %a@." Timer.print t;
sm

let globalThis_shim =
lazy
(let s =
{|
(function (Object) {
typeof globalThis !== 'object' && (
this ?
get() :
(Object.defineProperty(Object.prototype, '_T_', {
configurable: true,
get: get
}), _T_)
);
function get() {
var global = this || self;
global.globalThis = global;
delete Object.prototype._T_;
}
}(Object));
|}
in
let lex = Parse_js.Lexer.of_string s in
Parse_js.parse lex)

let export_node =
lazy
(let s =
Printf.sprintf
{|((typeof module === 'object' && module.exports) || %s)|}
Global_constant.global_object
in
let lex = Parse_js.Lexer.of_string s in
Parse_js.parse_expr lex)

let pack ~wrap_with_fun ~standalone { Linker.runtime_code = js; always_required_codes } =
let module J = Javascript in
let t = Timer.make () in
Expand Down Expand Up @@ -520,51 +554,54 @@ let pack ~wrap_with_fun ~standalone { Linker.runtime_code = js; always_required_
let wrap_in_iife ~use_strict js =
let var ident e = J.variable_declaration [ J.ident ident, (e, J.N) ], J.N in
let expr e = J.Expression_statement e, J.N in
let freenames =
let o = new Js_traverse.free in
let (_ : J.program) = o#program js in
o#get_free
let used =
(* We should ideally use free variables here, but it's much
faster to use an over-approximation looking at all variables. *)
let used = ref StringSet.empty in
let o =
object
inherit Js_traverse.iter

method ident =
function
| V _ -> ()
| S { name = Utf8 name; _ } ->
if
String.equal name Global_constant.exports
|| String.equal name Global_constant.old_global_object
then used := StringSet.add name !used
end
in
o#program js;
!used
in
let export_shim js =
if J.IdentSet.mem (J.ident Global_constant.exports_) freenames
if StringSet.mem Global_constant.exports used
then
if should_export wrap_with_fun
then var Global_constant.exports_ (J.EObj []) :: js
else
let export_node =
let s =
Printf.sprintf
{|((typeof module === 'object' && module.exports) || %s)|}
Global_constant.global_object
in
let lex = Parse_js.Lexer.of_string s in
Parse_js.parse_expr lex
in
var Global_constant.exports_ export_node :: js
else var Global_constant.exports_ (Lazy.force export_node) :: js
else js
in
let old_global_object_shim js =
if J.IdentSet.mem (J.ident Global_constant.old_global_object_) freenames
if StringSet.mem Global_constant.old_global_object used
then
var
Global_constant.old_global_object_
(J.EVar (J.ident Global_constant.global_object_))
:: js
else js
in

let js = export_shim js in
let js = old_global_object_shim js in
let js =
if use_strict
then expr (J.EStr (Utf8_string.of_string_exn "use strict")) :: js
else js
in
let efun args body = J.EFun (None, J.fun_ args body J.U) in
let sfun name args body = J.Function_declaration (name, J.fun_ args body J.U), J.U in
let mk f =
let js = export_shim js in
let js = old_global_object_shim js in
let js =
if use_strict
then expr (J.EStr (Utf8_string.of_string_exn "use strict")) :: js
else js
in
f [ J.ident Global_constant.global_object_ ] js
in
let mk f = f [ J.ident Global_constant.global_object_ ] js in
match wrap_with_fun with
| `Anonymous -> expr (mk efun)
| `Named name ->
Expand Down Expand Up @@ -607,31 +644,7 @@ if (typeof module === 'object' && module.exports) {
js @ export_node
| `Anonymous, _ -> js
| `Iife, false -> js
| `Iife, true ->
let e =
let s =
{|
(function (Object) {
typeof globalThis !== 'object' && (
this ?
get() :
(Object.defineProperty(Object.prototype, '_T_', {
configurable: true,
get: get
}), _T_)
);
function get() {
var global = this || self;
global.globalThis = global;
delete Object.prototype._T_;
}
}(Object));
|}
in
let lex = Parse_js.Lexer.of_string s in
Parse_js.parse lex
in
e @ js
| `Iife, true -> Lazy.force globalThis_shim @ js
in
if times () then Format.eprintf " packing: %a@." Timer.print t;
js
Expand Down
58 changes: 28 additions & 30 deletions compiler/lib/linker.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,44 +88,42 @@ end = struct
end

module Check = struct
class check_and_warn name pi =
class check_and_warn ~do_warn name pi =
object
inherit Js_traverse.free as super

method merge_info from =
let def = from#get_def in
let use = from#get_use in
let diff = Javascript.IdentSet.diff def use in
let diff =
Javascript.IdentSet.fold
(fun x acc ->
match x with
| S { name = Utf8_string.Utf8 s; _ } ->
if String.starts_with s ~prefix:"_" || String.equal s name
then acc
else s :: acc
| V _ -> acc)
diff
[]
in

(match diff with
| [] -> ()
| l ->
warn
"WARN unused for primitive %s at %s:@. %s@."
name
(loc pi)
(String.concat ~sep:", " l));
(if do_warn
then
let def = from#get_def in
let use = from#get_use in
let diff = Javascript.IdentSet.diff def use in
let diff =
Javascript.IdentSet.fold
(fun x acc ->
match x with
| S { name = Utf8_string.Utf8 s; _ } ->
if String.starts_with s ~prefix:"_" || String.equal s name
then acc
else s :: acc
| V _ -> acc)
diff
[]
in

match diff with
| [] -> ()
| l ->
warn
"WARN unused for primitive %s at %s:@. %s@."
name
(loc pi)
(String.concat ~sep:", " l));
super#merge_info from
end

let primitive ~name pi ~code ~requires ~has_flags =
let free =
if Config.Flag.warn_unused ()
then new check_and_warn name pi
else new Js_traverse.free
in
let free = new check_and_warn ~do_warn:(Config.Flag.warn_unused ()) name pi in
let _code = free#program code in
let freename = to_stringset free#get_free in
let freename =
Expand Down
Loading