_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); if(!name.ns[0].hasEmptyName() || ASObject::hasPropertyByMultiname(name, true, true) || !implEnable || (opt & ASObject::SKIP_IMPL)!=0) return ASObject::getVariableByMultiname(name,opt); //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)); _NR<ASObject> 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()); //Well, I don't how to pass multiname to an as function. I'll just pass the name as a string ASObject* arg=Class<ASString>::getInstanceS(name.normalizedName()); //We now suppress special handling implEnable=false; LOG(LOG_CALLS,"Proxy::getProperty"); incRef(); _NR<ASObject> ret=_MNR(f->call(this,&arg,1)); implEnable=true; return ret; }
void variables_map::initializeVar(const multiname& mname, ASObject* obj, multiname* typemname) { tiny_string name=mname.normalizedName(); const Type* type = NULL; /* If typename is resolvable right now, we coerce obj. * It it's not resolvable, then it must be a user defined class, * so we only allow Null and Undefined (which are both coerced to Null) */ if(!Type::isTypeResolvable(typemname)) { assert_and_throw(obj->is<Null>() || obj->is<Undefined>()); if(obj->is<Undefined>()) { //Casting undefined to an object (of unknown class) //results in Null obj->decRef(); obj = new Null; } } else { type = Type::getTypeFromMultiname(typemname); obj = type->coerce(obj); } Variables.insert(make_pair(name, variable(mname.ns[0], DECLARED_TRAIT, obj, typemname, type))); }
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()); //Well, I don't how to pass multiname to an as function. I'll just pass the name as a string ASObject* arg=Class<ASString>::getInstanceS(name.normalizedName()); //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; }
void Array::setVariableByMultiname(const multiname& name, ASObject* o, CONST_ALLOWED_FLAG allowConst) { assert_and_throw(implEnable); uint32_t index=0; if(!isValidMultiname(name,index)) return ASObject::setVariableByMultiname(name,o,allowConst); // Derived classes may be sealed! if (getClass() && getClass()->isSealed) throwError<ReferenceError>(kWriteSealedError, name.normalizedName(), getClass()->getQualifiedClassName()); if (index==0xFFFFFFFF) return; if(index>=size()) resize((uint64_t)index+1); if(data.count(index) && data[index].type==DATA_OBJECT && data[index].data) data[index].data->decRef(); if(!data.count(index)) data[index] = data_slot(); if(o->getObjectType()==T_INTEGER) { Integer* i=static_cast<Integer*>(o); data[index].data_i=i->val; data[index].type=DATA_INT; o->decRef(); } else { data[index].data=o; data[index].type=DATA_OBJECT; } }
bool Array::isValidMultiname(const multiname& name, uint32_t& index) { //First of all the multiname has to contain the null namespace //As the namespace vector is sorted, we check only the first one assert_and_throw(name.ns.size()!=0); if(!name.ns[0].hasEmptyName()) return false; if (name.name_type == multiname::NAME_STRING && !isIntegerWithoutLeadingZeros(name.normalizedName())) return false; return name.toUInt(index); }
void Vector::setVariableByMultiname(const multiname& name, ASObject* o, CONST_ALLOWED_FLAG allowConst) { assert_and_throw(name.ns.size()>0); if(!name.ns[0].hasEmptyName()) return ASObject::setVariableByMultiname(name, o, allowConst); unsigned int index=0; if(!Vector::isValidMultiname(getSystemState(),name,index)) { if (name.name_type == multiname::NAME_INT || (name.name_type == multiname::NAME_NUMBER && Number::isInteger(name.name_d))) throwError<RangeError>(kOutOfRangeError,name.normalizedName(getSystemState()),Integer::toString(vec.size())); if (!ASObject::hasPropertyByMultiname(name,false,true)) throwError<ReferenceError>(kWriteSealedError, name.normalizedName(getSystemState()), this->getClass()->getQualifiedClassName()); return ASObject::setVariableByMultiname(name, o, allowConst); } ASObject* o2 = this->vec_type->coerce(o); if(index < vec.size()) { if (vec[index]) vec[index]->decRef(); vec[index] = o2; } else if(!fixed && index == vec.size()) { vec.push_back( o2 ); } else { /* Spec says: one may not set a value with an index more than * one beyond the current final index. */ throwError<RangeError>(kOutOfRangeError, Integer::toString(index), Integer::toString(vec.size())); } }
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; }
bool Vector::isValidMultiname(const multiname& name, uint32_t& index) { //First of all the multiname has to contain the null namespace //As the namespace vector is sorted, we check only the first one assert_and_throw(name.ns.size()!=0); if(!name.ns[0].hasEmptyName()) return false; bool validIndex=name.toUInt(index, true); // Don't throw for non-numeric NAME_STRING or NAME_OBJECT // because they can still be valid built-in property names. if(!validIndex && (name.name_type==multiname::NAME_INT || name.name_type==multiname::NAME_NUMBER)) throwError<RangeError>(kOutOfRangeError, name.normalizedName(), "?"); return validIndex; }
void variables_map::killObjVar(const multiname& mname) { tiny_string name=mname.normalizedName(); const pair<var_iterator, var_iterator> ret=Variables.equal_range(name); assert_and_throw(ret.first!=ret.second); //Find the namespace var_iterator start=ret.first; for(;start!=ret.second;++start) { if(!is_disjoint(mname.ns,start->second.ns)) { Variables.erase(start); return; } } throw RunTimeException("Variable to kill not found"); }
/* this handles the [] operator, because vec[12] becomes vec.12 in bytecode */ _NR<ASObject> Vector::getVariableByMultiname(const multiname& name, GET_VARIABLE_OPTION opt) { if((opt & SKIP_IMPL)!=0 || !implEnable) return ASObject::getVariableByMultiname(name,opt); assert_and_throw(name.ns.size()>0); if(!name.ns[0].hasEmptyName()) return ASObject::getVariableByMultiname(name,opt); unsigned int index=0; if(!Vector::isValidMultiname(getSystemState(),name,index) || index == UINT32_MAX) { if (name.name_type == multiname::NAME_INT || (name.name_type == multiname::NAME_NUMBER && Number::isInteger(name.name_d))) throwError<RangeError>(kOutOfRangeError,Integer::toString(name.name_i),Integer::toString(vec.size())); _NR<ASObject> ret = ASObject::getVariableByMultiname(name,opt); if (ret.isNull()) throwError<ReferenceError>(kReadSealedError, name.normalizedName(getSystemState()), this->getClass()->getQualifiedClassName()); return ret; } if(index < vec.size()) { if (vec[index]) { vec[index]->incRef(); return _MNR(vec[index]); } else return _MNR(vec_type->coerce( getSystemState()->getNullRef() )); } else { throwError<RangeError>(kOutOfRangeError, Integer::toString(index), Integer::toString(vec.size())); } return NullRef; }
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; }
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); } }
int JSON::parse(const tiny_string &jsonstring, int pos, ASObject** parent , const multiname& key, IFunction *reviver) { while (jsonstring.charAt(pos) == ' ' || jsonstring.charAt(pos) == '\t' || jsonstring.charAt(pos) == '\n' || jsonstring.charAt(pos) == '\r' ) pos++; int len = jsonstring.numBytes(); if (pos < len) { char c = jsonstring.charAt(pos); switch(c) { case '{': pos = parseObject(jsonstring,pos,parent,key, reviver); break; case '[': pos = parseArray(jsonstring,pos,parent,key, reviver); break; case '"': pos = parseString(jsonstring,pos,parent,key); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': pos = parseNumber(jsonstring,pos,parent,key); break; case 't': pos = parseTrue(jsonstring,pos,parent,key); break; case 'f': pos = parseFalse(jsonstring,pos,parent,key); break; case 'n': pos = parseNull(jsonstring,pos,parent,key); break; default: throwError<SyntaxError>(kJSONInvalidParseInput); } } if (reviver) { bool haskey = key.name_type!= multiname::NAME_OBJECT; ASObject* params[2]; if (haskey) { params[0] = Class<ASString>::getInstanceS(key.normalizedName()); if ((*parent)->hasPropertyByMultiname(key,true,false)) { params[1] = (*parent)->getVariableByMultiname(key).getPtr(); params[1]->incRef(); } else params[1] = getSys()->getNullRef(); } else { params[0] = Class<ASString>::getInstanceS(""); params[1] = *parent; params[1]->incRef(); } ASObject *funcret=reviver->call(getSys()->getNullRef(), params, 2); if(funcret) { if (haskey) { if (funcret->is<Undefined>()) { (*parent)->deleteVariableByMultiname(key); funcret->decRef(); } else { (*parent)->setVariableByMultiname(key,funcret,ASObject::CONST_NOT_ALLOWED); } } else *parent= funcret; } } return pos; }
void ASObject::setVariableByMultiname(const multiname& name, ASObject* o, CONST_ALLOWED_FLAG allowConst, 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, &has_getter); if (obj && (obj->kind == CONSTANT_TRAIT && allowConst==CONST_NOT_ALLOWED)) { tiny_string err=tiny_string("Error #1074: Illegal write to read-only property ")+name.normalizedName(); if(classdef) err+=tiny_string(" on type ")+classdef->as<Class_base>()->getQualifiedClassName(); throw Class<ReferenceError>::getInstanceS(err); } 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->findBorrowedSettable(name,&has_getter); if(obj && cls->isFinal && !obj->setter) { tiny_string err=tiny_string("Error #1037: Cannot assign to a method ")+name.normalizedName()+tiny_string(" on ")+cls->getQualifiedClassName(); throw Class<ReferenceError>::getInstanceS(err); } } //Do not lookup in the prototype chain. This is tested behaviour 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); } }