ASFUNCTIONBODY(Array,forEach) { Array* th=static_cast<Array*>(obj); _NR<IFunction> f; ARG_UNPACK(f); if (f.isNull()) return NULL; ASObject* params[3]; std::map<uint32_t, data_slot>::iterator it=th->data.begin(); for(;it != th->data.end();++it) { assert_and_throw(it->second.type==DATA_OBJECT); params[0] = it->second.data; it->second.data->incRef(); params[1] = abstract_i(it->first); params[2] = th; th->incRef(); ASObject *funcret; if( argslen == 1 ) { funcret=f->call(getSys()->getNullRef(), params, 3); } else { args[1]->incRef(); funcret=f->call(args[1], params, 3); } if(funcret) funcret->decRef(); } return NULL; }
bool ASObject::hasPropertyByMultiname(const multiname& name, bool considerDynamic) { //We look in all the object's levels uint32_t validTraits=DECLARED_TRAIT; if(considerDynamic) validTraits|=DYNAMIC_TRAIT; if(Variables.findObjVar(name, NO_CREATE_TRAIT, validTraits)!=NULL) return true; if(classdef && classdef->Variables.findObjVar(name, NO_CREATE_TRAIT, BORROWED_TRAIT)!=NULL) return true; //Check prototype inheritance chain if(getClass()) { ASObject* proto = getClass()->prototype.getPtr(); while(proto) { if(proto->findGettable(name, false) != NULL) return true; proto = proto->getprop_prototype(); } } //Must not ask for non borrowed traits as static class member are not valid return false; }
ASFUNCTIONBODY(ASObject,propertyIsEnumerable) { assert_and_throw(argslen==1); multiname name; name.name_type=multiname::NAME_STRING; name.name_s=args[0]->toString(); name.ns.push_back(nsNameAndKind("",NAMESPACE)); name.isAttribute=false; unsigned int index = 0; if (obj->is<Array>()) // propertyIsEnumerable(index) isn't mentioned in the ECMA specs but is tested for { Array* a = static_cast<Array*>(obj); if (a->isValidMultiname(name,index)) { return abstract_b(index < (unsigned int)a->size()); } } if(obj->getClass()) { ASObject* proto = obj->getClass()->prototype.getPtr(); if (proto->hasPropertyByMultiname(name, true)) return abstract_b(false); } if (obj->hasPropertyByMultiname(name,true)) return abstract_b(true); return abstract_b(false); }
ASFUNCTIONBODY(JSON,_stringify) { _NR<ASObject> value; ARG_UNPACK(value); std::vector<ASObject *> path; tiny_string filter; IFunction* replacer = NULL; if (argslen > 1 && !args[1]->is<Null>() && !args[1]->is<Undefined>()) { if (args[1]->is<IFunction>()) { replacer = args[1]->as<IFunction>(); } else if (args[1]->is<Array>()) { filter = " "; Array* ar = args[1]->as<Array>(); for (uint64_t i = 0; i < ar->size(); i++) { filter += ar->at(i)->toString(); filter += " "; } } else throwError<TypeError>(kJSONInvalidReplacer); } tiny_string spaces = ""; if (argslen > 2) { ASObject* space = args[2]; spaces = " "; if (space->is<Number>() || space->is<Integer>() || space->is<UInteger>()) { int32_t v = space->toInt(); if (v < 0) v = 0; if (v > 10) v = 10; spaces = spaces.substr_bytes(0,v); } else if (space->is<Boolean>() || space->is<Null>()) { spaces = ""; } else { if(space->has_toString()) { _R<ASObject> ret = space->call_toString(); spaces = ret->toString(); } else spaces = space->toString(); if (spaces.numBytes() > 10) spaces = spaces.substr_bytes(0,10); } } tiny_string res = value->toJSON(path,replacer,spaces,filter); return Class<ASString>::getInstanceS(res); }
ASFUNCTIONBODY(Array,forEach) { assert_and_throw(argslen == 1 || argslen == 2); Array* th=static_cast<Array*>(obj); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; for(unsigned int i=0; i < th->data.size(); i++) { assert_and_throw(th->data[i].type==DATA_OBJECT); params[0] = th->data[i].data; th->data[i].data->incRef(); params[1] = abstract_i(i); params[2] = th; th->incRef(); ASObject *funcret; if( argslen == 1 ) { funcret=f->call(new Null, params, 3); } else { args[1]->incRef(); funcret=f->call(args[1], params, 3); } if(funcret) funcret->decRef(); } return NULL; }
tiny_string Vector::toJSON(std::vector<ASObject *> &path, IFunction *replacer, const tiny_string &spaces, const tiny_string &filter) { bool ok; tiny_string res = call_toJSON(ok,path,replacer,spaces,filter); if (ok) return res; // check for cylic reference if (std::find(path.begin(),path.end(), this) != path.end()) throwError<TypeError>(kJSONCyclicStructure); path.push_back(this); res += "["; bool bfirst = true; tiny_string newline = (spaces.empty() ? "" : "\n"); for (unsigned int i =0; i < vec.size(); i++) { tiny_string subres; ASObject* o = vec[i]; if (!o) o = getSystemState()->getNullRef(); if (replacer != NULL) { ASObject* params[2]; params[0] = abstract_di(getSystemState(),i); params[0]->incRef(); params[1] = o; params[1]->incRef(); ASObject *funcret=replacer->call(getSystemState()->getNullRef(), params, 2); if (funcret) subres = funcret->toJSON(path,NULL,spaces,filter); } else { subres = o->toJSON(path,replacer,spaces,filter); } if (!subres.empty()) { if (!bfirst) res += ","; res += newline+spaces; bfirst = false; res += subres; } } if (!bfirst) res += newline+spaces.substr_bytes(0,spaces.numBytes()/2); res += "]"; path.pop_back(); return res; }
ASFUNCTIONBODY(ASObject,hasOwnProperty) { assert_and_throw(argslen==1); multiname name; name.name_type=multiname::NAME_STRING; name.name_s=args[0]->toString(); name.ns.push_back(nsNameAndKind("",NAMESPACE)); name.isAttribute=false; if(obj->getClass()) { ASObject* proto = obj->getClass()->prototype.getPtr(); if (proto != obj && proto->hasPropertyByMultiname(name, true)) return abstract_b(false); } bool ret=obj->hasPropertyByMultiname(name, true); return abstract_b(ret); }
ASFUNCTIONBODY(Vector, every) { Vector* th=static_cast<Vector*>(obj); if (argslen < 1) throwError<ArgumentError>(kWrongArgumentCountError, "Vector.some", "1", Integer::toString(argslen)); if (!args[0]->is<IFunction>()) throwError<TypeError>(kCheckTypeFailedError, args[0]->getClassName(), "Function"); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; ASObject *funcRet; for(unsigned int i=0; i < th->size(); i++) { if (th->vec[i]) { params[0] = th->vec[i]; th->vec[i]->incRef(); } else params[0] = obj->getSystemState()->getNullRef(); params[1] = abstract_i(obj->getSystemState(),i); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(obj->getSystemState()->getNullRef(), params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if (funcRet->is<Undefined>() || funcRet->is<Null>()) throwError<TypeError>(kCallOfNonFunctionError, funcRet->toString()); if(!Boolean_concrete(funcRet)) { return funcRet; } funcRet->decRef(); } } return abstract_b(obj->getSystemState(),true); }
ASFUNCTIONBODY(Vector,_map) { Vector* th=static_cast<Vector*>(obj); _NR<IFunction> func; _NR<ASObject> thisObject; if (argslen >= 1 && !args[0]->is<IFunction>()) throwError<TypeError>(kCheckTypeFailedError, args[0]->getClassName(), "Function"); ARG_UNPACK(func)(thisObject,NullRef); Vector* ret= (Vector*)obj->getClass()->getInstance(true,NULL,0); ASObject* thisObj; for(uint32_t i=0;i<th->size();i++) { ASObject* funcArgs[3]; if (!th->vec[i]) funcArgs[0]=obj->getSystemState()->getNullRef(); else { if(th->vec[i]) { funcArgs[0]=th->vec[i]; funcArgs[0]->incRef(); } else funcArgs[0]=obj->getSystemState()->getUndefinedRef(); } funcArgs[1]=abstract_i(obj->getSystemState(),i); funcArgs[2]=th; funcArgs[2]->incRef(); if (thisObject.isNull()) thisObj = obj->getSystemState()->getNullRef(); else { thisObj = thisObject.getPtr(); thisObj->incRef(); } ASObject* funcRet=func->call(thisObj, funcArgs, 3); assert_and_throw(funcRet); ret->vec.push_back(funcRet); } return ret; }
ASFUNCTIONBODY(Vector, every) { Vector* th=static_cast<Vector*>(obj); if (argslen < 1) throw Class<ArgumentError>::getInstanceS("Error #1063"); if (!args[0]->is<IFunction>()) throw Class<TypeError>::getInstanceS("Error #1034"); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; ASObject *funcRet; for(unsigned int i=0; i < th->size(); i++) { if (th->vec[i]) { params[0] = th->vec[i]; th->vec[i]->incRef(); } else params[0] = getSys()->getNullRef(); params[1] = abstract_i(i); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(getSys()->getNullRef(), params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if (funcRet->is<Undefined>() || funcRet->is<Null>()) throw Class<TypeError>::getInstanceS("Error #1006"); if(!Boolean_concrete(funcRet)) { return funcRet; } funcRet->decRef(); } } return abstract_b(true); }
ASFUNCTIONBODY(Array,filter) { Array* th=static_cast<Array*>(obj); Array* ret=Class<Array>::getInstanceS(); _NR<IFunction> f; ARG_UNPACK(f); if (f.isNull()) return ret; ASObject* params[3]; ASObject *funcRet; std::map<uint32_t, data_slot>::iterator it=th->data.begin(); for(;it != th->data.end();++it) { assert_and_throw(it->second.type==DATA_OBJECT); params[0] = it->second.data; it->second.data->incRef(); params[1] = abstract_i(it->first); params[2] = th; th->incRef(); // ensure that return values are the original values ASObject *origval = it->second.data; it->second.data->incRef(); if(argslen==1) { funcRet=f->call(getSys()->getNullRef(), params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if(Boolean_concrete(funcRet)) ret->push(_MR(origval)); else origval->decRef(); funcRet->decRef(); } } return ret; }
ASFUNCTIONBODY(Vector,filter) { if (argslen < 1 || argslen > 2) throwError<ArgumentError>(kWrongArgumentCountError, "Vector.filter", "1", Integer::toString(argslen)); if (!args[0]->is<IFunction>()) throwError<TypeError>(kCheckTypeFailedError, args[0]->getClassName(), "Function"); Vector* th=static_cast<Vector*>(obj); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; Vector* ret= (Vector*)obj->getClass()->getInstance(true,NULL,0); ASObject *funcRet; for(unsigned int i=0;i<th->size();i++) { if (!th->vec[i]) continue; params[0] = th->vec[i]; th->vec[i]->incRef(); params[1] = abstract_i(obj->getSystemState(),i); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(obj->getSystemState()->getNullRef(), params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if(Boolean_concrete(funcRet)) { th->vec[i]->incRef(); ret->vec.push_back(th->vec[i]); } funcRet->decRef(); } } return ret; }
ASFUNCTIONBODY(Vector,filter) { if (argslen < 1 || argslen > 2) throw Class<ArgumentError>::getInstanceS("Error #1063"); if (!args[0]->is<IFunction>()) throw Class<TypeError>::getInstanceS("Error #1034"); Vector* th=static_cast<Vector*>(obj); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; Vector* ret= (Vector*)obj->getClass()->getInstance(true,NULL,0); ASObject *funcRet; for(unsigned int i=0;i<th->size();i++) { if (!th->vec[i]) continue; params[0] = th->vec[i]; th->vec[i]->incRef(); params[1] = abstract_i(i); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(getSys()->getNullRef(), params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if(Boolean_concrete(funcRet)) { th->vec[i]->incRef(); ret->vec.push_back(th->vec[i]); } funcRet->decRef(); } } return ret; }
ASObject* ASObject::getValueAt(int index) { obj_var* obj=Variables.getValueAt(index); assert_and_throw(obj); ASObject* ret; if(obj->getter) { //Call the getter LOG(LOG_CALLS,_("Calling the getter")); IFunction* getter=obj->getter->getOverride(); incRef(); ret=getter->call(this,NULL,0); ret->fake_decRef(); LOG(LOG_CALLS,_("End of getter")); } else ret=obj->var; return ret; }
ASFUNCTIONBODY(Vector, some) { if (argslen < 1) throw Class<ArgumentError>::getInstanceS("Error #1063"); if (!args[0]->is<IFunction>()) throw Class<TypeError>::getInstanceS("Error #1034"); Vector* th=static_cast<Vector*>(obj); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; ASObject *funcRet; for(unsigned int i=0; i < th->size(); i++) { if (!th->vec[i]) continue; params[0] = th->vec[i]; th->vec[i]->incRef(); params[1] = abstract_i(i); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(new Null, params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if(Boolean_concrete(funcRet)) { return funcRet; } funcRet->decRef(); } } return abstract_b(false); }
ASObject* Vector::generator(TemplatedClass<Vector>* o_class, ASObject* const* args, const unsigned int argslen) { assert_and_throw(argslen == 1); assert_and_throw(args[0]->getClass()); assert_and_throw(o_class->getTypes().size() == 1); Type* type = o_class->getTypes()[0]; if(args[0]->getClass() == Class<Array>::getClass()) { //create object without calling _constructor Vector* ret = o_class->getInstance(false,NULL,0); Array* a = static_cast<Array*>(args[0]); for(unsigned int i=0;i<a->size();++i) { ASObject* obj = a->at(i).getPtr(); obj->incRef(); //Convert the elements of the array to the type of this vector ret->vec.push_back( type->coerce(obj) ); } return ret; } else if(args[0]->getClass()->getTemplate() == Template<Vector>::getTemplate()) { Vector* arg = static_cast<Vector*>(args[0]); //create object without calling _constructor Vector* ret = o_class->getInstance(false,NULL,0); for(auto i = arg->vec.begin(); i != arg->vec.end(); ++i) { (*i)->incRef(); ret->vec.push_back( type->coerce(*i) ); } return ret; } else { throw Class<ArgumentError>::getInstanceS("global Vector() function takes Array or Vector"); } }
ASFUNCTIONBODY(Array,filter) { Array* th=static_cast<Array*>(obj); assert_and_throw(argslen==1 || argslen==2); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; Array* ret=Class<Array>::getInstanceS(); ASObject *funcRet; std::map<uint32_t, data_slot>::iterator it=th->data.begin(); for(;it != th->data.end();++it) { assert_and_throw(it->second.type==DATA_OBJECT); params[0] = it->second.data; it->second.data->incRef(); params[1] = abstract_i(it->first); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(getSys()->getNullRef(), params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if(Boolean_concrete(funcRet)) { it->second.data->incRef(); ret->push(_MR(it->second.data)); } funcRet->decRef(); } } return ret; }
ASFUNCTIONBODY(Array,filter) { Array* th=static_cast<Array*>(obj); assert_and_throw(argslen==1 || argslen==2); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; Array* ret=Class<Array>::getInstanceS(); ASObject *funcRet; for(unsigned int i=0;i<th->data.size();i++) { assert_and_throw(th->data[i].type==DATA_OBJECT); params[0] = th->data[i].data; th->data[i].data->incRef(); params[1] = abstract_i(i); params[2] = th; th->incRef(); if(argslen==1) { funcRet=f->call(new Null, params, 3); } else { args[1]->incRef(); funcRet=f->call(args[1], params, 3); } if(funcRet) { if(Boolean_concrete(funcRet)) { th->data[i].data->incRef(); ret->push(th->data[i].data); } funcRet->decRef(); } } return ret; }
void Proxy::setVariableByMultiname(const multiname& name, ASObject* o, CONST_ALLOWED_FLAG allowConst) { //If a variable named like this already exist, use that if(ASObject::hasPropertyByMultiname(name, true, false) || !implEnable) { ASObject::setVariableByMultiname(name,o,allowConst); return; } //Check if there is a custom setter defined, skipping implementation to avoid recursive calls multiname setPropertyName(NULL); setPropertyName.name_type=multiname::NAME_STRING; setPropertyName.name_s_id=getSys()->getUniqueStringId("setProperty"); setPropertyName.ns.push_back(nsNameAndKind(flash_proxy,NAMESPACE)); _NR<ASObject> proxySetter=getVariableByMultiname(setPropertyName,ASObject::SKIP_IMPL); if(proxySetter.isNull()) { ASObject::setVariableByMultiname(name,o,allowConst); return; } assert_and_throw(proxySetter->getObjectType()==T_FUNCTION); IFunction* f=static_cast<IFunction*>(proxySetter.getPtr()); ASObject* namearg = Class<ASString>::getInstanceS(name.normalizedName()); namearg->setProxyProperty(name); ASObject* args[2]; args[0]=namearg; args[1]=o; //We now suppress special handling implEnable=false; LOG(LOG_CALLS,_("Proxy::setProperty")); incRef(); _R<ASObject> ret=_MR( f->call(this,args,2) ); assert_and_throw(ret->is<Undefined>()); implEnable=true; }
/* * (creates and) sets the property 'prototype' to o * 'prototype' is usually DYNAMIC_TRAIT, but on Class_base * it is a DECLARED_TRAIT, which is gettable only */ void ASObject::setprop_prototype(_NR<ASObject>& o) { ASObject* obj = o.getPtr(); obj->incRef(); multiname prototypeName; prototypeName.name_type=multiname::NAME_STRING; prototypeName.name_s="prototype"; prototypeName.ns.push_back(nsNameAndKind("",NAMESPACE)); bool has_getter = false; variable* ret=findSettable(prototypeName,false, &has_getter); if(!ret && has_getter) throw Class<ReferenceError>::getInstanceS("Error #1074: Illegal write to read-only property prototype"); if(!ret) ret = Variables.findObjVar(prototypeName,DYNAMIC_TRAIT,DECLARED_TRAIT|DYNAMIC_TRAIT); if(ret->setter) { this->incRef(); _MR( ret->setter->call(this,&obj,1) ); } else ret->setVar(obj); }
bool Proxy::hasPropertyByMultiname(const multiname& name, bool considerDynamic, bool considerPrototype) { if (name.normalizedName() == "isAttribute") return true; //If a variable named like this already exist, use that bool asobject_has_property=ASObject::hasPropertyByMultiname(name, considerDynamic, considerPrototype); if(asobject_has_property || !implEnable) return asobject_has_property; //Check if there is a custom hasProperty defined, skipping implementation to avoid recursive calls multiname hasPropertyName(NULL); hasPropertyName.name_type=multiname::NAME_STRING; hasPropertyName.name_s_id=getSys()->getUniqueStringId("hasProperty"); hasPropertyName.ns.push_back(nsNameAndKind(flash_proxy,NAMESPACE)); _NR<ASObject> proxyHasProperty=getVariableByMultiname(hasPropertyName,ASObject::SKIP_IMPL); if(proxyHasProperty.isNull()) { return false; } assert_and_throw(proxyHasProperty->getObjectType()==T_FUNCTION); IFunction* f=static_cast<IFunction*>(proxyHasProperty.getPtr()); ASObject* namearg = Class<ASString>::getInstanceS(name.normalizedName()); namearg->setProxyProperty(name); ASObject* arg = namearg; //We now suppress special handling implEnable=false; LOG(LOG_CALLS,_("Proxy::hasProperty")); incRef(); _NR<ASObject> ret=_MNR(f->call(this,&arg,1)); implEnable=true; Boolean* b = static_cast<Boolean*>(ret.getPtr()); return b->val; }
_NR<ASObject> Proxy::getVariableByMultiname(const multiname& name, GET_VARIABLE_OPTION opt) { //It seems that various kind of implementation works only with the empty namespace assert_and_throw(name.ns.size()>0); _NR<ASObject> o; LOG(LOG_CALLS,"Proxy::getVar "<< name << " " << this->toDebugString()); if(ASObject::hasPropertyByMultiname(name, true, true) || !implEnable || (opt & ASObject::SKIP_IMPL)!=0) o = ASObject::getVariableByMultiname(name,opt); if (!o.isNull() || !implEnable || (opt & ASObject::SKIP_IMPL)!=0) return o; //Check if there is a custom getter defined, skipping implementation to avoid recursive calls multiname getPropertyName(NULL); getPropertyName.name_type=multiname::NAME_STRING; getPropertyName.name_s_id=getSys()->getUniqueStringId("getProperty"); getPropertyName.ns.push_back(nsNameAndKind(flash_proxy,NAMESPACE)); o=getVariableByMultiname(getPropertyName,ASObject::SKIP_IMPL); if(o.isNull()) return ASObject::getVariableByMultiname(name,opt); assert_and_throw(o->getObjectType()==T_FUNCTION); IFunction* f=static_cast<IFunction*>(o.getPtr()); ASObject* namearg = Class<ASString>::getInstanceS(name.normalizedName()); namearg->setProxyProperty(name); ASObject* arg = namearg; //We now suppress special handling implEnable=false; LOG(LOG_CALLS,"Proxy::getProperty "<< name.normalizedName() << " " << this->toDebugString()); incRef(); _NR<ASObject> ret=_MNR(f->call(this,&arg,1)); implEnable=true; return ret; }
bool Proxy::deleteVariableByMultiname(const multiname& name) { //If a variable named like this already exist, use that if(ASObject::hasPropertyByMultiname(name, true, false) || !implEnable) { return ASObject::deleteVariableByMultiname(name); } //Check if there is a custom deleter defined, skipping implementation to avoid recursive calls multiname deletePropertyName(NULL); deletePropertyName.name_type=multiname::NAME_STRING; deletePropertyName.name_s_id=getSys()->getUniqueStringId("deleteProperty"); deletePropertyName.ns.push_back(nsNameAndKind(flash_proxy,NAMESPACE)); _NR<ASObject> proxyDeleter=getVariableByMultiname(deletePropertyName,ASObject::SKIP_IMPL); if(proxyDeleter.isNull()) { return ASObject::deleteVariableByMultiname(name); } assert_and_throw(proxyDeleter->getObjectType()==T_FUNCTION); IFunction* f=static_cast<IFunction*>(proxyDeleter.getPtr()); ASObject* namearg = Class<ASString>::getInstanceS(name.normalizedName()); namearg->setProxyProperty(name); ASObject* arg = namearg; //We now suppress special handling implEnable=false; LOG(LOG_CALLS,_("Proxy::deleteProperty")); incRef(); _NR<ASObject> ret=_MNR(f->call(this,&arg,1)); implEnable=true; Boolean* b = static_cast<Boolean*>(ret.getPtr()); return b->val; }
ASFUNCTIONBODY(Vector,forEach) { if (argslen < 1) throwError<ArgumentError>(kWrongArgumentCountError, "Vector.forEach", "1", Integer::toString(argslen)); if (!args[0]->is<IFunction>()) throwError<TypeError>(kCheckTypeFailedError, args[0]->getClassName(), "Function"); Vector* th=static_cast<Vector*>(obj); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; for(unsigned int i=0; i < th->size(); i++) { if (!th->vec[i]) continue; params[0] = th->vec[i]; th->vec[i]->incRef(); params[1] = abstract_i(obj->getSystemState(),i); params[2] = th; th->incRef(); ASObject *funcret; if( argslen == 1 ) { funcret=f->call(obj->getSystemState()->getNullRef(), params, 3); } else { args[1]->incRef(); funcret=f->call(args[1], params, 3); } if(funcret) funcret->decRef(); } return NULL; }
ASFUNCTIONBODY(Vector,forEach) { if (argslen < 1) throw Class<ArgumentError>::getInstanceS("Error #1063"); if (!args[0]->is<IFunction>()) throw Class<TypeError>::getInstanceS("Error #1034"); Vector* th=static_cast<Vector*>(obj); IFunction* f = static_cast<IFunction*>(args[0]); ASObject* params[3]; for(unsigned int i=0; i < th->size(); i++) { if (!th->vec[i]) continue; params[0] = th->vec[i]; th->vec[i]->incRef(); params[1] = abstract_i(i); params[2] = th; th->incRef(); ASObject *funcret; if( argslen == 1 ) { funcret=f->call(getSys()->getNullRef(), params, 3); } else { args[1]->incRef(); funcret=f->call(args[1], params, 3); } if(funcret) funcret->decRef(); } return NULL; }
void NetStream::execute() { if(downloader->hasFailed()) { sys->currentVm->addEvent(this,Class<Event>::getInstanceS("ioError")); sys->downloadManager->destroy(downloader); return; } //The downloader hasn't failed yet at this point //mutex access to downloader istream s(downloader); s.exceptions ( istream::eofbit | istream::failbit | istream::badbit ); ThreadProfile* profile=sys->allocateProfiler(RGB(0,0,200)); profile->setTag("NetStream"); //We need to catch possible EOF and other error condition in the non reliable stream uint32_t decodedAudioBytes=0; uint32_t decodedVideoFrames=0; //The decoded time is computed from the decodedAudioBytes to avoid drifts uint32_t decodedTime=0; bool waitForFlush=true; try { ScriptDataTag tag; Chronometer chronometer; STREAM_TYPE t=classifyStream(s); if(t==FLV_STREAM) { FLV_HEADER h(s); if(!h.isValid()) throw ParseException("FLV is not valid"); unsigned int prevSize=0; bool done=false; do { //Check if threadAbort has been called, if so, stop this loop if(closed) done = true; UI32 PreviousTagSize; s >> PreviousTagSize; PreviousTagSize.bswap(); assert_and_throw(PreviousTagSize==prevSize); //Check tag type and read it UI8 TagType; s >> TagType; switch(TagType) { case 8: { AudioDataTag tag(s); prevSize=tag.getTotalLen(); if(audioDecoder==NULL) { audioCodec=tag.SoundFormat; switch(tag.SoundFormat) { case AAC: assert_and_throw(tag.isHeader()) #ifdef ENABLE_LIBAVCODEC audioDecoder=new FFMpegAudioDecoder(tag.SoundFormat, tag.packetData, tag.packetLen); #else audioDecoder=new NullAudioDecoder(); #endif tag.releaseBuffer(); break; case MP3: #ifdef ENABLE_LIBAVCODEC audioDecoder=new FFMpegAudioDecoder(tag.SoundFormat,NULL,0); #else audioDecoder=new NullAudioDecoder(); #endif decodedAudioBytes+= audioDecoder->decodeData(tag.packetData,tag.packetLen,decodedTime); //Adjust timing decodedTime=decodedAudioBytes/audioDecoder->getBytesPerMSec(); break; default: throw RunTimeException("Unsupported SoundFormat"); } if(audioDecoder->isValid() && sys->audioManager->pluginLoaded()) audioStream=sys->audioManager->createStreamPlugin(audioDecoder); } else { assert_and_throw(audioCodec==tag.SoundFormat); decodedAudioBytes+= audioDecoder->decodeData(tag.packetData,tag.packetLen,decodedTime); if(audioStream==0 && audioDecoder->isValid() && sys->audioManager->pluginLoaded()) audioStream=sys->audioManager->createStreamPlugin(audioDecoder); //Adjust timing decodedTime=decodedAudioBytes/audioDecoder->getBytesPerMSec(); } break; } case 9: { VideoDataTag tag(s); prevSize=tag.getTotalLen(); //If the framerate is known give the right timing, otherwise use decodedTime from audio uint32_t frameTime=(frameRate!=0.0)?(decodedVideoFrames*1000/frameRate):decodedTime; if(videoDecoder==NULL) { //If the isHeader flag is on then the decoder becomes the owner of the data if(tag.isHeader()) { //The tag is the header, initialize decoding #ifdef ENABLE_LIBAVCODEC videoDecoder= new FFMpegVideoDecoder(tag.codec,tag.packetData,tag.packetLen, frameRate); #else videoDecoder=new NullVideoDecoder(); #endif tag.releaseBuffer(); } else if(videoDecoder==NULL) { //First packet but no special handling #ifdef ENABLE_LIBAVCODEC videoDecoder=new FFMpegVideoDecoder(tag.codec,NULL,0,frameRate); #else videoDecoder=new NullVideoDecoder(); #endif videoDecoder->decodeData(tag.packetData,tag.packetLen, frameTime); decodedVideoFrames++; } Event* status=Class<NetStatusEvent>::getInstanceS("status", "NetStream.Play.Start"); getVm()->addEvent(this, status); status->decRef(); status=Class<NetStatusEvent>::getInstanceS("status", "NetStream.Buffer.Full"); getVm()->addEvent(this, status); status->decRef(); } else { videoDecoder->decodeData(tag.packetData,tag.packetLen, frameTime); decodedVideoFrames++; } break; } case 18: { tag = ScriptDataTag(s); prevSize=tag.getTotalLen(); //The frameRate of the container overrides the stream if(tag.metadataDouble.find("framerate") != tag.metadataDouble.end()) frameRate=tag.metadataDouble["framerate"]; break; } default: LOG(LOG_ERROR,_("Unexpected tag type ") << (int)TagType << _(" in FLV")); threadAbort(); } if(!tickStarted && isReady()) { { multiname onMetaDataName; onMetaDataName.name_type=multiname::NAME_STRING; onMetaDataName.name_s="onMetaData"; onMetaDataName.ns.push_back(nsNameAndKind("",NAMESPACE)); ASObject* callback = client->getVariableByMultiname(onMetaDataName); if(callback && callback->getObjectType() == T_FUNCTION) { ASObject* callbackArgs[1]; ASObject* metadata = Class<ASObject>::getInstanceS(); if(tag.metadataDouble.find("width") != tag.metadataDouble.end()) metadata->setVariableByQName("width", "", abstract_d(tag.metadataDouble["width"])); else metadata->setVariableByQName("width", "", abstract_d(getVideoWidth())); if(tag.metadataDouble.find("height") != tag.metadataDouble.end()) metadata->setVariableByQName("height", "", abstract_d(tag.metadataDouble["height"])); else metadata->setVariableByQName("height", "", abstract_d(getVideoHeight())); if(tag.metadataDouble.find("framerate") != tag.metadataDouble.end()) metadata->setVariableByQName("framerate", "", abstract_d(tag.metadataDouble["framerate"])); if(tag.metadataDouble.find("duration") != tag.metadataDouble.end()) metadata->setVariableByQName("duration", "", abstract_d(tag.metadataDouble["duration"])); if(tag.metadataInteger.find("canseekontime") != tag.metadataInteger.end()) metadata->setVariableByQName("canSeekToEnd", "", abstract_b(tag.metadataInteger["canseekontime"] == 1)); if(tag.metadataDouble.find("audiodatarate") != tag.metadataDouble.end()) metadata->setVariableByQName("audiodatarate", "", abstract_d(tag.metadataDouble["audiodatarate"])); if(tag.metadataDouble.find("videodatarate") != tag.metadataDouble.end()) metadata->setVariableByQName("videodatarate", "", abstract_d(tag.metadataDouble["videodatarate"])); //TODO: missing: audiocodecid (Number), cuePoints (Object[]), //videocodecid (Number), custommetadata's callbackArgs[0] = metadata; client->incRef(); metadata->incRef(); FunctionEvent* event = new FunctionEvent(static_cast<IFunction*>(callback), client, callbackArgs, 1); getVm()->addEvent(NULL,event); event->decRef(); } } tickStarted=true; if(frameRate==0) { assert(videoDecoder->frameRate); frameRate=videoDecoder->frameRate; } sys->addTick(1000/frameRate,this); //Also ask for a render rate equal to the video one (capped at 24) float localRenderRate=dmin(frameRate,24); sys->setRenderRate(localRenderRate); } profile->accountTime(chronometer.checkpoint()); if(aborting) { throw JobTerminationException(); } } while(!done); } else threadAbort(); }
_NR<ASObject> ASObject::getVariableByMultiname(const multiname& name, GET_VARIABLE_OPTION opt, Class_base* cls) { check(); assert(!cls || classdef->isSubClass(cls)); //Get from the current object without considering borrowed properties variable* obj=findGettable(name, false); if(!obj && cls) { //Look for borrowed traits before obj=cls->findGettable(name,true); } if(!obj && cls) { //Check prototype chain ASObject* proto = cls->prototype.getPtr(); while(proto) { obj = proto->findGettable(name, false); if(obj) { obj->var->incRef(); return _MNR(obj->var); } proto = proto->getprop_prototype(); } } if(!obj) return NullRef; if(obj->getter) { //Call the getter ASObject* target=this; if(target->classdef) { LOG(LOG_CALLS,_("Calling the getter on type ") << target->classdef->class_name); } else { LOG(LOG_CALLS,_("Calling the getter")); } IFunction* getter=obj->getter; target->incRef(); ASObject* ret=getter->call(target,NULL,0); LOG(LOG_CALLS,_("End of getter")); // No incRef because ret is a new instance return _MNR(ret); } else { assert_and_throw(!obj->setter); assert_and_throw(obj->var); if(obj->var->getObjectType()==T_FUNCTION && obj->var->as<IFunction>()->isMethod()) { LOG(LOG_CALLS,"Attaching this " << this << " to function " << name); //the obj reference is acquired by the smart reference this->incRef(); IFunction* f=obj->var->as<IFunction>()->bind(_MR(this),-1); //No incref is needed, as the function is a new instance return _MNR(f); } obj->var->incRef(); return _MNR(obj->var); } }
void NetStream::execute() { //checkPolicyFile only applies to per-pixel access, loading and playing is always allowed. //So there is no need to disallow playing if policy files disallow it. //We do need to check if per-pixel access is allowed. SecurityManager::EVALUATIONRESULT evaluationResult = sys->securityManager->evaluatePoliciesURL(url, true); if(evaluationResult == SecurityManager::NA_CROSSDOMAIN_POLICY) rawAccessAllowed = true; if(downloader->hasFailed()) { this->incRef(); sys->currentVm->addEvent(_MR(this),_MR(Class<Event>::getInstanceS("ioError"))); sys->downloadManager->destroy(downloader); return; } //The downloader hasn't failed yet at this point istream s(downloader); s.exceptions ( istream::eofbit | istream::failbit | istream::badbit ); ThreadProfile* profile=sys->allocateProfiler(RGB(0,0,200)); profile->setTag("NetStream"); bool waitForFlush=true; StreamDecoder* streamDecoder=NULL; //We need to catch possible EOF and other error condition in the non reliable stream try { Chronometer chronometer; streamDecoder=new FFMpegStreamDecoder(s); if(!streamDecoder->isValid()) threadAbort(); bool done=false; while(!done) { //Check if threadAbort has been called, if so, stop this loop if(closed) done = true; bool decodingSuccess=streamDecoder->decodeNextFrame(); if(decodingSuccess==false) done = true; if(videoDecoder==NULL && streamDecoder->videoDecoder) { videoDecoder=streamDecoder->videoDecoder; this->incRef(); getVm()->addEvent(_MR(this), _MR(Class<NetStatusEvent>::getInstanceS("status", "NetStream.Play.Start"))); this->incRef(); getVm()->addEvent(_MR(this), _MR(Class<NetStatusEvent>::getInstanceS("status", "NetStream.Buffer.Full"))); } if(audioDecoder==NULL && streamDecoder->audioDecoder) audioDecoder=streamDecoder->audioDecoder; if(audioStream==NULL && audioDecoder && audioDecoder->isValid() && sys->audioManager->pluginLoaded()) audioStream=sys->audioManager->createStreamPlugin(audioDecoder); if(audioStream && audioStream->paused() && !paused) { //The audio stream is paused but should not! //As we have new data fill the stream audioStream->fill(); } if(!tickStarted && isReady()) { multiname onMetaDataName; onMetaDataName.name_type=multiname::NAME_STRING; onMetaDataName.name_s="onMetaData"; onMetaDataName.ns.push_back(nsNameAndKind("",NAMESPACE)); ASObject* callback = client->getVariableByMultiname(onMetaDataName); if(callback && callback->getObjectType() == T_FUNCTION) { ASObject* callbackArgs[1]; ASObject* metadata = Class<ASObject>::getInstanceS(); double d; uint32_t i; if(streamDecoder->getMetadataDouble("width",d)) metadata->setVariableByQName("width", "",abstract_d(d),DYNAMIC_TRAIT); else metadata->setVariableByQName("width", "", abstract_d(getVideoWidth()),DYNAMIC_TRAIT); if(streamDecoder->getMetadataDouble("height",d)) metadata->setVariableByQName("height", "",abstract_d(d),DYNAMIC_TRAIT); else metadata->setVariableByQName("height", "", abstract_d(getVideoHeight()),DYNAMIC_TRAIT); if(streamDecoder->getMetadataDouble("framerate",d)) metadata->setVariableByQName("framerate", "",abstract_d(d),DYNAMIC_TRAIT); if(streamDecoder->getMetadataDouble("duration",d)) metadata->setVariableByQName("duration", "",abstract_d(d),DYNAMIC_TRAIT); if(streamDecoder->getMetadataInteger("canseekontime",i)) metadata->setVariableByQName("canSeekToEnd", "",abstract_b(i == 1),DYNAMIC_TRAIT); if(streamDecoder->getMetadataDouble("audiodatarate",d)) metadata->setVariableByQName("audiodatarate", "",abstract_d(d),DYNAMIC_TRAIT); if(streamDecoder->getMetadataDouble("videodatarate",d)) metadata->setVariableByQName("videodatarate", "",abstract_d(d),DYNAMIC_TRAIT); //TODO: missing: audiocodecid (Number), cuePoints (Object[]), //videocodecid (Number), custommetadata's client->incRef(); metadata->incRef(); callbackArgs[0] = metadata; callback->incRef(); _R<FunctionEvent> event(new FunctionEvent(_MR(static_cast<IFunction*>(callback)), _MR(client), callbackArgs, 1)); getVm()->addEvent(NullRef,event); } tickStarted=true; if(frameRate==0) { assert(videoDecoder->frameRate); frameRate=videoDecoder->frameRate; } sys->addTick(1000/frameRate,this); //Also ask for a render rate equal to the video one (capped at 24) float localRenderRate=dmin(frameRate,24); sys->setRenderRate(localRenderRate); } profile->accountTime(chronometer.checkpoint()); if(aborting) throw JobTerminationException(); } } catch(LightsparkException& e) { LOG(LOG_ERROR, "Exception in NetStream " << e.cause); threadAbort(); waitForFlush=false; } catch(JobTerminationException& e) { waitForFlush=false; } catch(exception& e) { LOG(LOG_ERROR, _("Exception in reading: ")<<e.what()); } if(waitForFlush) { //Put the decoders in the flushing state and wait for the complete consumption of contents if(audioDecoder) audioDecoder->setFlushing(); if(videoDecoder) videoDecoder->setFlushing(); if(audioDecoder) audioDecoder->waitFlushed(); if(videoDecoder) videoDecoder->waitFlushed(); this->incRef(); getVm()->addEvent(_MR(this), _MR(Class<NetStatusEvent>::getInstanceS("status", "NetStream.Play.Stop"))); this->incRef(); getVm()->addEvent(_MR(this), _MR(Class<NetStatusEvent>::getInstanceS("status", "NetStream.Buffer.Flush"))); } //Before deleting stops ticking, removeJobs also spin waits for termination sys->removeJob(this); tickStarted=false; sem_wait(&mutex); //Change the state to invalid to avoid locking videoDecoder=NULL; audioDecoder=NULL; //Clean up everything for a possible re-run sys->downloadManager->destroy(downloader); //This transition is critical, so the mutex is needed downloader=NULL; if(audioStream) sys->audioManager->freeStreamPlugin(audioStream); audioStream=NULL; sem_post(&mutex); delete streamDecoder; }
void ASObject::setVariableByMultiname(const multiname& name, ASObject* o, Class_base* cls) { check(); assert(!cls || classdef->isSubClass(cls)); //NOTE: we assume that [gs]etSuper and [sg]etProperty correctly manipulate the cur_level (for getActualClass) bool has_getter=false; variable* obj=findSettable(name, false, &has_getter); if(!obj && cls) { //Look for borrowed traits before //It's valid to override only a getter, so keep //looking for a settable even if a super class sets //has_getter to true. obj=cls->findSettable(name,true,&has_getter); } if(!obj && cls) { //Look in prototype chain ASObject* proto = cls->prototype.getPtr(); while(proto) { variable* tmp = proto->findSettable(name, false, NULL /*prototypes never have getters/setters*/); if(tmp) { if (tmp->kind != DYNAMIC_TRAIT) // dynamic prototype properties can be overridden obj = tmp; break; } proto = proto->getprop_prototype(); } } if(!obj) { if(has_getter) { tiny_string err=tiny_string("Error #1074: Illegal write to read-only property ")+name.normalizedName(); if(cls) err+=tiny_string(" on type ")+cls->getQualifiedClassName(); throw Class<ReferenceError>::getInstanceS(err); } //Create a new dynamic variable obj=Variables.findObjVar(name,DYNAMIC_TRAIT,DYNAMIC_TRAIT); } if(obj->setter) { //Call the setter LOG(LOG_CALLS,_("Calling the setter")); //Overriding function is automatically done by using cur_level IFunction* setter=obj->setter; //One argument can be passed without creating an array ASObject* target=this; target->incRef(); _R<ASObject> ret= _MR( setter->call(target,&o,1) ); assert_and_throw(ret->is<Undefined>()); LOG(LOG_CALLS,_("End of setter")); } else { assert_and_throw(!obj->getter); obj->setVar(o); } }
// Conversion to ASObject ASObject* ExtVariant::getASObject() const { ASObject* asobj; switch(getType()) { case EV_STRING: asobj = Class<ASString>::getInstanceS(getString().c_str()); break; case EV_INT32: asobj = abstract_i(getInt()); break; case EV_DOUBLE: asobj = abstract_d(getDouble()); break; case EV_BOOLEAN: asobj = abstract_b(getBoolean()); break; case EV_OBJECT: { ExtObject* objValue = getObject(); ExtVariant* property; uint32_t count; // We are converting an array, so lets set indexes if(objValue->getType() == ExtObject::EO_ARRAY) { asobj = Class<Array>::getInstanceS(); count = objValue->getLength(); static_cast<Array*>(asobj)->resize(count); for(uint32_t i = 0; i < count; i++) { property = objValue->getProperty(i); static_cast<Array*>(asobj)->set(i, property->getASObject()); delete property; } } // We are converting an object, so lets set variables else { asobj = Class<ASObject>::getInstanceS(); ExtIdentifier** ids; uint32_t count; std::stringstream conv; if(objValue != NULL && objValue->enumerate(&ids, &count)) { for(uint32_t i = 0; i < count; i++) { property = objValue->getProperty(*ids[i]); if(ids[i]->getType() == ExtIdentifier::EI_STRING) { asobj->setVariableByQName(ids[i]->getString(), "", property->getASObject(), DYNAMIC_TRAIT); } else { conv << ids[i]->getInt(); asobj->setVariableByQName(conv.str().c_str(), "", property->getASObject(), DYNAMIC_TRAIT); } delete property; delete ids[i]; } } delete ids; } if(objValue != NULL) delete objValue; } break; case EV_NULL: asobj = new Null; break; case EV_VOID: default: asobj = new Undefined; break; } return asobj; }