Skip to content

Commit

Permalink
[flow] Support globalThis
Browse files Browse the repository at this point in the history
Summary:
In this diff, we add support for [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis). According to the spec, it refers to the global scope, which means that `globalThis.foo` means access of the `foo` global.

In Flow, we can model it as a globally available namespace that contains all the other globals, plus the self referential `globalThis`. We only need to do it in type sig, since global libdef types are generated with type sig. In the validation of libdefs in checking, it will read the globalThis in the global libdef.

Since there is no obvious location for this, I give it `Loc.none`. The tests added ensure that no IDE services will break.

Close #7536
Close #7704

Changelog: [feature] Flow now supports [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis).

Reviewed By: panagosg7

Differential Revision: D59083293

fbshipit-source-id: d1bf7ce965549df5801c6fa063ae20b32e6472bb
  • Loading branch information
SamChou19815 authored and facebook-github-bot committed Jun 27, 2024
1 parent dce378a commit 0fe573e
Show file tree
Hide file tree
Showing 16 changed files with 1,011 additions and 55 deletions.
6 changes: 3 additions & 3 deletions src/parser_utils/exports/__tests__/exports_tests.ml
Original file line number Diff line number Diff line change
Expand Up @@ -744,23 +744,23 @@ let%expect_test "lib_class" =
declare class Foo {}
|};
[%expect {|
[(Named "Foo"); (NamedType "Foo")]
[(Named "globalThis"); (Named "Foo"); (NamedType "Foo")]
|}]

let%expect_test "lib_type" =
print_builtins {|
declare type T = string;
|};
[%expect {|
[(NamedType "T")]
[(NamedType "T"); (Named "globalThis")]
|}]

let%expect_test "lib_value" =
print_builtins {|
declare var foo : string;
|};
[%expect {|
[(Named "foo")]
[(Named "globalThis"); (Named "foo")]
|}]

let%expect_test "react_dollar_ac" =
Expand Down
89 changes: 89 additions & 0 deletions src/parser_utils/type_sig/__tests__/type_sig_tests.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5493,7 +5493,14 @@ let%expect_test "builtins" =
0. Variable {id_loc = [1:12-13];
name = "x"; def = (TyRef (Unqualified LocalRef {ref_loc = [1:15-16]; index = 1}))}
1. TypeAlias {id_loc = [2:5-6]; name = "T"; tparams = Mono; body = (Annot (String [2:9-15]))}
2. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values =
{ "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 2}));
"x" -> ([1:12-13], (Ref LocalRef {ref_loc = [1:12-13]; index = 0})) };
types = { "T" -> ([2:5-6], (Ref LocalRef {ref_loc = [2:5-6]; index = 1})) }}

Builtin global value globalThis
Builtin global value x
Builtin global type T |}]

Expand Down Expand Up @@ -5537,8 +5544,18 @@ let%expect_test "builtins_ignore_name_def_for_use_special_cased_names" =
3. TypeAlias {id_loc = [4:5-14];
name = "$ReadOnly"; tparams = Mono;
body = (Annot (Number [4:17-23]))}
4. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values =
{ "Array" -> ([3:14-19], (Ref LocalRef {ref_loc = [3:14-19]; index = 2}));
"globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 4})) };
types =
{ "$ReadOnly" -> ([4:5-14], (Ref LocalRef {ref_loc = [4:5-14]; index = 3}));
"T1" -> ([1:5-7], (Ref LocalRef {ref_loc = [1:5-7]; index = 0}));
"T2" -> ([2:5-7], (Ref LocalRef {ref_loc = [2:5-7]; index = 1})) }}

Builtin global value Array
Builtin global value globalThis
Builtin global type $ReadOnly
Builtin global type T1
Builtin global type T2 |}]
Expand All @@ -5553,7 +5570,12 @@ let%expect_test "builtin_cjs_module" =
[%expect {|
Local defs:
0. TypeAlias {id_loc = [1:5-6]; name = "T"; tparams = Mono; body = (Annot (String [1:9-15]))}
1. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 1})) };
types = { "T" -> ([1:5-6], (Ref LocalRef {ref_loc = [1:5-6]; index = 0})) }}

