Browse Source

Merged revision(s) 33859, 34275, 34309, 34327, 34367, 34369, 34634 from trunk:
* pas2jni: Explicitly release JNI local refs while executing method pointers.
........
* pas2jni: Fixed string constant handling after recent changes in FPC.
........
* pas2jni: Fixed invoking of Java event handlers from non-main threads.
........
* pas2jni: Fixed handling of identical names of classes, proctypes when they are defined in different units.
........
* pas2jni: Removed option to create event handler by passing Java method method name as a string. It is not safe, since the target method is treated as unused by Java and the method may be removed from the resulting application.
........
* pas2jni: Reverted r34367 and mark undesired event handler creation "deprecated".
........

git-svn-id: branches/fixes_3_0@34774 -

yury 8 years ago
parent
commit
80e88fdc34
2 changed files with 135 additions and 70 deletions
  1. 5 21
      utils/pas2jni/readme.txt
  2. 130 49
      utils/pas2jni/writer.pas

+ 5 - 21
utils/pas2jni/readme.txt

@@ -33,11 +33,12 @@ The following Pascal features are supported by pas2jni:
 - pointer type;
 - string types;
 - all numeric types;
-- method pointer.
+- method pointer;
+- setters/getters for array elements.
 
 USUPPORTED features:
-- array;
-- procedure pointer.
+- Full support for arrays;
+- procedure pointer (Not possible to implement. To workaround this limitation create a procedure handler in your Pascal code and call a method pointer declared in some global Pascal class. Then you can assign this method pointer from a Java code).
 
 Shared libraries, generated by pas2jni were tested with Java on Windows and Android. It should work on other systems as well.
 
@@ -84,9 +85,7 @@ In a Java code you get the following TMyClass instance:
 
 TMyClass myclass = TMyClass.Create();
 
-It is possible set a Java handler in 2 ways:
-
-1) Place the handler inline.
+Then you add the event handler in a usual Java way:
 
 ...
   myclass.setOnChange(
@@ -98,21 +97,6 @@ It is possible set a Java handler in 2 ways:
     );
 ...
 
-2) Define the handler as a method in a class.
-
-public class MyJavaClass {
-  private void DoOnChange(TObject Sender) {
-    // The handler code
-  }
-
-  public void main() {
-    ...
-    // Set the handler to the method with the "DoOnChange" name in the current class (this).
-    myclass.setOnChange( new TNotifyEvent(this, "DoOnChange") );
-    ...
-  }
-}
-
 COMMAND LINE OPTIONS
 
 Usage: pas2jni [options] <unit> [<unit2> <unit3> ...]

+ 130 - 49
utils/pas2jni/writer.pas

@@ -54,12 +54,45 @@ type
     property SIndent: string read FIndStr;
   end;
 
+  { TClassInfo }
+
+  TClassInfo = class
+  public
+    Def: TDef;
+    Funcs: TObjectList;
+    IsCommonClass: boolean;
+    constructor Create;
+    destructor Destroy; override;
+  end;
+
+  { TProcInfo }
+
+  TProcInfo = class
+  public
+    Name: string;
+    JniName: string;
+    JniSignature: string;
+  end;
+
+  { TClassList }
+
+  TClassList = class(TStringList)
+  private
+    function GetFullName(const AName: string; Def: TDef): string;
+  public
+    constructor Create;
+    function Add(const AName: string; Def: TDef; Info: TClassInfo): integer;
+    function IndexOf(const AName: string; Def: TDef): integer; reintroduce;
+    function GetClassName(Index: integer): string;
+    function GetClassInfo(Index: integer): TClassInfo;
+  end;
+
   { TWriter }
 
   TWriter = class
   private
     Fjs, Fps: TTextOutStream;
-    FClasses: TStringList;
+    FClasses: TClassList;
     FPkgDir: string;
     FUniqueCnt: integer;
     FThisUnit: TUnitDef;
@@ -169,6 +202,50 @@ begin
   Result:='{$ifdef windows} stdcall {$else} cdecl {$endif};';
 end;
 
