Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow call from native contract #1700

Merged
merged 13 commits into from
Jun 22, 2020
44 changes: 40 additions & 4 deletions src/neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ namespace Neo.SmartContract
{
public partial class ApplicationEngine : ExecutionEngine
{
private class InvocationState
{
public Type ReturnType;
public Delegate Callback;
}

public static event EventHandler<NotifyEventArgs> Notify;
public static event EventHandler<LogEventArgs> Log;

Expand All @@ -28,6 +34,7 @@ public partial class ApplicationEngine : ExecutionEngine
private readonly List<NotifyEventArgs> notifications = new List<NotifyEventArgs>();
private readonly List<IDisposable> disposables = new List<IDisposable>();
private readonly Dictionary<UInt160, int> invocationCounter = new Dictionary<UInt160, int>();
private readonly Dictionary<ExecutionContext, InvocationState> invocationStates = new Dictionary<ExecutionContext, InvocationState>();

public static IEnumerable<InteropDescriptor> Services => services.Values;
public TriggerType Trigger { get; }
Expand Down Expand Up @@ -55,13 +62,42 @@ internal bool AddGas(long gas)
return testMode || GasConsumed <= gas_amount;
}

internal void CallFromNativeContract(Action onComplete, UInt160 hash, string method, params StackItem[] args)
{
invocationStates.Add(CurrentContext, new InvocationState
{
ReturnType = typeof(void),
Callback = onComplete
});
CallContract(hash, ContractParameterType.Void, method, new VMArray(args));
}

internal void CallFromNativeContract<T>(Action<T> onComplete, UInt160 hash, string method, params StackItem[] args)
{
invocationStates.Add(CurrentContext, new InvocationState
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
{
ReturnType = typeof(T),
Callback = onComplete
});
CallContract(hash, ContractParameterType.Any, method, new VMArray(args));
}

protected override void ContextUnloaded(ExecutionContext context)
{
base.ContextUnloaded(context);
if (context.EvaluationStack == CurrentContext?.EvaluationStack) return;
int rvcount = context.GetState<ExecutionContextState>().RVCount;
if (rvcount != -1 && rvcount != context.EvaluationStack.Count)
throw new InvalidOperationException();
if (context.EvaluationStack != CurrentContext?.EvaluationStack)
{
int rvcount = context.GetState<ExecutionContextState>().RVCount;
if (rvcount != -1 && rvcount != context.EvaluationStack.Count)
throw new InvalidOperationException();
}
if (!(UncaughtException is null)) return;
shargon marked this conversation as resolved.
Show resolved Hide resolved
if (invocationStates.Count == 0) return;
if (!invocationStates.TryGetValue(CurrentContext, out InvocationState state)) return;
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
if (state.Callback is Action action)
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
action();
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
else
state.Callback.DynamicInvoke(Convert(Pop(), new InteropParameterDescriptor(state.ReturnType)));
}

protected override void LoadContext(ExecutionContext context)
Expand Down
15 changes: 10 additions & 5 deletions src/neo/SmartContract/InteropParameterDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,25 @@ internal class InteropParameterDescriptor
};

public InteropParameterDescriptor(ParameterInfo parameterInfo)
: this(parameterInfo.ParameterType)
{
Name = parameterInfo.Name;
Type = parameterInfo.ParameterType;
this.Name = parameterInfo.Name;
}

public InteropParameterDescriptor(Type type)
{
this.Type = type;
if (IsEnum)
{
Converter = converters[Type.GetEnumUnderlyingType()];
Converter = converters[type.GetEnumUnderlyingType()];
}
else if (IsArray)
{
Converter = converters[Type.GetElementType()];
Converter = converters[type.GetElementType()];
}
else
{
IsInterface = !converters.TryGetValue(Type, out var converter);
IsInterface = !converters.TryGetValue(type, out var converter);
if (IsInterface)
Converter = converters[typeof(InteropInterface)];
else
Expand Down