Skip to content

Commit 7353c30

Browse files
committed
Fix merge conflicts
2 parents fb42ce6 + 66ab1e1 commit 7353c30

File tree

30 files changed

+595
-210
lines changed

30 files changed

+595
-210
lines changed

CHANGELOG.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [unreleased]
88
### Added
9-
- Reimplement `String.split_at/2` to make sure Unicode library isn't compiled
10-
- Added `ElixirScript.JS.map_to_object/2` with options [keys: :string, symbols: false]
11-
- Added `ElixirScript.JS.object_to_map/1|2` with options [keys: :atom, recurse_array: true]
9+
- [Compiler will now accept a path to Elixir Files to compile](https://github.com/elixirscript/elixirscript/issues/420)
10+
- [Added `ElixirScript.JS.map_to_object/2` with options [keys: :string, symbols: false]](https://github.com/elixirscript/elixirscript/issues/362)
11+
- [Added `ElixirScript.JS.object_to_map/1|2` with options [keys: :atom, recurse_array: true]](https://github.com/elixirscript/elixirscript/issues/381)
12+
- [Fully implement `__info__` on modules](https://github.com/elixirscript/elixirscript/pull/378)
13+
- [Concurrent Compilation](https://github.com/elixirscript/elixirscript/issues/376)
14+
- [The following erlang functions have been implemented](https://github.com/elixirscript/elixirscript/issues/306):
15+
* :erlang.nodes/0
16+
* :erlang.nodes/1
17+
* :math.log2/1
18+
* :binary.copy/1
19+
* :binary.copy/2
20+
* :binary.part/2
21+
* :binary.part/3
22+
* :binary.replace/3
23+
* :binary.replace/4 (some options still missing)
1224

1325
### Fixed
1426
- Make sure not to add underscores to erlang functions
15-
- Make sure any variable names that are javascript keywords are handled properly
27+
- [Make sure any variable names that are javascript keywords are handled properly](https://github.com/elixirscript/elixirscript/issues/355)
28+
- [Make sure variables that begin with `_` are available](https://github.com/elixirscript/elixirscript/issues/356)
29+
- [Finding the use of functions within anonymous functions](https://github.com/elixirscript/elixirscript/issues/358)
30+
- [Reimplement `String.split_at/2` to make sure Unicode library isn't compiled](https://github.com/elixirscript/elixirscript/issues/353)
31+
- [byte_size does not work on binaries that started via "" elixir string syntax](https://github.com/elixirscript/elixirscript/issues/384)
32+
- [using . (dot) reference syntax on a map fails when value is a function](https://github.com/elixirscript/elixirscript/issues/380)
33+
- [Make sure that remote ast works correctly with variables](https://github.com/elixirscript/elixirscript/issues/390)
34+
- [Make sure == works as expected](https://github.com/elixirscript/elixirscript/issues/382)
35+
- [Make sure that erlang function names that are also JavaScript keywords are not filters](https://github.com/elixirscript/elixirscript/issues/359)
36+
- [erlang.error now throws errors resembling those in Elixir](https://github.com/elixirscript/elixirscript/pull/397)
37+
- [Map.get fails if key is tuple or list](https://github.com/elixirscript/elixirscript/issues/406)
1638

1739
## [0.30.0] - 2017-08-15
1840

lib/elixir_script/beam.ex

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule ElixirScript.Beam do
77
For protocols, this will return a list of
88
all the protocol implementations
99
"""
10-
@spec debug_info(atom) :: {:ok | :error, map | binary}
10+
@spec debug_info(atom | bitstring) :: {:ok | :error, map | binary}
1111
def debug_info(module)
1212

1313
# We get debug info from String and then replace
@@ -39,9 +39,23 @@ defmodule ElixirScript.Beam do
3939
do_debug_info(module)
4040
end
4141

42-
defp do_debug_info(module) when is_atom(module) do
43-
with {_, beam, beam_path} <- :code.get_object_code(module),
44-
{:ok, {^module, [debug_info: {:debug_info_v1, backend, data}]}} <- :beam_lib.chunks(beam, [:debug_info]),
42+
def debug_info(beam) when is_bitstring(beam) do
43+
do_debug_info(beam)
44+
end
45+
46+
defp do_debug_info(module, path \\ nil)
47+
48+
defp do_debug_info(module, _) when is_atom(module) do
49+
case :code.get_object_code(module) do
50+
{_, beam, beam_path} ->
51+
do_debug_info(beam, beam_path)
52+
:error ->
53+
{:error, "Unknown module"}
54+
end
55+
end
56+
57+
defp do_debug_info(beam, beam_path) do
58+
with {:ok, {module, [debug_info: {:debug_info_v1, backend, data}]}} <- :beam_lib.chunks(beam, [:debug_info]),
4559
{:ok, {^module, attribute_info}} = :beam_lib.chunks(beam, [:attributes]) do
4660

4761
if Keyword.get(attribute_info[:attributes], :protocol) do
@@ -62,6 +76,11 @@ defmodule ElixirScript.Beam do
6276
end
6377
end
6478

79+
defp process_debug_info({:ok, info}, nil) do
80+
info = Map.put(info, :last_modified, nil)
81+
{:ok, info}
82+
end
83+
6584
defp process_debug_info({:ok, info}, beam_path) do
6685
info = case File.stat(beam_path, time: :posix) do
6786
{:ok, file_info} ->

lib/elixir_script/cli.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ defmodule ElixirScript.CLI do
3636

3737
defp help_message do
3838
"""
39-
usage: elixirscript <module> [options]
39+
usage: elixirscript <module | path> [options]
4040
<module> the entry module of your application
41+
<path> the path to .ex(s) files to compile
4142
4243
options:
4344
-o --output [path] places output at the given path.

lib/elixir_script/compiler.ex

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ defmodule ElixirScript.Compiler do
22
@moduledoc """
33
The entry point for the ElixirScript compilation process.
44
Takes the given module(s) and compiles them and all modules
5-
and functions they use into JavaScript
5+
and functions they use into JavaScript.
6+
7+
Will also take a path to Elixir files
68
"""
79

810
@doc """
9-
Takes either a module name or a list of module names as
11+
Takes either a module name, list of module names, or a path as
1012
the entry point(s) of an application/library. From there
1113
it will determine which modules and functions are needed
1214
to be compiled.
@@ -22,36 +24,78 @@ defmodule ElixirScript.Compiler do
2224
2325
* `root`: Optional root for imports of FFI JavaScript modules. Defaults to `.`.
2426
"""
25-
@spec compile(atom | [atom], []) :: nil
26-
def compile(entry_modules, opts \\ []) do
27-
opts = build_compiler_options(opts, entry_modules)
28-
{:ok, pid} = ElixirScript.State.start_link()
27+
alias ElixirScript.{
28+
State,
29+
Translate,
30+
FindUsedModules,
31+
FindUsedFunctions,
32+
Output
33+
}
34+
alias ElixirScript.ModuleSystems.ES
35+
alias Kernel.ParallelCompiler
36+
37+
@spec compile(atom | [atom] | binary, []) :: nil
38+
def compile(path, opts \\ [])
39+
40+
def compile(path, opts) when is_binary(path) do
41+
opts = build_compiler_options(opts)
42+
{:ok, pid} = State.start_link()
43+
44+
path = if String.ends_with?(path, [".ex", ".exs"]) do
45+
path
46+
else
47+
Path.join([path, "**", "*.{ex,exs}"])
48+
end
49+
50+
files = Path.wildcard(path)
51+
52+
ParallelCompiler.files(files, [
53+
each_module: &on_module_compile(pid, &1, &2, &3)
54+
])
55+
56+
entry_modules = pid
57+
|> State.get_in_memory_modules
58+
|> Keyword.keys
59+
60+
do_compile(entry_modules, pid, opts)
61+
end
62+
63+
def compile(entry_modules, opts) do
64+
opts = build_compiler_options(opts)
65+
{:ok, pid} = State.start_link()
2966

3067
entry_modules = List.wrap(entry_modules)
3168

32-
ElixirScript.FindUsedModules.execute(entry_modules, pid)
69+
do_compile(entry_modules, pid, opts)
70+
end
3371

34-
ElixirScript.FindUsedFunctions.execute(entry_modules, pid)
72+
defp do_compile(entry_modules, pid, opts) do
73+
FindUsedModules.execute(entry_modules, pid)
3574

36-
modules = ElixirScript.State.list_modules(pid)
37-
ElixirScript.Translate.execute(modules, pid)
75+
FindUsedFunctions.execute(entry_modules, pid)
3876

39-
modules = ElixirScript.State.list_modules(pid)
40-
result = ElixirScript.Output.execute(modules, pid, opts)
77+
modules = State.list_modules(pid)
78+
Translate.execute(modules, pid)
4179

42-
ElixirScript.State.stop(pid)
80+
modules = State.list_modules(pid)
81+
result = Output.execute(modules, pid, opts)
82+
83+
State.stop(pid)
4384

4485
result
4586
end
4687

47-
defp build_compiler_options(opts, entry_modules) do
88+
defp build_compiler_options(opts) do
4889
default_options = Map.new
4990
|> Map.put(:output, Keyword.get(opts, :output))
5091
|> Map.put(:format, :es)
51-
|> Map.put(:entry_modules, entry_modules)
5292
|> Map.put(:root, Keyword.get(opts, :root, "."))
5393

5494
options = default_options
55-
Map.put(options, :module_formatter, ElixirScript.ModuleSystems.ES)
95+
Map.put(options, :module_formatter, ES)
96+
end
97+
98+
defp on_module_compile(pid, _file, module, beam) do
99+
State.put_in_memory_module(pid, module, beam)
56100
end
57101
end

lib/elixir_script/module_systems/es.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ defmodule ElixirScript.ModuleSystems.ES do
1212
imports ++ body ++ export
1313
end
1414

15+
def build_imports(js_imports) do
16+
js_imports
17+
|> Enum.map(fn
18+
{_module, name, _path, import_path} -> import_module(name, import_path)
19+
end)
20+
end
21+
22+
def build_export(exports) do
23+
if is_nil(exports), do: [], else: [export_module(exports)]
24+
end
25+
1526
defp import_module(import_name, from) do
1627
js_module_name = JS.identifier(import_name)
1728

lib/elixir_script/passes/find_used_functions.ex

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ defmodule ElixirScript.FindUsedFunctions do
99
"""
1010
@spec execute([atom], pid) :: nil
1111
def execute(entry_modules, pid) do
12-
Enum.each(entry_modules, fn
12+
entry_modules
13+
|> List.wrap
14+
|> Enum.each(fn
1315
module ->
1416
walk_module(module, pid)
1517
end)
1618

17-
modules = ElixirScript.State.list_modules(pid)
18-
Enum.each(modules, fn
19+
pid
20+
|> ElixirScript.State.list_modules
21+
|> Enum.each(fn
1922
{module, info} ->
2023
if get_in(info, [:attributes, :protocol_impl]) do
2124
walk_module(module, pid)
@@ -134,7 +137,7 @@ defmodule ElixirScript.FindUsedFunctions do
134137
walk(params, state)
135138
end
136139

137-
defp walk({:for, _, generators}, state) do
140+
defp walk({:for, _, generators}, state) when is_list(generators) do
138141
Enum.each(generators, fn
139142
{:<<>>, _, body} ->
140143
walk(body, state)

lib/elixir_script/passes/find_used_modules.ex

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,22 @@ defmodule ElixirScript.FindUsedModules do
88
"""
99
@spec execute([atom], pid) :: nil
1010
def execute(modules, pid) do
11-
Enum.each(List.wrap(modules), fn(module) ->
12-
if ElixirScript.State.get_module(pid, module) == nil do
13-
do_execute(module, pid)
14-
end
11+
modules
12+
|> List.wrap
13+
|> Enum.each(fn(module) ->
14+
do_execute(module, pid)
1515
end)
1616
end
1717

1818
defp do_execute(module, pid) do
19-
case ElixirScript.Beam.debug_info(module) do
19+
result = case ModuleState.get_in_memory_module(pid, module) do
20+
nil ->
21+
ElixirScript.Beam.debug_info(module)
22+
beam ->
23+
ElixirScript.Beam.debug_info(beam)
24+
end
25+
26+
case result do
2027
{:ok, info} ->
2128
walk_module(module, info, pid)
2229
{:ok, module, implementations} ->
@@ -71,8 +78,9 @@ defmodule ElixirScript.FindUsedModules do
7178
module: module
7279
}
7380

74-
Enum.each(reachable_defs, &walk(&1, state))
75-
81+
Enum.each(reachable_defs, fn(x) ->
82+
walk(x, state)
83+
end)
7684
end
7785

7886
defp walk_protocol(module, implementations, pid) do
@@ -128,7 +136,7 @@ defmodule ElixirScript.FindUsedModules do
128136
defp walk(form, state) when is_atom(form) and form not in [BitString, Function, PID, Port, Reference, Any, Elixir] do
129137
if ElixirScript.Translate.Module.is_elixir_module(form) and !ElixirScript.Translate.Module.is_js_module(form, state) do
130138
if ModuleState.get_module(state.pid, form) == nil do
131-
execute(form, state.pid)
139+
do_execute(form, state.pid)
132140
end
133141
end
134142
end
@@ -164,7 +172,7 @@ defmodule ElixirScript.FindUsedModules do
164172
walk(params, state)
165173
end
166174

167-
defp walk({:for, _, generators}, state) do
175+
defp walk({:for, _, generators}, state) when is_list(generators) do
168176
walk(Collectable, state)
169177

170178
Enum.each(generators, fn
@@ -292,7 +300,7 @@ defmodule ElixirScript.FindUsedModules do
292300
cond do
293301
ElixirScript.Translate.Module.is_elixir_module(module) ->
294302
if ModuleState.get_module(state.pid, module) == nil do
295-
execute(module, state.pid)
303+
do_execute(module, state.pid)
296304
end
297305
true ->
298306
walk(module, state)

lib/elixir_script/passes/output.ex

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ defmodule ElixirScript.Output do
3535
defp bundle(modules, opts, js_modules) do
3636
modules
3737
|> ElixirScript.Output.JSModule.compile(opts, js_modules)
38-
|> List.wrap
39-
|> Builder.program
40-
|> prepare_js_ast
41-
|> Generator.generate
38+
|> Task.async_stream(fn(js_part) ->
39+
js_part
40+
|> List.wrap
41+
|> Builder.program
42+
|> prepare_js_ast
43+
|> Generator.generate
44+
end)
45+
|> Stream.map(fn {:ok, js_code} -> js_code end)
46+
|> Enum.to_list
47+
|> Enum.join("\n")
4248
|> concat
4349
end
4450

lib/elixir_script/passes/output/js_module.ex

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ defmodule ElixirScript.Output.JSModule do
77
def compile(body, opts, js_modules) do
88
elixir = Helpers.declare("Elixir", J.object_expression([]))
99

10-
ast = opts.module_formatter.build(
11-
js_modules,
12-
[elixir, create_atom_table(), start(), load()] ++ body,
13-
J.identifier("Elixir")
14-
)
10+
imports = opts.module_formatter.build_imports(js_modules)
11+
exports = opts.module_formatter.build_export(J.identifier("Elixir"))
1512

16-
ast
13+
[elixir, create_atom_table(), start(), load(), imports] ++ body ++ [exports]
1714
end
1815

1916
def start do

lib/elixir_script/passes/translate.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ defmodule ElixirScript.Translate do
88
"""
99
@spec execute([atom], pid) :: nil
1010
def execute(modules, pid) do
11-
Enum.each(modules, fn
11+
modules
12+
|> List.wrap()
13+
|> Task.async_stream(fn
1214
{module, info} ->
1315
ElixirScript.Translate.Module.compile(module, info, pid)
1416
end)
17+
|> Stream.run()
1518
end
1619
end

0 commit comments

Comments
 (0)