+{ TClassList }
+
+function TClassList.IndexOf(const AName: string; Def: TDef): integer;
+begin
+  Result:=inherited IndexOf(GetFullName(AName, Def));
+end;
+
+function TClassList.GetClassName(Index: integer): string;
+var
+  i: integer;
+begin
+  Result:=Strings[Index];
+  i:=Pos('.', Result);
+  if i > 0 then
+    System.Delete(Result, 1, i);
+end;
+
+function TClassList.GetClassInfo(Index: integer): TClassInfo;
+begin
+  Result:=TClassInfo(Objects[Index]);
+end;
+
+function TClassList.GetFullName(const AName: string; Def: TDef): string;
+begin
+  if (Def = nil) or (Def.DefType = dtUnit) then
+    Result:=AName
+  else begin
+    while (Def.Parent <> nil) and (Def.DefType <> dtUnit) do
+      Def:=Def.Parent;
+    Result:=Def.Name + '.' + AName;
+  end;
+end;
+
+constructor TClassList.Create;
+begin
+  inherited Create;
+  Sorted:=True;
+end;
+
+function TClassList.Add(const AName: string; Def: TDef; Info: TClassInfo): integer;
+begin
+  Result:=AddObject(GetFullName(AName, Def), Info);
+end;
+
 { TTextOutStream }
 
 procedure TTextOutStream.SetIndednt(const AValue: integer);
@@ -210,24 +287,6 @@ begin
     Indent:=Indent - 1;
 end;
 
-type
-  { TClassInfo }
-  TClassInfo = class
-  public
-    Def: TDef;
-    Funcs: TObjectList;
-    IsCommonClass: boolean;
-    constructor Create;
-    destructor Destroy; override;
-  end;
-
-  TProcInfo = class
-  public
-    Name: string;
-    JniName: string;
-    JniSignature: string;
-  end;
-
 { TClassInfo }
 
 constructor TClassInfo.Create;
@@ -705,9 +764,9 @@ begin
     pi.JniSignature:=GetProcSignature(d);
     if AParent = nil then begin
       // Checking duplicate proc name and duplicate param types
-      ClassIdx:=FClasses.IndexOf(GetJavaClassName(d.Parent, ItemDef));
+      ClassIdx:=FClasses.IndexOf(GetJavaClassName(d.Parent, ItemDef), d.Parent);
       if ClassIdx >= 0 then begin
-        ci:=TClassInfo(FClasses.Objects[ClassIdx]);
+        ci:=FClasses.GetClassInfo(ClassIdx);
         j:=1;
         ss:=Copy(pi.JniSignature, 1, Pos(')', pi.JniSignature));
         repeat
@@ -973,16 +1032,16 @@ begin
       AParent:=d.Parent;
     end
     else
-      ClassIdx:=FClasses.IndexOf(GetJavaClassName(AParent, ItemDef));
+      ClassIdx:=FClasses.IndexOf(GetJavaClassName(AParent, ItemDef), AParent);
 
     if ClassIdx < 0 then begin
       ci:=TClassInfo.Create;
       ci.Def:=AParent;
       s:=GetJavaClassName(AParent, ItemDef);
       ci.IsCommonClass:=s <> AParent.Name;
-      ClassIdx:=FClasses.AddObject(s, ci);
+      ClassIdx:=FClasses.Add(s, AParent, ci);
     end;
-    TClassInfo(FClasses.Objects[ClassIdx]).Funcs.Add(pi);
+    FClasses.GetClassInfo(ClassIdx).Funcs.Add(pi);
     pi:=nil;
 
     // Java part
@@ -1170,19 +1229,30 @@ begin
       s:='double';
   end
   else begin
-    s:=DefToJavaType(d.VarType);
-    if d.VarType.DefType = dtType then
-      case TTypeDef(d.VarType).BasicType of
-        btLongWord, btInt64:
-          v:=v + 'L';
-        btBoolean:
-          if v = '1' then
-            v:='true'
-          else
-            v:='false';
-      end;
+    s:='';
+    case d.VarType.DefType of
+      dtType:
+        case TTypeDef(d.VarType).BasicType of
+          btLongWord, btInt64:
+            v:=v + 'L';
+          btBoolean:
+            if v = '1' then
+              v:='true'
+            else
+              v:='false';
+        end;
+      dtArray:
+        with TArrayDef(d.VarType) do
+          if (ElType.DefType = dtType) and (TTypeDef(ElType).BasicType in [btChar, btWideChar]) then
+            s:='String';
+    end;
+    if s = '' then
+      s:=DefToJavaType(d.VarType);
   end;