Builtin global value globalThis
Builtin global type T
Builtin module foo:
[2:15-18] CJSModule {type_exports = [||];
Expand All @@ -5576,7 +5598,12 @@ let%expect_test "builtin_cjs_ignore_later" =
[%expect {|
Local defs:
0. TypeAlias {id_loc = [1:5-6]; name = "T"; tparams = Mono; body = (Annot (String [1:9-15]))}
1. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 1})) };
types = { "T" -> ([1:5-6], (Ref LocalRef {ref_loc = [1:5-6]; index = 0})) }}

Builtin global value globalThis
Builtin global type T
Builtin module foo:
[2:15-18] CJSModule {type_exports = [||];
Expand All @@ -5603,7 +5630,12 @@ let%expect_test "builtin_cjs_module_auto_export_type" =
1. TypeAlias {id_loc = [3:14-16];
name = "T2"; tparams = Mono;
body = (Annot (String [3:19-25]))}
2. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 2})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-18] CJSModule {type_exports = [|(ExportTypeBinding 0); (ExportTypeBinding 1)|];
exports = (Some (Annot (String [4:26-32])));
Expand All @@ -5623,7 +5655,12 @@ let%expect_test "builtin_cjs_module_unused_type_exported" =
[%expect {|
Local defs:
0. TypeAlias {id_loc = [2:22-23]; name = "T"; tparams = Mono; body = (Annot (Number [2:26-32]))}
1. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 1})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-18] CJSModule {type_exports = [|(ExportTypeBinding 0)|];
exports = (Some (Annot (String [3:26-32])));
Expand All @@ -5644,7 +5681,12 @@ let%expect_test "builtin_cjs_module_used_type" =
[%expect {|
Local defs:
0. TypeAlias {id_loc = [2:15-16]; name = "T"; tparams = Mono; body = (Annot (Number [2:19-25]))}
1. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 1})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-18] CJSModule {type_exports = [|(ExportTypeBinding 0)|];
exports = (Some (TyRef (Unqualified LocalRef {ref_loc = [3:26-27]; index = 0})));
Expand All @@ -5664,7 +5706,12 @@ let%expect_test "builtin_cjs_module_used_type_exported" =
[%expect {|
Local defs:
0. TypeAlias {id_loc = [2:22-23]; name = "T"; tparams = Mono; body = (Annot (Number [2:26-32]))}
1. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 1})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-18] CJSModule {type_exports = [|(ExportTypeBinding 0)|];
exports = (Some (TyRef (Unqualified LocalRef {ref_loc = [3:26-27]; index = 0})));
Expand Down Expand Up @@ -5745,7 +5792,12 @@ let%expect_test "builtin_cjs_module_with_implicit_exports" =
8. TypeAlias {id_loc = [12:22-23];
name = "U"; tparams = Mono;
body = (Annot (String [12:26-32]))}
9. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 9})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-18] CJSModule {type_exports = [|(ExportTypeBinding 7); (ExportTypeBinding 8)|];
exports =
Expand Down Expand Up @@ -5794,6 +5846,13 @@ let%expect_test "builtin_es_module_default" =
}
|}];
[%expect {|
Local defs:
0. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 0})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-18] ESModule {type_exports = [||];
exports =
Expand Down Expand Up @@ -5822,10 +5881,15 @@ let%expect_test "builtin_module_import_typeof" =
0. Variable {id_loc = [2:21-22]; name = "x"; def = (Annot (String [2:24-30]))}
1. Variable {id_loc = [6:21-22];
name = "y"; def = (TyRef (Unqualified RemoteRef {ref_loc = [6:24-25]; index = 0}))}
2. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 2})) };
types = {}}

Remote refs:
0. ImportTypeof {id_loc = [5:17-18]; name = "x"; index = 0; remote = "x"}

