2009-04-13 17:44:48 +00:00
// GtkSharp.Generation.GObjectVM.cs - GObject specific part of VM creation
//
// Author: Christian Hoff <christian_hoff@gmx.net>
//
// Copyright (c) 2007 Novell, Inc.
// Copyright (c) 2009 Christian Hoff
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
namespace GtkSharp.Generation {
using System ;
using System.Collections ;
using System.IO ;
using System.Xml ;
public class GObjectVM : VirtualMethod
{
protected string class_struct_name ;
const bool force_glue_generation = false ;
public GObjectVM ( XmlElement elem , ObjectBase container_type ) : base ( elem , container_type )
{
parms . HideData = false ;
this . Protection = "protected" ;
class_struct_name = container_type . ClassStructName ;
}
// Some types don't install headers. In that case, the glue code will not compile.
bool BlockGlue {
get {
return elem . GetAttribute ( "block_glue" ) = = "1" ;
}
}
protected override string CallString {
get {
return String . Format ( "{0} ({1})" , IsStatic ? this . CName + "_handler" : "On" + this . Name , call . ToString ( ) ) ;
}
}
public void Generate ( GenerationInfo gen_info , ObjectBase implementor )
{
2012-11-15 14:06:07 +00:00
gen_info . CurrentMember = Name ;
2009-04-13 17:44:48 +00:00
if ( ! CanGenerate ( gen_info , implementor ) )
throw new NotSupportedException ( String . Format ( "Cannot generate virtual method {0}.{1}. Make sure a writable glue path was provided to the generator." , container_type . Name , this . CallString ) ) ;
GenerateOverride ( gen_info , implementor ) ;
GenerateCallback ( gen_info . Writer , implementor ) ;
if ( ! IsStatic )
GenerateUnmanagedInvocation ( gen_info , implementor ) ;
}
protected virtual bool CanGenerate ( GenerationInfo gen_info , ObjectBase implementor )
{
if ( implementor ! = null | | this . CName . Length = = 0 | | CodeType = = VMCodeType . None | | ( CodeType = = VMCodeType . Glue & & ! gen_info . GlueEnabled ) )
return false ;
else
return true ;
}
enum VMCodeType {
None ,
Managed ,
Glue
}
VMCodeType CodeType {
get {
2013-10-12 20:04:25 +00:00
if ( ! ( ( ObjectBase ) container_type ) . CanGenerateClassStruct | | force_glue_generation ) {
2009-04-13 17:44:48 +00:00
if ( BlockGlue )
return VMCodeType . None ;
else
return VMCodeType . Glue ;
} else
return VMCodeType . Managed ;
}
}
enum VMOverrideType {
Unspecified ,
DeclaringClass ,
ImplementingClass
}
/ * There are basically two types of static virtual methods :
* 1. VMs overridden in the declaring class ( e . g . AtkUtil vms ) :
* The VM is overridden in the class in which it is declared and not in the derived classes . In that case , the GAPI generates a static XYZHandler property
* in the declaring class .
* 2. VMs overridden in derived classes ( e . g . GIO is_supported vms ) :
* As with nonstatic vms , this VM type hooks into the class structure field of derived classes . This type is currently unsupported as it is rarely used
* and we would need anonymous methods for the callback ( we are using only * one * callback method ; the callback does not know to which type that method call
* has to be redirected ) .
* /
VMOverrideType OverrideType {
get {
if ( IsStatic ) {
switch ( elem . GetAttribute ( "override_in" ) ) {
case "declaring_class" :
return VMOverrideType . DeclaringClass ;
case "implementing_class" :
return VMOverrideType . ImplementingClass ;
default :
return VMOverrideType . Unspecified ;
}
} else
return VMOverrideType . ImplementingClass ;
}
}
protected virtual void GenerateOverride ( GenerationInfo gen_info , ObjectBase implementor )
{
if ( CodeType = = VMCodeType . Glue )
GenerateOverride_glue ( gen_info ) ;
else
GenerateOverride_managed ( gen_info . Writer ) ;
}
protected virtual void GenerateUnmanagedInvocation ( GenerationInfo gen_info , ObjectBase implementor )
{
if ( CodeType = = VMCodeType . Glue )
GenerateUnmanagedInvocation_glue ( gen_info ) ;
else
GenerateUnmanagedInvocation_managed ( gen_info ) ;
}
protected void GenerateOverrideBody ( StreamWriter sw )
{
sw . WriteLine ( "\t\tstatic {0}NativeDelegate {0}_cb_delegate;" , Name ) ;
sw . WriteLine ( "\t\tstatic " + Name + "NativeDelegate " + Name + "VMCallback {" ) ;
sw . WriteLine ( "\t\t\tget {" ) ;
sw . WriteLine ( "\t\t\t\tif ({0}_cb_delegate == null)" , Name ) ;
sw . WriteLine ( "\t\t\t\t\t{0}_cb_delegate = new {0}NativeDelegate ({0}_cb);" , Name ) ;
sw . WriteLine ( "\t\t\t\treturn {0}_cb_delegate;" , Name ) ;
sw . WriteLine ( "\t\t\t}" ) ;
sw . WriteLine ( "\t\t}" ) ;
sw . WriteLine ( ) ;
if ( IsStatic ) {
sw . WriteLine ( "\t\tpublic delegate {0} {1}Delegate ({2});" , retval . CSType , Name , Signature . ToString ( ) ) ;
sw . WriteLine ( "\t\tstatic {0}Delegate {1}_handler;" , Name , CName ) ;
sw . WriteLine ( ) ;
sw . WriteLine ( "\t\tpublic static " + Name + "Delegate " + Name + "Handler {" ) ;
sw . WriteLine ( "\t\t\tset {" ) ;
sw . WriteLine ( "\t\t\t\t{0}_handler = value;" , CName ) ;
sw . WriteLine ( "\t\t\t\tOverride{0} ((GLib.GType) typeof ({1}), value == null ? null : {0}VMCallback);" , Name , container_type . Name ) ;
sw . WriteLine ( "\t\t\t}" ) ;
sw . WriteLine ( "\t\t}" ) ;
} else {
sw . WriteLine ( "\t\tstatic void Override{0} (GLib.GType gtype)" , this . Name ) ;
sw . WriteLine ( "\t\t{" ) ;
sw . WriteLine ( "\t\t\tOverride{0} (gtype, {0}VMCallback);" , this . Name ) ;
sw . WriteLine ( "\t\t}" ) ;
}
sw . WriteLine ( ) ;
sw . WriteLine ( "\t\tstatic void Override{0} (GLib.GType gtype, {0}NativeDelegate callback)" , this . Name ) ;
sw . WriteLine ( "\t\t{" ) ;
}
protected void GenerateOverride_managed ( StreamWriter sw )
{
GenerateOverrideBody ( sw ) ;
// Override VM; class_offset var is generated by object generatable
2017-10-04 00:13:35 +00:00
sw . WriteLine ( "\t\t\tunsafe {" ) ;
sw . WriteLine ( "\t\t\t\tIntPtr* raw_ptr = (IntPtr*)(((long) gtype.GetClassPtr()) + (long) class_abi.GetFieldOffset(\"{0}\"));" , CName ) ;
sw . WriteLine ( "\t\t\t\t*raw_ptr = Marshal.GetFunctionPointerForDelegate((Delegate) callback);" ) ;
sw . WriteLine ( "\t\t\t}" ) ;
sw . WriteLine ( "\t\t}" ) ;
2009-04-13 17:44:48 +00:00
sw . WriteLine ( ) ;
}
protected void GenerateMethodBody ( StreamWriter sw , ClassBase implementor )
{
sw . WriteLine ( "\t\t[GLib.DefaultSignalHandler(Type=typeof(" + ( implementor ! = null ? implementor . QualifiedName : container_type . QualifiedName ) + "), ConnectionMethod=\"Override" + this . Name + "\")]" ) ;
sw . Write ( "\t\t{0} " , this . Protection ) ;
if ( this . modifiers ! = "" )
sw . Write ( "{0} " , this . modifiers ) ;
sw . WriteLine ( "virtual {0} On{1} ({2})" , retval . CSType , this . Name , Signature . ToString ( ) ) ;
sw . WriteLine ( "\t\t{" ) ;
sw . WriteLine ( "\t\t\t{0}Internal{1} ({2});" , retval . IsVoid ? "" : "return " , this . Name , Signature . GetCallString ( false ) ) ;
sw . WriteLine ( "\t\t}" ) ;
sw . WriteLine ( ) ;
2012-08-05 16:19:32 +00:00
// This method is to be invoked from existing VM implementations in the custom code
2009-04-13 17:44:48 +00:00
sw . WriteLine ( "\t\tprivate {0} Internal{1} ({2})" , retval . CSType , this . Name , Signature . ToString ( ) ) ;
sw . WriteLine ( "\t\t{" ) ;
}
void GenerateUnmanagedInvocation_managed ( GenerationInfo gen_info )
{
StreamWriter sw = gen_info . Writer ;
string native_call = "this.Handle" ;
if ( parms . Count > 0 )
native_call + = ", " + Body . GetCallString ( false ) ;
this . GenerateMethodBody ( sw , null ) ;
// Find the first unmanaged ancestor
2020-04-06 20:31:10 +00:00
sw . WriteLine ( $"\t\t\t{Name}NativeDelegate unmanaged = class_abi.BaseOverride<{Name}NativeDelegate>(this.LookupGType(), \" { CName } \ ");" ) ;
2009-04-13 17:44:48 +00:00
sw . Write ( "\t\t\tif (unmanaged == null) " ) ;
if ( parms . HasOutParam )
sw . WriteLine ( "throw new InvalidOperationException (\"No base method to invoke\");" ) ;
else if ( retval . IsVoid )
sw . WriteLine ( "return;" ) ;
else
sw . WriteLine ( "return {0};" , retval . DefaultValue ) ;
sw . WriteLine ( ) ;
Body . Initialize ( gen_info ) ;
sw . Write ( "\t\t\t" ) ;
if ( ! retval . IsVoid )
sw . Write ( "{0} __result = " , retval . MarshalType ) ;
sw . WriteLine ( "unmanaged ({0});" , native_call ) ;
Body . Finish ( gen_info . Writer , "" ) ;
if ( ! retval . IsVoid )
sw . WriteLine ( "\t\t\treturn {0};" , retval . FromNative ( "__result" ) ) ;
sw . WriteLine ( "\t\t}" ) ;
sw . WriteLine ( ) ;
}
/ * old glue code . This code is to be used if
* a ) the generated api file is version 1
* b ) an old Mono version ( < 2.4 ) is being used
* Punt it when we drop support for the parser version 1.
* /
private string CastFromInt ( string type )
{
return type ! = "int" ? "(" + type + ") " : "" ;
}
private string GlueSignature {
get {
string [ ] glue_params = new string [ this . IsStatic ? parms . Count + 1 : parms . Count + 2 ] ;
glue_params [ 0 ] = class_struct_name + " *class_struct" ;
if ( ! IsStatic )
glue_params [ 1 ] = container_type . CName + "* inst" ;
for ( int i = 0 ; i < parms . Count ; i + + )
glue_params [ i + ( IsStatic ? 1 : 2 ) ] = parms [ i ] . CType . Replace ( "const-" , "const " ) + " " + parms [ i ] . Name ;
return String . Join ( ", " , glue_params ) ;
}
}
private string DefaultGlueValue {
get {
if ( retval . IGen is EnumGen )
return String . Format ( "({0}) 0" , retval . CType ) ;
string val = retval . DefaultValue ;
switch ( val ) {
case "null" :
return "NULL" ;
case "false" :
return "FALSE" ;
case "true" :
return "TRUE" ;
case "GLib.GType.None" :
return "G_TYPE_NONE" ;
default :
return val ;
}
}
}
void GenerateOverride_glue ( GenerationInfo gen_info )
{
StreamWriter glue = gen_info . GlueWriter ;
StreamWriter sw = gen_info . Writer ;
string glue_name = String . Format ( "{0}sharp_{1}_override_{2}" , container_type . NS . ToLower ( ) . Replace ( "." , "_" ) , container_type . Name . ToLower ( ) , CName ) ;
sw . WriteLine ( "\t\t[DllImport (\"{0}\")]" , gen_info . GluelibName ) ;
sw . WriteLine ( "\t\tstatic extern void {0} (IntPtr class_struct, {1}NativeDelegate cb);" , glue_name , Name ) ;
sw . WriteLine ( ) ;
glue . WriteLine ( "void {0} ({1} *class_struct, gpointer cb);\n" , glue_name , class_struct_name ) ;
glue . WriteLine ( "void\n{0} ({1} *class_struct, gpointer cb)" , glue_name , class_struct_name ) ;
glue . WriteLine ( "{" ) ;
glue . WriteLine ( "\tclass_struct->{0} = cb;" , CName ) ;
glue . WriteLine ( "}" ) ;
glue . WriteLine ( ) ;
GenerateOverrideBody ( sw ) ;
2009-08-08 23:42:15 +00:00
sw . WriteLine ( "\t\t\t{0} (gtype.GetClassPtr (), callback);" , glue_name ) ;
2009-04-13 17:44:48 +00:00
sw . WriteLine ( "\t\t}" ) ;
sw . WriteLine ( ) ;
}
void GenerateUnmanagedInvocation_glue ( GenerationInfo gen_info )
{
StreamWriter glue = gen_info . GlueWriter ;
string glue_name = String . Format ( "{0}sharp_{1}_invoke_{2}" , container_type . NS . ToLower ( ) . Replace ( "." , "_" ) , container_type . Name . ToLower ( ) , CName ) ;
glue . WriteLine ( "{0} {1} ({2});\n" , retval . CType . Replace ( "const-" , "const " ) , glue_name , GlueSignature ) ;
glue . WriteLine ( "{0}\n{1} ({2})" , retval . CType . Replace ( "const-" , "const " ) , glue_name , GlueSignature ) ;
glue . WriteLine ( "{" ) ;
glue . Write ( "\tif (class_struct->{0})\n\t\t" , CName ) ;
if ( ! retval . IsVoid )
glue . Write ( "return " ) ;
string [ ] call_args = new string [ IsStatic ? parms . Count : parms . Count + 1 ] ;
if ( ! IsStatic )
call_args [ 0 ] = "inst" ;
for ( int i = 0 ; i < parms . Count ; i + + )
call_args [ IsStatic ? i : i + 1 ] = parms [ i ] . Name ;
glue . WriteLine ( "(* class_struct->{0}) ({1});" , CName , String . Join ( ", " , call_args ) ) ;
if ( ! retval . IsVoid )
glue . WriteLine ( "\treturn " + DefaultGlueValue + ";" ) ;
glue . WriteLine ( "}" ) ;
glue . WriteLine ( ) ;
StreamWriter sw = gen_info . Writer ;
sw . WriteLine ( "\t\t[DllImport (\"{0}\")]" , gen_info . GluelibName ) ;
sw . Write ( "\t\tstatic extern {0} {1} (IntPtr class_struct" , retval . MarshalType , glue_name ) ;
if ( ! IsStatic )
sw . Write ( ", IntPtr inst" ) ;
if ( parms . Count > 0 )
sw . Write ( ", {0}" , parms . ImportSignature ) ;
sw . WriteLine ( ");" ) ;
sw . WriteLine ( ) ;
GenerateMethodBody ( sw , null ) ;
Body . Initialize ( gen_info , false , false , String . Empty ) ;
2009-08-08 23:42:15 +00:00
string glue_call_string = "this.LookupGType ().GetThresholdType ().GetClassPtr ()" ;
2009-04-13 17:44:48 +00:00
if ( ! IsStatic )
glue_call_string + = ", Handle" ;
if ( parms . Count > 0 )
glue_call_string + = ", " + Body . GetCallString ( false ) ;
sw . Write ( "\t\t\t" ) ;
if ( ! retval . IsVoid )
sw . Write ( "{0} __result = " , retval . MarshalType ) ;
sw . WriteLine ( "{0} ({1});" , glue_name , glue_call_string ) ;
Body . Finish ( gen_info . Writer , "" ) ;
if ( ! retval . IsVoid )
sw . WriteLine ( "\t\t\treturn {0};" , retval . FromNative ( "__result" ) ) ;
sw . WriteLine ( "\t\t}" ) ;
sw . WriteLine ( ) ;
}
2011-02-20 18:11:08 +00:00
public override bool Validate ( LogWriter log )
2009-04-13 17:44:48 +00:00
{
2011-02-20 18:11:08 +00:00
if ( ! base . Validate ( log ) )
return false ;
2009-04-13 17:44:48 +00:00
bool is_valid = true ;
if ( this . IsStatic ) {
switch ( OverrideType ) {
case VMOverrideType . Unspecified :
2011-02-20 18:11:08 +00:00
log . Warn ( "Static virtual methods can only be generated if you provide info on how to override this method via the metadata " ) ;
2009-04-13 17:44:48 +00:00
is_valid = false ;
break ;
case VMOverrideType . ImplementingClass :
2011-02-20 18:11:08 +00:00
log . Warn ( "Overriding static virtual methods in the implementing class is not supported yet " ) ;
2009-04-13 17:44:48 +00:00
is_valid = false ;
break ;
}
}
return is_valid ;
}
}
}