-  Fjs.WriteLn(Format('public static final %s %s = %s;', [s, d.Name, v]));
+  v:=Format('public static final %s %s = %s;', [s, d.Name, v]);
+  if s = SUnsupportedType then
+    v:='// ' + v;
+  Fjs.WriteLn(v);
 end;
 
 procedure TWriter.WriteEnum(d: TDef);
@@ -1254,6 +1324,7 @@ begin
     Fps.WriteLn('var');
     Fps.IncI;
     Fps.WriteLn('_env: PJNIEnv;');
+    Fps.WriteLn('_new_env: boolean;');
     Fps.WriteLn('_mpi: _TMethodPtrInfo;');
     if d.Count > 0 then begin
       Fps.WriteLn(Format('_args: array[0..%d] of jvalue;', [d.Count - 1]));
@@ -1270,6 +1341,11 @@ begin
     Fps.WriteLn('begin');
     Fps.IncI;
     Fps.WriteLn('CurJavaVM^^.GetEnv(CurJavaVM, @_env, JNI_VERSION_1_6);');
+    Fps.WriteLn('_new_env:=_env = nil;');
+    Fps.WriteLn('if _new_env then CurJavaVM^^.AttachCurrentThread(CurJavaVM, @_env, nil);');
+    Fps.WriteLn('_env^^.PushLocalFrame(_env, 100);');
+    Fps.WriteLn('try');
+    Fps.IncI;
     Fps.WriteLn('_MethodPointersCS.Enter;');
     Fps.WriteLn('try');
     Fps.WriteLn('_mpi:=_TMethodPtrInfo(_MethodPointers[-integer(ptruint(Self)) - 1]);', 1);
@@ -1330,6 +1406,11 @@ begin
           end;
     end;
 
+    Fps.DecI;
+    Fps.WriteLn('finally');
+    Fps.WriteLn('_env^^.PopLocalFrame(_env, nil);', 1);
+    Fps.WriteLn('if _new_env then CurJavaVM^^.DetachCurrentThread(CurJavaVM);', 1);
+    Fps.WriteLn('end;');
     Fps.DecI;
     Fps.WriteLn('end;');
 
@@ -1356,7 +1437,7 @@ begin
   Fjs.IncI;
   Fjs.WriteLn(Format('{ mSignature = "%s"; }', [GetProcSignature(d)]));
   Fjs.WriteLn(Format('protected %s(long objptr, boolean cleanup) { _pasobj=objptr; }', [d.Name]));
-  Fjs.WriteLn(Format('public %s(Object Obj, String MethodName) { mObject=Obj; mName=MethodName; }', [d.Name]));
+  Fjs.WriteLn(Format('@Deprecated public %s(Object Obj, String MethodName) { mObject=Obj; mName=MethodName; }', [d.Name]));
   Fjs.WriteLn(Format('public %s() { mObject=this; mName="Execute"; }', [d.Name]));
   Fjs.WriteLn(Format('protected %s throws NoSuchMethodException { throw new NoSuchMethodException(); }', [GetJavaProcDeclaration(d, 'Execute')]));
   Fjs.DecI;
@@ -1770,6 +1851,7 @@ begin
       Fjs.WriteLn('public int Value;');
       Fjs.WriteLn('public int Ord() { return Value; }');
       Fjs.WriteLn('@Override public boolean equals(Object o) { return (o instanceof Integer) && Value == (Integer)o; }');
+      Fjs.WriteLn('public boolean equals(int v) { return Value == v; }');
       Fjs.WriteLn('@Override public int hashCode() { return Value; }');
       Fjs.DecI;
       Fjs.WriteLn('}');
