[Apollo-talk:52] Pursuit of edit.on_click <= Re: methods

Back to archive index

Take_tk ggb03****@nifty*****
Wed Nov 13 00:23:51 JST 2002


Hi! "repeater" <repea****@lucen*****>.  This is take_tk

[Apollo-talk:48] methods
"repeater" <repea****@lucen*****> wrote:

: 1.) the process of wrapping the methods, where and how

----
Pursuit of edit.on_click :

1.1.1. at the time of 'require "phi"'

'require "phi"' calls "phi.rb", and "phi.rb" calls "phi.so".

=>C:/PROGRA~1/Apollo/bin/phi.rb[phi]
require 'win32.rb'

=>C:/PROGRA~1/Apollo/bin/win32.rb[phi]
require 'phi.so'

Ruby performs Init_phi procedure, when it reads a library "phi.so".

=>C:/PROGRA~1/Apollo/src/Phi.dpr[Init_phi]
exports
  Init_phi;

=>C:/PROGRA~1/Apollo/src/PhiMainUnit.pas[Init_phi]
procedure Init_phi; cdecl;
begin
	....
  Init_Edit;
	....
end;

=>C:/PROGRA~1/Apollo/src/u/uEdit.pas[OutputPersistentClass]
procedure Init_edit;
begin
  ....
  cEdit := OutputPersistentClass(mPhi, TEdit, cWinControl, ap_iEdit_v);
  rb_define_method(cEdit, 'event_handle', @Edit_event_handle, 1);
  ....
end

OutputPersistentClass becomes effective, when phi.dpr is compiled with
MAKE_PROP definition.
It outputs the followings to a file called PhiProp.inc.
This is a Delphi source which add methods to the Edit object of ruby.
Then recompile Phi.dpr with the code.
I will explain this later. Here, I'll explain as the code exists already.

=>C:/PROGRA~1/Apollo/src/PhiProp.inc[edit]
  klass := cEdit;
  rb_define_method(klass, 'on_click=', @Prop_set_method, 1);
  rb_define_method(klass, 'on_click', @retnil, -1);

The line of "rb_define_method( .. );" defines a Ruby method called
Edit#event_handle. 
Contents is a Delphi function  Edit_event_handle.

=>C:/PROGRA~1/Apollo/src/u/uEdit.pas[Edit_event_handle]
function Edit_event_handle(This, name: Tvalue): Tvalue; cdecl;
begin
  EventHandle(This, name, [Handle]);
  result := Qnil;
end;

1.1.2. when "Edit.new"

This "Edit::event_handle" method is called in the following portion of
phi.rb. It is called by "initialize" of a Persistent class. That is, it
is called at the time of "Edit.new".

=>C:/PROGRA~1/Apollo/bin/phi.rb[EventHandle]
module Phi
  EventHandle = {}
  class Persistent
  ....
    def initialize(*args)
      super(*args)
      if EventHandle[type]
         EventHandle[type].each do|id|
           begin
             event_handle id
           rescue DelphiError
             #
           end
         end
      end
    end
  end
end

This "EventHandle" in the ruby script is a Hash object which contains {
class => [method_name1, .. ] }
The method names are added, when a method is defined or added by
"include" or "extend".

The line of "event_handle id" performs the following Delphi code. 
"Handle" is a general-purpose PhiHandle object defined by uHandle.pas.

=>C:/PROGRA~1/Apollo/src/u/uEdit.pas[Edit_event_handle]
function Edit_event_handle(This, name: Tvalue): Tvalue; cdecl;
begin
  EventHandle(This, name, [Handle]);
  result := Qnil;
end;

=>C:/PROGRA~1/Apollo/src/u/uHandle.pas[Handle]
var
  Handle: TPhiHandle;

The arguments of EventHandle, "This" if cEdit and "event_name" is
'on_click'.
EventHandle(cEdit,"on_click") performs a Delphi program "Edit1.OnClick :
= TPhiHandle.NotifyOnClick;".  

