Files
avmplus/core/DomainMgr.cpp
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

554 lines
20 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -*- 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"
namespace avmplus
{
/*
Theory of Operation
Definitions:
------------
parent - reference to parent Domain. Will be null in the "root" (system) domain
loaded - a map from class names to Classdef objects. (Classdef objects represent ready-to-use
class definitions.) This map is populated with classes loaded from SWFs via the load method.
cache - another map from class names to Classdef objects. The cache is populated as classes
are found during lookups via the find method.
There are two general classes of operations:
"add" : these methods are used when we load a chunk of ABC bytecode; they populate the "loaded"
maps, and simply indicate what name is associated with what Traits/Script definition
at a given Domain level.
Note that, as an optimization, these methods check for reachability; attempting to
add a name that is already defined at the current level will simply be ignored. This
includes names that are "shadowed" by a name definition from a parent domain, and is the
source of the "cacheIfFound" flag seen on various "find" methods: when adding new
definitions, we want to examine any already-cached items (to exclude shadowed names),
but we don't want to add to the cache prematurely (we want to defer that until
we know a definition is likely to be used, via the "find" methods).
"find" : look up a Traits/Script definition by name. This is the analog in our scheme of Java’s
ClassLoader.loadClass method, and implements the same lookup algorithm, which is:
- look in our cache for an already-resolved class by this name
- if none is found, immediately call find in the parent domain
- if this doesn’t find a class, look in our currently loaded classes
- if a class has been found, add it to our cache
- return the resolved class, or null.
Important properties of this algorithm:
1. Because of the immediate recursive call to the parent domain, the "topmost" class resident in
memory at the time of the lookup will be found. Here "topmost" means the class closest to the
root domain, along the path from the root to the domain in which find is called. (Note
that our actual implementation is iterative, not recursive.)
2. Because of caching, once a given class has been returned from a find request in a given
domain, that class will always be returned from subsequent calls to find in that domain. (This
guarantees the binding stability property that we want.)
3. When a class is requested from a "descendant" domain, and found in an "ancestor" domain,
the found class will be added to the cache of both the descendent and ancestor domains.
(It is not necessary to add to any Domains that may be in between the descendant and ancestor,
as any lookups to intermediate domains would find the cached value in the ancestor as well,
thus lazily getting the same effect.)
*/
inline bool isValidBinding(MethodInfo* mi)
{
return mi != NULL && mi != (MethodInfo*)BIND_AMBIGUOUS;
}
DomainMgr::DomainMgr(AvmCore* _core) : core(_core)
{
}
void DomainMgr::addNamedTraits(PoolObject* pool, Stringp name, Namespacep ns, Traits* traits)
{
// if we can find an existing trait by this name, the one we're adding
// if unreachable -- don't bother adding it. (Note that this examines
// only the Domain's table, not the Pool's)
// Note: It's important not to cache on find here; see earlier comments for details.
if (findTraitsInDomainByNameAndNSImpl(pool->domain, name, ns, /*cacheIfFound*/false) == NULL)
{
pool->domain->m_loadedTraits->add(name, ns, traits);
}
}
void DomainMgr::addNamedInstanceTraits(PoolObject* pool, Stringp name, Namespacep ns, Traits* itraits)
{
// if we can find an existing trait by this name, the one we're adding
// if unreachable -- don't bother adding it.
// Note: It's important not to cache on find here; see earlier comments for details.
if (findTraitsInPoolByNameAndNSImpl(pool, name, ns, /*cacheIfFound*/false) == NULL)
{
pool->m_loadedTraits->add(name, ns, itraits);
}
}
Traits* DomainMgr::findBuiltinTraitsByName(PoolObject* pool, Stringp name)
{
return pool->m_loadedTraits->getName(name);
}
Traits* DomainMgr::findTraitsInDomainByNameAndNSImpl(Domain* domain, Stringp name, Namespacep ns, bool cacheIfFound)
{
Traits* traits = NULL;
// First, look bottom-up to find the first cached instance.
for (uint32_t i = 0, n = domain->m_baseCount; i < n; ++i)
{
Domain* d = domain->m_bases[i];
if ((traits = (Traits*)d->m_cachedTraits->get(name, ns)) != NULL)
{
if (cacheIfFound)
{
if (i > 0)
{
// If cacheIfFound is true, we need to ensure the result
// is cached in both the "d" and "domain". But we know
// it's already in "d", so we only need to do anything
// if d != domain (which is true iff i > 0, since base[0] == domain)
AvmAssert(d != domain);
domain->m_cachedTraits->add(name, ns, traits);
}
}
return traits;
}
}
// No instance ever cached, so look top-down to find the first loaded instance.
// Note that this goes from N...1 (rather than N-1...0) so we can re-use the same unsigned var.
for (uint32_t i = domain->m_baseCount; i > 0; --i)
{
Domain* d = domain->m_bases[i-1];
if ((traits = (Traits*)d->m_loadedTraits->get(name, ns)) != NULL)
{
if (cacheIfFound)
{
// If cacheIfFound is true, we need to ensure the result
// is cached in both the "d" and "domain". But we know
// it's already in "d", so we only need to do anything
// if d != domain (which is true iff i > 1, since base[1-1] == domain)
if (i > 1)
{
AvmAssert(d != domain);
d->m_cachedTraits->add(name, ns, traits);
}
domain->m_cachedTraits->add(name, ns, traits);
}
return traits;
}
}
return NULL;
}
Traits* DomainMgr::findTraitsInPoolByNameAndNSImpl(PoolObject* pool, Stringp name, Namespacep ns, bool cacheIfFound)
{
Traits* traits = (Traits*)pool->m_cachedTraits->get(name, ns);
if (traits == NULL)
{
traits = findTraitsInDomainByNameAndNSImpl(pool->domain, name, ns, cacheIfFound);
if (traits == NULL)
{
traits = (Traits*)pool->m_loadedTraits->get(name, ns);
}
if (cacheIfFound && traits != NULL)
{
pool->m_cachedTraits->add(name, ns, traits);
}
}
return traits;
}
Traits* DomainMgr::findTraitsInPoolByNameAndNS(PoolObject* pool, Stringp name, Namespacep ns)
{
return findTraitsInPoolByNameAndNSImpl(pool, name, ns, /*cacheIfFound*/true);
}
Traits* DomainMgr::findTraitsInPoolByMultiname(PoolObject* pool, const Multiname& multiname)
{
// do full lookup of multiname, error if more than 1 match
// return Traits if 1 match, NULL if 0 match, BIND_AMBIGUOUS >1 match
Traits* found = NULL;
if (multiname.isBinding())
{
// multiname must not be an attr name, have wildcards, or have runtime parts.
for (int32_t i=0, n=multiname.namespaceCount(); i < n; i++)
{
Traits* t = findTraitsInPoolByNameAndNSImpl(pool, multiname.getName(), multiname.getNamespace(i), /*cacheIfFound*/true);
if (t != NULL)
{
if (found == NULL)
{
found = t;
}
else if (found != t)
{
// ambiguity
return (Traits*)BIND_AMBIGUOUS;
}
}
}
}
return found;
}
void DomainMgr::addNamedScript(PoolObject* pool, Stringp name, Namespacep ns, MethodInfo* script)
{
// if we can find an existing trait by this name, the one we're adding
// if unreachable -- don't bother adding it. (Note that this examines
// only the Domain's table, not the Pool's)
// Note: It's important not to cache on find here; see earlier comments for details.
if (ns->isPrivate())
{
if (findScriptInPoolByNameAndNSImpl(pool, name, ns, /*cacheIfFound*/false) == NULL)
pool->m_loadedScripts->add(name, ns, script);
}
else
{
if (findScriptInDomainByNameAndNSImpl(pool->domain, name, ns, /*cacheIfFound*/false) == NULL)
pool->domain->m_loadedScripts->add(name, ns, script);
}
}
MethodInfo* DomainMgr::findScriptInDomainByNameAndNSImpl(Domain* domain, Stringp name, Namespacep ns, bool cacheIfFound)
{
MethodInfo* mi = NULL;
// First, look bottom-up to find the first cached instance.
for (uint32_t i = 0, n = domain->m_baseCount; i < n; ++i)
{
Domain* d = domain->m_bases[i];
if ((mi = (MethodInfo*)d->m_cachedScripts->get(name, ns)) != NULL)
{
if (cacheIfFound)
{
if (i > 0)
{
AvmAssert(d != domain);
domain->m_cachedScripts->add(name, ns, mi);
}
}
return mi;
}
}
// No instance ever cached, so look top-down to find the first loaded instance.
for (uint32_t i = domain->m_baseCount; i > 0; --i)
{
Domain* d = domain->m_bases[i-1];
if ((mi = (MethodInfo*)d->m_loadedScripts->get(name, ns)) != NULL)
{
if (cacheIfFound)
{
if (i > 1)
{
AvmAssert(d != domain);
d->m_cachedScripts->add(name, ns, mi);
}
domain->m_cachedScripts->add(name, ns, mi);
}
return mi;
}
}
return NULL;
}
MethodInfo* DomainMgr::findScriptInDomainByMultinameImpl(Domain* domain, const Multiname& multiname, Namespace*& nsFound)
{
MethodInfo* mi = NULL;
// First, look bottom-up to find the first cached instance.
for (uint32_t i = 0, n = domain->m_baseCount; i < n; ++i)
{
Domain* d = domain->m_bases[i];
mi = (MethodInfo*)d->m_cachedScripts->getMulti(multiname, /*out*/nsFound);
if (isValidBinding(mi))
{
// if (cacheIfFound) -- always true here
{
if (i > 0)
{
AvmAssert(d != domain);
domain->m_cachedScripts->add(multiname.getName(), nsFound, mi);
}
}
return mi;
}
}
// No instance ever cached, so look top-down to find the first loaded instance.
for (uint32_t i = domain->m_baseCount; i > 0; --i)
{
Domain* d = domain->m_bases[i-1];
mi = (MethodInfo*)d->m_loadedScripts->getMulti(multiname, /*out*/nsFound);
if (isValidBinding(mi))
{
// if (cacheIfFound) -- always true here
{
if (i > 1)
{
AvmAssert(d != domain);
d->m_cachedScripts->add(multiname.getName(), nsFound, mi);
}
domain->m_cachedScripts->add(multiname.getName(), nsFound, mi);
}
return mi;
}
}
return NULL;
}
MethodInfo* DomainMgr::findScriptInPoolByNameAndNSImpl(PoolObject* pool, Stringp name, Namespacep ns, bool cacheIfFound)
{
MethodInfo* mi = (MethodInfo*)pool->m_cachedScripts->getName(name);
if (mi == NULL)
{
mi = findScriptInDomainByNameAndNSImpl(pool->domain, name, ns, cacheIfFound);
if (mi == NULL)
{
mi = (MethodInfo*)pool->m_loadedScripts->get(name, ns);
}
if (cacheIfFound && mi != NULL)
{
pool->m_cachedScripts->add(name, ns, mi);
}
}
return mi;
}
MethodInfo* DomainMgr::findScriptInPoolByMultiname(PoolObject* pool, const Multiname& multiname)
{
return findScriptInPoolByMultinameImpl(pool, multiname);
}
void DomainMgr::addNamedScriptEnvs(AbcEnv* abcEnv, const GCList<ScriptEnv>& envs)
{
// If the MethodInfo for this ScriptEnv isn't in the Domain's or Pool's
// map, then we must have filtered it out as unreachable: don't
// bother adding the ScriptEnv, as we'll never need to look it up.
// (Note that we don't need to bother checking the parent Domains
// for this, since we want to check loaded, not cached.) We can't rely
// on looking up by name, since scripts all tend to be named "global",
// so instead we make a temporary map of all the entries in the relevant
// Pool and Domain.
PoolObject* pool = abcEnv->pool();
DomainEnv* domainEnv = abcEnv->domainEnv();
Domain* domain = domainEnv->domain();
// we have no generic "set" type, so let's use a hashtable for the same purpose
// (a bit more mem, but short-lived and better average lookup time than using List<>)
HeapHashtable* ht = HeapHashtable::create(core->GetGC());
for (StMNHTMethodInfoIterator iter(pool->m_loadedScripts); iter.next(); )
{
if (!iter.key()) continue;
Atom const a = AvmCore::genericObjectToAtom(iter.value());
ht->add(a, a);
}
for (StMNHTMethodInfoIterator iter(domain->m_loadedScripts); iter.next(); )
{
if (!iter.key()) continue;
Atom const a = AvmCore::genericObjectToAtom(iter.value());
ht->add(a, a);
}
for (uint32_t i = 0, n = envs.length(); i < n; ++i)
{
ScriptEnv* se = envs[i];
AvmAssert(se->abcEnv() == abcEnv);
MethodInfo* mi = se->method;
AvmAssert(domainEnv->m_scriptEnvMap->get(mi) == NULL);
if (ht->get(AvmCore::genericObjectToAtom(mi)) == undefinedAtom)
continue;
domainEnv->m_scriptEnvMap->add(mi, se);
}
delete ht;
}
ScriptEnv* DomainMgr::mapScriptToScriptEnv(DomainEnv* domainEnv, MethodInfo* mi)
{
ScriptEnv* se = NULL;
if (mi != NULL)
{
se = domainEnv->m_scriptEnvMap->get(mi);
if (se == NULL)
{
// This is an item from a parent domain that's not in the MI->SE map yet.
// walk up the parent chain and find it. This can happen if
// we find a (cached) entry from a parent Domain; since addNamedScriptEnvs()
// only added mappings for the MI/SE's in this Domain/DomainEnv pair,
// we won't have the proper mapping. However, we know it must be in
// a parent (which is why we start at 1, not 0: no point in looking
// in our own map again), so lazily find it.
for (uint32_t i = 1, n = domainEnv->m_baseCount; i < n; ++i)
{
DomainEnv* de = domainEnv->m_bases[i];
if (mi->pool()->domain == de->domain())
{
se = de->m_scriptEnvMap->get(mi);
AvmAssert(se != NULL);
domainEnv->m_scriptEnvMap->add(mi, se);
break;
}
}
}
// If the MethodInfo was entered, but the script then failed verification,
// we might not have a matching ScriptEnv... so don't crash in Debug builds.
// (Caller already expects NULL and checks for it.)
AvmAssert(se == NULL || se->method == mi);
}
return se;
}
MethodInfo* DomainMgr::findScriptInPoolByMultinameImpl(PoolObject* pool, const Multiname& multiname)
{
MethodInfo* mi = (MethodInfo*)pool->m_cachedScripts->getMulti(multiname);
if (!isValidBinding(mi))
{
Namespacep nsFound;
mi = findScriptInDomainByMultinameImpl(pool->domain, multiname, /*out*/nsFound);
if (mi == NULL)
{
mi = (MethodInfo*)pool->m_loadedScripts->getMulti(multiname, /*out*/nsFound);
}
if (isValidBinding(mi))
{
AvmAssert(nsFound != NULL);
pool->m_cachedScripts->add(multiname.getName(), nsFound, mi);
}
}
return mi;
}
#ifdef DEBUGGER
MethodInfo* DomainMgr::findScriptInDomainByNameOnlyImpl(Domain* domain, Stringp name, Namespace*& nsFound)
{
MethodInfo* mi = NULL;
// First, look bottom-up to find the first cached instance.
for (uint32_t i = 0, n = domain->m_baseCount; i < n; ++i)
{
Domain* d = domain->m_bases[i];
mi = (MethodInfo*)d->m_cachedScripts->getName(name, &nsFound);
if (isValidBinding(mi))
{
// if (cacheIfFound) -- always true here
{
if (i > 0)
{
AvmAssert(d != domain);
domain->m_cachedScripts->add(name, nsFound, mi);
}
}
return mi;
}
}
// No instance ever cached, so look top-down to find the first loaded instance.
for (uint32_t i = domain->m_baseCount; i > 0; --i)
{
Domain* d = domain->m_bases[i-1];
mi = (MethodInfo*)d->m_loadedScripts->getName(name, &nsFound);
if (isValidBinding(mi))
{
// if (cacheIfFound) -- always true here
{
if (i > 1)
{
AvmAssert(d != domain);
d->m_cachedScripts->add(name, nsFound, mi);
}
domain->m_cachedScripts->add(name, nsFound, mi);
}
return mi;
}
}
return NULL;
}
MethodInfo* DomainMgr::findScriptInPoolByNameOnlyImpl(PoolObject* pool, Stringp name)
{
MethodInfo* mi = (MethodInfo*)pool->m_cachedScripts->getName(name);
if (mi == NULL)
{
Namespacep nsFound = NULL;
mi = findScriptInDomainByNameOnlyImpl(pool->domain, name, /*out*/nsFound);
if (mi == NULL)
{
mi = (MethodInfo*)pool->m_loadedScripts->getName(name, /*out*/&nsFound);
}
if (isValidBinding(mi))
{
AvmAssert(nsFound != NULL);
pool->m_cachedScripts->add(name, nsFound, mi);
}
}
return mi;
}
#endif // DEBUGGER
ScriptEnv* DomainMgr::findScriptEnvInDomainEnvByMultinameImpl(DomainEnv* domainEnv, const Multiname& multiname)
{
Namespacep nsFound = NULL;
MethodInfo* mi = findScriptInDomainByMultinameImpl(domainEnv->domain(), multiname, /*out*/nsFound);
ScriptEnv* se = mapScriptToScriptEnv(domainEnv, mi);
return se;
}
ScriptEnv* DomainMgr::findScriptEnvInDomainEnvByMultiname(DomainEnv* domainEnv, const Multiname& multiname)
{
return findScriptEnvInDomainEnvByMultinameImpl(domainEnv, multiname);
}
ScriptEnv* DomainMgr::findScriptEnvInAbcEnvByMultiname(AbcEnv* abcEnv, const Multiname& multiname)
{
MethodInfo* mi = findScriptInPoolByMultinameImpl(abcEnv->pool(), multiname);
ScriptEnv* se = mapScriptToScriptEnv(abcEnv->domainEnv(), mi);
return se;
}
#ifdef DEBUGGER
ScriptEnv* DomainMgr::findScriptEnvInDomainEnvByNameOnlyImpl(DomainEnv* domainEnv, Stringp name)
{
Namespacep nsFound = NULL;
MethodInfo* mi = findScriptInDomainByNameOnlyImpl(domainEnv->domain(), name, /*out*/nsFound);
ScriptEnv* se = mapScriptToScriptEnv(domainEnv, mi);
return se;
}
ScriptEnv* DomainMgr::findScriptEnvInAbcEnvByNameOnly(AbcEnv* abcEnv, Stringp name)
{
MethodInfo* mi = findScriptInPoolByNameOnlyImpl(abcEnv->pool(), name);
ScriptEnv* se = mapScriptToScriptEnv(abcEnv->domainEnv(), mi);
return se;
}
#endif // DEBUGGER
} // namespace avmplus