Builtin global value globalThis
Builtin module bar:
[4:15-18] ESModule {type_exports = [||];
exports = [|(ExportBinding 1)|];
Expand Down Expand Up @@ -5861,7 +5925,12 @@ let%expect_test "builtin_toplevel_import" =
1. Variable {id_loc = [6:21-22];
name = "y";
def = (TyRef (Unqualified BuiltinRef {ref_loc = [6:24-25]; type_ref = true; name = "x"}))}
2. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 2})) };
types = {}}

Builtin global value globalThis
Builtin module bar:
[5:15-18] ESModule {type_exports = [||];
exports = [|(ExportBinding 1)|];
Expand Down Expand Up @@ -5891,7 +5960,12 @@ let%expect_test "builtin_module_export_specifiers" =
Local defs:
0. Variable {id_loc = [2:14-15]; name = "x"; def = (Annot (String [2:18-24]))}
1. Variable {id_loc = [3:14-15]; name = "y"; def = (Annot (String [3:18-24]))}
2. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values = { "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 2})) };
types = {}}

Builtin global value globalThis
Builtin module foo:
[1:15-20] ESModule {type_exports = [||];
exports =
Expand Down Expand Up @@ -5964,7 +6038,14 @@ let%expect_test "builtin_declare_namespace" =
types =
{ "Baz" -> ([7:15-18], (Ref LocalRef {ref_loc = [7:15-18]; index = 4}));
"Boz" -> ([8:14-17], (Ref LocalRef {ref_loc = [8:14-17]; index = 5})) }}
8. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values =
{ "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 8}));
"ns" -> ([1:18-20], (Ref LocalRef {ref_loc = [1:18-20]; index = 7})) };
types = {}}

Builtin global value globalThis
Builtin global value ns |}]

let%expect_test "builtin_pattern" =
Expand All @@ -5984,6 +6065,13 @@ let%expect_test "builtin_pattern" =
(ObjValueField ([1:12-13], (
Value (NumberLit ([1:15-16], 0., "0"))), Polarity.Neutral)) }})}
1. Variable {id_loc = [2:7-8]; name = "p"; def = (Pattern 1)}
2. NamespaceBinding {id_loc = [0:0];
name = "globalThis";
values =
{ "globalThis" -> ([0:0], (Ref LocalRef {ref_loc = [0:0]; index = 2}));
"o" -> ([1:6-7], (Ref LocalRef {ref_loc = [1:6-7]; index = 0}));
"p" -> ([2:7-8], (Ref LocalRef {ref_loc = [2:7-8]; index = 1})) };
types = {}}

Pattern defs:
0. (Ref LocalRef {ref_loc = [2:12-13]; index = 0})
Expand All @@ -5992,6 +6080,7 @@ let%expect_test "builtin_pattern" =
0. (PDef 0)
1. PropP {id_loc = [2:7-8]; name = "p"; def = 0}

Builtin global value globalThis
Builtin global value o
Builtin global value p |}]

Expand Down
127 changes: 76 additions & 51 deletions src/parser_utils/type_sig/type_sig_parse.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1018,68 +1018,93 @@ module Scope = struct
| ConditionalTypeExtends _ ->
failwith "expected DeclareModule to still be the scope"

let namespace_binding_of_values_and_types scope (values, types) =
let values =
SMap.fold
(fun name binding acc ->
match binding with
| LocalBinding node ->
let local_binding = Local_defs.value node in
(match local_binding with
| VarBinding { id_loc; _ }
| LetConstBinding { id_loc; _ }
| ConstRefBinding { id_loc; _ }
| ConstFunBinding { id_loc; _ }
| ClassBinding { id_loc; _ }
| DeclareClassBinding { id_loc; _ }
| FunBinding { id_loc; _ }
| ComponentBinding { id_loc; _ }
| EnumBinding { id_loc; _ }
| NamespaceBinding { id_loc; _ } ->
SMap.add name (id_loc, val_ref ~type_only:false scope id_loc name) acc
| DeclareFunBinding { defs_rev; _ } ->
let (id_loc, _, _) = Nel.last defs_rev in
SMap.add name (id_loc, val_ref ~type_only:false scope id_loc name) acc
| TypeBinding _ -> acc)
| RemoteBinding _ -> acc)
values
SMap.empty
in
let types =
SMap.fold
(fun name binding acc ->
match binding with
| LocalBinding node ->
let local_binding = Local_defs.value node in
(match local_binding with
| VarBinding _
| LetConstBinding _
| ConstRefBinding _
| ConstFunBinding _
| ClassBinding _
| DeclareClassBinding _
| FunBinding _
| ComponentBinding _
| EnumBinding _
| NamespaceBinding _
| DeclareFunBinding _ ->
acc
| TypeBinding { id_loc; _ } ->
SMap.add name (id_loc, val_ref ~type_only:true scope id_loc name) acc)
| RemoteBinding _ -> acc)
types
SMap.empty
in
(values, types)

