diff --git a/deps/rabbit_common/src/code_version.erl b/deps/rabbit_common/src/code_version.erl new file mode 100644 index 0000000000..0a19a94295 --- /dev/null +++ b/deps/rabbit_common/src/code_version.erl @@ -0,0 +1,184 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ Federation. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. +%% +-module(code_version). + +-export([update/1]). + +update(Module) -> + AbsCode = get_abs_code(Module), + Forms = replace_forms(Module, get_otp_version() >= 18, AbsCode), + Code = compile_forms(Forms), + load_code(Module, Code). + +load_code(Module, Code) -> + unload(Module), + case code:load_binary(Module, "loaded by rabbit_common", Code) of + {module, _} -> + ok; + {error, _Reason} -> + throw(cannot_load) + end. + +unload(Module) -> + code:soft_purge(Module), + code:delete(Module). + +compile_forms(Forms) -> + case compile:forms(Forms, [debug_info]) of + {ok, _ModName, Code} -> + Code; + {ok, _ModName, Code, _Warnings} -> + Code; + _ -> + throw(cannot_compile_forms) + end. + +get_abs_code(Module) -> + get_forms(get_object_code(Module)). + +get_object_code(Module) -> + case code:get_object_code(Module) of + {_Mod, Code, _File} -> + Code; + error -> + throw(not_found) + end. + +get_forms(Code) -> + case beam_lib:chunks(Code, [abstract_code]) of + {ok, {_, [{abstract_code, {raw_abstract_v1, Forms}}]}} -> + Forms; + {ok, {_, [{abstract_code, no_abstract_code}]}} -> + throw(no_abstract_code); + {error, beam_lib, Reason} -> + throw({no_abstract_code, Reason}) + end. + +get_otp_version() -> + Version = erlang:system_info(otp_release), + case re:run(Version, "^[0-9][0-9]", [{capture, first, list}]) of + {match, [V]} -> + list_to_integer(V); + _ -> + %% Could be anything below R17, we are not interested + 0 + end. + +get_original_pairs(VersionSupport) -> + [{Orig, Arity} || {Orig, _Pre, _Post, Arity} <- VersionSupport]. + +get_delete_pairs(true, VersionSupport) -> + [{Pre, Arity} || {_Orig, Pre, _Post, Arity} <- VersionSupport]; +get_delete_pairs(false, VersionSupport) -> + [{Post, Arity} || {_Orig, _Pre, Post, Arity} <- VersionSupport]. + +get_rename_pairs(true, VersionSupport) -> + [{Post, Arity} || {_Orig, _Pre, Post, Arity} <- VersionSupport]; +get_rename_pairs(false, VersionSupport) -> + [{Pre, Arity} || {_Orig, Pre, _Post, Arity} <- VersionSupport]. + +get_name_pairs(true, VersionSupport) -> + [{Post, Orig} || {Orig, _Pre, Post, _Arity} <- VersionSupport]; +get_name_pairs(false, VersionSupport) -> + [{Pre, Orig} || {Orig, Pre, _Post, _Arity} <- VersionSupport]. + +delete_abstract_functions(ToDelete) -> + fun(Tree, Function) -> + case lists:member(Function, ToDelete) of + true -> + erl_syntax:comment(["Deleted unused function"]); + false -> + Tree + end + end. + +rename_abstract_functions(ToRename, ToName) -> + fun(Tree, {Name, _Arity} = Function) -> + case lists:member(Function, ToRename) of + true -> + FunctionName = proplists:get_value(Name, ToName), + erl_syntax:function( + erl_syntax:atom(FunctionName), + erl_syntax:function_clauses(Tree)); + false -> + Tree + end + end. + +replace_forms(Module, IsPost18, AbsCode) -> + Attr = Module:module_info(attributes), + VersionSupport = proplists:get_value(version_support, Attr), + Original = get_original_pairs(VersionSupport), + ToDelete = get_delete_pairs(IsPost18, VersionSupport), + DeleteFun = delete_abstract_functions(ToDelete ++ Original), + AbsCode0 = replace_function_forms(AbsCode, DeleteFun), + ToRename = get_rename_pairs(IsPost18, VersionSupport), + ToName = get_name_pairs(IsPost18, VersionSupport), + RenameFun = rename_abstract_functions(ToRename, ToName), + remove_exports(replace_function_forms(AbsCode0, RenameFun), ToDelete ++ ToRename). + +replace_function_forms(AbsCode, Fun) -> + ReplaceFunction = + fun(Tree) -> + case erl_syntax_lib:analyze_function(Tree) of + {_N, _A} = Function -> + Fun(Tree, Function); + _Other -> Tree + end + end, + Filter = fun(Tree) -> + case erl_syntax:type(Tree) of + function -> ReplaceFunction(Tree); + _Other -> Tree + end + end, + fold_syntax_tree(Filter, AbsCode). + +filter_export_pairs(Info, ToDelete) -> + lists:filter(fun(Pair) -> + not lists:member(Pair, ToDelete) + end, Info). + +remove_exports(AbsCode, ToDelete) -> + RemoveExports = + fun(Tree) -> + case erl_syntax_lib:analyze_attribute(Tree) of + {export, Info} -> + Remaining = filter_export_pairs(Info, ToDelete), + rebuild_export(Remaining); + _Other -> Tree + end + end, + Filter = fun(Tree) -> + case erl_syntax:type(Tree) of + attribute -> RemoveExports(Tree); + _Other -> Tree + end + end, + fold_syntax_tree(Filter, AbsCode). + +rebuild_export(Args) -> + erl_syntax:attribute( + erl_syntax:atom(export), + [erl_syntax:list( + [erl_syntax:arity_qualifier(erl_syntax:atom(N), + erl_syntax:integer(A)) + || {N, A} <- Args])]). + +fold_syntax_tree(Filter, Forms) -> + Tree = erl_syntax:form_list(Forms), + NewTree = erl_syntax_lib:map(Filter, Tree), + erl_syntax:revert_forms(NewTree). diff --git a/deps/rabbit_common/src/time_compat.erl b/deps/rabbit_common/src/time_compat.erl index b87c6cc550..934099e765 100644 --- a/deps/rabbit_common/src/time_compat.erl +++ b/deps/rabbit_common/src/time_compat.erl @@ -43,160 +43,240 @@ %% where it has not yet been deprecated. %% +-version_support( + [{monotonic_time, monotonic_time_pre_18, monotonic_time_post_18, 0}, + {monotonic_time, monotonic_time_pre_18, monotonic_time_post_18, 1}, + {erlang_system_time, erlang_system_time_pre_18, erlang_system_time_post_18, 0}, + {erlang_system_time, erlang_system_time_pre_18, erlang_system_time_post_18, 1}, + {os_system_time, os_system_time_pre_18, os_system_time_post_18, 0}, + {os_system_time, os_system_time_pre_18, os_system_time_post_18, 1}, + {time_offset, time_offset_pre_18, time_offset_post_18, 0}, + {time_offset, time_offset_pre_18, time_offset_post_18, 1}, + {convert_time_unit, convert_time_unit_pre_18, convert_time_unit_post_18, 0}, + {timestamp, timestamp_pre_18, timestamp_post_18, 0}, + {unique_integer, unique_integer_pre_18, unique_integer_post_18, 0}, + {unique_integer, unique_integer_pre_18, unique_integer_post_18, 1}]). + -export([monotonic_time/0, - monotonic_time/1, - erlang_system_time/0, - erlang_system_time/1, - os_system_time/0, - os_system_time/1, - time_offset/0, - time_offset/1, - convert_time_unit/3, - timestamp/0, - unique_integer/0, - unique_integer/1, - monitor/2, - system_info/1, - system_flag/2]). + monotonic_time_pre_18/0, + monotonic_time_post_18/0, + monotonic_time/1, + monotonic_time_pre_18/1, + monotonic_time_post_18/1, + erlang_system_time/0, + erlang_system_time_pre_18/0, + erlang_system_time_post_18/0, + erlang_system_time/1, + erlang_system_time_pre_18/1, + erlang_system_time_post_18/1, + os_system_time/0, + os_system_time_pre_18/0, + os_system_time_post_18/0, + os_system_time/1, + os_system_time_pre_18/1, + os_system_time_post_18/1, + time_offset/0, + time_offset_pre_18/0, + time_offset_post_18/0, + time_offset/1, + time_offset_pre_18/1, + time_offset_post_18/1, + convert_time_unit/3, + convert_time_unit_pre_18/3, + convert_time_unit_post_18/3, + timestamp/0, + timestamp_pre_18/0, + timestamp_post_18/0, + unique_integer/0, + unique_integer_pre_18/0, + unique_integer_post_18/0, + unique_integer/1, + unique_integer_pre_18/1, + unique_integer_post_18/1, + monitor/2, + system_info/1, + system_flag/2]). monotonic_time() -> - try - erlang:monotonic_time() - catch - error:undef -> - %% Use Erlang system time as monotonic time - erlang_system_time_fallback() - end. + code_version:update(?MODULE), + time_compat:monotonic_time(). + +monotonic_time_post_18() -> + erlang:monotonic_time(). + +monotonic_time_pre_18() -> + erlang_system_time_fallback(). monotonic_time(Unit) -> + code_version:update(?MODULE), + time_compat:monotonic_time(Unit). + +monotonic_time_post_18(Unit) -> try - erlang:monotonic_time(Unit) + erlang:monotonic_time(Unit) catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - %% Use Erlang system time as monotonic time - STime = erlang_system_time_fallback(), - try + error:badarg -> + erlang:error(badarg, [Unit]) + end. + +monotonic_time_pre_18(Unit) -> + %% Use Erlang system time as monotonic time + STime = erlang_system_time_fallback(), + try convert_time_unit_fallback(STime, native, Unit) - catch + catch error:bad_time_unit -> erlang:error(badarg, [Unit]) - end end. erlang_system_time() -> - try - erlang:system_time() - catch - error:undef -> - erlang_system_time_fallback() - end. + code_version:update(?MODULE), + time_compat:erlang_system_time(). + +erlang_system_time_post_18() -> + erlang:system_time(). + +erlang_system_time_pre_18() -> + erlang_system_time_fallback(). erlang_system_time(Unit) -> + code_version:update(?MODULE), + time_compat:erlang_system_time(Unit). + +erlang_system_time_post_18(Unit) -> try - erlang:system_time(Unit) + erlang:system_time(Unit) catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - STime = erlang_system_time_fallback(), - try + error:badarg -> + erlang:error(badarg, [Unit]) + end. + +erlang_system_time_pre_18(Unit) -> + STime = erlang_system_time_fallback(), + try convert_time_unit_fallback(STime, native, Unit) - catch + catch error:bad_time_unit -> erlang:error(badarg, [Unit]) - end end. os_system_time() -> - try - os:system_time() - catch - error:undef -> - os_system_time_fallback() - end. + code_version:update(?MODULE), + time_compat:os_system_time(). + +os_system_time_post_18() -> + os:system_time(). + +os_system_time_pre_18() -> + os_system_time_fallback(). os_system_time(Unit) -> + code_version:update(?MODULE), + time_compat:os_system_time(Unit). + +os_system_time_post_18(Unit) -> try - os:system_time(Unit) + os:system_time(Unit) catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - STime = os_system_time_fallback(), - try + error:badarg -> + erlang:error(badarg, [Unit]) + end. + +os_system_time_pre_18(Unit) -> + STime = os_system_time_fallback(), + try convert_time_unit_fallback(STime, native, Unit) - catch + catch error:bad_time_unit -> erlang:error(badarg, [Unit]) - end end. time_offset() -> - try - erlang:time_offset() - catch - error:undef -> - %% Erlang system time and Erlang monotonic - %% time are always aligned - 0 - end. + code_version:update(?MODULE), + time_compat:time_offset(). + +time_offset_post_18() -> + erlang:time_offset(). + +time_offset_pre_18() -> + %% Erlang system time and Erlang monotonic + %% time are always aligned + 0. time_offset(Unit) -> + code_version:update(?MODULE), + time_compat:time_offset(Unit). + +time_offset_post_18(Unit) -> try - erlang:time_offset(Unit) + erlang:time_offset(Unit) catch - error:badarg -> - erlang:error(badarg, [Unit]); - error:undef -> - try - _ = integer_time_unit(Unit) - catch - error:bad_time_unit -> erlang:error(badarg, [Unit]) - end, - %% Erlang system time and Erlang monotonic - %% time are always aligned - 0 + error:badarg -> + erlang:error(badarg, [Unit]) end. -convert_time_unit(Time, FromUnit, ToUnit) -> +time_offset_pre_18(Unit) -> try - erlang:convert_time_unit(Time, FromUnit, ToUnit) + _ = integer_time_unit(Unit) catch - error:undef -> - try - convert_time_unit_fallback(Time, FromUnit, ToUnit) - catch - _:_ -> - erlang:error(badarg, [Time, FromUnit, ToUnit]) - end; - error:Error -> + error:bad_time_unit -> erlang:error(badarg, [Unit]) + end, + %% Erlang system time and Erlang monotonic + %% time are always aligned + 0. + +convert_time_unit(Time, FromUnit, ToUnit) -> + code_version:update(?MODULE), + time_compat:convert_time_unit(Time, FromUnit, ToUnit). + +convert_time_unit_post_18(Time, FromUnit, ToUnit) -> + try + erlang:convert_time_unit(Time, FromUnit, ToUnit) + catch + error:Error -> erlang:error(Error, [Time, FromUnit, ToUnit]) end. -timestamp() -> +convert_time_unit_pre_18(Time, FromUnit, ToUnit) -> try - erlang:timestamp() + convert_time_unit_fallback(Time, FromUnit, ToUnit) catch - error:undef -> - erlang:now() + _:_ -> + erlang:error(badarg, [Time, FromUnit, ToUnit]) end. +timestamp() -> + code_version:update(?MODULE), + time_compat:timestamp(). + +timestamp_post_18() -> + erlang:timestamp(). + +timestamp_pre_18() -> + erlang:now(). + unique_integer() -> - try - erlang:unique_integer() - catch - error:undef -> - {MS, S, US} = erlang:now(), - (MS*1000000+S)*1000000+US - end. + code_version:update(?MODULE), + time_compat:unique_integer(). + +unique_integer_post_18() -> + erlang:unique_integer(). + +unique_integer_pre_18() -> + {MS, S, US} = erlang:now(), + (MS*1000000+S)*1000000+US. unique_integer(Modifiers) -> + code_version:update(?MODULE), + time_compat:unique_integer(Modifiers). + +unique_integer_post_18(Modifiers) -> try - erlang:unique_integer(Modifiers) + erlang:unique_integer(Modifiers) catch - error:badarg -> - erlang:error(badarg, [Modifiers]); - error:undef -> - case is_valid_modifier_list(Modifiers) of + error:badarg -> + erlang:error(badarg, [Modifiers]) + end. + +unique_integer_pre_18(Modifiers) -> + case is_valid_modifier_list(Modifiers) of true -> %% now() converted to an integer %% fullfill the requirements of @@ -206,7 +286,6 @@ unique_integer(Modifiers) -> (MS*1000000+S)*1000000+US; false -> erlang:error(badarg, [Modifiers]) - end end. monitor(Type, Item) ->