/********************************************************************** Filename : GFxFunction.cpp Content : Implementation of C and AS function objects Created : Authors : Artyom Bolgar Copyright : (c) 2001-2008 Scaleform Corp. All Rights Reserved. Notes : Licensees may use this file in accordance with the valid Scaleform Commercial License Agreement provided with the software. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE. **********************************************************************/ #include "GFxPlayerImpl.h" #include "GFxAction.h" #include "GASFunctionRefImpl.h" #include "AS/GASArrayObject.h" #include "GFxASString.h" #include "AS/GASStringObject.h" #include "GASAsFunctionObject.h" #include #include //#define GFC_TRACK_STACK_USAGE_PER_AS_FUNCTION_CALL // RAGE - add some diag context for actionscript calls #include "diag/output.h" // ////////// GASFunctionObject /////////// // #ifdef GFC_NO_GC ////////////////////////////////////////////////////////////////////////// // non-CC versions // GASFunctionObject::GASFunctionObject (GASStringContext* psc) : GASObject(psc) { GUNUSED(psc); // NOTE: psc can be 0 here to allow for default constructor; if we need // a valid 'psc' here in the future we need to remove the default constructor. pPrototype = NULL; } GASFunctionObject::GASFunctionObject (GASEnvironment* penv) : GASObject(penv) { GUNUSED(penv); pPrototype = NULL; } GASFunctionObject::~GASFunctionObject () { // Function object is being removed; make sure we cleanup // the prototype.constructor property to avoid crash. if (pPrototype) { pPrototype->CheckAndResetCtorRef(this); } } bool GASFunctionObject::SetMember(GASEnvironment *penv, const GASString& name, const GASValue& val, const GASPropFlags& flags) { if (name == penv->GetBuiltin(GASBuiltin_prototype)) { // Prototype is being set via ActionScript. // We need GASObject* pobj = val.ToObject(penv); if (pobj && pobj->IsBuiltinPrototype()) pPrototype = static_cast(pobj); else pPrototype = NULL; } return GASObject::SetMember(penv, name, val, flags); } void GASFunctionObject::SetPrototype(GASStringContext* psc, GASObject* pprototype) { pPrototype = pprototype; SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_prototype), GASValue(pprototype)); } #else //////////////////////////////////////////////// // CC versions // GASFunctionObject::GASFunctionObject (GASStringContext* psc) : GASObject(psc) { GUNUSED(psc); // NOTE: psc can be 0 here to allow for default constructor; if we need // a valid 'psc' here in the future we need to remove the default constructor. } GASFunctionObject::GASFunctionObject (GASEnvironment* penv) : GASObject(penv) { GUNUSED(penv); } GASFunctionObject::~GASFunctionObject () { } void GASFunctionObject::SetPrototype(GASStringContext* psc, GASObject* pprototype) { SetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_prototype), GASValue(pprototype)); } #endif // GFC_NO_GC void GASFunctionObject::SetProtoAndCtor(GASStringContext* psc, GASObject* pprototype) { Set__proto__(psc, pprototype); // function objects have "constructor" property as well GASFunctionRef ctor = pprototype->Get_constructor(psc); if (!ctor.IsNull()) Set_constructor(psc, ctor); } GASFunctionRef GASFunctionObject::ToFunction() { return GASFunctionRef(this); } GASObject* GASFunctionObject::GetPrototype(GASStringContext* psc) { GASValue prototypeVal; if (GetMemberRaw(psc, psc->GetBuiltin(GASBuiltin_prototype), &prototypeVal)) { return prototypeVal.ToObject(NULL); } return NULL; } ////////////////////////////////////////////////////////////////////////// // GASCFunctionObject GASCFunctionObject::GASCFunctionObject (GASStringContext* psc) : GASFunctionObject(psc), pFunction(NULL) { GUNUSED(psc); // NOTE: psc can be 0 here to allow for default constructor; if we need // a valid 'psc' here in the future we need to remove the default constructor. } GASCFunctionObject::GASCFunctionObject (GASEnvironment* penv) : GASFunctionObject(penv), pFunction(NULL) { GUNUSED(penv); } //GASCFunctionObject::GASCFunctionObject (GASEnvironment* penv, GASCFunctionDef* func) //: Def (func) //{ // GUNUSED(penv); //} // this constructor is used for C functions GASCFunctionObject::GASCFunctionObject(GASStringContext* psc, GASObject* pprototype, GASCFunctionPtr func) : GASFunctionObject(psc), pFunction(func) { // C-function objects have __proto__ = Object.prototype //!AB, correction: C-function objects have __proto__ = Function.prototype (see GASFunctionObject::GetMember) //Set__proto__ (pprototype); //GUNUSED(pprototype); // not sure yet, is it necessary or not at all... (AB) Set__proto__(psc, pprototype); } GASCFunctionObject::GASCFunctionObject(GASStringContext* psc, GASCFunctionPtr func) : GASFunctionObject(psc), pFunction(func) { Set__proto__(psc, psc->pContext->GetPrototype(GASBuiltin_Function)); } GASCFunctionObject::~GASCFunctionObject () { } // RAGE - added context messages void GASCFunctionObject::Invoke (const GASFnCall& fn, GASLocalFrame*, const char* BANK_ONLY(methodname)) { DIAG_CONTEXT_MESSAGE("AS CFn: %s", methodname); if (0 != pFunction) { GASObjectInterface* pthis = fn.ThisPtr; if (pthis && pthis->IsSuper()) { GASObjectInterface* prealThis = static_cast(pthis)->GetRealThis(); GASFnCall fn2(fn.Result, prealThis, fn.Env, fn.NArgs, fn.FirstArgBottomIndex); (pFunction)(fn2); static_cast(pthis)->ResetAltProto(); // reset alternative proto, if set } else (pFunction)(fn); } //Def.Invoke (fn, localFrame, NULL, pmethodName); } GASEnvironment* GASCFunctionObject::GetEnvironment(const GASFnCall& fn, GPtr*) { return fn.Env; } bool GASCFunctionObject::IsEqual(const GASFunctionObject& f) const { return f.IsCFunction() && pFunction == static_cast(f).pFunction; } ////////////////////////////////////////////////////////////////////////// // GASAsFunctionObject GASAsFunctionObject::GASAsFunctionObject(GASEnvironment* penv, GASActionBuffer* ab, UInt start, UInt length, const GASWithStackArray* pwithStack, GASActionBuffer::ExecuteType execType) : GASFunctionObject(penv), pMovieRoot(0), pActionBuffer(ab), StartPc(start), Length(length), // Need to pass default value for copy-constructing array Args(ArgSpec(0, penv->GetBuiltin(GASBuiltin_empty_))), Function2Flags(0), ExecType((UByte)execType), LocalRegisterCount(0) { GASSERT(pActionBuffer); if (pwithStack) WithStack = *pwithStack; if (penv && (execType!= GASActionBuffer::Exec_Event && execType!= GASActionBuffer::Exec_SpecialEvent)) { GFxASCharacter* ch = penv->GetTarget(); GASSERT (ch != 0); TargetHandle = ch->GetCharacterHandle(); // MA: MovieRoot Lives in diferent heap now! pMovieRoot = ch->GetMovieRoot(); } } GASObject* GASAsFunctionObject::CreateNewObject(GASEnvironment* penv) const { return GHEAP_NEW(penv->GetHeap()) GASObject(penv); } GFxASCharacter* GASAsFunctionObject::GetTargetCharacter() { if (!pMovieRoot) return 0; return TargetHandle->ResolveCharacter(pMovieRoot); } GASEnvironment* GASAsFunctionObject::GetEnvironment(const GASFnCall& fn, GPtr* ptargetCh) { GPtr ch = GetTargetCharacter(); GASEnvironment* pourEnv = 0; if (ch) { pourEnv = ch->GetASEnvironment(); } if (ptargetCh) *ptargetCh = ch; GASSERT(pourEnv || fn.Env); pourEnv = pourEnv ? pourEnv : fn.Env; // the final decision about environment... return pourEnv; } #ifdef GFC_TRACK_STACK_USAGE_PER_AS_FUNCTION_CALL volatile UInt32 __esp__ = 0, curESP = 0; #endif class InvokeContext { public: GASAsFunctionObject* pThis; GASEnvironment* pOurEnv; const GASFnCall& FnCall; GASLocalFrame* pLocalFrame; const char* pMethodName; GPtr TargetCh; GPtr FnEnvCh; GPtr PassedThisCh; GPtr PassedThisObj; GPtr CurLocalFrame; int LocalStackTop; InvokeContext(GASAsFunctionObject* pthis, const GASFnCall& fn, GASLocalFrame* localFrame, const char* pmethodName) : pThis(pthis), FnCall(fn), pLocalFrame(localFrame), pMethodName(pmethodName) { } void Setup(); void Cleanup(); private: InvokeContext& operator=(const InvokeContext&) { return *this; } }; void InvokeContext::Setup() { pOurEnv->CallPush(pThis); // 'this' can be different from currently accessible environment scope. // In most 'this' is passed through FnCall, while the implicit scope // 'pEnv' is the scope of the original function declaration. GASObjectInterface* pthis = FnCall.ThisPtr; //GASSERT(pthis); // pthis can be null (i.e. undefined), for example inside interval's handler GMemoryHeap* pheap = pOurEnv->GetHeap(); // Set up local stack frame, for parameters and locals. LocalStackTop = pOurEnv->GetLocalFrameTop(); if (pThis->IsFunction2() || (pThis->GetExecType() == GASActionBuffer::Exec_Function)) { CurLocalFrame = pOurEnv->CreateNewLocalFrame(); CurLocalFrame->PrevFrame = pLocalFrame; } else pOurEnv->AddFrameBarrier(); GASObjectInterface* passedThis = pthis; // need to hold passedThis to avoid its release during the function execution if (passedThis) { PassedThisCh = passedThis->ToASCharacter(); PassedThisObj = passedThis->ToASObject(); } if (pthis && pthis->IsSuper()) { pthis = static_cast(pthis)->GetRealThis(); } if (!pThis->IsFunction2()) { int i; int version = pOurEnv->GetVersion(); // AB, Flash 6 and below uses function 1 for methods and events too. // So, we need to care about setting correct "this". if (pthis) { GASValue thisVal; thisVal.SetAsObjectInterface(pthis); pOurEnv->AddLocal(pOurEnv->GetBuiltin(GASBuiltin_this), thisVal); } // save some info for recreation of "super". For function1 "super" is created // on demand. if (version >= 6 && CurLocalFrame) { CurLocalFrame->SuperThis = passedThis; } // Conventional function. if (CurLocalFrame && FnCall.Env && version >= 5) { // Optimization: function, type 1 may use "arguments" array and there is no // way to predict this usage. To avoid creation of this array for every call // (especially, the "arguments" array is used pretty rarely), we just // save some info, required for "lazy" creation of this array in the // GASEnvironment::GetVariableRaw method. if (pOurEnv != FnCall.Env) FnEnvCh = FnCall.Env->GetTarget(); // keep target char for FnCall.Env to ensure // it won't be freed before call is completed. CurLocalFrame->Env = FnCall.Env; CurLocalFrame->NArgs = FnCall.NArgs; CurLocalFrame->FirstArgBottomIndex = FnCall.FirstArgBottomIndex; CurLocalFrame->Callee = pOurEnv->CallTop(0); CurLocalFrame->Caller = pOurEnv->CallTop(1); } // Push the arguments onto the local frame. int ArgsToPass = G_Min(FnCall.NArgs, (int)pThis->Args.GetSize()); for (i = 0; i < ArgsToPass; i++) { GASSERT(pThis->Args[i].Register == 0); pOurEnv->AddLocal(pThis->Args[i].Name, FnCall.Arg(i)); } // need to fill remaining arguments by "undefined" values for (int n = (int)pThis->Args.GetSize(); i < n; i++) { GASSERT(pThis->Args[i].Register == 0); pOurEnv->AddLocal(pThis->Args[i].Name, GASValue()); } } else { // function2: most args go in registers; any others get pushed. // Create local registers. pOurEnv->AddLocalRegisters(pThis->LocalRegisterCount); // Handle the explicit args. int ArgsToPass = G_Min(FnCall.NArgs, (int)pThis->Args.GetSize()); int i; for (i = 0; i < ArgsToPass; i++) { if (pThis->Args[i].Register == 0) { // Conventional arg passing: create a local var. pOurEnv->AddLocal(pThis->Args[i].Name, FnCall.Arg(i)); } else { // Pass argument into a register. int reg = pThis->Args[i].Register; *(pOurEnv->LocalRegisterPtr(reg)) = FnCall.Arg(i); } } // need to fill remaining aguments by "undefined" values for (int n = (int)pThis->Args.GetSize(); i < n; i++) { if (pThis->Args[i].Register == 0) { // Conventional arg passing: create a local var. pOurEnv->AddLocal(pThis->Args[i].Name, GASValue()); } } GPtr superObj; if ((pThis->Function2Flags & 0x10) || !(pThis->Function2Flags & 0x20)) { // need to create super GASSERT(passedThis); GPtr proto = passedThis->Get__proto__(); //printf ("!!! passedThis.__proto__ = %s\n", (const char*)pOurEnv->GetGC()->FindClassName(pOurEnv->GetSC(), proto).ToCStr()); //printf ("!!! real pthis.__proto__ = %s\n", (const char*)pOurEnv->GetGC()->FindClassName(pOurEnv->GetSC(), pthis->Get__proto__()).ToCStr()); if (proto) { if (pMethodName) { // Correct the prototype according to the owner of the function. // This is important in the case if "this"'s class do not have // the method being invoked and only the base class has it. GPtr newProto = proto->FindOwner(pOurEnv->GetSC(), pOurEnv->CreateString(pMethodName)); if (newProto) { proto = newProto; //printf ("!!! newproto = %s\n", (const char*)pOurEnv->GetGC()->FindClassName(pOurEnv->GetSC(), newProto).ToCStr()); } } GASFunctionRef __ctor__ = proto->Get__constructor__(pOurEnv->GetSC()); //printf ("!!! __proto__.__ctor__ = %s\n", (const char*)pOurEnv->GetGC()->FindClassName(pOurEnv->GetSC(), __ctor__.GetObjectPtr()).ToCStr()); //printf ("!!! __proto__.__proto__ = %s\n", (const char*)pOurEnv->GetGC()->FindClassName(pOurEnv->GetSC(), proto->Get__proto__()).ToCStr()); superObj = *GHEAP_NEW(pheap) GASSuperObject(proto->Get__proto__(), pthis, __ctor__); } } // Handle the implicit args. int CurrentReg = 1; if (pThis->Function2Flags & 0x01) { // preload 'this' into a register. if (pthis) (*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetAsObjectInterface(pthis); else (*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetUndefined(); CurrentReg++; } if (pThis->Function2Flags & 0x02) { // Don't put 'this' into a local var. } else { // Put 'this' in a local var. GASValue pthisVal; if (pthis) pthisVal.SetAsObjectInterface(pthis); pOurEnv->AddLocal(pOurEnv->GetBuiltin(GASBuiltin_this), pthisVal); } // Init arguments GArray, if it's going to be needed. GPtr pargArray; if ((pThis->Function2Flags & 0x04) || ! (pThis->Function2Flags & 0x08)) { pargArray = *GHEAP_NEW(pheap) GASArrayObject(pOurEnv); pargArray->Resize(FnCall.NArgs); for (int i = 0; i < FnCall.NArgs; i++) pargArray->SetElement(i, FnCall.Arg(i)); } if (pThis->Function2Flags & 0x04) { // preload 'arguments' into a register. (*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetAsObject(pargArray.GetPtr()); CurrentReg++; } if (pThis->Function2Flags & 0x08) { // Don't put 'arguments' in a local var. } else { // Put 'arguments' in a local var. pOurEnv->AddLocal(pOurEnv->GetBuiltin(GASBuiltin_arguments), GASValue(pargArray.GetPtr())); pargArray->SetMemberRaw(pOurEnv->GetSC(), pOurEnv->GetBuiltin(GASBuiltin_callee), pOurEnv->CallTop(0), GASPropFlags::PropFlag_DontEnum | GASPropFlags::PropFlag_DontDelete | GASPropFlags::PropFlag_ReadOnly); pargArray->SetMemberRaw(pOurEnv->GetSC(), pOurEnv->GetBuiltin(GASBuiltin_caller), pOurEnv->CallTop(1), GASPropFlags::PropFlag_DontEnum | GASPropFlags::PropFlag_DontDelete | GASPropFlags::PropFlag_ReadOnly); } if (pThis->Function2Flags & 0x10) { // Put 'super' in a register. //GASSERT(superObj); (*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetAsObject(superObj); //(*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetAsFunction(superObj); CurrentReg++; } if (pThis->Function2Flags & 0x20) { // Don't put 'super' in a local var. } else { // Put 'super' in a local var. //GASSERT(superObj); GASValue superVal; superVal.SetAsObject(superObj); pOurEnv->AddLocal(pOurEnv->GetBuiltin(GASBuiltin_super), superVal); } if (pThis->Function2Flags & 0x40) { // Put '_root' in a register. (*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetAsCharacter( pOurEnv->GetTarget()->GetASRootMovie()); CurrentReg++; } if (pThis->Function2Flags & 0x80) { // Put 'Parent' in a register. GASValue parent; pOurEnv->GetVariable(pOurEnv->GetBuiltin(GASBuiltin__parent), &parent); (*(pOurEnv->LocalRegisterPtr(CurrentReg))) = parent; CurrentReg++; } if (pThis->Function2Flags & 0x100) { // Put 'pGlobal' in a register. (*(pOurEnv->LocalRegisterPtr(CurrentReg))).SetAsObject(pOurEnv->GetGC()->pGlobal); CurrentReg++; } } } void InvokeContext::Cleanup() { GASObjectInterface* passedThis = PassedThisObj.GetPtr(); if (passedThis && passedThis->IsSuper()) { static_cast(passedThis)->ResetAltProto(); // reset alternative proto, if set } if (!pThis->IsFunction2() || !(pThis->Function2Flags & 0x02)) { // wipe explicit "this" off from the local frame to prevent memory leak pOurEnv->SetLocal(pOurEnv->GetBuiltin(GASBuiltin_this), GASValue()); } if (!pThis->IsFunction2() || !(pThis->Function2Flags & 0x20)) { // wipe explicit "super" off from the local frame to prevent memory leak pOurEnv->SetLocal(pOurEnv->GetBuiltin(GASBuiltin_super), GASValue()); } if (CurLocalFrame) CurLocalFrame->ReleaseFramesForLocalFuncs (); // Clean up stack frame. pOurEnv->SetLocalFrameTop(LocalStackTop); if (pThis->IsFunction2()) { // Clean up the local registers. pOurEnv->DropLocalRegisters(pThis->LocalRegisterCount); } // finalize... if (pOurEnv) pOurEnv->CallPop(); } void GASAsFunctionObject::Invoke(const GASFnCall& fn, GASLocalFrame* localFrame, const char* pmethodName) { // do not make calls on unloaded ThisPtr if (fn.ThisPtr && fn.ThisPtr->IsASCharacter() && fn.ThisPtr->ToASCharacter()->IsUnloaded()) return; InvokeContext ctxt(this, fn, localFrame, pmethodName); // push current function object into the stack. // need for atguments.callee/.caller. ctxt.pOurEnv = GetEnvironment(fn, &ctxt.TargetCh); #if __BANK // RAGE const char* fileName = GetActionBuffer()->GetActionBufferData()->GetFileName(); if (fileName && !strncmp(fileName, "memory:", 7)) { const char* endOfFile = strchr(fileName + 7, ':'); // find the first : after the original "memory:" fileName = endOfFile ? endOfFile + 1 : fileName; } DIAG_CONTEXT_MESSAGE("AS Fn: %s :: %s", fileName, pmethodName); #endif if (ctxt.pOurEnv->GetTarget()->IsUnloaded()) { // Do not use environment from the unloaded target, use caller environment instead. ctxt.pOurEnv = fn.Env; } else { // if environment (pourEnv) is not NULL then use it (and it MUST be the same as // GASAsFunctionDef::GetEnvironment method returns, otherwise - call to GASAsFunctionDef::GetEnvironment GASSERT(ctxt.pOurEnv == GetEnvironment(fn, 0)); } GASSERT(ctxt.pOurEnv); #ifdef GFC_TRACK_STACK_USAGE_PER_AS_FUNCTION_CALL //volatile UInt32 curESP; #ifdef GFC_OS_WII asm { stw rsp, curESP } #elif defined(GFC_OS_XBOX360) __asm stw r1, curESP #elif defined(GFC_OS_PS3) volatile UInt32 curESP; __asm__ volatile ("stw 1, %0\n\t" : "Z=" (curESP)); #elif defined(GFC_OS_WIN32) && defined(GFC_CC_MSVC) __asm mov curESP, esp; #endif if (__esp__ != 0) printf("Call size is %d\n", __esp__ - curESP); __esp__ = curESP; #endif // GFC_TRACK_STAC_USAGE_PER_AS_FUNCTION_CALL if (!ctxt.pOurEnv->RecursionGuardStart()) { #ifndef GFC_NO_FXPLAYER_VERBOSE_ACTION_ERRORS if (ctxt.pOurEnv->IsVerboseActionErrors()) ctxt.pOurEnv->LogScriptError("Error: Stack overflow, max level of 255 nested calls is reached.\n"); #endif ctxt.pOurEnv->RecursionGuardEnd(); return; } ctxt.Setup(); // Execute the actions. pActionBuffer->Execute(ctxt.pOurEnv, StartPc, Length, fn.Result, &WithStack, GetExecType()); ctxt.Cleanup(); ctxt.pOurEnv->RecursionGuardEnd(); } // //////////// GASFunctionRefBase ////////////////// // void GASFunctionRefBase::Assign(const GASFunctionRefBase& orig) { if (this != &orig) { GASFunctionObject* pprevFunc = Function; if (!(Flags & FuncRef_Weak) && Function && (Function != orig.Function)) Function->Release (); Function = orig.Function; // avoiding load-hit-store, access to orig.Function rather than Function if (!(Flags & FuncRef_Weak) && orig.Function && (pprevFunc != orig.Function)) orig.Function->AddRef (); if (orig.LocalFrame != 0) SetLocalFrame (orig.LocalFrame, (orig.Flags & FuncRef_Internal)); else SetLocalFrame (0, 0); } } void GASFunctionRefBase::SetInternal (bool internal) { #ifdef GFC_NO_GC if (LocalFrame != 0 && bool(Flags & FuncRef_Internal) != internal) { if (!(Flags & FuncRef_Internal)) LocalFrame->Release (); else LocalFrame->AddRef (); } if (internal) Flags |= FuncRef_Internal; else Flags &= (~FuncRef_Internal); #else GUNUSED(internal); #endif } void GASFunctionRefBase::SetLocalFrame (GASLocalFrame* localFrame, bool internal) { if (LocalFrame != 0 && !(Flags & FuncRef_Internal)) LocalFrame->Release (); LocalFrame = localFrame; if (internal) Flags |= FuncRef_Internal; else Flags &= (~FuncRef_Internal); // avoiding load-hit-store, access to localFrame rather than LocalFrame if (localFrame != 0 && !(Flags & FuncRef_Internal)) localFrame->AddRef (); } #ifndef GFC_NO_GC template void GASFunctionRefBase::ForEachChild_GC(Collector* prcc) const { if (Function) Functor::Call(prcc, Function); if (LocalFrame) Functor::Call(prcc, LocalFrame); } GFC_GC_PREGEN_FOR_EACH_CHILD(GASFunctionRefBase) #endif // GFC_NO_GC ///////////// GASFunctionProto ////////////////// static const GASNameFunction GAS_FunctionObjectTable[] = { { "apply", &GASFunctionProto::Apply }, { "call", &GASFunctionProto::Call }, { "toString", &GASFunctionProto::ToString }, { "valueOf", &GASFunctionProto::ValueOf }, { 0, 0 } }; GASFunctionProto::GASFunctionProto(GASStringContext* psc, GASObject* pprototype, const GASFunctionRef& constructor, bool initFuncs) : GASPrototype(psc, pprototype, constructor) { if (initFuncs) InitFunctionMembers(psc, GAS_FunctionObjectTable); } void GASFunctionProto::GlobalCtor(const GASFnCall& fn) { if (fn.NArgs == 1) { // Seems this is just a type casting call, such as "Function(func)" // Just return the unchanged parameter. // Flash should generate a special opcode for type casting, and it does // do this for casts like "s : Selection = Selection(ss)". // But, for some reasons, if the cast operator looks like "f = Function(func)" // Flash generates a function call to global function "Function" with a param. // In this case, just return the passed parameter. Do the same, if // "f = new Function(func) is used; in this case f == func anyway. if (fn.Arg(0).IsFunction() || fn.Arg(0).IsFunctionName()) fn.Result->SetAsObject(fn.Arg(0).ToObject(fn.Env)); else fn.Result->SetNull(); } else { GPtr obj = *GHEAP_NEW(fn.Env->GetHeap()) GASCFunctionObject(fn.Env); fn.Result->SetAsObject(obj.GetPtr()); } } void GASFunctionProto::ToString(const GASFnCall& fn) { fn.Result->SetString(fn.Env->GetBuiltin(GASBuiltin_typeFunction_)); } void GASFunctionProto::ValueOf(const GASFnCall& fn) { GASSERT(fn.ThisPtr); fn.Result->SetAsObject(static_cast(fn.ThisPtr)); } void GASFunctionProto::Apply(const GASFnCall& fn) { GASSERT(fn.ThisPtr); GPtr objectHolder; GPtr charHolder; GASObjectInterface* thisObj = 0; int nArgs = 0; fn.Result->SetUndefined(); GPtr arguments; if (fn.NArgs >= 1) { thisObj = fn.Arg(0).ToObjectInterface(fn.Env); if (thisObj) { if (thisObj->IsASCharacter()) charHolder = thisObj->ToASCharacter(); else objectHolder = static_cast(thisObj); } } if (fn.NArgs >= 2) { // arguments array GASObject* args = fn.Arg(1).ToObject(fn.Env); if (args && args->GetObjectType() == GASObjectInterface::Object_Array) { arguments = static_cast(args); nArgs = arguments->GetSize(); // push arguments into the environment's stack if (nArgs > 0) { for (int i = nArgs - 1; i >= 0; --i) fn.Env->Push(*arguments->GetElementPtr(i)); } } } GASValue result; // invoke function if (!fn.ThisFunctionRef.IsNull()) { //!AB: if ThisFunctionRef is not null then we may do call with using it. // In this case localFrame will be correct, and this is critical for calling nested functions. fn.ThisFunctionRef.Invoke(GASFnCall(&result, thisObj, fn.Env, nArgs, fn.Env->GetTopIndex())); } else //!AB: is it necessary or ThisFunctionRef should be not null allways? { GPtr func = static_cast(fn.ThisPtr); func->Invoke(GASFnCall(&result, thisObj, fn.Env, nArgs, fn.Env->GetTopIndex()), 0, NULL); } // wipe out arguments if (nArgs > 0) { fn.Env->Drop(nArgs); } *fn.Result = result; } void GASFunctionProto::Call(const GASFnCall& fn) { GASSERT(fn.ThisPtr); GPtr objectHolder; GPtr charHolder; GASObjectInterface* thisObj = 0; int nArgs = 0; fn.Result->SetUndefined(); GPtr arguments; if (fn.NArgs >= 1) { thisObj = fn.Arg(0).ToObjectInterface(fn.Env); if (thisObj) { if (thisObj->IsASCharacter()) charHolder = thisObj->ToASCharacter(); else objectHolder = static_cast(thisObj); } } if (fn.NArgs >= 2) { nArgs = fn.NArgs - 1; for (int i = nArgs; i >= 1; --i) fn.Env->Push(fn.Arg(i)); } GASValue result; // invoke function if (!fn.ThisFunctionRef.IsNull()) { //!AB: if ThisFunctionRef is not null then we may do call with using it. // In this case localFrame will be correct, and this is critical for calling nested functions. fn.ThisFunctionRef.Invoke(GASFnCall(&result, thisObj, fn.Env, nArgs, fn.Env->GetTopIndex())); } else //!AB: is it necessary or ThisFunctionRef should be not null allways? { GPtr func = static_cast(fn.ThisPtr); func->Invoke(GASFnCall(&result, thisObj, fn.Env, nArgs, fn.Env->GetTopIndex()), 0, NULL); } // wipe out arguments if (nArgs > 0) { fn.Env->Drop(nArgs); } *fn.Result = result; } ///////////////////////////////////////////////////// GASFunctionCtorFunction::GASFunctionCtorFunction (GASStringContext* psc) : GASCFunctionObject(psc, GASFunctionProto::GlobalCtor) { GUNUSED(psc); }