diff options
| -rw-r--r-- | src/compiler/Frontend.cc | 6 | ||||
| -rw-r--r-- | src/compiler/codegen/arm/MethodCodegenDriver.cc | 234 | ||||
| -rw-r--r-- | src/compiler_test.cc | 16 | ||||
| -rw-r--r-- | src/object.h | 4 | ||||
| -rw-r--r-- | src/runtime_support.S | 38 | ||||
| -rw-r--r-- | src/runtime_support.h | 1 | ||||
| -rw-r--r-- | src/thread.cc | 42 | ||||
| -rw-r--r-- | src/thread.h | 2 | ||||
| -rw-r--r-- | test/IntMath/IntMath.java | 33 |
9 files changed, 293 insertions, 83 deletions
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc index d6f891b497..e9f8a3a299 100644 --- a/src/compiler/Frontend.cc +++ b/src/compiler/Frontend.cc @@ -705,6 +705,9 @@ bool oatCompileMethod(Method* method, art::InstructionSet insnSet) if (PrettyMethod(method).find("virtualCall") != std::string::npos) { compiling = true; } + if (PrettyMethod(method).find("tryThing") != std::string::npos) { + compiling = true; + } if (PrettyMethod(method).find("getFoo") != std::string::npos) { compiling = true; } @@ -714,6 +717,9 @@ bool oatCompileMethod(Method* method, art::InstructionSet insnSet) if (PrettyMethod(method).find("IntMath.<init>") != std::string::npos) { compiling = true; } + if (PrettyMethod(method).find("IntMathBase.<init>") != std::string::npos) { + compiling = true; + } if (PrettyMethod(method).find("java.lang.Object.<init>") != std::string::npos) { compiling = true; } diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc index d95b870001..eb9296bb05 100644 --- a/src/compiler/codegen/arm/MethodCodegenDriver.cc +++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc @@ -478,7 +478,7 @@ static int nextVCallInsnSP(CompilationUnit* cUnit, MIR* mir, skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel); skipTarget->defMask = ENCODE_ALL; skipBranch->generic.target = (LIR*)skipTarget; - // Get base_method->method_index [usr rLR, set r12] + // Get base_method->method_index [usr rLR, set r0] loadBaseDisp(cUnit, mir, rLR, Method::GetMethodIndexOffset().Int32Value(), r0, kUnsignedHalf, INVALID_SREG); @@ -528,62 +528,25 @@ static int loadArgRegs(CompilationUnit* cUnit, MIR* mir, return callState; } -/* - * Interleave launch code for INVOKE_INTERFACE. The target is - * identified using artFindInterfaceMethodInCache(class, ref, method, dex) - * Note that we'll have to reload "this" following the helper call. - * - * FIXME: do we need to have artFindInterfaceMethodInCache return - * a NULL if not found so we can throw exception here? Otherwise, - * may need to pass some additional info to allow the helper function - * to throw on its own. - */ +// Interleave launch code for INVOKE_INTERFACE. static int nextInterfaceCallInsn(CompilationUnit* cUnit, MIR* mir, DecodedInstruction* dInsn, int state, ArmLIR* rollback) { - UNIMPLEMENTED(FATAL) << "Need findInterfaceMethodInCache"; -#if 0 - RegLocation rlArg; switch(state) { - case 0: - // Load "this" [set r12] - rlArg = oatGetSrc(cUnit, mir, 0); - loadValueDirectFixed(cUnit, rlArg, r12); - // Get the current Method* [set arg2] - loadCurrMethodDirect(cUnit, r2); - // Is "this" null? [use r12] - genNullCheck(cUnit, oatSSASrc(mir,0), r12, - mir->offset, NULL); - // Get curMethod->clazz [set arg3] - loadBaseDisp(cUnit, mir, r2, OFFSETOF_MEMBER(Method, clazz), - r3, kWord, INVALID_SREG); - // Load this->class [usr r12, set arg0] - loadBaseDisp(cUnit, mir, r12, OFFSETOF_MEMBER(Class, clazz), - r3, kWord, INVALID_SREG); - // Load address of helper function - loadBaseDisp(cUnit, mir, rSELF, - OFFSETOF_MEMBER(Thread, pArtFindInterfaceMethodInCache), - rLR, kWord, INVALID_SREG); - // Get dvmDex - loadBaseDisp(cUnit, mir, r3, OFFSETOF_MEMBER(Class, pDvmDex), - r3, kWord, INVALID_SREG); - // Load ref [set arg1] - loadConstant(cUnit, r1, dInsn->vB); - // Call out to helper, target Method returned in ret0 - newLIR1(cUnit, kThumbBlxR, rLR); + case 0: // Load trampoline target + loadWordDisp(cUnit, rSELF, + OFFSETOF_MEMBER(Thread, pInvokeInterfaceTrampoline), + rLR); + // Load r0 with method index + loadConstant(cUnit, r0, dInsn->vB); break; - case 1: // Get the target compiled code address [use r0, set rLR] - loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, compiledInsns), - rLR, kWord, INVALID_SREG); default: return -1; } -#endif return state + 1; } - /* * Interleave launch code for INVOKE_SUPER. See comments * for nextVCallIns. @@ -592,49 +555,132 @@ static int nextSuperCallInsn(CompilationUnit* cUnit, MIR* mir, DecodedInstruction* dInsn, int state, ArmLIR* rollback) { - UNIMPLEMENTED(FATAL) << "Need INVOKE_SUPER implementation"; -#if 0 + DCHECK(rollback == NULL); RegLocation rlArg; + /* + * This is the fast path in which the target virtual method is + * fully resolved at compile time. Note also that this path assumes + * that the check to verify that the target method index falls + * within the size of the super's vtable has been done at compile-time. + */ + Method* baseMethod = cUnit->method->GetDexCacheResolvedMethods()-> + Get(dInsn->vB); + CHECK(baseMethod != NULL); + Class* superClass = cUnit->method->GetDeclaringClass()->GetSuperClass(); + CHECK(superClass != NULL); + int32_t target_idx = baseMethod->GetMethodIndex(); + CHECK(superClass->GetVTable()->GetLength() > target_idx); + Method* targetMethod = superClass->GetVTable()->Get(target_idx); + CHECK(targetMethod != NULL); switch(state) { - case 0: - // Get the current Method* [set r0] + case 0: // Get current Method* [set r0] loadCurrMethodDirect(cUnit, r0); // Load "this" [set r1] rlArg = oatGetSrc(cUnit, mir, 0); loadValueDirectFixed(cUnit, rlArg, r1); - // Get method->clazz [use r0, set r12] - loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, clazz), - r12, kWord, INVALID_SREG); - // Get pResmethods [use r0, set rLR] - loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, pResMethods), - rLR, kWord, INVALID_SREG); - // Get clazz->super [use r12, set r12] - loadBaseDisp(cUnit, mir, r12, OFFSETOF_MEMBER(Class, super), - r12, kWord, INVALID_SREG); - // Get base method [use rLR, set r0] - loadBaseDisp(cUnit, mir, rLR, dInsn->vB * 4, r0, - kWord, INVALID_SREG); + // Get method->declaring_class_ [use r0, set rLR] + loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(), + rLR); // Is "this" null? [use r1] genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir->offset, NULL); - // Get methodIndex [use r0, set rLR] - loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, methodIndex), - rLR, kUnsignedHalf, INVALID_SREG); - // Get vtableCount [use r12, set r0] - loadBaseDisp(cUnit, mir, r12, - OFFSETOF_MEMBER(Class, vtableCount), - r0, kWord, INVALID_SREG); - // Compare method index w/ vtable count [use r12, use rLR] - genRegRegCheck(cUnit, kArmCondGe, rLR, r0, mir->offset, NULL); - // get target Method* [use rLR, use r12, set r0] - loadBaseIndexed(cUnit, r0, r12, rLR, 2, kWord); - case 1: // Get the target compiled code address [use r0, set rLR] - loadBaseDisp(cUnit, mir, r0, OFFSETOF_MEMBER(Method, compiledInsns), - rLR, kWord, INVALID_SREG); + break; + case 1: // Get method->declaring_class_->super_class [usr rLR, set rLR] + loadWordDisp(cUnit, rLR, Class::SuperClassOffset().Int32Value(), + rLR); + break; + case 2: // Get ...->super_class_->vtable [u/s rLR] + loadWordDisp(cUnit, rLR, Class::VTableOffset().Int32Value(), rLR); + break; + case 3: // Get target method [use rLR, set r0] + loadWordDisp(cUnit, rLR, (target_idx * 4) + + art::Array::DataOffset().Int32Value(), r0); + break; + case 4: // Get the target compiled code address [uses r0, sets rLR] + loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR); + break; + default: + return -1; + } + return state + 1; +} + +/* Slow-path version of nextSuperCallInsn */ +static int nextSuperCallInsnSP(CompilationUnit* cUnit, MIR* mir, + DecodedInstruction* dInsn, int state, + ArmLIR* rollback) +{ + DCHECK(rollback != NULL); + RegLocation rlArg; + ArmLIR* skipBranch; + ArmLIR* skipTarget; + int tReg; + /* + * This handles the case in which the base method is not fully + * resolved at compile time. We must generate code to test + * for resolution a run time, bail to the slow path if not to + * fill in all the tables. In the latter case, we'll restart at + * at the beginning of the sequence. + */ + switch(state) { + case 0: // Get the current Method* [sets r0] + loadCurrMethodDirect(cUnit, r0); + break; + case 1: // Get method->dex_cache_resolved_methods_ [usr r0, set rLR] + loadWordDisp(cUnit, r0, + Method::GetDexCacheResolvedMethodsOffset().Int32Value(), rLR); + break; + case 2: // method->dex_cache_resolved_methods_->Get(meth_idx) [u/s rLR] + loadWordDisp(cUnit, rLR, (dInsn->vB * 4) + + art::Array::DataOffset().Int32Value(), rLR); + break; + case 3: // Resolved? + skipBranch = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0); + // Slowest path, bail to helper, rollback and retry + loadWordDisp(cUnit, rSELF, + OFFSETOF_MEMBER(Thread, pResolveMethodFromCode), rLR); + loadConstant(cUnit, r1, dInsn->vB); + newLIR1(cUnit, kThumbBlxR, rLR); + genUnconditionalBranch(cUnit, rollback); + // Resume normal slow path + skipTarget = newLIR0(cUnit, kArmPseudoTargetLabel); + skipTarget->defMask = ENCODE_ALL; + skipBranch->generic.target = (LIR*)skipTarget; + // Get base_method->method_index [usr rLR, set rLR] + loadBaseDisp(cUnit, mir, rLR, + Method::GetMethodIndexOffset().Int32Value(), rLR, + kUnsignedHalf, INVALID_SREG); + // Load "this" [set r1] + rlArg = oatGetSrc(cUnit, mir, 0); + loadValueDirectFixed(cUnit, rlArg, r1); + // Load curMethod->declaring_class_ [uses r0, sets r0] + loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(), + r0); + case 4: // Get method->declaring_class_->super_class [usr r0, set r0] + loadWordDisp(cUnit, r0, Class::SuperClassOffset().Int32Value(), r0); + break; + case 5: // Get ...->super_class_->vtable [u/s r0] + loadWordDisp(cUnit, r0, Class::VTableOffset().Int32Value(), r0); + // In load shadow fold vtable_ object header size into method_index_ + opRegImm(cUnit, kOpAdd, rLR, + art::Array::DataOffset().Int32Value() / 4); + if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { + // Range check, throw NSM on failure + tReg = oatAllocTemp(cUnit); + loadWordDisp(cUnit, r0, art::Array::LengthOffset().Int32Value(), + tReg); + genBoundsCheck(cUnit, tReg, rLR, mir->offset, NULL); + oatFreeTemp(cUnit, tReg); + } + // Get target Method* + loadBaseIndexed(cUnit, r0, r0, rLR, 2, kWord); + break; + case 6: // Get the target compiled code address [uses r0, sets rLR] + loadWordDisp(cUnit, r0, Method::GetCodeOffset().Int32Value(), rLR); + break; default: return -1; } -#endif return state + 1; } @@ -822,6 +868,10 @@ static void genInvokeStaticDirect(CompilationUnit* cUnit, MIR* mir, newLIR1(cUnit, kThumbBlxR, rLR); } +/* + * All invoke-interface calls bounce off of art_invoke_interface_trampoline, + * which will locate the target and continue on via a tail call. + */ static void genInvokeInterface(CompilationUnit* cUnit, MIR* mir) { DecodedInstruction* dInsn = &mir->dalvikInsn; @@ -847,13 +897,41 @@ static void genInvokeSuper(CompilationUnit* cUnit, MIR* mir) DecodedInstruction* dInsn = &mir->dalvikInsn; int callState = 0; ArmLIR* nullCk; + ArmLIR* rollback; + Method* baseMethod = cUnit->method->GetDexCacheResolvedMethods()-> + Get(dInsn->vB); + NextCallInsn nextCallInsn; + bool fastPath = true; + if (baseMethod == NULL) { + fastPath = false; + } else { + Class* superClass = cUnit->method->GetDeclaringClass()->GetSuperClass(); + if (superClass == NULL) { + fastPath = false; + } else { + int32_t target_idx = baseMethod->GetMethodIndex(); + if (superClass->GetVTable()->GetLength() <= target_idx) { + fastPath = false; + } else { + fastPath = (superClass->GetVTable()->Get(target_idx) != NULL); + } + } + } + if (fastPath) { + nextCallInsn = nextSuperCallInsn; + rollback = NULL; + } else { + nextCallInsn = nextSuperCallInsnSP; + rollback = newLIR0(cUnit, kArmPseudoTargetLabel); + rollback->defMask = -1; + } // TODO - redundantly loading arg0/r1 ("this") if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER) callState = genDalvikArgsNoRange(cUnit, mir, dInsn, callState, &nullCk, - false, nextSuperCallInsn, NULL); + false, nextCallInsn, rollback); else callState = genDalvikArgsRange(cUnit, mir, dInsn, callState, &nullCk, - nextSuperCallInsn, NULL); + nextCallInsn, rollback); // Finish up any of the call sequence not interleaved in arg loading while (callState >= 0) { callState = nextSuperCallInsn(cUnit, mir, dInsn, callState, NULL); diff --git a/src/compiler_test.cc b/src/compiler_test.cc index c144f7ccd1..656c7d4780 100644 --- a/src/compiler_test.cc +++ b/src/compiler_test.cc @@ -103,6 +103,19 @@ TEST_F(CompilerTest, BasicCodegen) { 10); } +// TODO: Need invoke-interface test + +TEST_F(CompilerTest, SuperTest) { + CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V"); + const ClassLoader* class_loader = LoadDex("IntMath"); + CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V"); + CompileVirtualMethod(class_loader, "IntMathBase", "tryThing", "()I"); + CompileDirectMethod(class_loader, "IntMath", "<init>", "()V"); + CompileVirtualMethod(class_loader, "IntMath", "tryThing", "()I"); + AssertStaticIntMethod(class_loader, "IntMath", "superTest", "(I)I", 4175, + 4141); +} + TEST_F(CompilerTest, ConstStringTest) { AssertStaticIntMethod(LoadDex("IntMath"), "IntMath", "constStringTest", "(I)I", 2468, 1234); @@ -231,6 +244,7 @@ TEST_F(CompilerTest, ManyArgs) { TEST_F(CompilerTest, VirtualCall) { CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V"); const ClassLoader* class_loader = LoadDex("IntMath"); + CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V"); CompileDirectMethod(class_loader, "IntMath", "<init>", "()V"); CompileVirtualMethod(class_loader, "IntMath", "virtualCall", "(I)I"); AssertStaticIntMethod(class_loader, "IntMath", "staticCall", "(I)I", 6, @@ -240,6 +254,7 @@ TEST_F(CompilerTest, VirtualCall) { TEST_F(CompilerTest, TestIGetPut) { CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V"); const ClassLoader* class_loader = LoadDex("IntMath"); + CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V"); CompileDirectMethod(class_loader, "IntMath", "<init>", "(I)V"); CompileDirectMethod(class_loader, "IntMath", "<init>", "()V"); CompileVirtualMethod(class_loader, "IntMath", "getFoo", "()I"); @@ -248,4 +263,5 @@ TEST_F(CompilerTest, TestIGetPut) { 111); } + } // namespace art diff --git a/src/object.h b/src/object.h index 5857571140..a0f3e38416 100644 --- a/src/object.h +++ b/src/object.h @@ -1512,6 +1512,10 @@ class Class : public Object, public StaticStorageBase { OFFSET_OF_OBJECT_MEMBER(Class, super_class_), false); } + static MemberOffset SuperClassOffset() { + return MemberOffset(OFFSETOF_MEMBER(Class, super_class_)); + } + void SetSuperClass(Class *new_super_class) { // super class is assigned once, except during class linker initialization Class* old_super_class = GetFieldObject<Class*>( diff --git a/src/runtime_support.S b/src/runtime_support.S index 46c2dd39b3..fe40cf2da9 100644 --- a/src/runtime_support.S +++ b/src/runtime_support.S @@ -1,6 +1,44 @@ #if defined(__arm__) .balign 4 + + .global art_invoke_interface_trampoline + .extern artFindInterfaceMethodInCache + .extern artFailedInvokeInterface +art_invoke_interface_trampoline: + /* + * All generated callsites for interface invokes will load arguments + * as usual - except instead of loading arg0/r0 with the target + * Method*, arg0/r0 will contain the method_idx. This wrapper will + * save arg1-arg3, load the caller's Method*, align the stack and + * call the helper artFindInterfaceMethodInCache(idx, this, method); + * NOTE: "this" is first visable argument of the target, and so can be + * found in arg1/r1. + * + * artFindInterfaceMethodInCache will attempt to locate the target + * and return a 64-bit result in r0/r1 consisting of the target + * Method* in r0 and method->code_ in r1. + * + * If unsuccessful, artFindInterfaceMethodInCache will return + * NULL/NULL. This is somewhat different than the usual + * mechanism of helper routines performing the unwind & throw. + * The reason is that this trampoline is not unwindable. In the + * event artFindInterfaceMethodInCache fails to resolve, the wrapper + * will prepare an unwindable environment and jump to another helper + * to do unwind/throw. + * + * On success this wrapper will restore arguments and *jump* to the + * target, leaving the lr pointing back to the original caller. + */ + stmdb sp!, {r1, r2, r3, lr} + ldr r2, [sp, #16] @ load caller's Method* + bl artFindInterfaceMethodInCache @ (method_idx, this, callerMethod) + mov r12, r1 @ save r0->code_ + ldmia sp!, {r1, r2, r3, lr} @ restore arguments + cmp r0, #0 @ did we find the target? + bxne r12 @ tail call to target if so + b artFailedInvokeInterface @ Will appear as if called directly + .global art_shl_long art_shl_long: /* diff --git a/src/runtime_support.h b/src/runtime_support.h index 8d65aaaacf..23126d79ae 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -8,6 +8,7 @@ extern "C" uint64_t art_shl_long(uint64_t, uint32_t); extern "C" uint64_t art_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_ushr_long(uint64_t, uint32_t); + extern "C" void art_invoke_interface_trampoline(void*, void*, void*, void*); /* Conversions */ extern "C" float __aeabi_i2f(int op1); // OP_INT_TO_FLOAT diff --git a/src/thread.cc b/src/thread.cc index aa24bec755..31786c9970 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -34,6 +34,42 @@ pid_t gettid() { return syscall(__NR_gettid);} pthread_key_t Thread::pthread_key_self_; +// Temporary debugging hook for compiler. +static void DebugMe(Method* method, uint32_t info) { + LOG(INFO) << "DebugMe"; + if (method != NULL) + LOG(INFO) << PrettyMethod(method); + LOG(INFO) << "Info: " << info; +} + +/* + * TODO: placeholder for a method that can be called by the + * invoke-interface trampoline to unwind and handle exception. The + * trampoline will arrange it so that the caller appears to be the + * callsite of the failed invoke-interface. See comments in + * compiler/runtime_support.S + */ +extern "C" void artFailedInvokeInterface() +{ + UNIMPLEMENTED(FATAL) << "Unimplemented exception throw"; +} + +// TODO: placeholder. See comments in compiler/runtime_support.S +extern "C" uint64_t artFindInterfaceMethodInCache(uint32_t method_idx, + Object* this_object , Method* caller_method) +{ + /* + * Note: this_object has not yet been null-checked. To match + * the old-world state, nullcheck this_object and load + * Class* this_class = this_object->GetClass(). + * See comments and possible thrown exceptions in old-world + * Interp.cpp:dvmInterpFindInterfaceMethod, and complete with + * new-world FindVirtualMethodForInterface. + */ + UNIMPLEMENTED(FATAL) << "Unimplemented invoke interface"; + return 0LL; +} + // TODO: placeholder. This is what generated code will call to throw static void ThrowException(Thread* thread, Throwable* exception) { /* @@ -98,6 +134,7 @@ void Thread::InitFunctionPointers() { pD2l = D2L; pLdivmod = __aeabi_ldivmod; pLmul = __aeabi_lmul; + pInvokeInterfaceTrampoline = art_invoke_interface_trampoline; #endif pAllocFromCode = Array::AllocFromCode; pAllocObjectFromCode = Class::AllocObjectFromCode; @@ -113,15 +150,12 @@ void Thread::InitFunctionPointers() { pThrowException = ThrowException; pInitializeTypeFromCode = InitializeTypeFromCode; pResolveMethodFromCode = ResolveMethodFromCode; + pDebugMe = DebugMe; #if 0 bool (Thread::*pUnlockObject)(Thread*, Object*); int (Thread::*pInstanceofNonTrivialFromCode)(const Class*, const Class*); -Method* (Thread::*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, DvmDex*); bool (Thread::*pUnlockObjectFromCode)(Thread*, Object*); void (Thread::*pLockObjectFromCode)(Thread*, Object*); -Object* (Thread::*pAllocObjectFromCode)(Class*, int); -void (Thread::*pThrowException)(Thread*, Object*); -bool (Thread::*pHandleFillArrayDataFromCode)(Array*, const uint16_t*); #endif } diff --git a/src/thread.h b/src/thread.h index 8a297d6b88..fc523a3d97 100644 --- a/src/thread.h +++ b/src/thread.h @@ -174,6 +174,7 @@ class Thread { static const size_t kDefaultStackSize = 64 * KB; // Runtime support function pointers + void (*pDebugMe)(Method*, uint32_t); void* (*pMemcpy)(void*, const void*, size_t); uint64_t (*pShlLong)(uint64_t, uint32_t); uint64_t (*pShrLong)(uint64_t, uint32_t); @@ -219,6 +220,7 @@ class Thread { void (*pHandleFillArrayDataFromCode)(Array*, const uint16_t*); Class* (*pInitializeTypeFromCode)(uint32_t, Method*); void (*pResolveMethodFromCode)(Method*, uint32_t); + void (*pInvokeInterfaceTrampoline)(void*, void*, void*, void*); class StackVisitor { public: diff --git a/test/IntMath/IntMath.java b/test/IntMath/IntMath.java index 05cecce0b2..ba7d575927 100644 --- a/test/IntMath/IntMath.java +++ b/test/IntMath/IntMath.java @@ -1,6 +1,6 @@ // Copyright 2011 Google Inc. All Rights Reserved. -class IntMath { +class IntMath extends IntMathBase { public static boolean mBoolean1, mBoolean2; public static byte mByte1, mByte2; @@ -16,13 +16,28 @@ class IntMath { private int foo_; public IntMath(int stuff) { + super(); foo_ = stuff; } public IntMath() { + super(); foo_ = 123; } + int tryThing() { + int val = super.tryThing(); + return val + 10; + } + + static int superTest(int x) { + IntMath instance = new IntMath(); + IntMath base = instance; + int val1 = instance.tryThing(); + int val2 = base.tryThing(); + return val1 + val2 + x; + } + static int constClassTest(int x) { Class c = String.class; if (c != null) { @@ -837,5 +852,21 @@ class IntMath { } else { System.out.printf("catchBlock FAILED: %d\n", res); } + + res = superTest(4141); + if (res == 4175) { + System.out.printf("superTest PASSED\n"); + } else { + System.out.printf("superTest FAILED: %d\n", res); + } + } +} + +class IntMathBase { + IntMathBase() { + } + + int tryThing() { + return 7; } } |