=>C:/PROGRA~1/Apollo/src/u/uProp.pas[EventHandle]
procedure EventHandle(This, event_name: Tvalue; Handles: array of TObject);
var
  Obj: TObject;
  FPropInfo: PPropInfo;
  ATypeInfo: PTypeInfo;
  AMethod: TMethod;
  AHandle: TObject;
  name: ShortString;
  i: Integer;
begin
  ap_data_get_object(This, TObject, Obj);
  FPropInfo := GetPropInfo(Obj, Capitalize1(dl_caption(event_name)));
  if FPropInfo = nil then
    ap_raise(ap_eDelphiError, 'property not found');
  ATypeInfo := FPropInfo^.PropType^;
  if ATypeInfo^.Kind <> tkMethod then
    ap_raise(ap_eDelphiError, 'not method');
  name := ATypeInfo^.Name;
  name := Copy(name, 2, Length(name)-6) + FPropInfo^.Name; // => "NotifyOnClick"
  for i := Low(Handles) to High(Handles) do
  begin
    AHandle := Handles[i];
    AMethod.Code := AHandle.MethodAddress(name);
    AMethod.Data := AHandle;
    if AMethod.Code = nil then
    else
    begin
      SetMethodProp(Obj, FPropInfo, AMethod);  // OnXX := doXX;
    end;
  end;
end;

"name" is assigned with "NotifyOnClick", which is constructed "Notify",
the core name of the class name "TNotifyEvent" and the event name
"OnClick".
The line "SetMethodProp(Obj, FPropInfo, and AMethod);" is equivalent to
a Delphi program "Edit1.OnClick : = TPhiHandle.NotifyOnClick;".  
Then, when an event occurs, TPhiHandle.NotifyOnClick comes to be called.

1.1.3. when the event occurred

When the event occurs, TPhiHandle.NotifyOnClick is called.
NotifyOnClick is defined in the following portion.

=>C:/PROGRA~1/Apollo/src/PhiHandle.pas[NotifyOnClick]
procedure TPhiHandle.NotifyOnClick(Sender: TObject);
begin
  doNotify(Sender, 'on_click');
end;

procedure TPhiHandle.doNotify(Sender: TObject; name: PChar);
var
  recv, data: Tvalue;
begin
  recv := TComponent(Sender).tag;
  if recv = 0 then Exit;
  data := rb_ary_new;
  rb_ary_push(data, rb_intern(name));
  rb_ary_push(data, recv);
  PhiCallProtect(data);
end;

"PhiCallProtect(data);" should perform ruby script "edit1.on_click(self)".

=>C:/PROGRA~1/Apollo/src/PhiMainUnit.pas[PhiCallProtect]
function PhiCallProtect(data: Tvalue): Tvalue;
var
  state: Integer;
begin
  result := ap_protect(PhiCall, data, state);
  if state <> 0 then PhiFail;
end;

function PhiCall(data: Tvalue): Tvalue; cdecl;
var
  id: Tvalue;
begin
  id := rb_ary_shift(data);
  result := PhiEventCall(id, data);
end;

function PhiEventCall(id: Tid; args: Tvalue): Tvalue;
var
  hash, key, obj, recv: Tvalue;
begin
  result := Qnil;
  try
    hash := rb_iv_get(ap_ary_ptr(args)^, '@events');
    if RTYPE(hash) = T_HASH then
    begin
      key := ID2SYM(id);
      obj := rb_hash_aref(hash, key);
      if obj <> ap_hash_ifnone(hash) then
        result := rb_apply(obj, id_call, args);  // call "proc" type handler
    end;
    if result = Qnil then
    begin
      recv := rb_ary_shift(args);
      if RTEST(recv) then
        result := rb_apply(recv, id, args);  // call "def" type handler
    end;
  except
    on E: Exception do
      ap_raise(ap_eDelphiError, E.message);
  end;
end;

PhiEventCall で最初に on_click= でセットされた proc 型のイベントハンドラを呼び出し、次に def 型のイベントハンドラを呼び出す。これで行き止まり。

PhiEventCall calls "proc" type handler first, and secondly calls "def" type handler.

1.1.4. precompile

It returns to a start.

