// this = argv[0] (ignored) // arg1 = argv[1] // argN = argv[argc] Atom RegExpClass::construct(int argc, Atom* argv) { AvmCore* core = this->core(); Stringp pattern; Atom patternAtom = (argc>0) ? argv[1] : undefinedAtom; Atom optionsAtom = (argc>1) ? argv[2] : undefinedAtom; if (AvmCore::istype(patternAtom, traits()->itraits)) { // Pattern is a RegExp object if (optionsAtom != undefinedAtom) { // ECMA 15.10.4.1 says to throw an error if flags specified toplevel()->throwTypeError(kRegExpFlagsArgumentError); } // Return a clone of the RegExp object RegExpObject* regExpObject = (RegExpObject*)AvmCore::atomToScriptObject(patternAtom); return (new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(regExpObject))->atom(); } else { if (patternAtom != undefinedAtom) { pattern = core->string(argv[1]); } else { // cn: disable this, breaking ecma3 tests. was: todo look into this. it's what SpiderMonkey does. pattern = core->kEmptyString; //core->newConstantStringLatin1("(?:)"); } } Stringp options = NULL; if (optionsAtom != undefinedAtom) { options = core->string(optionsAtom); } RegExpObject* inst = new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(this, pattern, options); return inst->atom(); }
VTable* VTable::newParameterizedVTable(Traits* param_traits, Stringp fullname) { Toplevel* toplevel = this->toplevel(); AvmCore* core = toplevel->core(); Namespacep traitsNs = this->traits->ns(); GCRef<builtinClassManifest> builtinClasses = toplevel->builtinClasses(); GCRef<ObjectVectorClass> vectorobj_cls = builtinClasses->get_Vector_objectClass(); GCRef<ScopeChain> vectorobj_cscope = vectorobj_cls->vtable->init->scope(); GCRef<ScopeChain> vectorobj_iscope = vectorobj_cls->ivtable()->init->scope(); VTable* objVecVTable = vectorobj_cls->vtable; AbcEnv* objVecAbcEnv = vectorobj_cscope->abcEnv(); Toplevel* objVecToplevel = objVecVTable->toplevel(); VTable* objVecIVTable = objVecVTable->ivtable; // these cases should all be filtered out by the caller; // we only want to handle Vector<SomeObject> here AvmAssert(param_traits != NULL && param_traits != toplevel->intClass()->vtable->traits->itraits && param_traits != toplevel->uintClass()->vtable->traits->itraits && param_traits != toplevel->numberClass()->vtable->traits->itraits); PoolObject* traitsPool = this->traits->pool; Stringp classname = core->internString(fullname->appendLatin1("$")); Traits* ctraits = core->domainMgr()->findTraitsInPoolByNameAndNS(traitsPool, classname, traitsNs); Traits* itraits; if (!ctraits) { // important: base the new ctraits on objVecVTable->traits, *not* this->traits; // we want the result to be based off ObjectVectorClass, not VectorClass // (otherwise sizeofInstance would be too small and we'd be crashy) ctraits = objVecVTable->traits->newParameterizedCTraits(classname, traitsNs); ctraits->verifyBindings(toplevel); itraits = traitsPool->resolveParameterizedType(toplevel, this->ivtable->traits, param_traits); ctraits->itraits = itraits; } else { itraits = ctraits->itraits; } AvmAssert(itraits != NULL); VTable* class_ivtable = builtinClasses->get_ClassClass()->ivtable(); VTable* cvtab = core->newVTable(ctraits, class_ivtable, objVecToplevel); ScopeChain* cvtab_cscope = vectorobj_cscope->cloneWithNewVTable(core->GetGC(), cvtab, objVecAbcEnv); VTable* ivtab = core->newVTable(itraits, objVecIVTable, objVecToplevel); ScopeChain* ivtab_iscope = vectorobj_iscope->cloneWithNewVTable(core->GetGC(), ivtab, objVecAbcEnv); cvtab->ivtable = ivtab; ivtab->init = objVecIVTable->init; cvtab->resolveSignatures(cvtab_cscope); ivtab->resolveSignatures(ivtab_iscope); return cvtab; }
// E4X 13.3.2, page 67 Atom QNameClass::construct(int argc, Atom* argv) { AvmCore* core = this->core(); if (argc == 0) return (new (core->GetGC(), ivtable()->getExtraSize()) QNameObject(this, undefinedAtom))->atom(); if (argc == 1) { if (core->isObject(argv[1]) && AvmCore::istype(argv[1], core->traits.qName_itraits)) return argv[1]; return (new (core->GetGC(), ivtable()->getExtraSize()) QNameObject(this, argv[1]))->atom(); } else { Atom a = argv[1]; if (a == undefinedAtom) { // ns undefined same as unspecified return (new (core->GetGC(), ivtable()->getExtraSize()) QNameObject(this, argv[2]))->atom(); } else { Namespace* ns; if (AvmCore::isNull(a)) ns = NULL; // It's important to preserve the incoming namespace because it's type // may not be public. I.E... // namespace ns; // q = new QName(ns, "name"); // ns is a NS_PackageInternal ns // If we ever use this QName as a multiname, we need to preserve the exact ns else if (core->isNamespace(a)) ns = core->atomToNamespace(a); else ns = core->newNamespace (a); return (new (core->GetGC(), ivtable()->getExtraSize()) QNameObject(this, ns, argv[2]))->atom(); } } }
RegExpClass::RegExpClass(VTable* cvtable) : ClassClosure(cvtable) { AvmAssert(traits()->getSizeOfInstance() == sizeof(RegExpClass)); AvmCore* core = this->core(); ScriptObject* object_prototype = toplevel()->objectClass->prototype; prototype = new (core->GetGC(), ivtable()->getExtraSize()) RegExpObject(this,object_prototype); kindex = core->internConstantStringLatin1("index"); kinput = core->internConstantStringLatin1("input"); }
Stringp FileClass::read(Stringp filename) { Toplevel* toplevel = this->toplevel(); AvmCore* core = this->core(); if (!filename) { toplevel->throwArgumentError(kNullArgumentError, "filename"); } UTF8String* filenameUTF8 = filename->toUTF8String(); FILE *fp = fopen(filenameUTF8->c_str(), "r"); if (fp == NULL) { toplevel->throwError(kFileOpenError, filename); } fseek(fp, 0L, SEEK_END); long len = ftell(fp); #ifdef UNDER_CE fseek (fp, 0L, SEEK_SET); #else rewind(fp); #endif unsigned char *c = new unsigned char[len+1]; len = (long)fread(c, 1, len, fp); c[len] = 0; fclose(fp); if (len >= 3) { // UTF8 BOM if ((c[0] == 0xef) && (c[1] == 0xbb) && (c[2] == 0xbf)) { return core->newString(((char *)c) + 3, len - 3); } else if ((c[0] == 0xfe) && (c[1] == 0xff)) { //UTF-16 big endian c += 2; len = (len - 2) >> 1; Stringp out = new (core->GetGC()) String(len); wchar *buffer = out->lockBuffer(); for (long i = 0; i < len; i++) { buffer[i] = (c[0] << 8) + c[1]; c += 2; } out->unlockBuffer(); return out; } else if ((c[0] == 0xff) && (c[1] == 0xfe))
ScriptObject* TypeDescriber::describeMetadataInfo(PoolObject* pool, uint32_t metadata_index) { AvmCore* core = m_toplevel->core(); const uint8_t* metadata_pos = pool->metadata_infos[metadata_index]; const uint32_t name_index = (metadata_pos) ? AvmCore::readU30(metadata_pos) : 0; // A bit of a hack: if the pool is builtin, always omit metadata chunks with names of "Version" // or "native", since these are used for reserved purposes internally. Stringp name = poolstr(pool, name_index); AvmAssert(name->isInterned() && core->kVersion->isInterned() && str(kstrid_native)->isInterned()); if (pool->isBuiltin && (name == core->kVersion || name == str(kstrid_native))) return NULL; const uint32_t val_count = (metadata_pos) ? AvmCore::readU30(metadata_pos) : 0; ScriptObject* o = new_object(); ArrayObject* a = new_array(); if (val_count > 0) { GC* gc = core->GetGC(); List<uint32_t> key_indexes(gc); List<uint32_t> val_indexes(gc); read_u30_list(key_indexes, val_count, metadata_pos); read_u30_list(val_indexes, val_count, metadata_pos); for (uint32_t i = 0; i < val_count; ++i) { ScriptObject* v = new_object(); const KVPair props[] = { { kstrid_key, strAtom(poolstr(pool, key_indexes.get(i))) }, { kstrid_value, strAtom(poolstr(pool, val_indexes.get(i))) }, }; setpropmulti(v, props, elem_count(props)); pushobj(a, v); } } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_value, objAtom(a) }, }; setpropmulti(o, props, elem_count(props)); return o; }
// E4X 13.5.2, pg 87 // this = argv[0] (ignored) // arg1 = argv[1] // argN = argv[argc] Atom XMLListClass::construct(int argc, Atom* argv) { AvmCore* core = this->core(); if ((!argc) || AvmCore::isNullOrUndefined(argv[1])) { return ToXMLList (core->kEmptyString->atom()); } // if args[0] is xmllist, create new list and call append if (AvmCore::isXMLList(argv[1])) { XMLListObject *l = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass()); l->_append (argv[1]); return l->atom(); } return ToXMLList(argv[1]); }
Atom DomainObject::loadBytes(ByteArrayObject *b) { AvmCore* core = this->core(); if (!b) toplevel()->throwTypeError(kNullArgumentError, core->toErrorString("bytes")); ShellCodeContext* codeContext = new (core->GetGC()) ShellCodeContext(); codeContext->m_domainEnv = domainEnv; // parse new bytecode size_t len = b->get_length(); ScriptBuffer code = core->newScriptBuffer(len); VMPI_memcpy(code.getBuffer(), &b->GetByteArray()[0], len); Toplevel *toplevel = domainToplevel; return core->handleActionBlock(code, 0, domainEnv, toplevel, NULL, codeContext); }
MethodEnv* VTable::makeMethodEnv(MethodInfo* func, ScopeChain* scope) { AvmCore* core = this->core(); AbcEnv* abcEnv = scope->abcEnv(); MethodEnv* methodEnv = MethodEnv::create(core->GetGC(), func, scope); // register this env in the callstatic method table int method_id = func->method_id(); if (method_id != -1) { AvmAssert(abcEnv->pool() == func->pool()); if (abcEnv->getMethod(method_id) == NULL) { abcEnv->setMethod(method_id, methodEnv); } #ifdef AVMPLUS_VERBOSE else if (func->pool()->isVerbose(VB_traits)) { core->console << "WARNING: tried to re-register global MethodEnv for " << func << "\n"; } #endif } return methodEnv; }
Atom SamplerScript::getMemberNames(ScriptObject* self, Atom o, bool instanceNames) { #ifdef DEBUGGER AvmCore* core = self->core(); MMgc::GC* gc = core->GetGC(); Sampler* s = core->get_sampler(); if (!s || !trusted(self)) return undefinedAtom; if (AvmCore::isObject(o)) { Traits *t = AvmCore::atomToScriptObject(o)->traits(); if(AvmCore::istype(o, CLASS_TYPE) && instanceNames && t->itraits) t = t->itraits; if (s->slotIteratorVTable == NULL) s->slotIteratorVTable = _newVT(self->toplevel(), self->traits()->pool, sizeof(SlotIterator)); return (new (gc) SlotIterator(t, s->slotIteratorVTable))->atom(); } #else (void)self; (void)o; (void)instanceNames; #endif return undefinedAtom; }
// E4X 10.4, pg 36 Atom XMLListClass::ToXMLList(Atom arg) { AvmCore* core = this->core(); if (AvmCore::isNullOrUndefined(arg)) { toplevel()->throwTypeError( (arg == undefinedAtom) ? kConvertUndefinedToObjectError : kConvertNullToObjectError); return arg; } if (AvmCore::isXMLList(arg)) { return arg; } else if (AvmCore::isXML(arg)) { XMLObject *x = AvmCore::atomToXMLObject(arg); Multiname m; bool bFound = x->getQName (&m); Atom parent = x->parent(); if (parent == undefinedAtom) parent = nullObjectAtom; XMLListObject *xl = XMLListObject::create(core->GetGC(), toplevel()->xmlListClass(), parent, bFound ? &m : 0); xl->_append (arg); return xl->atom(); } else { Toplevel* toplevel = this->toplevel(); Stringp s = core->string(arg); if (s->matchesLatin1("<>", 2, 0) && s->matchesLatin1("</>", 3, s->length()-3)) s = s->substr(2, s->length() - 5); Namespace *defaultNamespace = toplevel->getDefaultNamespace(); // We handle this step in the XMLObject constructor to avoid concatenation huge strings together // parentString = <parent xnlns=defaultNamespace> s </parent> // 3. Parse parentString as a W3C element information info e // 4. If the parse fails, throw a SyntaxError exception // 5. x = toXML(e); //StringBuffer parentString (core); //parentString << "<parent xmlns=\""; //parentString << defaultNamespace->getURI(); //parentString << "\">"; //parentString << s; //parentString << "</parent>"; XMLObject *x = XMLObject::create(core->GetGC(), toplevel->xmlClass(), s, defaultNamespace); XMLListObject *xl = XMLListObject::create(core->GetGC(), toplevel->xmlListClass()); for (uint32_t i = 0; i < x->getNode()->_length(); i++) { E4XNode *c = x->getNode()->_getAt (i); c->setParent (NULL); // !!@ trying to emulate rhino behavior here // Add the default namespace to our top element. Namespace *ns = toplevel->getDefaultNamespace(); c->_addInScopeNamespace (core, ns, core->findPublicNamespace()); xl->_appendNode (c); } return xl->atom(); } }
// E4X 10.3, page 32 Atom XMLClass::ToXML(Atom arg) { Toplevel* toplevel = this->toplevel(); AvmCore* core = this->core(); if (AvmCore::isNullOrUndefined(arg)) { toplevel->throwTypeError( (arg == undefinedAtom) ? kConvertUndefinedToObjectError : kConvertNullToObjectError); return arg; } else if (AvmCore::isXML(arg)) { return arg; } else if (AvmCore::isXMLList(arg)) { XMLListObject *xl = AvmCore::atomToXMLList(arg); if (xl->_length() == 1) { return xl->_getAt(0)->atom(); } else { toplevel->throwTypeError(kXMLMarkupMustBeWellFormed); return 0;//notreached } } else { Namespace *defaultNamespace = toplevel->getDefaultNamespace(); // 2. Parse parentString as a W3C element information info e // 3. If the parse fails, throw a SyntaxError exception XMLObject *x = new (core->GetGC()) XMLObject(toplevel->xmlClass(), core->string(arg), defaultNamespace); // 4. x = toXML(e); // 5. if x.length == 0 // return new xml object if (!x->getNode()->_length()) { x->setNode( new (core->GetGC()) TextE4XNode(0, core->kEmptyString) ); } // 6. else if x.length == 1 // x[0].parent = null // return x[0] else if (x->getNode()->_length() == 1) { x->setNode( x->getNode()->_getAt (0)); // discard parent node x->getNode()->setParent (0); } // 7. else throw a syntaxError else { // check for multiple nodes where the first n-1 are PI/comment nodes and the // last one is an element node. Just ignore the PI/comment nodes and return // the element node. (bug 148526). // Will also now ignore any text nodes that just contain whitespace (leading // or trailing) the one valid element node. If multiple element nodes are found, // that is a failing case as well. (bug 192355) E4XNode *node = x->getNode(); E4XNode *validNode = NULL; for (uint32 i = 0; i < node->_length(); i++) { E4XNode *n = node->_getAt (i); if (n->getClass() == E4XNode::kElement) { if (validNode != NULL) toplevel->throwTypeError(kXMLMarkupMustBeWellFormed); validNode = n; } else if (n->getClass() == E4XNode::kText) { if (!n->getValue()->isWhitespace()) { toplevel->throwTypeError(kXMLMarkupMustBeWellFormed); } } } // No valid nodes found if (!validNode) toplevel->throwTypeError(kXMLMarkupMustBeWellFormed); x->setNode( validNode ); // discard parent node validNode->setParent (0); } return x->atom(); } }
void VTable::resolveSignatures(ScopeChain* scope) { AvmAssert(scope != NULL); if( this->linked ) return; // don't mark as resolved until the end of the function: // if traits->resolveSignatures() throws, we end up with the VTable as // "resolved" but the Traits not, which makes us crash in unpredictable ways. if (!traits->isResolved()) { traits->resolveSignatures(toplevel()); traits->setDeclaringScopes(scope->scopeTraits()); } #if defined(DEBUG) || defined(_DEBUG) // have to use local variables for CodeWarrior Traits* traitsBase = traits->base; Traits* baseTraits = base ? base->traits : 0; // make sure the traits of the base vtable matches the base traits AvmAssert((base == NULL && traits->base == NULL) || (base != NULL && traitsBase == baseTraits)); #endif // DEBUG AvmCore* core = traits->core; MMgc::GC* gc = core->GetGC(); if (traits->init && !this->init) { this->init = makeMethodEnv(traits->init, scope); } // populate method table const TraitsBindingsp td = traits->getTraitsBindings(); const TraitsBindingsp btd = td->base; for (uint32_t i = 0, n = td->methodCount; i < n; i++) { MethodInfo* method = td->getMethod(i); if (btd && i < btd->methodCount && method == btd->getMethod(i)) { // inherited method // this->methods[i] = base->methods[i]; WB(gc, this, &methods[i], base->methods[i]); continue; } // new definition if (method != NULL) { //this->methods[i] = new (gc) MethodEnv(method, this); WB(gc, this, &methods[i], makeMethodEnv(method, scope)); continue; } #ifdef AVMPLUS_VERBOSE if (traits->pool->isVerbose(VB_traits)) { // why would the compiler assign sparse disp_id's? traits->core->console << "WARNING: empty disp_id " << i << " on " << traits << "\n"; } #endif } // this is done here b/c this property of the traits isn't set until the // Dictionary's ClassClosure is called if (base && base->traits->isDictionary()) traits->set_isDictionary(); traits->core->exec->notifyVTableResolved(this); linked = true; }
ArrayClass::ArrayClass(VTable* cvtable) : ClassClosure(cvtable), kComma(core()->internConstantStringLatin1(",")) { AvmCore* core = this->core(); Toplevel* toplevel = this->toplevel(); toplevel->arrayClass = this; AvmAssert(traits()->getSizeOfInstance() == sizeof(ArrayClass)); VTable* ivtable = this->ivtable(); ScriptObject* objectPrototype = toplevel->objectClass->prototype; prototype = new (core->GetGC(), ivtable->getExtraSize()) ArrayObject(ivtable, objectPrototype, 0); // According to ECMAscript spec (ECMA-262.pdf) // generic support: concat, join, pop, push, reverse, shift, slice, sort, splice, unshift // NOT generic: toString, toLocaleString // unknown: sortOn (our own extension) #if defined(_DEBUG) // AtomArray DRC unit tests, put here b/c this always runs once, has a GC * and // this class has to do with arrays. this is more convienent that trying to test // through actionscript // create an atom Stringp s = core->newConstantStringLatin1("foo"); Atom a = s->atom(); AvmAssert(s->RefCount()==0); AtomArray *ar = new (gc()) AtomArray(); // test push/pop ar->push(a); AvmAssert(s->RefCount()==1); ar->push(a); AvmAssert(s->RefCount()==2); ar->pop(); AvmAssert(s->RefCount()==1); // reverse ar->push(a); AvmAssert(s->RefCount()==2); ar->reverse(); AvmAssert(s->RefCount()==2); // shift ar->shift(); AvmAssert(s->RefCount()==1); // splice AtomArray *ar2 = new (gc()) AtomArray(); ar->push(a); ar2->push(ar); AvmAssert(s->RefCount()==4); ar->splice(1, 2, 1, ar2, 0); // [a,a,a] AvmAssert(s->RefCount()==5); // unshift Atom as[4] = {a,a,a,a}; ar->unshift(as, 4); AvmAssert(s->RefCount()==9); // removeAt ar->removeAt(1); AvmAssert(s->RefCount()==8); // setAt ar->setAt(2, a); AvmAssert(s->RefCount()==8); // insert ar->insert(2, a); AvmAssert(s->RefCount()==9); // clear ar->clear(); AvmAssert(s->RefCount() == 2); ar2->clear(); AvmAssert(s->RefCount() == 0); gc()->Free(ar); gc()->Free(ar2); #endif }
ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags) { if (!(flags & INCLUDE_TRAITS)) return NULL; AvmCore* core = m_toplevel->core(); GC* gc = core->GetGC(); TraitsBindingsp tb = traits->getTraitsBindings(); TraitsMetadatap tm = traits->getTraitsMetadata(); ScriptObject* o = new_object(); ArrayObject* bases = NULL; ArrayObject* metadata = NULL; ArrayObject* interfaces = NULL; ArrayObject* methods = NULL; ArrayObject* accessors = NULL; ArrayObject* variables = NULL; ScriptObject* constructor = NULL; if (flags & INCLUDE_BASES) { metadata = new_array(); PoolObject* class_mdpool; const uint8_t* class_md = tm->getMetadataPos(class_mdpool); if (class_md) addDescribeMetadata(metadata, class_mdpool, class_md); } if (flags & INCLUDE_BASES) { bases = new_array(); for (Traitsp b = traits->base; b; b = b->base) pushstr(bases, describeClassName(b)); } if (flags & INCLUDE_INTERFACES) { interfaces = new_array(); // our TraitsBindings only includes our own interfaces, not any we might have inherited, // so walk the tree. there might be redundant interfaces listed in the inheritance, // so use a list to remove dupes List<Traitsp> unique(gc); for (Traitsp b = traits; b; b = b->base) { TraitsBindingsp tbi = b->getTraitsBindings(); for (uint32_t i = 0; i < tbi->interfaceCapacity; ++i) { Traitsp ti = tbi->getInterface(i); if (ti && ti->isInterface && unique.indexOf(ti) < 0) { unique.add(ti); pushstr(interfaces, describeClassName(ti)); } } } } // constructor if (flags & INCLUDE_CONSTRUCTOR) { AbstractFunction* initMethod = traits->init; if (initMethod && initMethod->param_count) { constructor = describeParams(initMethod); } } if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES)) { // recover slot/method metadata and method-declarer information. // make a flattened set of bindings so we don't have to check for overrides as we go. // This is not terribly efficient, but doesn't need to be. MultinameHashtable* mybind = new (gc) MultinameHashtable(); addBindings(mybind, tb, flags); // Don't want interface methods, so post-process and wipe out any // bindings that were added for (uint32_t i = 0; i < tb->interfaceCapacity; ++i) { Traitsp ti = tb->getInterface(i); if (ti && ti->isInterface) { TraitsBindingsp tbi = ti->getTraitsBindings(); for (int32_t index = 0; (index = tbi->next(index)) != 0; ) { Stringp name = tbi->keyAt(index); Namespacep ns = tbi->nsAt(index); mybind->add(name, ns, BIND_NONE); } } } // yuck, replicate buggy behavior in FP9/10 List<Namespacep> nsremoval(gc); if (flags & HIDE_NSURI_METHODS) { for (uint32_t i = 0; i < tb->interfaceCapacity; ++i) { Traitsp ti = tb->getInterface(i); // already did interfaces, don't need to do them again if (ti && !ti->isInterface) { TraitsBindingsp tbi = ti->getTraitsBindings(); for (int32_t index = 0; (index = tbi->next(index)) != 0; ) { Namespacep ns = tbi->nsAt(index); if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0) nsremoval.add(ns); } } } } for (int32_t index = 0; (index = mybind->next(index)) != 0; ) { Binding binding = mybind->valueAt(index); Stringp name = mybind->keyAt(index); Namespacep ns = mybind->nsAt(index); Stringp nsuri = ns->getURI(); TraitsMetadata::MetadataPtr md1 = NULL; TraitsMetadata::MetadataPtr md2 = NULL; PoolObject* md1pool = NULL; PoolObject* md2pool = NULL; // We only display public members -- exposing private namespaces could compromise security. if (ns->getType() != Namespace::NS_Public) continue; if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0) continue; ScriptObject* v = new_object(); const BindingKind bk = AvmCore::bindingKind(binding); switch (bk) { case BKIND_CONST: case BKIND_VAR: { if (!(flags & INCLUDE_VARIABLES)) continue; const uint32_t slotID = AvmCore::bindingToSlotId(binding); const KVPair props[] = { { kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) }, { kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) }, }; setpropmulti(v, props, elem_count(props)); if (!variables) variables = new_array(); pushobj(variables, v); md1 = tm->getSlotMetadataPos(slotID, md1pool); break; } case BKIND_METHOD: { if (!(flags & INCLUDE_METHODS)) continue; const uint32_t methodID = AvmCore::bindingToMethodId(binding); const AbstractFunction* af = tb->getMethod(methodID); Traitsp declaringTraits = af->declaringTraits; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_returnType, strAtom(describeClassName(af->returnTraits())) }, { kstrid_parameters, objAtom(describeParams(af)) }, }; setpropmulti(v, props, elem_count(props)); if (!methods) methods = new_array(); pushobj(methods, v); md1 = tm->getMethodMetadataPos(methodID, md1pool); break; } case BKIND_GET: case BKIND_SET: case BKIND_GETSET: { if (!(flags & INCLUDE_ACCESSORS)) continue; const uint32_t methodID = AvmCore::hasGetterBinding(binding) ? AvmCore::bindingToGetterId(binding) : AvmCore::bindingToSetterId(binding); const AbstractFunction* af = tb->getMethod(methodID); Traitsp declaringTraits = af->declaringTraits; Traitsp accessorType = AvmCore::hasGetterBinding(binding) ? af->returnTraits() : af->paramTraits(1); static const uint8_t bk2str[8] = { uint8_t(kstrid_emptyString), // BKIND_NONE uint8_t(kstrid_emptyString), // BKIND_METHOD uint8_t(kstrid_emptyString), // BKIND_VAR uint8_t(kstrid_emptyString), // BKIND_CONST uint8_t(kstrid_emptyString), // BKIND_ITRAMP uint8_t(kstrid_readonly), // BKIND_GET uint8_t(kstrid_writeonly), // BKIND_SET uint8_t(kstrid_readwrite) // BKIND_GETSET }; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_access, strAtom(str(StringId(bk2str[bk]))) }, { kstrid_type, strAtom(describeClassName(accessorType)) }, }; setpropmulti(v, props, elem_count(props)); if (AvmCore::hasGetterBinding(binding)) md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool); if (AvmCore::hasSetterBinding(binding)) md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool); if (!accessors) accessors = new_array(); pushobj(accessors, v); break; } case BKIND_NONE: break; } ArrayObject* vm = NULL; if ((flags & INCLUDE_METADATA) && (md1 || md2)) { vm = new_array(); addDescribeMetadata(vm, md1pool, md1); addDescribeMetadata(vm, md2pool, md2); } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) }, { kstrid_metadata, objAtom(vm) }, }; setpropmulti(v, props, elem_count(props)); } } const KVPair props[] = { { kstrid_bases, objAtom(bases) }, { kstrid_interfaces, objAtom(interfaces) }, { kstrid_metadata, objAtom(metadata) }, { kstrid_accessors, objAtom(accessors) }, { kstrid_methods, objAtom(methods) }, { kstrid_variables, objAtom(variables) }, { kstrid_constructor, objAtom(constructor) }, }; setpropmulti(o, props, elem_count(props)); return o; }
void MethodInfo::verify(Toplevel *toplevel, AbcEnv* abc_env) { AvmAssert(declaringTraits()->isResolved()); resolveSignature(toplevel); AvmCore* core = this->pool()->core; if (isNative()) { union { GprMethodProc implGPR; AvmThunkNativeThunker thunker; AvmThunkNativeThunkerN thunkerN; } u; #ifdef DEBUGGER if (core->debugger()) { MethodSignaturep ms = getMethodSignature(); if (ms->returnTraitsBT() == BUILTIN_number) u.thunkerN = MethodInfo::debugEnterExitWrapperN; else u.thunker = MethodInfo::debugEnterExitWrapper32; } else #endif { u.thunker = this->thunker(); } this->setNativeImpl(u.implGPR); } else { #ifdef DEBUGGER // just a fake CallStackNode here, so that if we throw a verify error, // we get a stack trace with the method being verified as its top entry. CallStackNode callStackNode(this); #endif /* DEBUGGER */ PERFM_NTPROF("verify-ticks"); CodeWriter* coder = NULL; Verifier verifier(this, toplevel, abc_env); /* These "buf" declarations are an unfortunate but expedient hack: the existing CodeWriter classes (eg CodegenLIR, etc) have historically always been stack-allocated, thus they have no WB protection on member fields. Recent changes were made to allow for explicit cleanup() of them in the event of exception, but there was a latent bug waiting to happen: the actual automatic var was going out of scope, but still being referenced (via the 'coder' pointer) in the exception handler, but the area being pointed to may or may not still be valid. The ideal fix for this would simply be to heap-allocate the CodeWriters, but that would require going thru the existing code carefully and inserting WB where appropriate. Instead, this "expedient" hack uses placement new to ensure the memory stays valid into the exception handler. Note: the lack of a call to the dtor of the CodeWriter(s) is not an oversight. Calling cleanup() on coder is equivalent to running the dtor for all of the CodeWriters here. Note: allocated using arrays of intptr_t (rather than char) to ensure alignment is acceptable. */ #define MAKE_BUF(name, type) \ intptr_t name[(sizeof(type)+sizeof(intptr_t)-1)/sizeof(intptr_t)] #if defined FEATURE_NANOJIT MAKE_BUF(jit_buf, CodegenLIR); #if defined AVMPLUS_WORD_CODE MAKE_BUF(teeWriter_buf, TeeWriter); #endif #ifdef FEATURE_CFGWRITER MAKE_BUF(cfg_buf, CFGWriter); #endif #endif #if defined AVMPLUS_WORD_CODE MAKE_BUF(translator_buf, WordcodeEmitter); #else MAKE_BUF(stubWriter_buf, CodeWriter); #endif TRY(core, kCatchAction_Rethrow) { #if defined FEATURE_NANOJIT if ((core->IsJITEnabled()) && !suggestInterp()) { PERFM_NTPROF("verify & IR gen"); // note placement-new usage! CodegenLIR* jit = new(jit_buf) CodegenLIR(this); #if defined AVMPLUS_WORD_CODE WordcodeEmitter* translator = new(translator_buf) WordcodeEmitter(this, toplevel); TeeWriter* teeWriter = new(teeWriter_buf) TeeWriter(translator, jit); coder = teeWriter; #else coder = jit; #endif #ifdef FEATURE_CFGWRITER // analyze code and generate LIR CFGWriter* cfg = new(cfg_buf) CFGWriter(this, coder); coder = cfg; #endif verifier.verify(coder); PERFM_TPROF_END(); if (!jit->overflow) { // assembler LIR into native code jit->emitMD(); } // the MD buffer can overflow so we need to re-iterate // over the whole thing, since we aren't yet robust enough // to just rebuild the MD code. // mark it as interpreted and try to limp along if (jit->overflow) { if (core->JITMustSucceed()) { Exception* e = new (core->GetGC()) Exception(core, core->newStringLatin1("JIT failed")->atom()); e->flags |= Exception::EXIT_EXCEPTION; core->throwException(e); } setInterpImpl(); } #ifdef AVMPLUS_WORD_CODE else { if (_abc.word_code.translated_code) { set_word_code(core->GetGC(), NULL); } if (_abc.word_code.exceptions) { _abc.word_code.exceptions = NULL; } } #endif } else { // NOTE copied below #if defined AVMPLUS_WORD_CODE WordcodeEmitter* translator = new(translator_buf) WordcodeEmitter(this, toplevel); coder = translator; #else CodeWriter* stubWriter = new(stubWriter_buf) CodeWriter(); coder = stubWriter; #endif verifier.verify(coder); // pass2 dataflow setInterpImpl(); // NOTE end copy } #else // FEATURE_NANOJIT // NOTE copied from above #if defined AVMPLUS_WORD_CODE WordcodeEmitter* translator = new(translator_buf) WordcodeEmitter(this, toplevel); coder = translator; #else CodeWriter* stubWriter = new(stubWriter_buf) CodeWriter(); coder = stubWriter; #endif verifier.verify(coder); // pass2 dataflow setInterpImpl(); // NOTE end copy #endif // FEATURE_NANOJIT if (coder) { coder->cleanup(); coder = NULL; } } CATCH (Exception *exception) { // clean up verifier verifier.~Verifier(); // call cleanup all the way down the chain // each stage calls cleanup on the next one if (coder) coder->cleanup(); // re-throw exception core->throwException(exception); } END_CATCH END_TRY PERFM_TPROF_END(); }
ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags, Toplevel* toplevel) { if (!(flags & INCLUDE_TRAITS)) return NULL; AvmCore* core = m_toplevel->core(); GC* gc = core->GetGC(); TraitsBindingsp tb = traits->getTraitsBindings(); TraitsMetadatap tm = traits->getTraitsMetadata(); ScriptObject* o = new_object(); ArrayObject* bases = NULL; ArrayObject* metadata = NULL; ArrayObject* interfaces = NULL; ArrayObject* methods = NULL; ArrayObject* accessors = NULL; ArrayObject* variables = NULL; ScriptObject* constructor = NULL; if (flags & INCLUDE_METADATA) { metadata = new_array(); PoolObject* class_mdpool; const uint8_t* class_md = tm->getMetadataPos(class_mdpool); if (class_md) addDescribeMetadata(metadata, class_mdpool, class_md); } if (flags & INCLUDE_BASES) { bases = new_array(); for (Traitsp b = traits->base; b; b = b->base) pushstr(bases, describeClassName(b)); } if (flags & INCLUDE_INTERFACES) { interfaces = new_array(); for (InterfaceIterator iter(traits); iter.hasNext();) { Traits* ti = iter.next(); pushstr(interfaces, describeClassName(ti)); } } // constructor if (flags & INCLUDE_CONSTRUCTOR) { MethodInfo* initMethod = traits->init; if (initMethod) { initMethod->resolveSignature(toplevel); MethodSignaturep ms = initMethod->getMethodSignature(); if (ms->param_count() > 0) { constructor = describeParams(initMethod, ms); } } } if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES)) { // recover slot/method metadata and method-declarer information. // make a flattened set of bindings so we don't have to check for overrides as we go. // This is not terribly efficient, but doesn't need to be. MultinameBindingHashtable* mybind = MultinameBindingHashtable::create(gc); addBindings(m_toplevel->core(), mybind, tb, flags); // Don't want interface methods, so post-process and wipe out any // bindings that were added. for (InterfaceIterator ifc_iter(traits); ifc_iter.hasNext();) { Traitsp ti = ifc_iter.next(); TraitsBindingsp tbi = ti->getTraitsBindings(); StTraitsBindingsIterator iter(tbi); while (iter.next()) { if (!iter.key()) continue; mybind->add(iter.key(), iter.ns(), BIND_NONE); } } // yuck, replicate buggy behavior in FP9/10 RCList<Namespace> nsremoval(gc, kListInitialCapacity); if (flags & HIDE_NSURI_METHODS) { for (TraitsBindingsp tbi = tb->base; tbi; tbi = tbi->base) { StTraitsBindingsIterator iter(tbi); while (iter.next()) { if (!iter.key()) continue; Namespacep ns = iter.ns(); if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0) { nsremoval.add(ns); } } } } StMNHTBindingIterator iter(mybind); while (iter.next()) { if (!iter.key()) continue; Stringp name = iter.key(); Namespacep ns = iter.ns(); Binding binding = iter.value(); Stringp nsuri = ns->getURI(); TraitsMetadata::MetadataPtr md1 = NULL; TraitsMetadata::MetadataPtr md2 = NULL; PoolObject* md1pool = NULL; PoolObject* md2pool = NULL; // We only display public members -- exposing private namespaces could compromise security. if (ns->getType() != Namespace::NS_Public) { continue; } if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0) { continue; } ScriptObject* v = new_object(); const BindingKind bk = AvmCore::bindingKind(binding); switch (bk) { case BKIND_CONST: case BKIND_VAR: { if (!(flags & INCLUDE_VARIABLES)) continue; const uint32_t slotID = AvmCore::bindingToSlotId(binding); const KVPair props[] = { { kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) }, { kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) }, }; setpropmulti(v, props, elem_count(props)); if (!variables) variables = new_array(); pushobj(variables, v); md1 = tm->getSlotMetadataPos(slotID, md1pool); break; } case BKIND_METHOD: { if (!(flags & INCLUDE_METHODS)) continue; const uint32_t methodID = AvmCore::bindingToMethodId(binding); MethodInfo* mi = tb->getMethod(methodID); mi->resolveSignature(toplevel); MethodSignaturep ms = mi->getMethodSignature(); Traitsp declaringTraits = mi->declaringTraits(); const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_returnType, strAtom(describeClassName(ms->returnTraits())) }, { kstrid_parameters, objAtom(describeParams(mi, ms)) }, }; setpropmulti(v, props, elem_count(props)); if (!methods) methods = new_array(); pushobj(methods, v); md1 = tm->getMethodMetadataPos(methodID, md1pool); break; } case BKIND_GET: case BKIND_SET: case BKIND_GETSET: { if (!(flags & INCLUDE_ACCESSORS)) continue; const uint32_t methodID = AvmCore::hasGetterBinding(binding) ? AvmCore::bindingToGetterId(binding) : AvmCore::bindingToSetterId(binding); MethodInfo* mi = tb->getMethod(methodID); mi->resolveSignature(toplevel); MethodSignaturep ms = mi->getMethodSignature(); Traitsp declaringTraits = mi->declaringTraits(); // The verifier does not check the signature of a setter // except when it is invoked. We must be prepared for the // case in which it has no arguments. Traitsp accessorType; if (AvmCore::hasGetterBinding(binding)) { accessorType = ms->returnTraits(); } else { // If setter is malformed, just use '*' as a placeholder. accessorType = (ms->param_count() < 1) ? NULL : ms->paramTraits(1); } static const uint8_t bk2str[8] = { uint8_t(kstrid_emptyString), // BKIND_NONE uint8_t(kstrid_emptyString), // BKIND_METHOD uint8_t(kstrid_emptyString), // BKIND_VAR uint8_t(kstrid_emptyString), // BKIND_CONST uint8_t(kstrid_emptyString), // unused uint8_t(kstrid_readonly), // BKIND_GET uint8_t(kstrid_writeonly), // BKIND_SET uint8_t(kstrid_readwrite) // BKIND_GETSET }; const KVPair props[] = { { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) }, { kstrid_access, strAtom(str(StringId(bk2str[bk]))) }, { kstrid_type, strAtom(describeClassName(accessorType)) }, }; setpropmulti(v, props, elem_count(props)); if (AvmCore::hasGetterBinding(binding)) md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool); if (AvmCore::hasSetterBinding(binding)) md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool); if (!accessors) accessors = new_array(); pushobj(accessors, v); break; } case BKIND_NONE: break; default: break; } ArrayObject* vm = NULL; if ((flags & INCLUDE_METADATA) && (md1 || md2)) { vm = new_array(); addDescribeMetadata(vm, md1pool, md1); addDescribeMetadata(vm, md2pool, md2); } const KVPair props[] = { { kstrid_name, strAtom(name) }, { kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) }, { kstrid_metadata, objAtom(vm) }, }; setpropmulti(v, props, elem_count(props)); } } const KVPair props[] = { { kstrid_bases, objAtom(bases) }, { kstrid_interfaces, objAtom(interfaces) }, { kstrid_metadata, objAtom(metadata) }, { kstrid_accessors, objAtom(accessors) }, { kstrid_methods, objAtom(methods) }, { kstrid_variables, objAtom(variables) }, { kstrid_constructor, objAtom(constructor) }, }; setpropmulti(o, props, elem_count(props)); return o; }