/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "avmplus.h" #ifdef AVMPLUS_VERBOSE #define verbose_only(...) __VA_ARGS__ #else #define verbose_only(...) #endif namespace avmplus { /** * parse a .abc file completely * @param code * @return */ PoolObject* AbcParser::decodeAbc(AvmCore* core, ScriptBuffer code, Toplevel* toplevel, Domain* domain, const NativeInitializer* natives, ApiVersion apiVersion) { int version; int result = canParse(code, &version); #ifdef AVMPLUS_VERBOSE if (core->isVerbose(VB_parse)) core->console << "major=" << (version&0xFFFF) << " minor=" << (version>>16) << "\n"; #endif if (result != 0) { switch (result) { case kInvalidMagicError: toplevel->throwVerifyError(kInvalidMagicError, core->toErrorString(version>>16), core->toErrorString(version&0xFFFF)); case kCorruptABCError: default: toplevel->throwVerifyError(kCorruptABCError); } } #ifdef VMCFG_AOT if(!natives) // no natives -- runtime-loaded script { if(version == 0 && code.getSize() == 24) // only ABC "references" -- these are 0.0 version followed by SHA1 { void *sha1 = (void *)(code.getBuffer() + 4); for(uint32_t i = 0; i < nAOTInfos; i++) { const AOTInfo *aotInfo = &aotInfos[i]; AvmAssert(aotInfo != NULL); MMGC_STATIC_ASSERT(sizeof(aotInfo->origABCSHA1) == 20); if(!memcmp(aotInfo->origABCSHA1, sha1, 20)) { ScriptBuffer code = ScriptBuffer(new (core->GetGC()) ReadOnlyScriptBufferImpl(aotInfo->abcBytes, aotInfo->nABCBytes)); NativeInitializer ninit(core, NULL, aotInfo, 0, 0); PoolObject *pool = decodeAbc(core, code, toplevel, domain, &ninit, apiVersion); AvmAssert(pool != NULL); AvmAssert(!pool->isBuiltin); AvmAssert(pool->aotInfo == aotInfo); #ifdef DEBUGGER // TODO: is there a better pace to do this for both builtins and user scripts? addAOTDebugInfo(pool); #endif return pool; } } } toplevel->throwVerifyError( kCorruptABCError ); // not a reference or not found return NULL; } #endif AbcParser parser(core, code, toplevel, domain, natives); PoolObject *pObject = parser.parse(apiVersion); if ( !pObject ) { toplevel->throwVerifyError( kCorruptABCError ); /*NOTREACHED*/ return NULL; } // register this pool giving it a unique id based on MethodInfo count core->assignPoolId(pObject); return pObject; } #if defined(VMCFG_AOT) && defined(DEBUGGER) void AbcParser::addAOTDebugInfo(PoolObject *pool) { AvmCore *core = pool->core; const AOTInfo *aotInfo = pool->aotInfo; Debugger *debugger = core->debugger(); if(debugger) { const MethodDebugInfo* methodDebugInfos = aotInfo->methodDebugInfos; if(methodDebugInfos != NULL) { // create the structure representing the ABC AbcFile* abc = AbcFile::create(core->GetGC(), core, 0); for(int n = 0; n < (int)aotInfo->nABCMethods; n++) { MethodInfo *f = pool->getMethodInfo(n); // create DMIs const MethodDebugInfo &info = methodDebugInfos[n]; DebuggerMethodInfo* dmi = DebuggerMethodInfo::create(core, info.local_count, -1, info.scope_count); pool->_method_dmi.set(n, dmi); // set the file in the MethodInfo f->setFile(abc); // get a SourceFile SourceFile *src = NULL; if(info.file_name) { Stringp fileName = pool->getString(info.file_name); src = abc->sourceNamed(fileName); if(!src) { src = SourceFile::create(core->GetGC(), fileName); abc->sourceAdd(src); } } // set register names for(int i = 0; i < info.local_count; i++) { int name = info.local_names[i]; if(name) f->setRegName(i, pool->getString(name)); } // add line info if(src) for(int i = 0; i < info.line_count; i++) src->addLine(info.lines[i], f, 0); } // hook the ABC in debugger->pool2abcIndex.add(pool, (const void *)debugger->abcList.length()); debugger->abcList.add(abc); } } } #endif int AbcParser::canParse(ScriptBuffer code, int* version) { if (version != NULL) *version = 0; if (code.getSize() < 4) return kCorruptABCError; int v = AvmCore::readU16(&code[0]) | AvmCore::readU16(&code[2])<<16; if (version != NULL) *version = v; switch (v) { #ifdef VMCFG_AOT case 0: // just a "reference" to an AOT-ed ABC! #endif case (46<<16|16): // Original ABC version number #ifdef VMCFG_SWF12 case (47<<16|12): // Flash player TBD return 0; #endif #ifdef VMCFG_SWF13 case (47<<16|13): // Flash player TBD return 0; #endif #ifdef VMCFG_SWF14 case (47<<16|14): // Flash player Anza return 0; #endif #ifdef VMCFG_SWF15 case (47<<16|15): // Flash player Brannan return 0; #endif #ifdef VMCFG_SWF16 case (47<<16|16): // Flash player Cyril return 0; #endif #ifdef VMCFG_SWF17 case (47<<16|17): // Flash player Dolores return 0; #endif #ifdef VMCFG_SWF18 case (47<<16|18): // Flash player TBD return 0; #endif #ifdef VMCFG_SWF19 case (47<<16|19): // Flash player TBD return 0; #endif #ifdef VMCFG_SWF20 case (47<<16|20): // Flash player TBD return 0; #endif default: return kInvalidMagicError; } } AbcParser::AbcParser(AvmCore* core, ScriptBuffer code, Toplevel* toplevel, Domain* domain, const NativeInitializer* natives) : instances(core->GetGC(), 0), toplevel(toplevel), domain(domain), metaNames(core->GetGC(), 0), cachedOsrThresholdString(NULL), cachedOsrThresholdValue(0) { this->core = core; this->code = code; this->pool = NULL; this->version = AvmCore::readU16(&code[0]) | AvmCore::readU16(&code[2])<<16; this->pos = &code[4]; this->natives = natives; abcStart = &code[0]; abcEnd = &code[(int)code.getSize()]; classCount = 0; // Flag overrides based on version go here if (this->version >= (47<<16|12)) { // Flash Player Wasabi } if (this->version >= (47<<16|13)) { // Flash Player Serrano } if (this->version >= (47<<16|14)) { // Flash Player Anza } if (this->version >= (47<<16|15)) { // Flash Player Brannan } if (this->version >= (47<<16|16)) { // Flash Player Cyril } if (this->version >= (47<<16|17)) { // Flash Player Dolores } if (this->version >= (47<<16|18)) { // Flash Player Ellis } if (this->version >= (47<<16|19)) { // Flash Player TBD } if (this->version >= (47<<16|20)) { // Flash Player TBD } } Namespacep AbcParser::parseNsRef(const uint8_t* &pc) const { uint32_t index = readU30(pc); if (index == 0) { return NULL; // AnyNamespace } if (index >= pool->constantNsCount) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantNsCount)); return pool->cpool_ns[index]; } #if defined(VMCFG_AOT) || defined(AVMPLUS_VERBOSE) void AbcParser::parseTypeName(const uint8_t* &pc, Multiname& m) const { // only save the type name for now. verifier will resolve to traits uint32_t index = readU30(pc); if (index == 0) { // type is * m.setNamespace(core->getPublicNamespace(pool)); m.setName(core->kAsterisk); return; } if (index >= pool->cpool_mn_offsets.length()) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->cpool_mn_offsets.length())); pool->parseMultiname(m, index); } #endif uint32_t AbcParser::resolveBindingName(const uint8_t* &p, Multiname &m) const { uint32_t index = readU30(p); if (index == 0 || index >= pool->cpool_mn_offsets.length()) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->cpool_mn_offsets.length())); pool->resolveBindingNameNoCheck(index, m, toplevel); // Only builtin traits names can have multiple namespaces, which are used as an implementation // mechanism of the versioning API, and express multiple introducing versions. if (m.isBinding() && (m.isQName() || pool->isBuiltin)) return index; else { toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(index)); return 0; // not reached } } MethodInfo* AbcParser::resolveMethodInfo(uint32_t index) const { const uint32_t c = pool->methodCount(); if (index >= c) toplevel->throwVerifyError(kMethodInfoExceedsCountError, core->toErrorString(index), core->toErrorString(c)); MethodInfo* f = pool->getMethodInfo(index); if (!f) toplevel->throwVerifyError(kMethodInfoOrderError, core->toErrorString(index)); return f; } Stringp AbcParser::resolveUtf8(uint32_t index) const { if (index > 0 && index < pool->constantStringCount) { return pool->getString(index); } toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantStringCount)); return NULL; } Stringp AbcParser::parseName(const uint8_t* &pc) const { uint32_t index = readU30(pc); if (index == 0) return NULL; return resolveUtf8(index); } PoolObject* AbcParser::parse(ApiVersion apiVersion) { // Loading a new ABC file always invalidates the traits cache(s) core->tbCache()->flush(); core->tmCache()->flush(); // constant pool parseCpool(apiVersion); // parse all methodInfos in one pass. Nested functions must come before outer functions parseMethodInfos(); // parse all metadataInfos - AVM+ doesn't care about this, so we are really just skipping them parseMetadataInfos(); // parse classes. base classes must come first. if (!parseInstanceInfos()) return NULL; bool first = false; if (CLASS_TYPE == NULL) { // if we haven't got types from the builtin file yet, do it now. first = true; core->traits.initInstanceTypes(pool); // register "void" addNamedTraits(core->getPublicNamespace(kApiVersion_VM_ALLVERSIONS), VOID_TYPE->name(), VOID_TYPE); } // type information about class objects parseClassInfos(); if (first) { core->traits.initClassTypes(pool); } // scripts if( !parseScriptInfos() ) return NULL; // method bodies: code, exception info, and activation traits parseMethodBodies(); return pool; } // check to see if we are trying to read past the file end or the beginning. #define CHECK_POS(pos) do { if ((pos) < abcStart || (pos) >= abcEnd ) toplevel->throwVerifyError(kCorruptABCError); } while (0) void AbcParser::parseExecPolicyAttributes(const uint8_t* metadata, MethodInfo* m) { // The metadata has already been scanned by parseMetadataInfo, thus // there is no need to check for the end of the ABC block or to validate // the range of the indices. We ignore unrecognized keys, but validate // arguments on keys that we handle. const uint8_t* p = metadata; readU30(p); // Skip index for "ExecPolicy" string. int values_count = readU30(p); for (int q = 0; q < values_count; ++q) { Stringp key = pool->getString(readU30(p)); Stringp val = pool->getString(readU30(p)); if (key == core->kOSR) { uint32_t threshold; if (val == cachedOsrThresholdString) { threshold = cachedOsrThresholdValue; } else { // For convenience and consistency, we require the value // to have the form of an index property, though any string // recognizable as an unsigned integer could be permitted. // TODO: Provide a more specific error message. if (!val->parseIndex(threshold)) toplevel->throwVerifyError(kCorruptABCError); cachedOsrThresholdString = val; cachedOsrThresholdValue = threshold; } // Unless OSR is globally disabled, set the OSR threshold for this // method as specified in the attribute and enable OSR if nonzero. // Silently ignore attempts to set an OSR threshold for a native method. if (core->config.osr_enabled && !m->isNative()) m->setOSR(threshold); } } } /** * setting up a traits that extends another one. Two parser passes are required, * one for sizing the traits object and the other for allocating it and filling * it in. */ Traits* AbcParser::parseTraits(uint16_t sizeofInstance, uint16_t offsetofSlots, Traits* base, Namespacep ns, Stringp name, MethodInfo* script, TraitsPosPtr traitsPos, TraitsPosType posType, Namespacep protectedNamespace, bool makeFinal) { uint32_t nameCount = readU30(pos); // Very generous check for nameCount being way too large. if (nameCount > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); // Interfaces cant't have base traits if (posType == TRAITSTYPE_INTERFACE && base != NULL) toplevel->throwVerifyError(kCannotExtendError, name, core->toErrorString(base)); #ifdef AVMPLUS_VERBOSE if (pool->isVerbose(VB_parse)) core->console << " trait_count=" << nameCount << "\n"; #endif Traits* traits = Traits::newTraits(pool, base, sizeofInstance,offsetofSlots , traitsPos, posType); traits->set_names(ns, name); traits->protectedNamespace = protectedNamespace; GCList methodsOf(core->GetGC(), kListInitialCapacity); for (uint32_t i=0; i < nameCount; i++) { Multiname mn; const uint32_t qn_index = resolveBindingName(pos, mn); Namespacep ns; const NamespaceSet* nss; if (mn.namespaceCount() > 1) { nss = (NamespaceSet*) mn.getNsset(); ns = nss->nsAt(0); } else { ns = mn.getNamespace(); if (!ns) toplevel->throwVerifyError(kIllegalNamespaceError); nss = NamespaceSet::create(core->GetGC(), ns); } Stringp name = mn.getName(); CHECK_POS(pos); int tag = *pos++; TraitKind kind = (TraitKind) (tag & 0x0f); uint32_t class_index = 0; uint32_t method_index = 0; uint32_t slot_id = 0; uint32_t value_index = 0; uint32_t earlyDispId = 0; bool needsDxns = false; // The ABC format restricts the metadata index to a 30-bit // unsigned quantity, so uint32_t(~0) should be safe as a sentinel. uint32_t execpolicy_index = uint32_t(~0); #ifdef AVMPLUS_VERBOSE Multiname typeName; #endif // Read in the trait entry. switch (kind) { case TRAIT_Slot: case TRAIT_Const: slot_id = readU30(pos); // slot id #ifdef AVMPLUS_VERBOSE parseTypeName(pos, typeName); #else readU30(pos); #endif value_index = readU30(pos); // value index if (value_index) { CHECK_POS(pos); pos += 1; // value_kind } break; case TRAIT_Class: readU30(pos); // slot id class_index = readU30(pos); break; case TRAIT_Getter: case TRAIT_Setter: case TRAIT_Method: earlyDispId = readU30(pos); // disp id method_index = readU30(pos); break; default: // unsupported traits type toplevel->throwVerifyError(kUnsupportedTraitsKindError, core->toErrorString(kind)); } const uint8_t* meta_pos = pos; if (tag & ATTR_metadata) { const uint32_t metadataCount = readU30(pos); for (uint32_t metadata = 0; metadata < metadataCount; ++metadata) { const uint32_t index = readU30(pos); if (index >= pool->metadataCount) toplevel->throwVerifyError(kCorruptABCError); Stringp name = metaNames[index]; if (name == core->kNeedsDxns) needsDxns = true; if (name == core->kExecPolicy) { execpolicy_index = index; } } } switch (kind) { case TRAIT_Slot: case TRAIT_Const: case TRAIT_Class: { if (script) addNamedScript(nss, name, script); if (kind == TRAIT_Class) { // get the class type if (class_index >= classCount) toplevel->throwVerifyError(kClassInfoExceedsCountError, core->toErrorString(class_index), core->toErrorString(classCount)); // class_index could be legal (= pool->classCount()), so handle that case. Traits* ctraits = class_index < pool->classCount() ? pool->getClassTraits(class_index) : NULL; if (!ctraits) toplevel->throwVerifyError(kClassInfoOrderError, core->toErrorString(class_index)); #ifdef AVMPLUS_VERBOSE if (pool->isVerbose(VB_parse)) { core->console << " " << traitNames[kind] << " name=" << Multiname::Format(ns, name) << " slot_id=" << slot_id << " type=" << ctraits << "\n"; } #endif if (script) { // promote instance type to the vm-wide type table. // map type name to traits addNamedTraits(nss, name, ctraits->itraits); if (tag & ATTR_metadata) { ctraits->itraits->setMetadataPos(meta_pos); } } else { if (tag & ATTR_metadata) { ctraits->setMetadataPos(meta_pos); } } } else { #ifdef AVMPLUS_VERBOSE if (pool->isVerbose(VB_parse)) { core->console << " " << traitNames[kind] << " name=" << Multiname::Format(ns, name) << " slot_id=" << slot_id << " value_index=" << value_index << " type=" << typeName << "\n"; } #endif } break; } case TRAIT_Getter: case TRAIT_Setter: case TRAIT_Method: { #ifdef AVMPLUS_VERBOSE if (pool->isVerbose(VB_parse)) { core->console << " " << traitNames[kind] << " name=" << Multiname::Format(ns, name) << " disp_id=" << earlyDispId << " (ignored)" << " method_index=" << method_index << " attr=" << ((tag&ATTR_final)?"final":"virtual"); if (tag&ATTR_override) core->console << "|override"; core->console << "\n"; } #endif // id is unused here MethodInfo* f = resolveMethodInfo(method_index); if (core->config.methodNames) { pool->_method_name_indices.set(method_index, -int32_t(qn_index)); } // Don't call makeMethodOf() yet: if the Traits are fuzzed // (and thus verifyBindings() fails), we don't want to have // the Method associated with these Traits, as trying to parse // the Traits could crash. methodsOf.add(f); f->setKind(kind); // sets the _isGetter/_isSetter flags if ((tag & ATTR_final) || makeFinal) f->setFinal(); if (tag & ATTR_override) f->setOverride(); if (needsDxns) f->setNeedsDxns(); if (execpolicy_index < uint32_t(~0)) parseExecPolicyAttributes(pool->metadata_infos[execpolicy_index], f); // only export one name for an accessor // (note that addNamedScript checks for redundancy internally, // no need to do so here) if (script) addNamedScript(nss, name, script); break; } default: // unsupported traits type toplevel->throwVerifyError(kUnsupportedTraitsKindError, core->toErrorString(kind)); } #ifdef AVMPLUS_VERBOSE if (tag & ATTR_metadata) { uint32_t metadataCount = readU30(meta_pos); for (uint32_t metadata = 0; metadata < metadataCount; ++metadata) { uint32_t index = readU30(meta_pos); if (pool->isVerbose(VB_parse)) core->console << " [" << metaNames[index] << "]\n"; } } #endif } if (makeFinal) traits->final = true; traits->verifyBindings(toplevel); // verifyBindings() hasn't thrown, so it's OK to associate the MethodInfos // with the Traits. for (uint32_t i = 0, n = methodsOf.length(); i < n; ++i) { if (!methodsOf[i]->makeMethodOf(traits)) toplevel->throwVerifyError(kCorruptABCError); } return traits; } void AbcParser::parseMethodInfos() { int methodCount = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "method_count=" << methodCount << "\n"; } #endif int size = methodCount == 0 ? 1 : methodCount; if (size > (abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); MMGC_MEM_TYPE(pool); pool->_methods.ensureCapacity(size); #ifdef DEBUGGER if (core->debugger()) { pool->_method_dmi.ensureCapacity(size); for (int i = 0; i < size; ++i) pool->_method_dmi.set(i, NULL); } #endif if (core->config.methodNames) { pool->_method_name_indices.ensureCapacity(size); } #ifdef AVMPLUS_VERBOSE const uint8_t* startpos = pos; #endif for (int i=0; i < methodCount; i++) { #ifdef AVMPLUS_VERBOSE int offset = (int)(pos-startpos); #endif const uint8_t* info_pos = pos; // MethodInfo const int param_count = readU30(pos); #ifdef VMCFG_AOT const uint8_t* ret_type_pos = pos; #endif // @todo -- we should add an AbcParser equivalent of skipU32; // then the next two clauses would be // skipU32(pos, param_count+1); // in non-verbose builds #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { Multiname returnTypeName; parseTypeName(pos, returnTypeName); core->console << " " << offset << ":method["<isVerbose(VB_parse)) { core->console << " paramType["<isVerbose(VB_parse)) { core->console << " name_index=" << name_index; if (name_index > 0 && name_index < pool->constantStringCount) core->console << " \"" << pool->getString(name_index) << "\""; core->console << "\n flags=" << (uint32_t)abcFlags << "\n"; } #endif const NativeMethodInfo* ni = NULL; if (abcFlags & abcMethod_NATIVE) { ni = natives ? natives->getNativeInfo(i) : NULL; #ifdef VMCFG_VERIFYALL if (core->config.verifyonly) { // if it has native methods then treat it as builtin pool->isBuiltin = true; } else #endif if (!ni) { if (pool->isBuiltin) { // If this assert hits, you're missing a native method. Method "i" // corresponds to the function of the same number in // source\avmglue\avmglue.h if you're running the Flash player. AvmAssertMsg(0, "missing native method decl"); } toplevel->throwVerifyError(kIllegalNativeMethodError); } } #ifdef VMCFG_AOT bool isCompiled = false; AvmThunkNativeHandler handler; // NativeMethodInfo.handler is a union of // pointer to function and pointer to member function. // Zero the structure so that the entire thing is // initialized. // See bugzilla#647660 NativeMethodInfo compiledMethodInfo = {{0}, 0, 0}; { Multiname returnTypeName; parseTypeName(ret_type_pos, returnTypeName); if (!ni && natives->getCompiledInfo(&compiledMethodInfo, &handler, returnTypeName, i)) { ni = &compiledMethodInfo; isCompiled = true; } } #endif const int optional_count = (abcFlags & abcMethod_HAS_OPTIONAL) ? readU30(pos) : 0; MethodInfo* info = MethodInfo::create(core->GetGC(), i, pool, info_pos, abcFlags, ni); #ifdef VMCFG_AOT if (isCompiled) info->setAotCompiled(handler); #endif if (core->config.methodNames) { pool->_method_name_indices.set(i, int32_t(name_index)); } if (abcFlags & abcMethod_HAS_OPTIONAL) { for( int j = 0; j < optional_count; ++j) { readU30(pos); ++pos; // Kind bytes for each default value } if (!optional_count || (optional_count > param_count)) { // cannot have more optional params than total params toplevel->throwVerifyError(kCorruptABCError); } } if (abcFlags & abcMethod_HAS_PARAM_NAMES) { // AVMPlus doesn't care about the param names, just skip past them for( int j = 0; j < param_count; ++j ) { readU30(pos); } } // save method info pointer. we will verify code later. pool->_methods.set(i, info); #ifdef DEBUGGER // don't create the corresponding DebuggerMethodInfo yet, we'll do that in parseMethodBodies #endif } } void AbcParser::parseMetadataInfos() { uint32_t metadataCount = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "metadata_count=" << metadataCount << "\n"; } #endif if (metadataCount > uint32_t(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); pool->metadata_infos.ensureCapacity(metadataCount); pool->metadataCount = metadataCount; if (metadataCount > 0) { metaNames.ensureCapacity(metadataCount-1); // Pre-size the list for (uint32_t i=0; i < metadataCount; i++) { pool->metadata_infos.set(i, pos); // MetadataInfo uint32_t index = readU30(pos); Stringp name = resolveUtf8(index); metaNames.set(i, name); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << name; } #endif int values_count = readU30(pos); if (values_count > 0) { #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "("; } #endif for(int q = 0; q < values_count; ++q) { uint32_t a = readU30(pos); uint32_t b = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << a << "," << b; if (q+1 < values_count) core->console << " "; } #else (void)a; (void)b; #endif } #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << ")"; } #endif } #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "\n"; } #endif } } } void AbcParser::parseMethodBodies() { int bodyCount = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "bodies_count=" << bodyCount << "\n"; } #endif #ifdef AVMPLUS_VERBOSE const uint8_t* startpos = pos; #endif for (int i=0; i < bodyCount; i++) { #ifdef AVMPLUS_VERBOSE int offset = (int)(pos-startpos); #endif uint32_t method_index = readU30(pos); MethodInfo* info = resolveMethodInfo(method_index); const uint8_t *body_pos = pos; int max_stack = readU30(pos); (void)max_stack; int local_count = readU30(pos); (void)local_count; // can't do this check here because param_count isn't available // until the method is resolved. (we could do some tricks to extract // it, but we just defer this check to Verifier::verify instead.) //if (local_count < info->param_count()+1) //{ // // must have enough locals to hold all parameters including this // toplevel->throwVerifyError(kCorruptABCError); //} // TODO change file format, just want local max_scope int init_scope_depth = readU30(pos); (void)init_scope_depth; int max_scope_depth = readU30(pos); (void)max_scope_depth; int code_length = readU30(pos); if (code_length <= 0 #ifdef VMCFG_AOT && !info->needActivation() // AOT allows a dummy body so that it can represent activation traits #endif ) { toplevel->throwVerifyError(kInvalidCodeLengthError, core->toErrorString(code_length)); } // check to see if we are trying to jump past the file end or the beginning. if ( pos < abcStart || pos+code_length >= abcEnd ) toplevel->throwVerifyError(kCorruptABCError); pos += code_length; int exception_count = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":method["<setHasExceptions(); for (int i=0; i= pool->cpool_mn_offsets.length()) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(name_index), core->toErrorString(pool->constantCount)); pool->parseMultiname(qn, name_index); } if (pool->isVerbose(VB_parse)) { core->console << " exception["<console << "\n"; } #else readU30(pos); // from readU30(pos); // to readU30(pos); // target readU30(pos); // type name if (version != (46<<16|15)) { const uint32_t name_index = readU30(pos); // variable name if (name_index >= pool->cpool_mn_offsets.length()) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(name_index), core->toErrorString(pool->constantCount)); } #endif } } if (info->hasMethodBody()) { // Interface methods should not have bodies Traits* declaringTraits = info->declaringTraits(); if (declaringTraits && declaringTraits->isInterface()) { toplevel->throwVerifyError(kIllegalInterfaceMethodBodyError, core->toErrorString(info)); } #ifdef DEBUGGER if (core->debugger()) { DebuggerMethodInfo* dmi = DebuggerMethodInfo::create(core, local_count, code_length, max_scope_depth - init_scope_depth); pool->_method_dmi.set(method_index, dmi); } #endif // if non-zero, we have a duplicate method body - throw a verify error if (info->abc_body_pos()) { toplevel->throwVerifyError(kDuplicateMethodBodyError, core->toErrorString(info)); } AvmAssert(!info->isResolved()); info->set_abc_body_pos(body_pos); // there will be a traits_count here, even if abcMethod_NEED_ACTIVATION is not // set. So we parse the same way all the time. We could reduce file size and // memory by omitting the count + traits completely. const uint8_t* traits_pos = pos; int nameCount = readU30(pos); if (info->needActivation() || nameCount > 0) { pos = traits_pos; Namespacep ns = NULL; Stringp name = NULL; #ifdef AVMPLUS_VERBOSE if (core->config.methodNames) { ns = core->getPublicNamespace(pool); name = core->internString(info->getMethodNameWithTraits(declaringTraits)); } #endif // activation traits are raw types, not subclasses of object. this is // okay because they aren't accessable to the programming model. Traits* act = parseTraits(sizeof(ScriptObject), sizeof(ScriptObject), NULL, ns, name, NULL, traits_pos, TRAITSTYPE_ACTIVATION, NULL); info->init_activationTraits(act); } else { // TODO remove this assert once abc format is adjusted AvmAssert(nameCount == 0); } } else { #ifdef VMCFG_AOT if (info->needActivation()) { Namespacep ns = NULL; Stringp name = NULL; #ifdef AVMPLUS_VERBOSE if (core->config.methodNames) { ns = core->getPublicNamespace(pool); name = core->internString(info->getMethodNameWithTraits(info->declaringTraits())); } #endif // activation traits are raw types, not subclasses of object. this is // okay because they aren't accessable to the programming model. Traits* act = parseTraits(sizeof(ScriptObject), sizeof(ScriptObject), NULL, ns, name, NULL, pos, TRAITSTYPE_ACTIVATION, NULL); info->init_activationTraits(act); pool->aotInfo->activationTraits[method_index] = act; } else #endif { // native methods should not have bodies! toplevel->throwVerifyError(kIllegalNativeMethodBodyError, core->toErrorString(info)); } } } } void AbcParser::parseCpool(ApiVersion apiVersion) { pool = PoolObject::create(core, code, pos, apiVersion); pool->domain = domain; #ifdef VMCFG_AOT AvmAssert(natives != 0); pool->isBuiltin = natives->hasBuiltins(); pool->aotInfo = natives->get_aotInfo(); #else pool->isBuiltin = (natives != NULL); #endif verbose_only(pool->verbose_vb = core->config.verbose_vb); // pool picks up global settings verbose_only(const uint8_t* startpos); uint32_t int_count = readU30(pos); // sanity check to prevent huge allocations if (int_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); DataList& cpool_int = pool->cpool_int; cpool_int.ensureCapacity(int_count); pool->constantIntCount = int_count; verbose_only(startpos = pos); for(uint32_t i = 1; i < int_count; ++i) { verbose_only(int offset = (int)(pos-startpos)); // S32 value cpool_int.set(i, readS32(pos)); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_int["<<(uint32_t)i<<"]=" <console << cpool_int[i] << "\n"; } #endif } uint32_t uint_count = readU30(pos); if (uint_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); DataList& cpool_uint = pool->cpool_uint; cpool_uint.ensureCapacity(uint_count); pool->constantUIntCount = uint_count; verbose_only(startpos = pos); for(uint32_t i = 1; i < uint_count; ++i) { verbose_only(int offset = (int)(pos-startpos)); // U32 value cpool_uint.set(i, (unsigned)readS32(pos)); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_uint["<console << (double)cpool_uint[i]; core->console << "\n"; } #endif } uint32_t double_count = readU30(pos); if (double_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); GCList& cpool_double = pool->cpool_double; cpool_double.ensureCapacity(double_count); pool->constantDoubleCount = double_count; verbose_only(startpos = pos); for(uint32_t i = 1; i < double_count; ++i) { verbose_only(int offset = (int)(pos-startpos)); double value = readDouble(pos); cpool_double.set(i, (GCDouble*)(core->allocDouble(value)&~7)); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_double["<console << value; core->console << "\n"; } #endif } #ifdef VMCFG_FLOAT if(pool->hasFloatSupport()) { // read float constant pool uint32_t float_count = readU30(pos); if (float_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); GCList& cpool_float = pool->cpool_float; cpool_float.ensureCapacity(float_count); pool->constantFloatCount = float_count; verbose_only(startpos = pos); for(uint32_t i = 1; i < float_count; ++i) { verbose_only(int offset = (int)(pos-startpos)); float value = readFloat(pos); cpool_float.set(i, (GCFloat*)(core->allocFloat(value)&~7)); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_float["<console << value; core->console << "\n"; } #endif } // read float4 constant pool uint32_t float4_count = readU30(pos); if (float4_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); GCList& cpool_float4 = pool->cpool_float4; cpool_float4.ensureCapacity(float4_count); pool->constantFloat4Count = float4_count; verbose_only(startpos = pos); for(uint32_t i = 1; i < float4_count; ++i) { verbose_only(int offset = (int)(pos-startpos)); float4_t value = readFloat4(pos); cpool_float4.set(i, (GCFloat4*)(core->allocFloat4(value)&~7)); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { const float* fp = reinterpret_cast(&value); core->console << " " << offset << ":" << "cpool_float4["<console << fp[0] << "," << fp[1] << "," << fp[2] << "," << fp[3]; core->console << "\n"; } #endif } } else { pool->constantFloatCount = 0; pool->constantFloat4Count = 0; } #endif // VMCFG_FLOAT // In principle, if the numeric pools are sized to make element zero available then initialize that element. // // Note that for int, uint, double, float, and float4 we still throw VerifyError in the Verifier if element // zero is referenced from an instruction, but that really should change, the documentation has traditionally // said that they're available. // // In practice, the Debugger may actually access these values if they are referenced even in content that // does not verify, so make them available always. Bugzilla 520888. pool->cpool_int.set(0, 0); pool->cpool_uint.set(0, 0); pool->cpool_double.set(0, (GCDouble*) atomPtr(core->kNaN)); #ifdef VMCFG_FLOAT pool->cpool_float.set(0, (GCFloat*) atomPtr(core->kFltNaN)); pool->cpool_float4.set(0, (GCFloat4*) atomPtr(core->kFlt4NaN)); #endif uint32_t string_count = readU30(pos); if (string_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); MMGC_MEM_TYPE(pool); pool->setupConstantStrings(string_count); #ifdef AVMPLUS_VERBOSE startpos = pos; #endif pool->_abcStringStart = pos; ConstantStringData* dataP = pool->_abcStrings->data; AvmAssert(core->kEmptyString != NULL); dataP->str = core->kEmptyString; for(uint32_t i = 1; i < string_count; ++i) { #ifdef AVMPLUS_VERBOSE int offset = (int)(pos-startpos); #endif // save the uint8_t position into the cpool array (++dataP)->abcPtr = pos; // number of characters // todo - is compiler emitting no. of chars or no. of bytes? // don't call readU30(), as it can throw. call readU32 and sanity-check ourself here. uint32_t len = AvmCore::readU32(pos); // check to see if we are trying to read past the file end or the beginning. // FIXME: bug #545652, should check if UTF8 is valid, skipping for compatibility. if ((len & 0xc0000000) || pos < abcStart || pos+len >= abcEnd) { // if we throw a verify error here, _abcStringEnd will never be set, and _abcStrings // will be left in an inconsistent state. having _abcStringStart set but not _abcStringEnd // can cause dynamicizeStrings to make poor decisions. So clean up before throwing. pool->_abcStringStart = NULL; pool->_abcStringEnd = NULL; VMPI_memset(pool->_abcStrings->data, 0, string_count*sizeof(ConstantStringData*)); toplevel->throwVerifyError(kCorruptABCError); } #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_string["<console.writeN( (const char*)pos, len); core->console << "\n"; } #endif // skip the UTF-8 string pos += len; } pool->_abcStringEnd = pos; uint32_t ns_count = readU30(pos); if (ns_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); RCList &cpool_ns = pool->cpool_ns; MMGC_MEM_TYPE(pool); cpool_ns.ensureCapacity(ns_count); pool->constantNsCount = ns_count; #ifdef AVMPLUS_VERBOSE startpos = pos; #endif for( uint32_t i = 1; i < ns_count; ++i ) { #ifdef AVMPLUS_VERBOSE int offset = (int)(pos-startpos); #endif CHECK_POS(pos); CPoolKind kind = (CPoolKind) *(pos++); switch(kind) { case CONSTANT_Namespace: case CONSTANT_PackageNamespace: case CONSTANT_PackageInternalNs: case CONSTANT_ProtectedNamespace: case CONSTANT_ExplicitNamespace: case CONSTANT_StaticProtectedNs: { uint32_t index = readU30(pos); Namespace::NamespaceType type = Namespace::NS_Public; switch(kind) { case CONSTANT_PackageInternalNs: type = Namespace::NS_PackageInternal; break; case CONSTANT_ProtectedNamespace: type = Namespace::NS_Protected; break; case CONSTANT_ExplicitNamespace: type = Namespace::NS_Explicit; break; case CONSTANT_StaticProtectedNs: type = Namespace::NS_StaticProtected; break; default: break; } if (index) { Stringp uri = resolveUtf8(index); // Default everything to kApiVersion_VM_ALLVERSIONS for now; // getBaseURIAndApiVersion() will overwrite apiVersion // if the uri has a version mark. (Note that non-builtin // ABC should never have version marks, but we call this anyway // to strip any possible mark; this is of dubious value but it's // what we've done in the past, so we'll continue to do so.) ApiVersion apiVersion = kApiVersion_VM_ALLVERSIONS; bool const hasVersionMark = core->getBaseURIAndApiVersion(uri, apiVersion); if (pool->isBuiltin) { // Finally, builtin public namespaces that are in the versioned-uri list, // but don't have an explicit version mark, get whisked off into the VM_INTERNAL // hidey hole. if (!hasVersionMark && type == Namespace::NS_Public && core->isVersionedURI(uri)) apiVersion = kApiVersion_VM_INTERNAL; } else { // If it's not builtin, it always gets the pool's apiVersion, // even if the uri has a version mark (which shouldn't happen // with standard tools, but a deliberately constructed ABC // chunk could attempt to do so in order to access otherwise // unavailable API). (But only for if (type == Namespace::NS_Public) apiVersion = pool->getApiVersion(); } Namespacep ns = core->internNamespace(core->newNamespace(uri, type, apiVersion)); cpool_ns.set(i, ns); } else { // issue this looks wrong. should uri be ""? Atom uri = undefinedAtom; cpool_ns.set(i, core->internNamespace(core->newNamespace(uri, type))); } break; } case CONSTANT_PrivateNs: { uint32_t index = readU30(pos); Stringp uri = index ? resolveUtf8(index) : (Stringp)core->kEmptyString; Namespacep ns = Namespace::create(core->GetGC(), nullStringAtom, uri, Namespace::NS_Private, kApiVersion_VM_ALLVERSIONS); cpool_ns.set(i, ns); break; } default: { toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(i)); } } #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_ns["< (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); GCList& cpool_ns_set = pool->cpool_ns_set; cpool_ns_set.ensureCapacity(ns_set_count); pool->constantNsSetCount = ns_set_count; #ifdef AVMPLUS_VERBOSE startpos = pos; #endif for( uint32_t i = 1; i < ns_set_count; ++i) { #ifdef AVMPLUS_VERBOSE int offset = (int)(pos-startpos); #endif uint32_t ns_count = readU30(pos); if (ns_count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); NamespaceSet* namespace_set = NamespaceSet::_create(core->GetGC(), ns_count); for(uint32_t j=0; j < ns_count; ++j) { Namespacep ns = parseNsRef(pos); if (!ns) toplevel->throwVerifyError(kIllegalNamespaceError); namespace_set->_initNsAt(j, ns); } cpool_ns_set.set(i, namespace_set); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_ns_set["< (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); DataList& cpool_mn_offsets = pool->cpool_mn_offsets; MMGC_MEM_TYPE(pool); cpool_mn_offsets.ensureCapacity(mn_count); for(uint32_t i = 1; i < mn_count; ++i ) { CHECK_POS(pos); uint32_t const offset = uint32_t(pos - pool->_abcStart); cpool_mn_offsets.set(i, offset); CPoolKind kind = (CPoolKind)*pos++; switch(kind) { case CONSTANT_Qname: case CONSTANT_QnameA: { // U16 namespace_index // U16 name_index // parse a multiname with one namespace (aka qname) parseNsRef(pos); parseName(pos); break; } case CONSTANT_RTQname: case CONSTANT_RTQnameA: { // U16 name_index // parse a multiname with just a name; ns fetched at runtime parseName(pos); break; } case CONSTANT_RTQnameL: case CONSTANT_RTQnameLA: { break; } case CONSTANT_Multiname: case CONSTANT_MultinameA: { parseName(pos); uint32_t index = readU30(pos); if (!index || index >= pool->constantNsSetCount) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantNsSetCount)); // If it is in the range of Namespace Sets then it must be a namespace set/ break; } case CONSTANT_MultinameL: case CONSTANT_MultinameLA: { uint32_t index = readU30(pos); if (!index || index >= pool->constantNsSetCount) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantNsSetCount)); // If it is in the range of Namespace Sets then it must be a namespace set. break; } case CONSTANT_TypeName: { uint32_t index = readU30(pos); // compare index against mn_count, *not* cpool_mn_offsets.length(), as the latter is still being built... // it's ok to forward-reference here. if(!index || index >= mn_count) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(mn_count)); index = readU30(pos); if(index != 1) toplevel->throwVerifyError(kCorruptABCError); index = readU30(pos); // validate that parameterized type is valid. (note that index == 0 is // legal here, and is used for Vector<*>) if (index >= mn_count) toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(mn_count)); break; } default: toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(i)); } #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << offset << ":" << "cpool_mn["<parseMultiname(name, i); core->console << name; core->console << "\n"; } #endif } { // Note that BitSet uses mmfx memory, not gc memory, so we must not // throw an exception while it's alive, lest the memory leak. Hence // the gyration with "goto corrupted" below. BitSet seen(mn_count); // verify that parameterized types have no cycles. // we have to do this in a second pass since forward-references are legal for (uint32_t i = 1; i < mn_count; ++i ) { const uint8_t* p = pool->_abcStart + cpool_mn_offsets[i]; if (*p++ != CONSTANT_TypeName) continue; uint32_t basetype_index = readU30(p); // type being parameterized (currently, always Vector) // the type being parameterized can't itself be a parameterized type. // (note that this check also means cycles for this value are impossible // and need not be checked) const uint8_t* p_basetype = pool->_abcStart + cpool_mn_offsets[basetype_index]; if (*p_basetype == CONSTANT_TypeName) goto corrupted; // don't throw: that would leak the BitSet readU30(p); // param count (currently always 1) uint32_t pt_index = readU30(p); // param type(s) (currently exactly one) // now check for cycles. we can nest arbitrarily deep (eg Vector>>) // but we can't have a cycle. seen.reset(); for (;;) { if (pt_index == 0) // Vector<*> is a special case break; seen.set(pt_index); const uint8_t* p_paramtype = pool->_abcStart + cpool_mn_offsets[pt_index]; if (*p_paramtype++ != CONSTANT_TypeName) break; readU30(p_paramtype); // skip type being parameterized (currently, always Vector) readU30(p_paramtype); // skip param count (currently always 1) pt_index = readU30(p_paramtype); // param type(s) (currently exactly one) if (seen.get(pt_index)) goto corrupted; // don't throw: that would leak the BitSet } } } return; corrupted: toplevel->throwVerifyError(kCorruptABCError); } void AbcParser::addNamedTraits(NamespaceSetp nss, Stringp name, Traits* itraits) { // check to see if the base namespace has been added, if so then // all versions have been added Namespace* ns = nss->nsAt(0); if (!ns->isPrivate()) { ApiVersion apiVersion = kApiVersion_VM_INTERNAL; for (NamespaceSetIterator iter(nss); iter.hasNext();) { Namespacep nsi = iter.next(); ApiVersion a = core->getValidApiVersion(nsi->getApiVersion()); if (a < apiVersion) apiVersion = a; } ns = core->getVersionedNamespace(ns, apiVersion); core->domainMgr()->addNamedTraits(pool, name, ns, itraits); } } void AbcParser::addNamedTraits(Namespacep ns, Stringp name, Traits* itraits) { if (!ns->isPrivate()) { core->domainMgr()->addNamedTraits(pool, name, ns, itraits); } } void AbcParser::addNamedScript(NamespaceSetp nss, Stringp name, MethodInfo* script) { Namespacep ns = nss->nsAt(0); // just need one if (!ns->isPrivate()) { ApiVersion apiVersion = kApiVersion_VM_INTERNAL; for (NamespaceSetIterator iter(nss); iter.hasNext();) { Namespacep nsi = iter.next(); ApiVersion a = core->getValidApiVersion(nsi->getApiVersion()); if (a < apiVersion) apiVersion = a; } ns = core->getVersionedNamespace(ns, apiVersion); } core->domainMgr()->addNamedScript(pool, name, ns, script); } bool AbcParser::parseScriptInfos() { /* U16 script_count ScriptInfo[script_count] { U16 init_index // method_info index of init function Traits script_traits // traits for the global object of this package } */ uint32_t count = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "script_count=" << count << "\n"; } #endif #ifdef AVMPLUS_VERBOSE const uint8_t* startpos = pos; #endif if (count == 0) { return true; } if (count > (uint32_t)(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); pool->_scripts.ensureCapacity(count); // make global objects subclasses of Object for (uint32_t i=0; i < count; i++) { const uint8_t* script_pos = pos; int init_index = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << (int)(script_pos-startpos) << ":script[" << i << "]" << " init_index=" << init_index << "\n"; } #endif MethodInfo* script = resolveMethodInfo(init_index); Traits* declaringTraits = script->declaringTraits(); if (declaringTraits != NULL) { // method has already been bound to a different type. Can't bind it twice because // it can only have one environment, for its scope chain and super references. toplevel->throwVerifyError(kAlreadyBoundError, core->toErrorString(script), core->toErrorString(declaringTraits)); } Traits* traits = parseTraits(sizeof(ScriptObject), sizeof(ScriptObject), core->traits.object_itraits, core->getPublicNamespace(pool), core->kglobal, script, script_pos, TRAITSTYPE_SCRIPT, NULL); if( !traits ) return false; // parseTraits failed // global object, make it dynamic traits->set_needsHashtable(true); script->makeMethodOf(traits); traits->init = script; script->setStaticInit(); pool->_scripts.set(i, traits); #ifdef VMCFG_AOT pool->aotInfo->scriptTraits[i] = traits; #endif } return true; } bool AbcParser::parseInstanceInfos() { classCount = readU30(pos); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << "class_count=" << classCount <<"\n"; } #endif #ifdef AVMPLUS_VERBOSE const uint8_t* startpos = pos; #endif if (classCount == 0) { return true; } if (classCount > uintptr_t(abcEnd - pos)) toplevel->throwVerifyError(kCorruptABCError); // allocate room for class infos early, to handle nested classes pool->_classes.ensureCapacity(classCount); instances.ensureCapacity(classCount); for (uint32_t i=0; i < classCount; i++) { const uint8_t* instancepos = pos; // CONSTANT_QName name of this class Multiname mn; resolveBindingName(pos, mn); Namespacep ns; Stringp name; name = mn.getName(); const NamespaceSet* nss; if (mn.namespaceCount() > 1) { nss = mn.getNsset(); ns = nss->nsAt(0); } else { ns = mn.getNamespace(); if (!ns) toplevel->throwVerifyError(kIllegalNamespaceError); nss = NamespaceSet::create(core->GetGC(), ns); } // resolving base class type means class heirarchy must be a Tree Traits* baseTraits = pool->resolveTypeName(pos, toplevel); if ((baseTraits && baseTraits->final) || (CLASS_TYPE != NULL && baseTraits == CLASS_TYPE) || // note, builtins are allowed to override Function (FUNCTION_TYPE != NULL && baseTraits == FUNCTION_TYPE && !pool->isBuiltin)) { // error - attempt to extend final class #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << mn << " can't extend final class " << baseTraits << "\n"; } #endif toplevel->throwVerifyError(kCannotExtendFinalClass, core->toErrorString(&mn)); } if (baseTraits && baseTraits->isInterface()) { // error, can't extend interface toplevel->throwVerifyError(kCannotExtendError, core->toErrorString(&mn), core->toErrorString(baseTraits)); } // read flags: bit 0: sealed // bit 1: final // bit 2: interface // bit 3: protected CHECK_POS(pos); int flags = *pos++; // read protected namespace Namespacep protectedNamespace = NULL; if (flags & 8) { protectedNamespace = parseNsRef(pos); } int interfaceCount = readU30(pos); const uint8_t* interfacePos = pos; if(interfaceCount) { if (interfaceCount >= 0x10000000) toplevel->throwVerifyError(kCorruptABCError); for( int x = 0; x < interfaceCount; ++ x ) { Traits *t = pool->resolveTypeName(pos, toplevel); if (!t || !t->isInterface()) { // error, can't implement non-interface type toplevel->throwVerifyError(kCannotImplementError, core->toErrorString(&mn), core->toErrorString(t)); } } } // TODO make sure the inheritance is legal. // - can't override final members // - overrides agree with base class signature uint32_t iinit_index = readU30(pos); MethodInfo* iinit = resolveMethodInfo(iinit_index); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { // TODO: fixup this math here, since the 2's are all wrong core->console << " " << (int)(instancepos-startpos) << ":instance[" << i << "]" << " " << mn; if (baseTraits) core->console << " extends " << baseTraits; core->console << " interface_count=" << interfaceCount << " iinit_index=" << iinit_index << "\n"; } #endif TraitsPosType postype = (flags & 4) ? TRAITSTYPE_INTERFACE : TRAITSTYPE_INSTANCE; uint16_t sizeofInstance, offsetofSlots; // If this is a native class, use the stated instance size and offset to slot area. const NativeClassInfo* nativeEntry; if (natives && (nativeEntry = natives->get_class(i)) != NULL && nativeEntry->sizeofInstance) { sizeofInstance = nativeEntry->sizeofInstance; offsetofSlots = nativeEntry->offsetofSlotsInstance; // note that 0 means "put the slots at the end of my immediate parent", // but don't assert here: builtin classes without any slots (e.g., Object) // emit a 0 here for simplicity. Since we won't be generating any slot // offsets for the class anyway, it doesn't matter... // AvmAssert(offsetofSlots > 0); } else { // Since nativegen.py emits "synthetic" C++ classes for all builtin classes (except interfaces), // only non-builtin classes should hit this code; since they don't have C++ representations, // "overlap" of fields between parent and child isn't possibly so we can safely declare the // offsetOfSlots to be an the end of the base. #ifdef VMCFG_VERIFYALL // (Don't bother with this assert in verifyonly mode, as avmglue.abc (etc) will appear to be // non-builtin (since we specify them as arguments, triggering false positives) if (!core->config.verifyonly) #endif { AvmAssert(postype == TRAITSTYPE_INTERFACE || !pool->isBuiltin); } // 0 means "put the slots at the end of my immediate parent" offsetofSlots = 0; // Assume ScriptObject for now sizeofInstance = uint16_t(sizeof(ScriptObject)); // Search the inheritance chain for any native classes. for (Traits* b = baseTraits; b != NULL; b = b->base) { if (b->getSizeOfInstance() > sizeof(ScriptObject)) { // non-Object base class uses a subclass of ScriptObject, so use that size. sizeofInstance = b->getSizeOfInstance(); break; } } } Traits* itraits = parseTraits(sizeofInstance, offsetofSlots, baseTraits, ns, name, 0, instancepos, postype, protectedNamespace, (flags&2) != 0); if( !itraits ) return false; if (!baseTraits && core->traits.object_itraits == NULL) { // save object traits core->traits.object_itraits = itraits; itraits->builtinType = BUILTIN_object; } // AS3 language decision: dynamic is not inherited. itraits->set_needsHashtable((flags&1) == 0); if (itraits->isInterface()) { // check for slotCount != 0 now done at resolve time // interface base must be * if (baseTraits) { // error, can't extend this type toplevel->throwVerifyError(kCannotExtendError, core->toErrorString(&mn), core->toErrorString(baseTraits)); } } Traits* declaringTraits = iinit->declaringTraits(); if (declaringTraits != NULL) { // method has already been bound to a different type. Can't bind it twice because // it can only have one environment, for its scope chain and super references. toplevel->throwVerifyError(kAlreadyBoundError, core->toErrorString(iinit), core->toErrorString(declaringTraits)); } iinit->makeMethodOf(itraits); itraits->init = iinit; #ifdef AVMPLUS_VERBOSE // print the interfaces. interfaces have been resolved above so any // errors would have been reported earlier. for (int j=0, n=interfaceCount; j < n; j++) { Traits *interfaceTraits = pool->resolveTypeName(interfacePos, toplevel); if(pool->isVerbose(VB_parse)) { core->console << " interface["<domainMgr()->addNamedInstanceTraits(pool, name, ns, itraits); } return true; } void AbcParser::parseClassInfos() { if (classCount == 0) { return; } #ifdef AVMPLUS_VERBOSE const uint8_t* startpos = pos; #endif for (uint32_t i=0; i < classCount; i++) { // CONSTANT_Multiname name of this class Traits* itraits = instances[i]; Namespacep ns = itraits->ns(); Stringp name = itraits->name(); const uint8_t* class_pos = pos; uint32_t cinit_index = readU30(pos); MethodInfo* cinit = resolveMethodInfo(cinit_index); #ifdef AVMPLUS_VERBOSE if(pool->isVerbose(VB_parse)) { core->console << " " << (int)(class_pos-startpos) << ":class[" << i << "]" << " " << ns << "::" << name; core->console << " cinit_index=" << cinit_index << "\n"; } #endif const NativeClassInfo* nativeEntry = natives ? natives->get_class(i) : NULL; bool haveClassNativeInfo = nativeEntry && nativeEntry->sizeofClass; Traits* ctraits = parseTraits(haveClassNativeInfo ? nativeEntry->sizeofClass : sizeof(ClassClosure), haveClassNativeInfo ? nativeEntry->offsetofSlotsClass : sizeof(ClassClosure), CLASS_TYPE, ns, core->internString(core->concatStrings(name, core->newConstantStringLatin1("$"))), NULL, class_pos, TRAITSTYPE_CLASS, itraits->protectedNamespace); if (haveClassNativeInfo) { ctraits->hasCustomConstruct = nativeEntry->hasCustomConstruct; itraits->isRestrictedInheritance = nativeEntry->isRestrictedInheritance; itraits->isAbstractBase = nativeEntry->isAbstractBase; } ctraits->setCreateClassClosureProc(haveClassNativeInfo ? nativeEntry->createClassClosure : ClassClosure::createClassClosure); Traits* declaringTraits = cinit->declaringTraits(); if (declaringTraits != NULL) { // method has already been bound to a different type. Can't bind it twice because // it can only have one environment, for its scope chain and super references. toplevel->throwVerifyError(kAlreadyBoundError, core->toErrorString(cinit), core->toErrorString(declaringTraits)); } cinit->makeMethodOf(ctraits); ctraits->init = cinit; ctraits->itraits = itraits; ctraits->set_needsHashtable(true); cinit->setStaticInit(); pool->_classes.set(i, ctraits); } } uint32_t AbcParser::readU30(const uint8_t*& p) const { // We have added kBufferPadding bytes to the end of the main swf buffer. // Why? Here we can read from 1 to 5 bytes. If we were to // put the required safety checks at each uint8_t read, we would slow // parsing of the file down. With this buffer, only one check at the // top of this function is necessary. (we will read on into our own memory) CHECK_POS(p); return toplevel->readU30(p); } double AbcParser::readDouble(const uint8_t* &p) const { // check to see if we are trying to read past the file end. if (p < abcStart || p+7 >= abcEnd ) toplevel->throwVerifyError(kCorruptABCError); double_overlay d; d.words.lsw = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24; d.words.msw = p[4] | p[5]<<8 | p[6]<<16 | p[7]<<24; p += 8; return d.value; } #ifdef VMCFG_FLOAT float AbcParser::readFloat(const uint8_t* &p) const { // check to see if we are trying to read past the file end. if (p < abcStart || p+3 >= abcEnd ) toplevel->throwVerifyError(kCorruptABCError); float_overlay flt; flt.word = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24; p+=4; return flt.value; } float4_t AbcParser::readFloat4(const uint8_t* &p) const { // check to see if we are trying to read past the file end. if (p < abcStart || p + 15 >= abcEnd ) toplevel->throwVerifyError(kCorruptABCError); float4_t val; uint32_t* pval = reinterpret_cast(&val); for(int i=0;i<4;i++){ pval[i] = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24; p+=4; } return val; } #endif }