=>C:/PROGRA~1/Apollo/src/u/uEdit.pas[OutputPersistentClass]
procedure Init_edit;
begin
  ....
  cEdit := OutputPersistentClass(mPhi, TEdit, cWinControl, ap_iEdit_v);
  ....
end

OutputPersistentClass becomes effective, when phi.dpr is compiled with MAKE_PROP definition.
It outputs the followings to a file called PhiProp.inc.
This is a Delphi source which add methods to the Edit object of ruby.
Then recompile Phi.dpr with the code.

=>C:/PROGRA~1/Apollo/src/PhiProp.inc[edit]
  klass := cEdit;
  rb_define_method(klass, 'on_click=', @Prop_set_method, 1);
  rb_define_method(klass, 'on_click', @retnil, -1);

=>C:/PROGRA~1/Apollo/src/PhiProp.bat[MAKE_PROP]
dcc32 -B -DMAKE_PROP phi
cd ..\bin
ruby_ap -rphi.so -e0
cd ..\src
dcc32 -B phi > out
ruby PhiProp.rb out
dcc32 -B phi
del PhiProp.inc~
del out

=>C:/PROGRA~1/Apollo/src/u/uPersistent.pas[OutputPersistentClass]
function OutputPersistentClass(module: Tvalue; AClass: TPersistentClass; super: Tvalue; func: TAllocFunc): Tvalue;
var
  v, t: Tvalue;
  S: string;
  i: Integer;
begin
  v := cPersistent;
  t := rb_funcall2(super, rb_intern('>'), 1, @v);
  if RTEST(t) then
    ap_raise(ap_eDelphiError, AClass.ClassName +': '+ rb_class2name(super)+ ' > ' +rb_class2name(v)+ ' in OutputPersistentClass');
  try
    RegisterClass(AClass);
  except
    on E: EFilerError do;
  end;
  S := AClass.ClassName;
  PhiAllocFuncList.AddObject(S, @func);
  i := 1;
  if Copy(S, 1, 5) = 'TPhi_' then i := 5;
  result := rb_define_class_under(module, PChar(S)+i, super);
  rb_iv_set(result, '_class', rb_data_object_alloc(ap_cObject, AClass.ClassInfo, nil, nil));
  OutputProp(result, AClass);
  rb_ary_push(vPhiComponents, result);
end;

=>C:/PROGRA~1/Apollo/src/u/uHandle.pas[PhiAllocFuncList]
function PhiAllocFuncList: TStringList;
begin
  result := Handle.AllocFuncList;
end;

=>C:/PROGRA~1/Apollo/src/PhiHandle.pas[AllocFuncList]
type
  TPhiHandle = class(TObject)
  private
    AppDoneThread: TNotifyEvent;
  public
    ExtentList: TObjectList;
    ObjectList: TObjectList;
    AllocFuncList: TStringList;
    EventFuncList: TStringList;

=>C:/PROGRA~1/Apollo/src/u/uProp.pas[OutputProp]
{$IFDEF MAKE_PROP}
procedure OutputProp(cClass: Tvalue; AClass: TClass);
var
  ATypeInfo: PTypeInfo;
  APropList: TPropList;
  Count, i: Integer;
  ATypeData: PTypeData;
  APropInfo: TPropInfo;
  name: PChar;
  ary, defined_p: Tvalue;
  argc: Integer;
  args: array of Tvalue;
  Readable, Writable: Boolean;