(* a `declare namespace` exports every binding. *)
let finalize_declare_namespace_exn ~is_type_only scope tbls id_loc name =
match scope with
| DeclareNamespace { values; types; parent; _ } ->
let values =
SMap.fold
(fun name binding acc ->
match binding with
| LocalBinding node ->
let local_binding = Local_defs.value node in
(match local_binding with
| VarBinding { id_loc; _ }
| LetConstBinding { id_loc; _ }
| ConstRefBinding { id_loc; _ }
| ConstFunBinding { id_loc; _ }
| ClassBinding { id_loc; _ }
| DeclareClassBinding { id_loc; _ }
| FunBinding { id_loc; _ }
| ComponentBinding { id_loc; _ }
| EnumBinding { id_loc; _ }
| NamespaceBinding { id_loc; _ } ->
SMap.add name (id_loc, val_ref ~type_only:false scope id_loc name) acc
| DeclareFunBinding { defs_rev; _ } ->
let (id_loc, _, _) = Nel.last defs_rev in
SMap.add name (id_loc, val_ref ~type_only:false scope id_loc name) acc
| TypeBinding _ -> acc)
| RemoteBinding _ -> acc)
values
SMap.empty
in
let types =
SMap.fold
(fun name binding acc ->
match binding with
| LocalBinding node ->
let local_binding = Local_defs.value node in
(match local_binding with
| VarBinding _
| LetConstBinding _
| ConstRefBinding _
| ConstFunBinding _
| ClassBinding _
| DeclareClassBinding _
| FunBinding _
| ComponentBinding _
| EnumBinding _
| NamespaceBinding _
| DeclareFunBinding _ ->
acc
| TypeBinding { id_loc; _ } ->
SMap.add name (id_loc, val_ref ~type_only:true scope id_loc name) acc)
| RemoteBinding _ -> acc)
types
SMap.empty
in
let (values, types) = namespace_binding_of_values_and_types scope (values, types) in
bind_local
~type_only:is_type_only
parent
tbls
name
(NamespaceBinding { id_loc; name; values; types })
| _ -> failwith "The scope must be lexical"

let bind_globalThis scope tbls ~global_this_loc =
match scope with
| Global global_scope ->
let name = "globalThis" in
let (values, types) =
namespace_binding_of_values_and_types scope (global_scope.values, global_scope.types)
in
let loc = push_loc tbls global_this_loc in
let values =
(* Patch in globalThis so that we can do globalThis.globalThis *)
SMap.add name (loc, val_ref ~type_only:false scope loc name) values
in
bind_local
~type_only:false
scope
tbls
name
(NamespaceBinding { id_loc = loc; name; values; types })
ignore2
| _ -> failwith "finalize_globalThis must be called after parsing all lib files on global scope"
end

module ObjAnnotAcc = struct
Expand Down
1 change: 1 addition & 0 deletions src/parser_utils/type_sig/type_sig_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let parse_libs opts ordered_asts =
let scope = Scope.create_global () in
let tbls = create_tables () in
List.iter (parse_lib opts scope tbls) ordered_asts;
Scope.bind_globalThis scope tbls ~global_this_loc:Loc.none;
(tbls, Scope.builtins_exn scope)

let pack_builtins (tbls, (global_values, global_types, global_modules)) =
Expand Down
Loading

0 comments on commit 0fe573e

Please sign in to comment.