diff --git a/Project.toml b/Project.toml index 0da2b37..e417830 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MetaModelica" uuid = "9d7f2a79-07b5-5542-8b19-c0100dda6b06" authors = ["Martin Sjölund ", "John Tinnerholm "] -version = "0.3.0" +version = "0.3.1" [deps] Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" diff --git a/src/functionInheritance.jl b/src/functionInheritance.jl index 080870a..88d82c8 100644 --- a/src/functionInheritance.jl +++ b/src/functionInheritance.jl @@ -25,50 +25,62 @@ TODO: @ExtendedAnonFunction and @ExtendedFunction do not work for inline #= Indicates that a symbolic parameter does not have a default argument. =# struct NoDefaultArg end -function getSignatureAsArrayFor(func::Function)::Array - methodLst = methods(func) - sizeOfLeastGenericSignature = size(methodLst.ms, 1) - @assert(sizeOfLeastGenericSignature >= 1, - "Invalid function passed to getSignatureAsArrayFor(). Size was: $(sizeOfLeastGenericSignature)") - argumentSymbols::Array = Base.method_argnames(methodLst.ms[sizeOfLeastGenericSignature])[2:end] - #= Creates an array with the maximum number of positional arguments. =# - defaultValues = code_lowered(func)[1].code[1].args[2:end] - #= Construct an array of (Symbol, default value or NoDefaultArg). =# - local signatureArray::Array = [] - local nDefaultValues::Integer = size(defaultValues, 1) - local nArgumentSymbols::Integer = size(argumentSymbols, 1) +function getSignatureAsArrayFor(func::Function)::Vector + local methodLst = collect(methods(func)) + @assert(!isempty(methodLst), + "Invalid function passed to getSignatureAsArrayFor(). No methods found.") + #= The full (highest-arity) method carries every positional argument name. =# + local fullMethod = argmax(m -> m.nargs, methodLst) + local argumentSymbols::Vector = Base.method_argnames(fullMethod)[2:end] + local nArgumentSymbols::Int = length(argumentSymbols) + local signatureArray::Vector = [] + #= Default values live in the lowest-arity auto-generated forwarding method, + whose body is `self(slots..., defaults...)`. When only the full method + exists the function has no defaults. =# + local defaultCarrier = argmin(ci -> ci.nargs, code_lowered(func)) + if defaultCarrier.nargs == fullMethod.nargs + for s in argumentSymbols + push!(signatureArray, (s, NoDefaultArg())) + end + return signatureArray + end + local forwardingCall = nothing + for st in defaultCarrier.code + if st isa Expr && st.head === :call + forwardingCall = st + break + end + end + @assert(forwardingCall !== nothing, + "Could not locate the default-forwarding call in getSignatureAsArrayFor().") + #= Skip the forwarded function/self; remaining args align with the full signature. =# + local forwardedArgs = forwardingCall.args[2:end] for i = 1:nArgumentSymbols - #= - If no default value is present, use NoDefaultArg. - This case occurs if i is larger than the number of default values. - Sometimes Core.SlotNumber occurs, sometimes it does not. - =# - if i > nDefaultValues - push!(signatureArray, (argumentSymbols[i], NoDefaultArg())) - elseif typeof(defaultValues[i]) == Core.SlotNumber + local val = forwardedArgs[i] + if val isa Core.SlotNumber || val isa Core.Argument push!(signatureArray, (argumentSymbols[i], NoDefaultArg())) else - push!(signatureArray, (argumentSymbols[i], defaultValues[i])) + push!(signatureArray, (argumentSymbols[i], val)) end end signatureArray end -function getNewFunctionArgs(functionToExtend, func::Function)::Array{Tuple} +function getNewFunctionArgs(functionToExtend, func::Function)::Vector{Tuple} #= These are the arguments that we want to change. =# - functionToExtendArgs::Array = functionToExtend.args[2:end] #= Skip function symbol. =# - oldFunctionSig::Array = getSignatureAsArrayFor(func) - local functionToExtendArgsAsTuples::Array{Tuple} = [] + functionToExtendArgs::Vector = functionToExtend.args[2:end] #= Skip function symbol. =# + oldFunctionSig::Vector = getSignatureAsArrayFor(func) + local functionToExtendArgsAsTuples::Vector{Tuple} = [] for t in functionToExtendArgs #= Convert expr to tuple. =# pair = Tuple(t.args) @assert(size(pair, 1) >= 2, "Incorrect parameter passed to @ExtendFunction") push!(functionToExtendArgsAsTuples, (first(pair), last(pair))) end - local numberOfArguments::Integer = size(functionToExtendArgsAsTuples, 1) + local numberOfArguments::Int = size(functionToExtendArgsAsTuples, 1) #= Create arguments for the new function. Generally, new functions have more symbols than the old. =# - newArgsAsString::Array = [] + newArgsAsString::Vector = [] for i = 1:numberOfArguments symToFind = first(functionToExtendArgsAsTuples[i]) findFunc(x) = first(x) == symToFind @@ -101,7 +113,7 @@ end function makeFunctionHelper(functionToExtend::Expr, __module__::Module)::Tuple funcSym::Symbol = functionToExtend.args[1] func::Function = getFuncFromSym(funcSym, __module__) - args::Array{Tuple} = getNewFunctionArgs(functionToExtend, func) + args::Vector{Tuple} = getNewFunctionArgs(functionToExtend, func) newFuncArgs::Tuple = Tuple(map(tupleToKwOrSymbol, args)) argSymArr::Tuple = Tuple(map(tupleToArgSym, args)) (func, newFuncArgs, argSymArr)