From 5a8d7e0a8c22ac53e64a81d341e2086621c49837 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 21 Sep 2024 10:48:59 +0200 Subject: [PATCH] Stop wrapping Julia modules in special objects This resolves issues with outdated caches in our module wrappers. --- pkg/JuliaInterface/gap/JuliaInterface.gd | 29 ++--------- pkg/JuliaInterface/gap/JuliaInterface.gi | 66 +++++++++++++----------- pkg/JuliaInterface/read.g | 5 -- pkg/JuliaInterface/src/JuliaInterface.c | 7 ++- pkg/JuliaInterface/tst/utils.tst | 12 ++++- 5 files changed, 57 insertions(+), 62 deletions(-) diff --git a/pkg/JuliaInterface/gap/JuliaInterface.gd b/pkg/JuliaInterface/gap/JuliaInterface.gd index 517bfa206..64a759e42 100644 --- a/pkg/JuliaInterface/gap/JuliaInterface.gd +++ b/pkg/JuliaInterface/gap/JuliaInterface.gd @@ -109,8 +109,7 @@ #! However not every object living on the Julia side is in this filter. #! For example Julia booleans and small Int values are directly #! translated to GAP booleans and small integers, while for Julia functions -#! and wrappers dedicated wrappers are used for improved efficiency -#! respectively additional features. +#! dedicated wrappers are used to improve efficiency and add features. #! @BeginExampleSession #! gap> JuliaEvalString( "x = 4" );; #! gap> Julia.x; @@ -119,8 +118,6 @@ #! false #! gap> IsJuliaObject( Julia.sqrt ); #! false -#! gap> IsJuliaObject( Julia.Main ); -#! false #! @EndExampleSession DeclareCategory( "IsJuliaObject", IsObject ); @@ -138,14 +135,10 @@ BindGlobal("TheTypeJuliaObject", NewType( JuliaObjectFamily, IsJuliaObject )); #! This admits implementing high-level wrapper objects #! for &Julia; objects that behave just like the &Julia; objects #! when used as arguments in calls to &Julia; functions. -#! #! #! Objects in #! should not be in the filter #! . -#! -#! For example, any Julia modules such as Julia.Base are -#! in the filter . DeclareCategory( "IsJuliaWrapper", IsObject ); #! @Arguments obj @@ -153,18 +146,6 @@ DeclareCategory( "IsJuliaWrapper", IsObject ); #! is an attribute for &GAP; objects in the filter #! . #! The value must be a &Julia; object. -#! @BeginExampleSession -#! gap> Julia; -#! -#! gap> IsJuliaObject( Julia ); -#! false -#! gap> IsJuliaWrapper( Julia ); -#! true -#! gap> ptr:= JuliaPointer( Julia ); -#! -#! gap> IsJuliaObject( ptr ); -#! true -#! @EndExampleSession DeclareAttribute( "JuliaPointer", IsJuliaWrapper ); #! @Arguments obj @@ -181,10 +162,10 @@ DeclareAttribute( "JuliaPointer", IsJuliaWrapper ); #! gap> Julia.GAP.julia_to_gap; #! #! @EndExampleSession -DeclareCategory( "IsJuliaModule", IsJuliaWrapper and IsRecord ); +DeclareCategory( "IsJuliaModule", IsJuliaObject and IsRecord ); BindGlobal( "TheFamilyOfJuliaModules", NewFamily( "TheFamilyOfJuliaModules" ) ); -BindGlobal( "TheTypeOfJuliaModules", NewType( TheFamilyOfJuliaModules, IsJuliaModule and IsAttributeStoringRep ) ); +BindGlobal( "TheTypeOfJuliaModules", NewType( TheFamilyOfJuliaModules, IsJuliaModule and HasName ) ); #! @Section Creating &Julia; objects @@ -271,9 +252,7 @@ DeclareGlobalVariable( "Julia" ); #! @BeginExampleSession #! gap> JuliaTypeInfo( Julia.GAP ); #! "Module" -#! gap> JuliaTypeInfo( JuliaPointer( Julia.GAP ) ); -#! "Module" -#! gap> JuliaTypeInfo( JuliaEvalString( "sqrt(2)" ) ); +#! gap> JuliaTypeInfo( Julia.sqrt(2) ); #! "Float64" #! gap> JuliaTypeInfo( 1 ); #! "Int64" diff --git a/pkg/JuliaInterface/gap/JuliaInterface.gi b/pkg/JuliaInterface/gap/JuliaInterface.gi index d28a5f3c2..a58880157 100644 --- a/pkg/JuliaInterface/gap/JuliaInterface.gi +++ b/pkg/JuliaInterface/gap/JuliaInterface.gi @@ -4,22 +4,9 @@ # Implementations # -BindGlobal( "_JULIA_MODULE_TYPE", _JuliaGetGlobalVariableByModule( "Module", "Core" ) ); BindGlobal( "_JULIA_FUNCTION_TYPE", _JuliaGetGlobalVariableByModule( "Function", "Core" ) ); BindGlobal( "_JULIA_ISA", _WrapJuliaFunction( _JuliaGetGlobalVariableByModule( "isa", "Core" ) ) ); - -BindGlobal( "_WrapJuliaModule", - function( name, julia_pointer ) - local str; - - str:= Concatenation( "" ); - - return ObjectifyWithAttributes( rec( storage := rec( ) ), - TheTypeOfJuliaModules, - Name, str, - String, str, - JuliaPointer, julia_pointer ); -end ); +BindGlobal( "_JULIA_GAP", _JuliaGetGapModule() ); InstallMethod( ViewString, [ "IsFunction and IsInternalRep and HasNameFunction" ], @@ -31,59 +18,78 @@ InstallMethod( ViewString, TryNextMethod(); end ); +InstallMethod( ViewString, + [ "IsJuliaModule" ], + function( module ) + return Concatenation( "" ); + end ); + +InstallMethod( Name, + [ "IsJuliaModule" ], + function( module ) + return Concatenation( "" ); + end ); + InstallMethod( \., - [ "IsJuliaModule", "IsPosInt" ], + [ "IsJuliaModule", "IsPosInt and IsSmallIntRep" ], function( module, rnum ) local rnam, var; - if IsBound\.( module!.storage, rnum ) then - return \.(module!.storage, rnum ); - fi; - rnam := NameRNam( rnum ); - var := _JuliaGetGlobalVariableByModule( rnam, JuliaPointer( module ) ); + if IsIdenticalObj(module, Julia) and rnam = "GAP" then + ## Ensure that the Julia module GAP is always accessible as Julia.GAP, + ## even while it is still being initialized, and also if it not actually + ## exported to the Julia Main module + return _JULIA_GAP; + fi; + + var := _JuliaGetGlobalVariableByModule( rnam, module ); if var = fail then Error( rnam, " is not bound in Julia" ); fi; if _JULIA_ISA( var, _JULIA_FUNCTION_TYPE ) then var := _WrapJuliaFunction( var ); - elif _JULIA_ISA( var, _JULIA_MODULE_TYPE ) then - var := _WrapJuliaModule( rnam, var ); - \.\:\=( module!.storage, rnum, var ); fi; return var; end ); InstallMethod( \.\:\=, - [ "IsJuliaModule", "IsPosInt", "IsObject" ], + [ "IsJuliaModule", "IsPosInt and IsSmallIntRep", "IsObject" ], function( module, rnum, obj ) Julia.GAP._setglobal( module, JuliaSymbol( NameRNam( rnum ) ), obj ); end ); InstallMethod( IsBound\., - [ "IsJuliaModule", "IsPosInt" ], + [ "IsJuliaModule", "IsPosInt and IsSmallIntRep" ], function( module, rnum ) - if IsBound\.( module!.storage, rnum ) then + local rnam; + + rnam := NameRNam( rnum ); + if IsIdenticalObj(module, Julia) and rnam = "GAP" then + ## Ensure that the Julia module GAP is always accessible as Julia.GAP, + ## even while it is still being initialized, and also if it not actually + ## exported to the Julia Main module return true; fi; - return fail <> _JuliaGetGlobalVariableByModule( NameRNam( rnum ), JuliaPointer( module ) ); + + return fail <> _JuliaGetGlobalVariableByModule( rnam, module ); end ); InstallMethod( Unbind\., - [ "IsJuliaModule", "IsPosInt" ], + [ "IsJuliaModule", "IsPosInt and IsSmallIntRep" ], function( module, rnum ) Error( "cannot unbind Julia variables" ); end ); InstallMethod(RecNames, [ "IsJuliaModule" ], function( obj ) - return JuliaToGAP( IsList, Julia.GAP.get_symbols_in_module( JuliaPointer( obj ) ), true ); + return JuliaToGAP( IsList, Julia.GAP.get_symbols_in_module( obj ), true ); end); -InstallValue( Julia, _WrapJuliaModule( "Main", _JuliaGetGlobalVariableByModule( "Main", "Main" ) ) ); +InstallValue( Julia, _JuliaGetGlobalVariableByModule( "Main", "Main" ) ); InstallGlobalFunction( "JuliaIncludeFile", function( filename, module_name... ) diff --git a/pkg/JuliaInterface/read.g b/pkg/JuliaInterface/read.g index 06b4ed4d3..c73e00b4c 100644 --- a/pkg/JuliaInterface/read.g +++ b/pkg/JuliaInterface/read.g @@ -5,11 +5,6 @@ # ReadPackage( "JuliaInterface", "gap/JuliaInterface.gi"); -## Ensure that the Julia module GAP is always accessible as Julia.GAP, -## even while it is still being initialized, and also if it not actually -## exported to the Julia Main module -Julia!.storage.GAP := _WrapJuliaModule( "GAP", _JuliaGetGapModule() ); - ReadPackage( "JuliaInterface", "gap/adapter.gi"); ReadPackage( "JuliaInterface", "gap/calls.gi"); ReadPackage( "JuliaInterface", "gap/convert.gi"); diff --git a/pkg/JuliaInterface/src/JuliaInterface.c b/pkg/JuliaInterface/src/JuliaInterface.c index a39223c6c..22d163464 100644 --- a/pkg/JuliaInterface/src/JuliaInterface.c +++ b/pkg/JuliaInterface/src/JuliaInterface.c @@ -21,6 +21,7 @@ static jl_datatype_t * JULIA_GAPFFE_type; static jl_datatype_t * gap_datatype_mptr; +static Obj TheTypeOfJuliaModules; static Obj TheTypeJuliaObject; static UInt T_JULIA_OBJ; @@ -114,7 +115,10 @@ jl_value_t * GET_JULIA_OBJ(Obj o) static Obj JuliaObjectTypeFunc(Obj o) { - return TheTypeJuliaObject; + if (jl_typeis(GET_JULIA_OBJ(o), jl_module_type)) + return TheTypeOfJuliaModules; + else + return TheTypeJuliaObject; } Obj NewJuliaObj(jl_value_t * v) @@ -282,6 +286,7 @@ static Int InitKernel(StructInitInfo * module) // init filters and functions InitHdlrFuncsFromTable(GVarFuncs); + InitCopyGVar("TheTypeOfJuliaModules", &TheTypeOfJuliaModules); InitCopyGVar("TheTypeJuliaObject", &TheTypeJuliaObject); T_JULIA_OBJ = RegisterPackageTNUM("JuliaObject", JuliaObjectTypeFunc); diff --git a/pkg/JuliaInterface/tst/utils.tst b/pkg/JuliaInterface/tst/utils.tst index 3df6c57e1..734a06ca6 100644 --- a/pkg/JuliaInterface/tst/utils.tst +++ b/pkg/JuliaInterface/tst/utils.tst @@ -56,7 +56,7 @@ gap> _JuliaGetGlobalVariableByModule("Base","sqrt"); Error, sqrt is not a module gap> _JuliaGetGlobalVariableByModule("sqrt","Base"); -gap> _JuliaGetGlobalVariableByModule("sqrt", JuliaPointer(Julia.Base)); +gap> _JuliaGetGlobalVariableByModule("sqrt", Julia.Base); ## @@ -85,5 +85,15 @@ gap> path:= GetJuliaScratchspace( "test_scratch" );; gap> IsDirectoryPath( path ); true +# Julia modules should not get cached, see #1044 +gap> JuliaEvalString("module foo x = 1 end"); + +gap> Julia.foo.x; +1 +gap> JuliaEvalString("module foo x = 2 end"); + +gap> Julia.foo.x; +2 + ## gap> STOP_TEST( "utils.tst" );