1 %%% Copyright (c) 2007- Facebook
2 %%% Distributed under the Thrift Software License
4 %%% See accompanying file LICENSE or visit the Thrift site at:
5 %%% http://developers.facebook.com/thrift/
12 %%% class b : public a a:super() -> b.
17 -export([start_new
/2, get/2, set
/3, call
/2, call
/3, inspect
/1, class
/1, is_object
/1]).
18 -export([call1
/3]). %% only for thrift_oop_server ... don't use it
19 -export([behaviour_info
/1]).
21 -include("thrift.hrl").
24 %% state for the call loop
26 obj
, %% the current object (on which we want to invoke MFA)
27 module
, %% the current module we're considering
28 func
, %% the method name (i.e. the function we're trying to invoke in Module)
29 args
, %% the arguments, the first of which is Obj
30 tried
, %% a (backwards) list of modules we've tried
31 first_obj
%% the original object
35 %%% behavior definition
38 behaviour_info(callbacks
) ->
49 %% TODO: voids take only ok as return?
50 start_new(none
=Resv
, _
) ->
51 ?
ERROR("can't instantiate ~p: class name is a reserved word", [Resv
]),
53 start_new(Class
, Args
) ->
54 {ok
, Pid
} = gen_server:start_link(thrift_oop_server
, {Class
, Args
}, []),
57 %% get(Obj, Field) -> term()
58 %% looks up Field in Obj or its ancestor objects
60 call(Obj
, attr
, [get, Field
, get]).
62 set(Obj
, Field
, Value
) -> %% TODO: could be tail-recursive
64 case apply_if_defined(Module
, attr
, [Obj
, set
, Field
, Value
]) of
67 case get_superobject(Obj
) of
69 Superobj1
= set(Superobj
, Field
, Value
),
70 Module:attr(Obj
, set
, super
, Superobj1
);
72 error(missing_attr_set
, Field
, Obj
)
77 %% ** dynamic method dispatch **
79 %% calls Module:Func(*Args) if it exists
80 %% if not, Module <- Module:super() and try again recursively
82 %% Module:attr(*Args) is handled specially:
83 %% Obj needs to be replaced with Obj's "superobject"
88 call(Obj
, Func
, ArgsProper
) ->
89 ?
INFO("oop:call called: Obj=~p Func=~p ArgsProper=~p", [inspect(Obj
), Func
, ArgsProper
]),
90 case call1(Obj
, Func
, ArgsProper
) of
92 {error
, Kind
, S1
} -> error(Kind
, S1
)
95 call1(Obj
, Func
, ArgsProper
) ->
100 args
= [Obj
|ArgsProper
], %% prepend This to args
106 call1(S
= #cstate
{obj
=Obj
, module
=Module
, func
=Func
, args
=Args
}) ->
107 %% ?INFO("call1~n obj=~p~n MFA=~p, ~p, ~p", [inspect(Obj), Module, Func, Args]),
108 %% io:format("call ~p~n", [Module]),
109 case apply_if_defined(Module
, Func
, Args
) of
110 {ok
, Value
} -> {ok
, Value
};
111 undef
-> call1_try_super(S
)
114 call1_try_super(S
= #cstate
{func
=attr
, module
=Module
, tried
=Tried
}) ->
115 case Module:super() of
116 none
-> {error
, missing_attr
, S
};
117 Superclass
-> call1_try_super_attr(Superclass
, S
)
119 call1_try_super(S
= #cstate
{func
=Func
, module
=Module
, tried
=Tried
}) ->
120 case Module:super() of
121 none
-> {error
, missing_method
, S
};
125 tried
= [Module
|Tried
]
130 call1_try_super_attr(Superclass
, S
= #cstate
{obj
=Obj
, module
=Module
, args
=Args
, tried
=Tried
}) ->
131 %% look for attrs in the "super object"
132 case get_superobject(Obj
) of
133 undef
-> {error
, missing_superobj
, S
};
134 {ok
, Superobj
} when Module
== ?
CLASS(Obj
) ->
135 %% replace This with Superobj
138 args
= [Superobj
|tl(Args
)],
140 tried
= [Module
|Tried
]
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
150 case is_object(Obj
) of
152 DeepList
= inspect1(Obj
, "#<"),
153 lists:flatten(DeepList
);
155 thrift_utils:sformat("~p", [Obj
])
159 thrift_utils:sformat("INSPECT_ERROR(~p) ~p", [E
, Obj
])
161 %% TODO(cpiro): bring this back once we're done testing:
162 %% _:E -> thrift_utils:sformat("~p", [Obj])
165 inspect1(Obj
, Str
) ->
167 Inspect
= Class:inspect(Obj
),
168 Current
= atom_to_list(Class
) ++ ": " ++ Inspect
,
170 case get_superobject(Obj
) of
172 inspect1(Superobj
, Str
++ Current
++ " | ");
174 Str
++ Current
++ ">"
177 %% class(Obj) -> atom() = Class
179 class(Obj
) when is_tuple(Obj
) ->
180 %% if it's an object its first element will be a class name, and it'll have super/0
181 case apply_if_defined(?
CLASS(Obj
), super
, []) of
187 %% is the tuple/record an object?
188 %% is_object(Obj) = bool()
189 is_object(Obj
) when is_tuple(Obj
) ->
194 is_object(_
) -> false
.
200 %% apply_if_defined(MFA) -> {ok, apply(MFA)}
202 %% this could be worth some money
203 apply_if_defined(M
, F
, A
) ->
204 apply_if_defined({M
,F
,A
}).
206 apply_if_defined({M
,F
,A
} = MFA
) ->
208 %% io:format("apply ~p ~p ~p~n", [M,F,A]),
211 error:Kind
when Kind
== undef
; Kind
== function_clause
->
212 case erlang:get_stacktrace() of
213 %% the first stack call should match MFA when `apply' fails because the function is undefined
214 %% they won't match if the function is currently running and an error happens in the middle
215 [MFA
|_
] -> undef
; % trapped successfully
217 io:format("DONIT THE EXIT THING ~p~n", [Kind
]),
218 exit({Kind
, ST
}) % some unrelated error, re-exit
222 get_superobject(Obj
) ->
223 apply_if_defined(?
CLASS(Obj
), attr
, [Obj
, get, super
, get]).
229 tried(S
= #cstate
{module
=Module
, tried
=Tried
}) ->
230 lists:reverse([Module
|Tried
]).
232 error(missing_superobj
, S
= #cstate
{obj
=Obj
}) ->
233 exit({missing_superobj
, {inspect(Obj
), tried(S
)}});
234 error(missing_method
, S
= #cstate
{obj
=Obj
, func
=Func
, args
=Args
}) ->
235 exit({missing_method
, {Func
, inspect(Obj
), tl(Args
), tried(S
)}});
236 error(missing_attr
, S
= #cstate
{args
=Args
, first_obj
=FirstObj
}) ->
237 exit({missing_attr
, {hd(tl(Args
)), inspect(FirstObj
), tried(S
)}}).
239 error(missing_attr_set
, Field
, Obj
) ->
240 BT
= "..", %% TODO: give a backtrace
241 exit({missing_attr
, {Field
, inspect(Obj
), BT
}}).