private IP5Any Caller(bool noarg, int level, Opcode.ContextValues cxt) { if (level >= CallStack.Count) { if (cxt == Opcode.ContextValues.SCALAR) return new P5Scalar(this); else return new P5List(this); } StackFrame frame; if (level == 0) frame = CallStack.Peek(); else { frame = new StackFrame(); foreach (var f in CallStack) { frame = f; if (level == 0) break; --level; } } if (cxt == Opcode.ContextValues.SCALAR) return new P5Scalar(this, frame.Package); else if (noarg) return new P5List( this, new P5Scalar(this, frame.Package), new P5Scalar(this, frame.File), new P5Scalar(this, frame.Line)); else { var callcxt = frame.Context == Opcode.ContextValues.VOID ? new P5Scalar(this) : frame.Context == Opcode.ContextValues.SCALAR ? new P5Scalar(this, "") : new P5Scalar(this, 1); var subname = frame.Code != null ? new P5Scalar(this, frame.Code.Name) : new P5Scalar(this); return new P5List( this, new P5Scalar(this, frame.Package), new P5Scalar(this, frame.File), new P5Scalar(this, frame.Line), subname, new P5Scalar(this), // hasargs callcxt, // context new P5Scalar(this), // evaltext new P5Scalar(this), // is_require new P5Scalar(this), // hints new P5Scalar(this)); // warnings } }
public virtual IP5Any Call(Runtime runtime, Opcode.ContextValues context, P5Array args) { // TODO emit this in the subroutine prologue/epilogue code, // as is done for eval BLOCK P5ScratchPad pad = scratchpad; if (scratchpad != null && !is_main) pad = scratchpad.NewScope(runtime); int size = runtime.CallStack.Count; try { runtime.CallStack.Push(new StackFrame(runtime.Package, runtime.File, runtime.Line, this, context, false)); return subref(runtime, context, pad, args); } finally { StackFrame frame = new StackFrame(); // the while() is required for tail calls, // until proper tail calls are implemented while (runtime.CallStack.Count > size) frame = runtime.CallStack.Pop(); if (size > 0) { runtime.Package = frame.Package; runtime.File = frame.File; runtime.Line = frame.Line; } } }