private static void EmitInvoker(EmitHelper emit, MethodInfo method, int?argc, bool setter) { var parameters = method.GetParameters(); var methodArgC = parameters.Length; if (!argc.HasValue) { EmitThrowIfArgumentCountMismatch(emit, methodArgC); argc = methodArgC; } emit.LdArg(retvalArgIndex); // instance method if (!method.IsStatic) { emit.LdArg(objArgIndex); } // prepare arguments for (var i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; if (i < argc.Value) { var changeTypeMethod = CefConvert.GetChangeTypeMethod(typeof(cef_v8value_t *), parameter.ParameterType); EmitLdV8Argument(emit, i); emit.Call(changeTypeMethod); } else { // push default arg if (!parameter.IsOptional) { throw new JSBindingException("MethodInvoker compilation error."); } EmitLdRawDefaultValue(emit, parameter.RawDefaultValue); } } // call target method if (method.IsStatic) { emit.Call(method); } else { emit.CallVirt(method); } if (method.ReturnType == typeof(void)) { // If method is setter, then it can be called with retval == null from V8Accessor. if (setter) { var returnValueLabel = emit.DefineLabel(); emit.Dup() .BrTrueS(returnValueLabel) .Pop() .Ret() .MarkLabel(returnValueLabel); ; } if (ForceVoidToUndefined) { emit.Call(createUndefinedNativeV8ValueMethod); } else { emit.LdNull(); } } else { // convert return value var retValchangeTypeMethod = CefConvert.GetChangeTypeMethod(method.ReturnType, typeof(cef_v8value_t *)); emit.Call(retValchangeTypeMethod); } // store result at retval emit.StIndI(); // return emit.Ret(); }
private static object InvokeScript(CefV8Context context, string memberName, params object[] args) { if (context == null) { throw new ArgumentNullException("context"); } if (!context.Enter()) { throw new CefException("Failed to enter V8 context."); } try { // TODO: this list can be private list of context, 'cause we can invoke only one function at one time List <CefV8Value> proxies = new List <CefV8Value>(16); // javascript 'this' object CefV8Value obj = null; CefV8Value target = context.GetGlobal(); proxies.Add(target); if (!memberName.Contains('.')) { obj = target; target = obj.GetValue(memberName); proxies.Add(target); } else { foreach (var member in memberName.Split('.')) { obj = target; target = obj.GetValue(member); // TODO: do analysis of target - if it is not an object - throw if (!target.IsObject) { throw new CefException("Argument 'memberName' must be member access expression to a function. Invalid object in path."); } proxies.Add(target); } } if (!target.IsFunction) { throw new ArgumentException("Argument 'memberName' must be member access expression to a function."); } CefV8Value[] v8Args; if (args.Length == 0) { v8Args = null; } else { v8Args = new CefV8Value[args.Length]; // TODO: InvokeScript core can be optimized by prevent recreating arrays for (var i = 0; i < args.Length; i++) { var value = CefConvert.ToV8Value(args[i]); v8Args[i] = value; } } var v8RetVal = target.ExecuteFunctionWithContext(context, obj, v8Args); // force destroing of proxies, to avoid unneccessary GC load (CLR and V8) foreach (var proxy in proxies) { proxy.Dispose(); } proxies.Clear(); // FIXME: not sure if exception CAN be null, this need to be checked if (v8RetVal.HasException) { var exception = v8RetVal.GetException(); throw new JavaScriptException(exception.GetMessage()); } //if (!string.IsNullOrEmpty(exception)) var result = CefConvert.ToObject(v8RetVal); v8RetVal.Dispose(); return(result); } finally { if (!context.Exit()) { throw new CefException("Failed to exit V8 context."); } } }