private void Init(RuntimeCore runtimeCore) { if (mInterpreter != null) { return; } mInterpreter = new Interpreter(runtimeCore, true); int ci = activation.JILRecord.classIndex; int fi = activation.JILRecord.funcIndex; string className = ""; if (Constants.kInvalidIndex != ci) { var classNode = mInterpreter.runtime.exe.classTable.ClassNodes[ci]; mFNode = classNode.ProcTable.Procedures[fi]; className = classNode.Name; } var argTypes = new List <Type>(activation.ParameterTypes); if (null != mFNode && mFNode.IsAutoGeneratedThisProc) { argTypes.RemoveAt(0); } FFIHandler handler = FFIHandlers[activation.ModuleType]; FFIFunctionPointer functionPointer = handler.GetFunctionPointer( activation.ModuleName, className, activation.FunctionName, argTypes, activation.ReturnType); mFunctionPointer = Validate(functionPointer) ? functionPointer : null; mFunctionPointer.IsDNI = activation.IsDNI; mInterpreter.runtime.executingBlock = runtimeCore.RunningBlock; activation.JILRecord.globs = runtimeCore.DSExecutable.runtimeSymbols[runtimeCore.RunningBlock].GetGlobalSize(); }
public override StackValue Execute(ProtoCore.Runtime.Context c, List <StackValue> formalParameters, ProtoCore.DSASM.StackFrame stackFrame, RuntimeCore runtimeCore) { // ensure there is no data race, function resolution and execution happens in parallel // but for FFI we want it to be serial cause the code we are calling into may not cope // with parallelism. // // we are always looking and putting our function pointers in handler with each lang // so better lock for FFIHandler (being static) it will be good object to lock // lock (FFIHandlers) { Interpreter interpreter = new Interpreter(runtimeCore, true); // Setup the stack frame data StackValue svThisPtr = stackFrame.ThisPtr; int ci = activation.JILRecord.classIndex; int fi = activation.JILRecord.funcIndex; int returnAddr = stackFrame.ReturnPC; int blockDecl = stackFrame.FunctionBlock; int blockCaller = stackFrame.FunctionCallerBlock; int framePointer = runtimeCore.RuntimeMemory.FramePointer; int locals = activation.JILRecord.locals; FFIHandler handler = FFIHandlers[activation.ModuleType]; FFIActivationRecord r = activation; string className = ""; if (activation.JILRecord.classIndex > 0) { className = runtimeCore.DSExecutable.classTable.ClassNodes[activation.JILRecord.classIndex].Name; } List <ProtoCore.Type> argTypes = new List <Type>(r.ParameterTypes); ProcedureNode fNode = null; if (ProtoCore.DSASM.Constants.kInvalidIndex != ci) { fNode = interpreter.runtime.exe.classTable.ClassNodes[ci].ProcTable.Procedures[fi]; } // Check if this is a 'this' pointer function overload that was generated by the compiler if (null != fNode && fNode.IsAutoGeneratedThisProc) { int thisPtrIndex = 0; bool isStaticCall = svThisPtr.IsPointer && Constants.kInvalidPointer == svThisPtr.Pointer; if (isStaticCall) { stackFrame.ThisPtr = formalParameters[thisPtrIndex]; } argTypes.RemoveAt(thisPtrIndex); // Comment Jun: Execute() can handle a null this pointer. // But since we dont even need to to reach there if we dont have a valid this pointer, then just return null if (formalParameters[thisPtrIndex].IsNull) { runtimeCore.RuntimeStatus.LogWarning(ProtoCore.Runtime.WarningID.DereferencingNonPointer, Resources.kDeferencingNonPointer); return(StackValue.Null); } // These are the op types allowed. Validity.Assert(formalParameters[thisPtrIndex].IsPointer || formalParameters[thisPtrIndex].IsDefaultArgument); svThisPtr = formalParameters[thisPtrIndex]; formalParameters.RemoveAt(thisPtrIndex); } FFIFunctionPointer functionPointer = handler.GetFunctionPointer(r.ModuleName, className, r.FunctionName, argTypes, r.ReturnType); mFunctionPointer = Validate(functionPointer) ? functionPointer : null; mFunctionPointer.IsDNI = activation.IsDNI; if (mFunctionPointer == null) { return(ProtoCore.DSASM.StackValue.Null); } { interpreter.runtime.executingBlock = runtimeCore.RunningBlock; activation.JILRecord.globs = runtimeCore.DSExecutable.runtimeSymbols[runtimeCore.RunningBlock].GetGlobalSize(); // Params formalParameters.Reverse(); for (int i = 0; i < formalParameters.Count; i++) { interpreter.Push(formalParameters[i]); } List <StackValue> registers = interpreter.runtime.GetRegisters(); // Comment Jun: the depth is always 0 for a function call as we are reseting this for each function call // This is only incremented for every language block bounce int depth = 0; StackFrameType callerType = stackFrame.CallerStackFrameType; // FFI calls do not have execution states runtimeCore.RuntimeMemory.PushFrameForLocals(locals); StackFrame newStackFrame = new StackFrame(svThisPtr, ci, fi, returnAddr, blockDecl, blockCaller, callerType, StackFrameType.Function, depth, framePointer, 0, registers, 0); runtimeCore.RuntimeMemory.PushStackFrame(newStackFrame); //is there a way the current stack be passed across and back into the managed runtime by FFI calling back into the language? //e.g. DCEnv* carrying all the stack information? look at how vmkit does this. // = jilMain.Run(ActivationRecord.JILRecord.pc, null, true); //double[] tempArray = GetUnderlyingArray<double>(jilMain.runtime.rmem.stack); Object ret = mFunctionPointer.Execute(c, interpreter); StackValue op; if (ret == null) { op = StackValue.Null; } else if (ret is StackValue) { op = (StackValue)ret; } else if (ret is Int64 || ret is int) { op = StackValue.BuildInt((Int64)ret); } else if (ret is double) { op = StackValue.BuildDouble((double)ret); } else { throw new ArgumentException(string.Format("FFI: incorrect return type {0} from external function {1}:{2}", activation.ReturnType.Name, activation.ModuleName, activation.FunctionName)); } // Clear the FFI stack frame // FFI stack frames have no local variables interpreter.runtime.rmem.FramePointer = (int)interpreter.runtime.rmem.GetAtRelative(StackFrame.FrameIndexFramePointer).IntegerValue; interpreter.runtime.rmem.PopFrame(StackFrame.StackFrameSize + formalParameters.Count); return(op); } } }
public override StackValue Execute(ProtoCore.Runtime.Context c, List <StackValue> formalParameters, ProtoCore.DSASM.StackFrame stackFrame, Core core) { // ensure there is no data race, function resolution and execution happens in parallel // but for FFI we want it to be serial cause the code we are calling into may not cope // with parallelism. // // we are always looking and putting our function pointers in handler with each lang // so better lock for FFIHandler (being static) it will be good object to lock // lock (FFIHandlers) { ProtoCore.DSASM.Interpreter interpreter = new ProtoCore.DSASM.Interpreter(core, true); StackValue svThisPtr = stackFrame.GetAt(StackFrame.AbsoluteIndex.kThisPtr); StackValue svBlockDecl = stackFrame.GetAt(DSASM.StackFrame.AbsoluteIndex.kFunctionBlock); // Setup the stack frame data //int thisPtr = (int)stackFrame.GetAt(DSASM.StackFrame.AbsoluteIndex.kThisPtr).opdata; int ci = activation.JILRecord.classIndex; int fi = activation.JILRecord.funcIndex; int returnAddr = (int)stackFrame.GetAt(DSASM.StackFrame.AbsoluteIndex.kReturnAddress).opdata; int blockDecl = (int)svBlockDecl.opdata; int blockCaller = (int)stackFrame.GetAt(DSASM.StackFrame.AbsoluteIndex.kFunctionCallerBlock).opdata; int framePointer = core.Rmem.FramePointer; int locals = activation.JILRecord.locals; FFIHandler handler = FFIHandlers[activation.ModuleType]; FFIActivationRecord r = activation; string className = ""; if (activation.JILRecord.classIndex > 0) { className = core.DSExecutable.classTable.ClassNodes[activation.JILRecord.classIndex].name; } bool gcThisPtr = false; List <ProtoCore.Type> argTypes = new List <Type>(r.ParameterTypes); ProcedureNode fNode = null; if (ProtoCore.DSASM.Constants.kInvalidIndex != ci) { fNode = interpreter.runtime.exe.classTable.ClassNodes[ci].vtable.procList[fi]; } // Check if this is a 'this' pointer function overload that was generated by the compiler if (null != fNode && fNode.isAutoGeneratedThisProc) { int thisPtrIndex = 0; bool isStaticCall = svThisPtr.IsPointer && Constants.kInvalidPointer == (int)svThisPtr.opdata; if (isStaticCall) { thisPtrIndex = formalParameters.Count - 1; } argTypes.RemoveAt(thisPtrIndex); // Comment Jun: Execute() can handle a null this pointer. // But since we dont even need to to reach there if we dont have a valid this pointer, then just return null if (formalParameters[thisPtrIndex].IsNull) { core.RuntimeStatus.LogWarning(ProtoCore.RuntimeData.WarningID.kDereferencingNonPointer, ProtoCore.RuntimeData.WarningMessage.kDeferencingNonPointer); return(StackValue.Null); } // These are the op types allowed. Validity.Assert(formalParameters[thisPtrIndex].IsPointer || formalParameters[thisPtrIndex].IsDefaultArgument); svThisPtr = formalParameters[thisPtrIndex]; gcThisPtr = true; formalParameters.RemoveAt(thisPtrIndex); } FFIFunctionPointer functionPointer = handler.GetFunctionPointer(r.ModuleName, className, r.FunctionName, argTypes, r.ReturnType); mFunctionPointer = Validate(functionPointer) ? functionPointer : null; mFunctionPointer.IsDNI = activation.IsDNI; if (mFunctionPointer == null) { return(ProtoCore.DSASM.StackValue.Null); } List <object> ps = new List <object>(); //obsolete { interpreter.runtime.executingBlock = core.RunningBlock; activation.JILRecord.globs = core.DSExecutable.runtimeSymbols[core.RunningBlock].GetGlobalSize(); // Params formalParameters.Reverse(); for (int i = 0; i < formalParameters.Count; i++) { interpreter.Push(formalParameters[i]); } List <StackValue> registers = new List <DSASM.StackValue>(); interpreter.runtime.SaveRegisters(registers); // Comment Jun: the depth is always 0 for a function call as we are reseting this for each function call // This is only incremented for every language block bounce int depth = 0; StackFrameType callerType = (StackFrameType)stackFrame.GetAt(StackFrame.AbsoluteIndex.kCallerStackFrameType).opdata; // FFI calls do not have execution states core.Rmem.PushStackFrame(svThisPtr, ci, fi, returnAddr, blockDecl, blockCaller, callerType, ProtoCore.DSASM.StackFrameType.kTypeFunction, depth, framePointer, registers, locals, 0); //is there a way the current stack be passed across and back into the managed runtime by FFI calling back into the language? //e.g. DCEnv* carrying all the stack information? look at how vmkit does this. // = jilMain.Run(ActivationRecord.JILRecord.pc, null, true); //double[] tempArray = GetUnderlyingArray<double>(jilMain.runtime.rmem.stack); Object ret = mFunctionPointer.Execute(c, interpreter); StackValue op; if (ret == null) { op = StackValue.Null; } else if (ret is StackValue) { op = (StackValue)ret; } else if (ret is Int64 || ret is int) { op = StackValue.BuildInt((Int64)ret); } else if (ret is double) { op = StackValue.BuildDouble((double)ret); } else { throw new ArgumentException(string.Format("FFI: incorrect return type {0} from external function {1}:{2}", activation.ReturnType.Name, activation.ModuleName, activation.FunctionName)); } // gc the parameters if (gcThisPtr)// && core.Options.EnableThisPointerFunctionOverload) { // thisptr is sent as parameter, so need to gc it. // but when running in expression interpreter mode, do not GC because in DSASM.Executive.DecRefCounter() related GC functions, // the reference count will not be changed in expression interpreter mode. if (core.ExecMode != ProtoCore.DSASM.InterpreterMode.kExpressionInterpreter) { interpreter.runtime.Core.Rmem.Heap.GCRelease(new StackValue[] { svThisPtr }, interpreter.runtime); } } interpreter.runtime.Core.Rmem.Heap.GCRelease(formalParameters.ToArray(), interpreter.runtime); // increment the reference counter of the return value interpreter.runtime.GCRetain(op); // Clear the FFI stack frame // FFI stack frames have no local variables interpreter.runtime.rmem.FramePointer = (int)interpreter.runtime.rmem.GetAtRelative(ProtoCore.DSASM.StackFrame.kFrameIndexFramePointer).opdata; interpreter.runtime.rmem.PopFrame(ProtoCore.DSASM.StackFrame.kStackFrameSize + formalParameters.Count); return(op); //DSASM.Mirror.ExecutionMirror.Unpack(op, core.heap, core); } } }