throw ArithmeticException on divide-by-zero

This commit is contained in:
Joel Dice 2010-12-19 17:47:21 -07:00
parent d18240cbd6
commit 306f1282d0
12 changed files with 386 additions and 74 deletions

View File

@ -1017,14 +1017,20 @@ class Context {
}
}
virtual intptr_t getThunk(TernaryOperation op, unsigned size, unsigned) {
virtual intptr_t getThunk(TernaryOperation op, unsigned size, unsigned,
bool* threadParameter)
{
*threadParameter = false;
if (size == 8) {
switch (op) {
case Divide:
*threadParameter = true;
return local::getThunk(t, divideLongThunk);
case Remainder:
return local::getThunk(t, moduloLongThunk);
*threadParameter = true;
return local::getThunk(t, moduloLongThunk);
case FloatAdd:
return local::getThunk(t, addDoubleThunk);
@ -1061,9 +1067,11 @@ class Context {
assert(t, size == 4);
switch (op) {
case Divide:
*threadParameter = true;
return local::getThunk(t, divideIntThunk);
case Remainder:
*threadParameter = true;
return local::getThunk(t, moduloIntThunk);
case FloatAdd:
@ -2358,26 +2366,47 @@ absoluteInt(int32_t a)
}
int64_t
divideLong(int64_t b, int64_t a)
divideLong(MyThread* t, int64_t b, int64_t a)
{
return a / b;
if (LIKELY(b)) {
return a / b;
} else {
t->exception = makeThrowable(t, Machine::ArithmeticExceptionType);
unwind(t);
}
}
int64_t
divideInt(int32_t b, int32_t a)
divideInt(MyThread* t, int32_t b, int32_t a)
{
return a / b;
if (LIKELY(b)) {
return a / b;
} else {
t->exception = makeThrowable(t, Machine::ArithmeticExceptionType);
unwind(t);
}
}
int64_t
moduloLong(int64_t b, int64_t a)
moduloLong(MyThread* t, int64_t b, int64_t a)
{
return a % b;
if (LIKELY(b)) {
return a % b;
} else {
t->exception = makeThrowable(t, Machine::ArithmeticExceptionType);
unwind(t);
}
}
int64_t
moduloInt(int32_t b, int32_t a) {
return a % b;
moduloInt(MyThread* t, int32_t b, int32_t a)
{
if (LIKELY(b)) {
return a % b;
} else {
t->exception = makeThrowable(t, Machine::ArithmeticExceptionType);
unwind(t);
}
}
uint64_t
@ -3949,6 +3978,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
case idiv: {
Compiler::Operand* a = frame->popInt();
Compiler::Operand* b = frame->popInt();
if (inTryBlock(t, code, ip - 1)) {
c->saveLocals();
frame->trace(0, 0);
}
frame->pushInt(c->div(4, a, b));
} break;
@ -4262,6 +4297,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
case irem: {
Compiler::Operand* a = frame->popInt();
Compiler::Operand* b = frame->popInt();
if (inTryBlock(t, code, ip - 1)) {
c->saveLocals();
frame->trace(0, 0);
}
frame->pushInt(c->rem(4, a, b));
} break;
@ -4459,6 +4500,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
case ldiv_: {
Compiler::Operand* a = frame->popLong();
Compiler::Operand* b = frame->popLong();
if (inTryBlock(t, code, ip - 1)) {
c->saveLocals();
frame->trace(0, 0);
}
frame->pushLong(c->div(8, a, b));
} break;
@ -4564,6 +4611,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
case lrem: {
Compiler::Operand* a = frame->popLong();
Compiler::Operand* b = frame->popLong();
if (inTryBlock(t, code, ip - 1)) {
c->saveLocals();
frame->trace(0, 0);
}
frame->pushLong(c->rem(8, a, b));
} break;
@ -7186,9 +7239,10 @@ invoke(Thread* thread, object method, ArgumentList* arguments)
return r;
}
class SegFaultHandler: public System::SignalHandler {
class SignalHandler: public System::SignalHandler {
public:
SegFaultHandler(): m(0) { }
SignalHandler(Machine::Type type, Machine::Root root, unsigned fixedSize):
m(0), type(type), root(root), fixedSize(fixedSize) { }
virtual bool handleSignal(void** ip, void** base, void** stack,
void** thread)
@ -7204,14 +7258,14 @@ class SegFaultHandler: public System::SignalHandler {
static_cast<void**>(*stack) - t->arch->frameReturnAddressSize(),
*base, t->continuation, t->trace);
if (ensure(t, FixedSizeOfNullPointerException + traceSize(t))) {
if (ensure(t, fixedSize + traceSize(t))) {
atomicOr(&(t->flags), Thread::TracingFlag);
t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
t->exception = makeThrowable(t, type);
atomicAnd(&(t->flags), ~Thread::TracingFlag);
} else {
// not enough memory available for a new NPE and stack trace
// -- use a preallocated instance instead
t->exception = root(t, Machine::NullPointerException);
// not enough memory available for a new exception and stack
// trace -- use a preallocated instance instead
t->exception = vm::root(t, root);
}
// printTrace(t, t->exception);
@ -7235,6 +7289,9 @@ class SegFaultHandler: public System::SignalHandler {
}
Machine* m;
Machine::Type type;
Machine::Root root;
unsigned fixedSize;
};
bool
@ -7289,6 +7346,12 @@ class MyProcessor: public Processor {
allocator(allocator),
roots(0),
bootImage(0),
segFaultHandler(Machine::NullPointerExceptionType,
Machine::NullPointerException,
FixedSizeOfNullPointerException),
divideByZeroHandler(Machine::ArithmeticExceptionType,
Machine::ArithmeticException,
FixedSizeOfArithmeticException),
codeAllocator(s, 0, 0),
callTableSize(0),
useNativeFeatures(useNativeFeatures)
@ -7744,6 +7807,10 @@ class MyProcessor: public Processor {
segFaultHandler.m = t->m;
expect(t, t->m->system->success
(t->m->system->handleSegFault(&segFaultHandler)));
divideByZeroHandler.m = t->m;
expect(t, t->m->system->success
(t->m->system->handleDivideByZero(&divideByZeroHandler)));
}
virtual void callWithCurrentContinuation(Thread* t, object receiver) {
@ -7798,7 +7865,8 @@ class MyProcessor: public Processor {
Allocator* allocator;
object roots;
BootImage* bootImage;
SegFaultHandler segFaultHandler;
SignalHandler segFaultHandler;
SignalHandler divideByZeroHandler;
FixedAllocator codeAllocator;
ThunkCollection thunks;
ThunkCollection bootThunks;

View File

@ -4336,6 +4336,19 @@ loadLocal(Context* c, unsigned footprint, unsigned index)
return c->locals[index].value;
}
Value*
register_(Context* c, int number)
{
assert(c, (1 << number) & (c->arch->generalRegisterMask()
| c->arch->floatRegisterMask()));
Site* s = registerSite(c, number);
ValueType type = ((1 << number) & c->arch->floatRegisterMask())
? ValueFloat: ValueGeneral;
return value(c, type, s, s);
}
void
appendCombine(Context* c, TernaryOperation type,
unsigned firstSize, Value* first,
@ -4349,25 +4362,34 @@ appendCombine(Context* c, TernaryOperation type,
uint64_t secondRegisterMask;
c->arch->planSource(type, firstSize, &firstTypeMask, &firstRegisterMask,
secondSize, &secondTypeMask, &secondRegisterMask,
resultSize, &thunk);
secondSize, &secondTypeMask, &secondRegisterMask,
resultSize, &thunk);
if (thunk) {
Stack* oldStack = c->stack;
bool threadParameter;
intptr_t handler = c->client->getThunk
(type, firstSize, resultSize, &threadParameter);
unsigned stackSize = ceiling(secondSize, BytesPerWord)
+ ceiling(firstSize, BytesPerWord);
local::push(c, ceiling(secondSize, BytesPerWord), second);
local::push(c, ceiling(firstSize, BytesPerWord), first);
if (threadParameter) {
++ stackSize;
local::push(c, 1, register_(c, c->arch->thread()));
}
Stack* argumentStack = c->stack;
c->stack = oldStack;
appendCall
(c, value
(c, ValueGeneral, constantSite
(c, c->client->getThunk(type, firstSize, resultSize))),
0, 0, result, resultSize, argumentStack,
ceiling(secondSize, BytesPerWord) + ceiling(firstSize, BytesPerWord),
0);
(c, value(c, ValueGeneral, constantSite(c, handler)), 0, 0, result,
resultSize, argumentStack, stackSize, 0);
} else {
append
(c, new (c->zone->allocate(sizeof(CombineEvent)))
@ -4792,6 +4814,12 @@ appendBranch(Context* c, TernaryOperation type, unsigned size, Value* first,
if (thunk) {
Stack* oldStack = c->stack;
bool threadParameter;
intptr_t handler = c->client->getThunk
(type, size, size, &threadParameter);
assert(c, not threadParameter);
local::push(c, ceiling(size, BytesPerWord), second);
local::push(c, ceiling(size, BytesPerWord), first);
@ -4801,9 +4829,8 @@ appendBranch(Context* c, TernaryOperation type, unsigned size, Value* first,
Value* result = value(c, ValueGeneral);
appendCall
(c, value
(c, ValueGeneral, constantSite(c, c->client->getThunk(type, size, 4))),
0, 0, result, 4, argumentStack,
ceiling(size, BytesPerWord) * 2, 0);
(c, ValueGeneral, constantSite(c, handler)), 0, 0, result, 4,
argumentStack, ceiling(size, BytesPerWord) * 2, 0);
appendBranch(c, thunkBranch(c, type), 4, value
(c, ValueGeneral, constantSite(c, static_cast<int64_t>(0))),
@ -6167,14 +6194,7 @@ class MyCompiler: public Compiler {
}
virtual Operand* register_(int number) {
assert(&c, (1 << number) & (c.arch->generalRegisterMask()
| c.arch->floatRegisterMask()));
Site* s = registerSite(&c, number);
ValueType type = ((1 << number) & c.arch->floatRegisterMask())
? ValueFloat: ValueGeneral;
return value(&c, type, s, s);
return local::register_(&c, number);
}
Promise* machineIp() {

View File

@ -30,7 +30,7 @@ class Compiler {
virtual intptr_t getThunk(BinaryOperation op, unsigned size,
unsigned resultSize) = 0;
virtual intptr_t getThunk(TernaryOperation op, unsigned size,
unsigned resultSize) = 0;
unsigned resultSize, bool* threadParameter) = 0;
};
static const unsigned Aligned = 1 << 0;

View File

@ -1665,6 +1665,11 @@ interpret(Thread* t)
case idiv: {
int32_t b = popInt(t);
int32_t a = popInt(t);
if (UNLIKELY(b == 0)) {
exception = makeThrowable(t, Machine::ArithmeticExceptionType);
goto throw_;
}
pushInt(t, a / b);
} goto loop;
@ -1968,6 +1973,11 @@ interpret(Thread* t)
int32_t b = popInt(t);
int32_t a = popInt(t);
if (UNLIKELY(b == 0)) {
exception = makeThrowable(t, Machine::ArithmeticExceptionType);
goto throw_;
}
pushInt(t, a % b);
} goto loop;
@ -2187,6 +2197,11 @@ interpret(Thread* t)
int64_t b = popLong(t);
int64_t a = popLong(t);
if (UNLIKELY(b == 0)) {
exception = makeThrowable(t, Machine::ArithmeticExceptionType);
goto throw_;
}
pushLong(t, a / b);
} goto loop;
@ -2269,6 +2284,11 @@ interpret(Thread* t)
int64_t b = popLong(t);
int64_t a = popLong(t);
if (UNLIKELY(b == 0)) {
exception = makeThrowable(t, Machine::ArithmeticExceptionType);
goto throw_;
}
pushLong(t, a % b);
} goto loop;

View File

@ -2035,8 +2035,13 @@ boot(Thread* t)
(t, Machine::NullPointerExceptionType));
if (t->exception == 0) {
setRoot(t, Machine::ArrayIndexOutOfBoundsException,
makeThrowable(t, Machine::ArrayIndexOutOfBoundsExceptionType));
setRoot(t, Machine::ArithmeticException,
makeThrowable(t, Machine::ArithmeticExceptionType));
if (t->exception == 0) {
setRoot(t, Machine::ArrayIndexOutOfBoundsException,
makeThrowable(t, Machine::ArrayIndexOutOfBoundsExceptionType));
}
}
}

View File

@ -1203,6 +1203,7 @@ class Machine {
ShutdownHooks,
ObjectsToFinalize,
NullPointerException,
ArithmeticException,
ArrayIndexOutOfBoundsException,
VirtualFileFinders,
VirtualFiles

View File

@ -69,14 +69,17 @@ const int AltSegFaultSignal = InvalidSignal;
const unsigned AltSegFaultSignalIndex = 3;
const int PipeSignal = SIGPIPE;
const unsigned PipeSignalIndex = 4;
const int DivideByZeroSignal = SIGFPE;
const unsigned DivideByZeroSignalIndex = 5;
const int signals[] = { VisitSignal,
SegFaultSignal,
InterruptSignal,
AltSegFaultSignal,
PipeSignal };
PipeSignal,
DivideByZeroSignal };
const unsigned SignalCount = 5;
const unsigned SignalCount = 6;
class MySystem;
MySystem* system;
@ -530,6 +533,8 @@ class MySystem: public System {
expect(this, system == 0);
system = this;
memset(handlers, 0, sizeof(handlers));
registerHandler(&nullHandler, InterruptSignalIndex);
registerHandler(&nullHandler, PipeSignalIndex);
registerHandler(&nullHandler, VisitSignalIndex);
@ -631,6 +636,10 @@ class MySystem: public System {
return s;
}
virtual Status handleDivideByZero(SignalHandler* handler) {
return registerHandler(handler, DivideByZeroSignalIndex);
}
virtual Status visit(System::Thread* st, System::Thread* sTarget,
ThreadVisitor* visitor)
{
@ -847,12 +856,25 @@ handleSignal(int signal, siginfo_t* info, void* context)
} break;
case SegFaultSignal:
case AltSegFaultSignal: {
if (signal == SegFaultSignal) {
case AltSegFaultSignal:
case DivideByZeroSignal: {
switch (signal) {
case SegFaultSignal:
index = SegFaultSignalIndex;
} else {
break;
case AltSegFaultSignal:
index = AltSegFaultSignalIndex;
break;
case DivideByZeroSignal:
index = DivideByZeroSignalIndex;
break;
default:
abort();
}
bool jump = system->handlers[index]->handleSignal
(&ip, &base, &stack, &thread);

View File

@ -126,6 +126,7 @@ class System {
virtual Status make(Monitor**) = 0;
virtual Status make(Local**) = 0;
virtual Status handleSegFault(SignalHandler* handler) = 0;
virtual Status handleDivideByZero(SignalHandler* handler) = 0;
virtual Status visit(Thread* thread, Thread* target,
ThreadVisitor* visitor) = 0;
virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types,

View File

@ -205,6 +205,8 @@
(type nullPointerException java/lang/NullPointerException)
(type arithmeticException java/lang/ArithmeticException)
(type illegalStateException java/lang/IllegalStateException)
(type illegalArgumentException java/lang/IllegalArgumentException)

View File

@ -49,6 +49,11 @@ class MutexResource {
HANDLE m;
};
const unsigned SegFaultIndex = 0;
const unsigned DivideByZeroIndex = 1;
const unsigned HandlerCount = 2;
class MySystem;
MySystem* system;
@ -510,17 +515,57 @@ class MySystem: public System {
};
MySystem(const char* crashDumpDirectory):
segFaultHandler(0),
oldSegFaultHandler(0),
oldHandler(0),
crashDumpDirectory(crashDumpDirectory)
{
expect(this, system == 0);
system = this;
memset(handlers, 0, sizeof(handlers));
mutex = CreateMutex(0, false, 0);
assert(this, mutex);
}
bool findHandler() {
for (unsigned i = 0; i < HandlerCount; ++i) {
if (handlers[i]) return true;
}
return false;
}
int registerHandler(System::SignalHandler* handler, int index) {
if (handler) {
handlers[index] = handler;
if (oldHandler == 0) {
#ifdef ARCH_x86_32
oldHandler = SetUnhandledExceptionFilter(handleException);
#elif defined ARCH_x86_64
AddVectoredExceptionHandler(1, handleException);
oldHandler = reinterpret_cast<LPTOP_LEVEL_EXCEPTION_FILTER>(1);
#endif
}
return 0;
} else if (handlers[index]) {
handlers[index] = 0;
if (not findHandler()) {
#ifdef ARCH_x86_32
SetUnhandledExceptionFilter(oldHandler);
oldHandler = 0;
#elif defined ARCH_x86_64
// do nothing, handlers are never "unregistered" anyway
#endif
}
return 0;
} else {
return 1;
}
}
virtual void* tryAllocate(unsigned sizeInBytes) {
return malloc(sizeInBytes);
}
@ -578,27 +623,11 @@ class MySystem: public System {
}
virtual Status handleSegFault(SignalHandler* handler) {
if (handler) {
segFaultHandler = handler;
return registerHandler(handler, SegFaultIndex);
}
#ifdef ARCH_x86_32
oldSegFaultHandler = SetUnhandledExceptionFilter(handleException);
#elif defined ARCH_x86_64
AddVectoredExceptionHandler(1, handleException);
oldSegFaultHandler = 0;
#endif
return 0;
} else if (segFaultHandler) {
segFaultHandler = 0;
#ifdef ARCH_x86_32
SetUnhandledExceptionFilter(oldSegFaultHandler);
#elif defined ARCH_x86_64
//do nothing, handlers are never "unregistered" anyway
#endif
return 0;
} else {
return 1;
}
virtual Status handleDivideByZero(SignalHandler* handler) {
return registerHandler(handler, DivideByZeroIndex);
}
virtual Status visit(System::Thread* st UNUSED, System::Thread* sTarget,
@ -795,8 +824,8 @@ class MySystem: public System {
}
HANDLE mutex;
System::SignalHandler* segFaultHandler;
LPTOP_LEVEL_EXCEPTION_FILTER oldSegFaultHandler;
SignalHandler* handlers[HandlerCount];
LPTOP_LEVEL_EXCEPTION_FILTER oldHandler;
const char* crashDumpDirectory;
};
@ -867,7 +896,15 @@ dump(LPEXCEPTION_POINTERS e, const char* directory)
LONG CALLBACK
handleException(LPEXCEPTION_POINTERS e)
{
System::SignalHandler* handler = 0;
if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
handler = system->handlers[SegFaultIndex];
} else if (e->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
handler = system->handlers[DivideByZeroIndex];
}
if (handler) {
#ifdef ARCH_x86_32
void* ip = reinterpret_cast<void*>(e->ContextRecord->Eip);
void* base = reinterpret_cast<void*>(e->ContextRecord->Ebp);
@ -880,8 +917,8 @@ handleException(LPEXCEPTION_POINTERS e)
void* thread = reinterpret_cast<void*>(e->ContextRecord->Rbx);
#endif
bool jump = system->segFaultHandler->handleSignal
(&ip, &base, &stack, &thread);
bool jump = handler->handleSignal(&ip, &base, &stack, &thread);
#ifdef ARCH_x86_32
e->ContextRecord->Eip = reinterpret_cast<DWORD>(ip);
e->ContextRecord->Ebp = reinterpret_cast<DWORD>(base);
@ -897,10 +934,10 @@ handleException(LPEXCEPTION_POINTERS e)
if (jump) {
return EXCEPTION_CONTINUE_EXECUTION;
}
}
if (system->crashDumpDirectory) {
dump(e, system->crashDumpDirectory);
}
if (system->crashDumpDirectory) {
dump(e, system->crashDumpDirectory);
}
return EXCEPTION_CONTINUE_SEARCH;

135
test/DivideByZero.java Normal file
View File

@ -0,0 +1,135 @@
public class DivideByZero {
private static int divide(int n, int d) {
return n / d;
}
private static int modulo(int n, int d) {
return n % d;
}
private static long divide(long n, long d) {
return n / d;
}
private static long modulo(long n, long d) {
return n % d;
}
private static float divide(float n, float d) {
return n / d;
}
private static float modulo(float n, float d) {
return n % d;
}
private static double divide(double n, double d) {
return n / d;
}
private static double modulo(double n, double d) {
return n % d;
}
public static void main(String[] args) {
try {
int x = 1 / 0;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
int x = 1 % 0;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
int y = 2;
int x = y / 0;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
int y = 2;
int x = y % 0;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
int z = 0;
int y = 2;
int x = y / z;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
int z = 0;
int y = 2;
int x = y % z;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
long z = 0;
long y = 2;
long x = y / z;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
long z = 0;
long y = 2;
long x = y % z;
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
divide(5, 0);
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
modulo(6, 0);
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
divide(5L, 0L);
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
try {
modulo(6L, 0L);
throw new RuntimeException();
} catch (ArithmeticException e) {
e.printStackTrace();
}
divide(5F, 0F);
modulo(6F, 0F);
divide(5D, 0D);
modulo(6D, 0D);
}
}

1
vm.pro
View File

@ -57,6 +57,7 @@
-keep public class java.lang.ClassCastException
-keep public class java.lang.ClassNotFoundException
-keep public class java.lang.NullPointerException
-keep public class java.lang.ArithmeticException
-keep public class java.lang.InterruptedException
-keep public class java.lang.StackOverflowError
-keep public class java.lang.NoSuchFieldError