void BIF_IsObject(ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) // IsObject(obj) is currently equivalent to (obj && obj=""), but much more intuitive. { int i; for (i = 0; i < aParamCount && TokenToObject(*aParam[i]); ++i); aResultToken.value_int64 = (__int64)(i == aParamCount); // TRUE if all are objects. }
void BIF_ObjCreate(ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) { IObject *obj = NULL; if (aParamCount == 1) // L33: POTENTIALLY UNSAFE - Cast IObject address to object reference. { if (obj = TokenToObject(*aParam[0])) { // Allow &obj == Object(obj), but AddRef() for equivalence with ComObjActive(comobj). obj->AddRef(); aResultToken.value_int64 = (__int64)obj; return; // symbol is already SYM_INTEGER. } obj = (IObject *)TokenToInt64(*aParam[0]); if (obj < (IObject *)1024) // Prevent some obvious errors. obj = NULL; else obj->AddRef(); } else obj = Object::Create(aParam, aParamCount); if (obj) { aResultToken.symbol = SYM_OBJECT; aResultToken.object = obj; // DO NOT ADDREF: after we return, the only reference will be in aResultToken. } else { aResultToken.symbol = SYM_STRING; aResultToken.marker = _T(""); } }
ResultType BIF_ObjMethod(int aID, ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) { aResultToken.symbol = SYM_STRING; aResultToken.marker = _T(""); Object *obj = dynamic_cast<Object*>(TokenToObject(*aParam[0])); if (!obj) return OK; // Return "". return obj->CallBuiltin(aID, aResultToken, aParam + 1, aParamCount - 1); }
void BIF_ObjInvoke(ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) { int invoke_type; IObject *obj; ExprTokenType *obj_param; // Since ObjGet/ObjSet/ObjCall are not publicly accessible as functions, Func::mName // (passed via aResultToken.marker) contains the actual flag rather than a name. invoke_type = (int)(INT_PTR)aResultToken.marker; // Set default return value; ONLY AFTER THE ABOVE. aResultToken.symbol = SYM_STRING; aResultToken.marker = _T(""); obj_param = *aParam; // aParam[0]. Load-time validation has ensured at least one parameter was specified. ++aParam; --aParamCount; if (obj = TokenToObject(*obj_param)) { bool param_is_var = obj_param->symbol == SYM_VAR; if (param_is_var) // Since the variable may be cleared as a side-effect of the invocation, call AddRef to ensure the object does not expire prematurely. // This is not necessary for SYM_OBJECT since that reference is already counted and cannot be released before we return. Each object // could take care not to delete itself prematurely, but it seems more proper, more reliable and more maintainable to handle it here. obj->AddRef(); obj->Invoke(aResultToken, *obj_param, invoke_type, aParam, aParamCount); if (param_is_var) obj->Release(); } // Invoke meta-functions of g_MetaObject. else if (INVOKE_NOT_HANDLED == g_MetaObject.Invoke(aResultToken, *obj_param, invoke_type | IF_META, aParam, aParamCount) // If above did not handle it, check for attempts to access .base of non-object value (g_MetaObject itself). // CALL is unsupported (for simplicity); SET is supported only in "multi-dimensional" mode: "".base[x]:=y && invoke_type != IT_CALL && (invoke_type == IT_SET ? aParamCount > 2 : aParamCount) && !_tcsicmp(TokenToString(*aParam[0]), _T("base"))) { if (aParamCount > 1) // "".base[x] or similar { // Re-invoke g_MetaObject without meta flag or "base" param. ExprTokenType base_token; base_token.symbol = SYM_OBJECT; base_token.object = &g_MetaObject; g_MetaObject.Invoke(aResultToken, base_token, invoke_type, aParam + 1, aParamCount - 1); } else // "".base { // Return a reference to g_MetaObject. No need to AddRef as g_MetaObject ignores it. aResultToken.symbol = SYM_OBJECT; aResultToken.object = &g_MetaObject; } } }
void BIF_ObjNew(ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) { aResultToken.symbol = SYM_STRING; aResultToken.marker = _T(""); ExprTokenType *class_token = aParam[0]; // Save this to be restored later. IObject *class_object = TokenToObject(*class_token); if (!class_object) return; Object *new_object = Object::Create(NULL, 0); if (!new_object) return; new_object->SetBase(class_object); ExprTokenType name_token, this_token; name_token.symbol = SYM_STRING; name_token.marker = Object::sMetaFuncName[4]; // __New this_token.symbol = SYM_OBJECT; this_token.object = new_object; aParam[0] = &name_token; if (class_object->Invoke(aResultToken, this_token, IT_CALL | IF_METAOBJ, aParam, aParamCount) != EARLY_RETURN) { // Although it isn't likely to happen, if __New points at a built-in function or if mBase // (or an ancestor) is not an Object (i.e. it's a ComObject), aResultToken can be set even when // the result is not EARLY_RETURN. So make sure to clean up any result we're not going to use. if (aResultToken.symbol == SYM_OBJECT) aResultToken.object->Release(); if (aResultToken.mem_to_free) { // This can be done by our caller, but is done here for maintainability; i.e. because // some callers might expect mem_to_free to be NULL when the result isn't a string. free(aResultToken.mem_to_free); aResultToken.mem_to_free = NULL; } // Either it wasn't handled (i.e. neither this class nor any of its super-classes define __New()), // or there was no explicit "return", so just return the new object. aResultToken.symbol = SYM_OBJECT; aResultToken.object = new_object; } else new_object->Release(); aParam[0] = class_token; }
void BIF_ObjNew(ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) { aResultToken.symbol = SYM_STRING; aResultToken.marker = _T(""); ExprTokenType *class_token = aParam[0]; // Save this to be restored later. IObject *class_object = TokenToObject(*class_token); if (!class_object) return; Object *new_object = Object::Create(NULL, 0); if (!new_object) return; new_object->SetBase(class_object); ExprTokenType name_token, this_token; this_token.symbol = SYM_OBJECT; this_token.object = new_object; name_token.symbol = SYM_STRING; aParam[0] = &name_token; ResultType result; LPTSTR buf = aResultToken.buf; // In case Invoke overwrites it via the union. // __Init was added so that instance variables can be initialized in the correct order // (beginning at the root class and ending at class_object) before __New is called. // It shouldn't be explicitly defined by the user, but auto-generated in DefineClassVars(). name_token.marker = _T("__Init"); result = class_object->Invoke(aResultToken, this_token, IT_CALL | IF_METAOBJ, aParam, 1); if (result != INVOKE_NOT_HANDLED) { // See similar section below for comments. if (aResultToken.symbol == SYM_OBJECT) aResultToken.object->Release(); if (aResultToken.mem_to_free) { free(aResultToken.mem_to_free); aResultToken.mem_to_free = NULL; } // Reset to defaults for __New, invoked below. aResultToken.symbol = SYM_STRING; aResultToken.marker = _T(""); aResultToken.buf = buf; if (result == FAIL) { aParam[0] = class_token; // Restore it to original caller-supplied value. return; } } // __New may be defined by the script for custom initialization code. name_token.marker = Object::sMetaFuncName[4]; // __New if (class_object->Invoke(aResultToken, this_token, IT_CALL | IF_METAOBJ, aParam, aParamCount) != EARLY_RETURN) { // Although it isn't likely to happen, if __New points at a built-in function or if mBase // (or an ancestor) is not an Object (i.e. it's a ComObject), aResultToken can be set even when // the result is not EARLY_RETURN. So make sure to clean up any result we're not going to use. if (aResultToken.symbol == SYM_OBJECT) aResultToken.object->Release(); if (aResultToken.mem_to_free) { // This can be done by our caller, but is done here for maintainability; i.e. because // some callers might expect mem_to_free to be NULL when the result isn't a string. free(aResultToken.mem_to_free); aResultToken.mem_to_free = NULL; } // Either it wasn't handled (i.e. neither this class nor any of its super-classes define __New()), // or there was no explicit "return", so just return the new object. aResultToken.symbol = SYM_OBJECT; aResultToken.object = new_object; } else new_object->Release(); aParam[0] = class_token; }