@@ -1891,10 +1973,10 @@ begin
 
   Fps.WriteLn('const');
   for i:=0 to FClasses.Count - 1 do begin
-    ci:=TClassInfo(FClasses.Objects[i]);
+    ci:=FClasses.GetClassInfo(i);
     if ci.Funcs.Count = 0 then
       continue;
-    Fps.WriteLn(Format('  _%sNativeMethods: array[0..%d] of JNINativeMethod = (', [GetClassPrefix(ci.Def, FClasses[i]), ci.Funcs.Count - 1]));
+    Fps.WriteLn(Format('  _%sNativeMethods: array[0..%d] of JNINativeMethod = (', [GetClassPrefix(ci.Def, FClasses.GetClassName(i)), ci.Funcs.Count - 1]));
     for j:=0 to ci.Funcs.Count - 1 do begin
       with TProcInfo(ci.Funcs[j]) do
         Fps.Write(Format('    (name: ''%s''; signature: ''%s''; fnPtr: @%s)', [Name, JniSignature, JniName]));
@@ -1953,7 +2035,7 @@ begin
   end;
 
   for i:=0 to FClasses.Count - 1 do begin
-    ci:=TClassInfo(FClasses.Objects[i]);
+    ci:=FClasses.GetClassInfo(i);
     s:=GetTypeInfoVar(ci.Def);
     if (s = '') or (ci.IsCommonClass) then
       s:='nil'
@@ -1962,13 +2044,13 @@ begin
     if ci.Funcs.Count = 0 then
       ss:='nil'
     else
-      ss:=Format('@_%sNativeMethods', [GetClassPrefix(ci.Def, FClasses[i])]);
+      ss:=Format('@_%sNativeMethods', [GetClassPrefix(ci.Def, FClasses.GetClassName(i))]);
     fn:='';
     if ci.Def <> nil then
       if ci.Def.DefType in [dtSet, dtEnum] then
         fn:=', ''Value'', ''I''';
     Fps.WriteLn(Format('if not _Reg(''%s'', %s, %d, %s%s) then exit;',
-                       [GetJavaClassPath(ci.Def, FClasses[i]), ss, ci.Funcs.Count, s, fn]));
+                       [GetJavaClassPath(ci.Def, FClasses.GetClassName(i)), ss, ci.Funcs.Count, s, fn]));
   end;
 
   Fps.WriteLn('Result:=JNI_VERSION_1_6;');
@@ -2249,10 +2331,10 @@ procedure TWriter.RegisterPseudoClass(d: TDef);
 var
   ci: TClassInfo;
 begin
-  if FClasses.IndexOf(d.Name) < 0 then begin
+  if FClasses.IndexOf(d.Name, d) < 0 then begin
     ci:=TClassInfo.Create;
     ci.Def:=d;
-    FClasses.AddObject(d.Name, ci);
+    FClasses.Add(d.Name, d, ci);
   end;
 end;
 
@@ -2305,13 +2387,13 @@ begin
   pi.Name:=Name;
   pi.JniName:=JniName;
   pi.JniSignature:=Signature;
-  i:=FClasses.IndexOf(ParentDef.AliasName);
+  i:=FClasses.IndexOf(ParentDef.AliasName, ParentDef);
   if i < 0 then begin
     ci:=TClassInfo.Create;
     ci.Def:=ParentDef;
-    i:=FClasses.AddObject(ParentDef.AliasName, ci);
+    i:=FClasses.Add(ParentDef.AliasName, ParentDef, ci);
   end;
-  TClassInfo(FClasses.Objects[i]).Funcs.Add(pi);
+  FClasses.GetClassInfo(i).Funcs.Add(pi);
 end;
 
 function TWriter.GetProcSignature(d: TProcDef): string;
@@ -2398,8 +2480,7 @@ var
   i: integer;
 begin
   Units:=TStringList.Create;
-  FClasses:=TStringList.Create;
-  FClasses.Sorted:=True;
+  FClasses:=TClassList.Create;
   JavaPackage:='pas';
   IncludeList:=TStringList.Create;
   IncludeList.Duplicates:=dupIgnore;