Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions jre_emul/Classes/JavaThrowable.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,34 +84,41 @@ static bool ShouldFilterStackElement(JavaLangStackTraceElement *element) {
return false;
}

static void ProcessRawStack(RawStack *rawStack, NSMutableArray *frames, bool applyFilter) {
for (unsigned i = 0; i < rawStack->count_; i++) {
JavaLangStackTraceElement *element =
[[JavaLangStackTraceElement alloc] initWithLong:(jlong)rawStack->frames_[i]];
if (!applyFilter || !ShouldFilterStackElement(element)) {
[frames addObject:element];
}
[element release];
}
}

jarray Java_java_lang_Throwable_nativeGetStackTrace(
JNIEnv *_env_, jclass _cls_, jobject stackState) {
RawStack *rawStack = stackState;
NSMutableArray *frames = [NSMutableArray array];
if (rawStack) {
ProcessRawStack(rawStack, frames, true);
JavaLangStackTraceElement *element = [frames lastObject];
if (rawStack && rawStack->count_ > 0) {
char **stackSymbols = backtrace_symbols(rawStack->frames_, rawStack->count_);
NSStringEncoding encoding = [NSString defaultCStringEncoding];

for (unsigned i = 0; i < rawStack->count_; i++) {
NSString *symbolStr = [NSString stringWithCString:stackSymbols[i] encoding:encoding];
JavaLangStackTraceElement *element =
JavaLangStackTraceElement_createFromSymbolWithLong_withNSString_((jlong)rawStack->frames_[i], symbolStr);

if (!ShouldFilterStackElement(element)) {
[frames addObject:element];
}
}

// Remove initial Method.invoke(), so app's main method is last.
JavaLangStackTraceElement *element = [frames lastObject];
if ([[element getClassName] isEqualToString:@"JavaLangReflectMethod"] &&
[[element getMethodName] isEqualToString:@"invoke"]) {
[frames removeLastObject];
}
// If symbols were removed, the stack trace will be empty at this point.
// In order to help with debugging, return the raw stack trace.
if ([frames count] == 0) {
ProcessRawStack(rawStack, frames, false);
for (unsigned i = 0; i < rawStack->count_; i++) {
NSString *symbolStr = [NSString stringWithCString:stackSymbols[i] encoding:encoding];
JavaLangStackTraceElement *element =
JavaLangStackTraceElement_createFromSymbolWithLong_withNSString_((jlong)rawStack->frames_[i], symbolStr);
[frames addObject:element];
}
}
free(stackSymbols);
}
return [IOSObjectArray arrayWithNSArray:frames type:JavaLangStackTraceElement_class_()];
}
Expand Down
41 changes: 26 additions & 15 deletions jre_emul/Classes/java/lang/StackTraceElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ public StackTraceElement(String className, String methodName, String fileName, i
this.address = address;
}

public static native StackTraceElement createFromSymbol(long address, String symbol) /*-[
JavaLangStackTraceElement *element = [[JavaLangStackTraceElement alloc] initWithLong:address];
NSStringEncoding encoding = [NSString defaultCStringEncoding];
PopulateElementFromSymbol(element, [symbol UTF8String], encoding);
return AUTORELEASE(element);
]-*/;

@Override
public String toString() {
initializeFromAddress();
Expand Down Expand Up @@ -239,21 +246,10 @@ static void DemangleSwiftMethod(
}
]-*/

/**
* Implements lazy loading of symbol information from application.
*/
private native void initializeFromAddress() /*-[
if (self->address_ == 0L // Is there an address to initialze from?
|| self->hexAddress_) { // Already initialized?
return;
}
void *shortStack[1];
shortStack[0] = (void *)self->address_;
char **stackSymbol = backtrace_symbols(shortStack, 1);
NSStringEncoding encoding = [NSString defaultCStringEncoding];

// Extract hexAddress.
char *start = strstr(*stackSymbol, "0x"); // Skip text before address.
/*-[
static void PopulateElementFromSymbol(JavaLangStackTraceElement *self, const char *stackSymbol, NSStringEncoding encoding) {
char *symbolCopy = strdup(stackSymbol);
char *start = strstr(symbolCopy, "0x");
char *addressEnd = strstr(start, " ");
char *hex = strndup(start, addressEnd - start);
self->hexAddress_ = [[NSString alloc] initWithCString:hex encoding:encoding];
Expand Down Expand Up @@ -325,6 +321,21 @@ private native void initializeFromAddress() /*-[
if ([self->methodName_ isEqual:JavaLangStackTraceElement_UNKNOWN]) {
self->methodName_ = JavaLangStackTraceElement_STRIPPED;
}
free(symbolCopy);
}
]-*/

/** Implements lazy loading of symbol information from application. */
private native void initializeFromAddress() /*-[
if (self->address_ == 0L // Is there an address to initialze from?
|| self->hexAddress_) { // Already initialized?
return;
}
void *shortStack[1];
shortStack[0] = (void *)self->address_;
char **stackSymbol = backtrace_symbols(shortStack, 1);
NSStringEncoding encoding = [NSString defaultCStringEncoding];
PopulateElementFromSymbol(self, *stackSymbol, encoding);
free(stackSymbol);
]-*/;
}
24 changes: 24 additions & 0 deletions jre_emul/misc_tests/com/google/j2objc/ThrowableTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.j2objc.util.ReflectionUtil;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Method;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
Expand Down Expand Up @@ -123,6 +124,29 @@ public void testThrowableToStringFormat() {
assertTrue(ReflectionUtil.matchClassNamePrefix(new Throwable("oops").toString(), expected));
}

public void testSymbolParsing() {
// A typical Objective-C symbol string generated by J2ObjC
String symbol =
"0 MyApp 0x0000000100000f14 [ComGoogleJ2objcThrowableTest"
+ " testSymbolParsing] + 0";
long address = 0x100000f14L;

// Create a StackTraceElement from the symbol.
StackTraceElement element = StackTraceElement.createFromSymbol(address, symbol);
assertNotNull(element);
assertEquals("com.google.j2objc.ThrowableTest", element.getClassName());
assertEquals("testSymbolParsing", element.getMethodName());

// Create an element via the public constructor and verify equivalence.
StackTraceElement expectedElement =
new StackTraceElement("com.google.j2objc.ThrowableTest", "testSymbolParsing", null, -1);

assertEquals(expectedElement, element);
assertEquals(expectedElement.toString(), element.toString());
assertEquals(expectedElement.getClassName(), element.getClassName());
assertEquals(expectedElement.getMethodName(), element.getMethodName());
}

public void testNSExceptionDescriptionUnchanged() {
assertEquals(NSEXCEPTION_MESSAGE, getNSExceptionDescription());
}
Expand Down