blob: 08c0e37d3c9820c0774f33fbd46130b23df670cb [file] [log] [blame]
Christopher Piro094823a2007-07-18 00:26:12 +00001%%% Copyright (c) 2007- Facebook
2%%% Distributed under the Thrift Software License
3%%%
4%%% See accompanying file LICENSE or visit the Thrift site at:
5%%% http://developers.facebook.com/thrift/
6
7-module(oop).
8
Christopher Piro5b3a8f72007-08-01 22:27:37 +00009-export([get/2, set/3, call/2, call/3, inspect/1, start_new/2, is_object/1, class/1]).
Christopher Piro094823a2007-07-18 00:26:12 +000010-export([behaviour_info/1]).
11
Christopher Piro5b3a8f72007-08-01 22:27:37 +000012-include("thrift.hrl").
Christopher Piro094823a2007-07-18 00:26:12 +000013-include("oop.hrl").
14
15%%%
16%%% behavior definition
17%%%
18
19behaviour_info(callbacks) ->
20 [
21 {attr, 4},
22 {super, 0}
23 ];
24behaviour_info(_) ->
25 undefined.
26
27%%
28
29-define(TRIED, lists:reverse([TryModule|TriedRev])).
30
31%% no super attr defined
32-define(NOSUPEROBJ, exit({missing_attr_super, {inspect(Obj), ?TRIED}})).
33
34-define(NOMETHOD, exit({missing_method, {Method, inspect(Obj), tl(Args), ?TRIED}})).
35
36-define(NOATTR, exit({missing_attr, {hd(tl(Args)), inspect(FirstObj), ?TRIED}})).
37
38-define(NOATTR_SET, exit({missing_attr, {Field, inspect(Obj), ".." %% TODO: give a backtrace
39 }})).
40
41
42%%% get(Obj, Field) -> term()
43%%% looks up Field in Obj or its ancestor objects
44
45get(Obj, Field) ->
46 call(Obj, attr, [get, Field, get]).
47
48set(Obj, Field, Value) -> %% TODO: could be tail-recursive
49 Module = ?CLASS(Obj),
50 try
51 Module:attr(Obj, set, Field, Value)
52 catch
53 error:Kind when Kind == undef; Kind == function_clause ->
54 case get_superobject(Obj) of
55 { ok, Superobj } ->
56 Super1 = set(Superobj, Field, Value),
57 try
58 Module:attr(Obj, set, super, Super1)
59 catch %% TODO(cpiro): remove check
60 X -> exit({burnsauce, X})
61 end;
62 none ->
63 ?NOATTR_SET
64 end
65 end.
Christopher Piro094823a2007-07-18 00:26:12 +000066
67%%% C++ <-> Erlang
68%%% classes modules
69%%% class b : public a a:super() -> b.
70%%%
71
72get_superobject(Obj) ->
73 try
74 {ok, (?CLASS(Obj)):attr(Obj, get, super, get)}
75 catch
76 error:Kind when Kind == undef; Kind == function_clause ->
77 none
78 end.
79
Christopher Piro5b3a8f72007-08-01 22:27:37 +000080is_object(Obj) when is_tuple(Obj) ->
81 try
82 (?CLASS(Obj)):super(), %% if it's an object its first element will be a class name, and it'll have super/0
83 true
84 catch
85 error:Kind when Kind == undef; Kind == function_clause ->
86 false
87 end;
88is_object(_) ->
89 false.
90
Christopher Piro094823a2007-07-18 00:26:12 +000091call(Obj, Method, ArgsProper) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +000092 %% error_logger:info_msg("call called: Obj=~p Method=~p ArgsProper=~p", [inspect(Obj), Method, ArgsProper]),
Christopher Piro094823a2007-07-18 00:26:12 +000093 Args = [Obj|ArgsProper], %% prepend This to args
94 TryModule = ?CLASS(Obj),
95 call_loop(Obj, Method, Args, TryModule, [], Obj).
96
97call(Obj, Method) ->
98 call(Obj, Method, []).
99
100call_loop(Obj, Method, Args, TryModule, TriedRev, FirstObj) ->
101 try
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000102 %% error_logger:info_msg("call_loop~n ~p~n ~p~n ~p~n ~p", [inspect(Obj), Method, Args, TryModule]),
Christopher Piro094823a2007-07-18 00:26:12 +0000103 apply(TryModule, Method, Args)
104 catch
105 error:Kind when Kind == undef; Kind == function_clause ->
106 case { TryModule:super(), Method } of
107 { none, attr } ->
108 ?NOATTR;
109
110 { none, _ } ->
111 ?NOMETHOD;
112
113 { Superclass, attr } ->
114 %% look for attrs in the "super object"
115
116 case get_superobject(Obj) of
117 {ok, Superobj} when (TryModule == ?CLASS(Obj)) ->
118 %% replace This with Superobj
119 NewArgs = [Superobj|tl(Args)],
120 call_loop(Superobj, Method, NewArgs,
121 Superclass, [TryModule|TriedRev], FirstObj);
122
123 {ok, _Superobj} -> % failed guard TODO(cpiro): removeme
124 exit(oh_noes);
125
126 none -> ?NOSUPEROBJ
127 end;
128
129 { SuperClass, _ } ->
130 call_loop(Obj, Method, Args,
131 SuperClass, [TryModule|TriedRev], FirstObj)
132 end
133 end.
134
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000135class(Obj) when is_tuple(Obj) ->
136 case is_object(Obj) of
137 true ->
138 ?CLASS(Obj);
139 false ->
140 none
141 end;
142class(_) ->
143 none.
144
145%% careful: not robust against records beginning with a class name
146%% (note: we can't just guard with is_record(?CLASS(Obj), Obj) since we
147%% can't/really really shouldn't require all record definitions in this file
Christopher Piro094823a2007-07-18 00:26:12 +0000148inspect(Obj) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000149 try
150 case is_object(Obj) of
151 true ->
152 DeepList = inspect_loop(Obj, "#<"),
153 lists:flatten(DeepList);
154 false ->
155 thrift_utils:sformat("~p", [Obj])
156 end
157 catch
158 _:E ->
159 thrift_utils:sformat("INSPECT_ERROR(~p) ~p", [E, Obj])
160
161 %% TODO(cpiro): bring this back once we're done testing:
162 %% _:E -> thrift_utils:sformat("~p", [Obj])
163 end.
Christopher Piro094823a2007-07-18 00:26:12 +0000164
165inspect_loop(Obj, Str) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000166 Class = ?CLASS(Obj),
167 Inspect = Class:inspect(Obj),
168 Current = atom_to_list(Class) ++ ": " ++ Inspect,
169
Christopher Piro094823a2007-07-18 00:26:12 +0000170 case get_superobject(Obj) of
171 { ok, Superobj } ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000172 inspect_loop(Superobj, Str ++ Current ++ " | ");
Christopher Piro094823a2007-07-18 00:26:12 +0000173 none ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000174 Str ++ Current ++ ">"
Christopher Piro094823a2007-07-18 00:26:12 +0000175 end.
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000176
Christopher Piro094823a2007-07-18 00:26:12 +0000177%% TODO: voids take only ok as return?
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000178start_new(none=Resv, _) ->
179 error_logger:format("can't instantiate ~p: class name is a reserved word", [Resv]),
180 error;
Christopher Piro094823a2007-07-18 00:26:12 +0000181start_new(Class, Args) ->
Christopher Piro5b3a8f72007-08-01 22:27:37 +0000182 {ok, Pid} = gen_server:start_link(thrift_oop_server, {Class, Args}, []),
183 Pid.