About the Title
"classes of the same package" means classes that share the same package access. Please pay attention to the fact that class a.b.c.Foo doesn't have package access to class a.b.Bar . Because the latter can't access to the former if the former's modifier is default.
Problem
If I split two classes in the same packages into two dex files, even though I load them correctly, I will also get some error while running, the logcat likes:
I/dalvikvm( 6498): DexOpt: illegal method access (call Lcom/fish47/multidex/Foo;.isWholeWord (Lcom/fish47/multidex/Foo;)Z from Lcom/fish47/multidex/TestMatchWord;)
I/dalvikvm( 6498): Could not find method com.fish47.multidex.core.Foo.isWholeWord, referenced from method com.fish47.multidex.core.TestMatchWord.test_english
W/dalvikvm( 6498): VFY: unable to resolve virtual method 758: Lcom/fish47/multidex/Foo;.isWholeWord (Lcom/fish47/multidex/Foo;)Z
Conjecture
That's code which pop out the error message below:
vm/analysis/Optimize.c ==> line: 697 - 714
/* access allowed? */
tweakLoader(referrer, resMethod->clazz);
bool allowed = dvmCheckMethodAccess(referrer, resMethod);
untweakLoader(referrer, resMethod->clazz);
if (!allowed) {
IF_LOGI() {
char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
resMethod->clazz->descriptor, resMethod->name, desc,
referrer->descriptor);
free(desc);
}
if (pFailure != NULL)
*pFailure = VERIFY_ERROR_ACCESS_METHOD;
return NULL;
}
Pay attention to the value of resClass->classLoader . If the two classes aren't from the same dex file, its value will be set 0xdead3333.
vm/analysis/Optimize.c ==> line: 285 - 299
static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
{
if (!gDvm.optimizing)
return;
assert(referrer->classLoader == NULL);
assert(resClass->classLoader == NULL);
if (!gDvm.optimizingBootstrapClass) {
/* class loader for an array class comes from element type */
if (dvmIsArrayClass(resClass))
resClass = resClass->elementClass;
if (referrer->pDvmDex != resClass->pDvmDex)
resClass->classLoader = (Object*) 0xdead3333;
}
}
This is a trick, it lets checkAccess(...) method return false at last, if the two classes are in the same package, accessible to each other but not to public.
vm/oo/AccessCheck.c ==> line: 88 - 116
static bool checkAccess(const ClassObject* accessFrom,
const ClassObject* accessTo, u4 accessFlags)
{
/* quick accept for public access */
if (accessFlags & ACC_PUBLIC)
return true;
/* quick accept for access from same class */
if (accessFrom == accessTo)
return true;
/* quick reject for private access from another class */
if (accessFlags & ACC_PRIVATE)
return false;
/*
* Semi-quick test for protected access from a sub-class, which may or
* may not be in the same package.
*/
if (accessFlags & ACC_PROTECTED)
if (dvmIsSubClass(accessFrom, accessTo))
return true;
/*
* Allow protected and private access from other classes in the same
* package.
*/
return dvmInSamePackage(accessFrom, accessTo);
}
vm/oo/AccessCheck.c ==> line: 39 - 83
bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
{
...
/* class loaders must match */
if (class1->classLoader != class2->classLoader)
return false;
...
}