begin
  argc := 1;
  SetLength(args, argc);
  args[0] := Qtrue;
  ary := rb_class_instance_methods(argc, @args, cClass);

  ATypeInfo := PTypeInfo(AClass.ClassInfo);
  ATypeData := GetTypeData(ATypeInfo);
  Count := ATypeData^.PropCount;
  GetPropInfos(ATypeInfo, @APropList);

  OutputClassName(cClass);

  for i := 0 to Count-1 do
  begin
    APropInfo := APropList[i]^;
    ATypeData := GetTypeData(APropInfo.PropType^);

    name := PChar(LowerCase1(APropInfo.Name));
    if name = 'tag' then continue;

    Readable := APropInfo.GetProc <> nil;
    Writable := APropInfo.SetProc <> nil;

    { cClass#name method defined? }
    defined_p := rb_ary_includes(ary, rb_str_new2(name));
    if not RTEST(defined_p) then
    begin
    case APropInfo.PropType^^.Kind of
    tkInteger, tkChar, tkWChar:
      begin
        if Writable then OutputAttrSet(cClass, name, 'Prop_set_integer');
        if Readable then OutputAttrGet(cClass, name, 'Prop_get_integer');
      end;
        ....
    tkMethod:
      begin
        if Writable then OutputAttrSet(cClass, name, 'Prop_set_method');
        if Readable then OutputMethod(cClass, name, 'retnil');
      end;
    end;

    end;
  end;
end;
{$ELSE}
procedure OutputProp(cClass: Tvalue; AClass: TClass);
begin
// nothing
end;
{$ENDIF}

Event handler should be matched to tkMethod.

    tkMethod:
      begin
        if Writable then OutputAttrSet(cClass, name, 'Prop_set_method');
        if Readable then OutputMethod(cClass, name, 'retnil');
      end;

OutputMethod performs the following output. This is the dammy handler
for "def" type handler.

=>C:/PROGRA~1/Apollo/src/PhiProp.inc[edit]
  rb_define_method(klass, 'on_click', @retnil, -1);

C:/PROGRA~1/Apollo/src/u/uProp.pas[OutputMethod]
procedure OutputMethod(klass: Tvalue; name, func: PChar);
begin
  rb_define_method(klass, name, nil, 0);
  WriteLn(FProp, '  rb_define_method(klass, ''', name, ''', @', func, ', -1);');
end;

OutputAttrSet performs the following output. This is for setting a "proc"
type handler.

=>C:/PROGRA~1/Apollo/src/PhiProp.inc[edit]
  rb_define_method(klass, 'on_click=', @Prop_set_method, 1);

=>C:/PROGRA~1/Apollo/src/u/uProp.pas[OutputAttrSet]
procedure OutputAttrSet(klass: Tvalue; name, attr: PChar);
begin
  rb_define_method(klass, PChar(joinEq(name)), nil, 0);
  WriteLn(FProp, '  rb_define_method(klass, ''', name, '='', @', attr, ', 1);');
end;

1.1.5. when "Edit.on_click = proc{ .. }"

In this one, further pursuit of Prop_set_method is needed.

=>C:/PROGRA~1/Apollo/src/u/uProp.pas[Prop_set_method]
function Prop_set_method(This, v: Tvalue): Tvalue; cdecl;
var
  hash, key: Tvalue;
  name: string;
  id: Tid;
begin
  hash := rb_iv_get(This, '@events');
  name := LowerCase1(rb_id2name(rb_frame_last_func));
  SetLength(name, Length(name)-1); // chop!
  id := rb_intern(PChar(name));
  key := ID2SYM(id);
  if rb_respond_to(This, id_event_handle) <> 0 then
       rb_funcall2(This, id_event_handle, 1, @key);
  rb_hash_aset(hash, key, v);
  result := v;
end;

The instance variable of a ruby Hash object called "@events" is prepared.
rb_frame_last_func contains the method name "on_click="
The last "=" is removed. And puts "on_click" into "name".
And it calls Edit.event_handle with "on_click".
And "@events["on_click"] = proc_object"
It calls Edit.event_handle method with "on_click", if it exists

=>C:/PROGRA~1/Apollo/src/u/uProp.pas[id_event_handle]
var
  id_event_handle: Tid;
procedure Init_Prop;
begin
  id_event_handle := rb_intern('event_handle');
end;

event_handle is already explained.
It performs a Delphi program "Edit1.OnClick : = TPhiHandle.NotifyOnClick;".  

=>C:/PROGRA~1/Apollo/src/u/uEdit.pas[Edit_event_handle]
function Edit_event_handle(This, name: Tvalue): Tvalue; cdecl;
begin
  EventHandle(This, name, [Handle]);
  result := Qnil;
end;
---

 huiiii.

take_tk = kumagai hidetake



More information about the Apollo-talk mailing list
Back to archive index