diff --git a/vm/ByteCodeTranslator/src/cn1_globals.h b/vm/ByteCodeTranslator/src/cn1_globals.h index ccfa660fe6..bef4e4a91e 100644 --- a/vm/ByteCodeTranslator/src/cn1_globals.h +++ b/vm/ByteCodeTranslator/src/cn1_globals.h @@ -1024,9 +1024,14 @@ extern void initMethodStack(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObje int methodBlockOffset = threadStateData->tryBlockOffset; -#ifdef __OBJC__ +#if defined(__APPLE__) && defined(__OBJC__) extern JAVA_OBJECT fromNSString(CODENAME_ONE_THREAD_STATE, NSString* str); extern NSString* toNSString(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT o); +#else +#define NSLog(...) printf(__VA_ARGS__); printf("\n") +typedef int BOOL; +#define YES 1 +#define NO 0 #endif extern JAVA_OBJECT __NEW_ARRAY_JAVA_BOOLEAN(CODENAME_ONE_THREAD_STATE, JAVA_INT size); diff --git a/vm/ByteCodeTranslator/src/cn1_globals.m b/vm/ByteCodeTranslator/src/cn1_globals.m index 8b2438d8b8..2c1010599f 100644 --- a/vm/ByteCodeTranslator/src/cn1_globals.m +++ b/vm/ByteCodeTranslator/src/cn1_globals.m @@ -15,8 +15,13 @@ #include "java_lang_Runnable.h" #include "java_lang_System.h" #include "java_lang_ArrayIndexOutOfBoundsException.h" +#if defined(__APPLE__) && defined(__OBJC__) #import #import +#else +#include +#define NSLog(...) printf(__VA_ARGS__); printf("\n") +#endif // The amount of memory allocated between GC cycle checks (generally 30 seconds) // that triggers "High-frequency" GC mode. When "High-frequency" mode is triggered, @@ -70,8 +75,9 @@ static JAVA_BOOLEAN isEdt(long threadId) { } // Gets the amount of free memory in the system. - static natural_t get_free_memory(void) + static long get_free_memory(void) { +#if defined(__APPLE__) && defined(__OBJC__) mach_port_t host_port; mach_msg_type_number_t host_size; vm_size_t pagesize; @@ -85,8 +91,11 @@ static natural_t get_free_memory(void) return 0; } /* Stats in bytes */ - natural_t mem_free = vm_stat.free_count * pagesize; + long mem_free = vm_stat.free_count * pagesize; return mem_free; +#else + return 1024 * 1024 * 100; // Stub: 100MB +#endif } // Initializes the GC thresholds based on the free memory on the device. @@ -674,7 +683,9 @@ void preSweepCount(CODENAME_ONE_THREAD_STATE) { } void printObjectsPostSweep(CODENAME_ONE_THREAD_STATE) { +#if defined(__APPLE__) && defined(__OBJC__) NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; +#endif // this should be the last class used int classTypeCount[cn1_array_3_id_java_util_Vector + 1]; @@ -706,10 +717,14 @@ void printObjectsPostSweep(CODENAME_ONE_THREAD_STATE) { if(classTypeCount[iter] > 0) { if(classTypeCountPreSweep[iter] - classTypeCount[iter] > 0) { if(iter > cn1_array_start_offset) { +#if defined(__APPLE__) && defined(__OBJC__) NSLog(@"There are %i instances of %@ taking up %i bytes, %i were cleaned which saved %i bytes", classTypeCount[iter], [NSString stringWithUTF8String:arrayOfNames[iter]], sizeInHeapForType[iter], classTypeCountPreSweep[iter] - classTypeCount[iter], sizeInHeapForTypePreSweep[iter] - sizeInHeapForType[iter]); +#endif } else { JAVA_OBJECT str = STRING_FROM_CONSTANT_POOL_OFFSET(classNameLookup[iter]); +#if defined(__APPLE__) && defined(__OBJC__) NSLog(@"There are %i instances of %@ taking up %i bytes, %i were cleaned which saved %i bytes", classTypeCount[iter], toNSString(threadStateData, str), sizeInHeapForType[iter], classTypeCountPreSweep[iter] - classTypeCount[iter], sizeInHeapForTypePreSweep[iter] - sizeInHeapForType[iter]); +#endif } } actualTotalMemory += sizeInHeapForType[iter]; @@ -719,11 +734,15 @@ void printObjectsPostSweep(CODENAME_ONE_THREAD_STATE) { NSLog(@"**** GC cycle complete ****"); free(arrayOfNames); +#if defined(__APPLE__) && defined(__OBJC__) [pool release]; +#endif } void printObjectTypesInHeap(CODENAME_ONE_THREAD_STATE) { +#if defined(__APPLE__) && defined(__OBJC__) NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; +#endif // this should be the last class used int classTypeCount[cn1_array_3_id_java_util_Vector + 1]; @@ -756,10 +775,14 @@ void printObjectTypesInHeap(CODENAME_ONE_THREAD_STATE) { float f = ((float)classTypeCount[iter]) / ((float)t) * 100.0f; float f2 = ((float)sizeInHeapForType[iter]) / ((float)totalAllocatedHeap) * 100.0f; if(iter > cn1_array_start_offset) { +#if defined(__APPLE__) && defined(__OBJC__) NSLog(@"There are %i instances of %@ which is %i percent its %i bytes which is %i mem percent", classTypeCount[iter], [NSString stringWithUTF8String:arrayOfNames[iter]], (int)f, sizeInHeapForType[iter], (int)f2); +#endif } else { JAVA_OBJECT str = STRING_FROM_CONSTANT_POOL_OFFSET(classNameLookup[iter]); +#if defined(__APPLE__) && defined(__OBJC__) NSLog(@"There are %i instances of %@ which is %i percent its %i bytes which is %i mem percent", classTypeCount[iter], toNSString(threadStateData, str), (int)f, sizeInHeapForType[iter], (int)f2); +#endif } actualTotalMemory += sizeInHeapForType[iter]; } @@ -767,7 +790,9 @@ void printObjectTypesInHeap(CODENAME_ONE_THREAD_STATE) { NSLog(@"Actual ram = %i vs total mallocs = %i", actualTotalMemory, totalAllocatedHeap); free(arrayOfNames); +#if defined(__APPLE__) && defined(__OBJC__) [pool release]; +#endif } #endif @@ -796,6 +821,7 @@ void codenameOneGCSweep() { #ifdef DEBUG_GC_ALLOCATIONS int classId = o->className; +#if defined(__APPLE__) && defined(__OBJC__) NSString* whereIs; if(classId > 0) { whereIs = (NSString*)((struct obj__java_lang_String*)STRING_FROM_CONSTANT_POOL_OFFSET(classId))->java_lang_String_nsString; @@ -824,6 +850,7 @@ void codenameOneGCSweep() { } NSLog(@"Sweeping: %X, Mark: %i, Allocated: %@ %i , type: %@, toString: '%@'", (int)o, o->__codenameOneGcMark, whereIs, o->line, [NSString stringWithUTF8String:o->__codenameOneParentClsReference->clsName], ns); } +#endif #endif removeObjectFromHeapCollection(threadStateData, o); @@ -1249,6 +1276,7 @@ void initConstantPool() { JAVA_OBJECT utf8String = NULL; +#if defined(__APPLE__) && defined(__OBJC__) JAVA_OBJECT fromNSString(CODENAME_ONE_THREAD_STATE, NSString* str) { if (str == nil) { return JAVA_NULL; @@ -1274,6 +1302,7 @@ JAVA_OBJECT fromNSString(CODENAME_ONE_THREAD_STATE, NSString* str) { finishedNativeAllocations(); return s; } +#endif const char* stringToUTF8(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT str) { if(str == NULL) { @@ -1308,6 +1337,7 @@ JAVA_OBJECT fromNSString(CODENAME_ONE_THREAD_STATE, NSString* str) { return cs; } +#if defined(__APPLE__) && defined(__OBJC__) NSString* toNSString(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT o) { if(o == JAVA_NULL) { return 0; @@ -1323,6 +1353,7 @@ JAVA_OBJECT fromNSString(CODENAME_ONE_THREAD_STATE, NSString* str) { str->java_lang_String_nsString = (JAVA_LONG)x; return st; } +#endif JAVA_OBJECT __NEW_ARRAY_JAVA_BOOLEAN(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { JAVA_OBJECT o = allocArray(threadStateData, size, &class_array1__JAVA_BOOLEAN, sizeof(JAVA_ARRAY_BOOLEAN), 1); diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java index 425f6c7a90..5790a9d5da 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java @@ -244,6 +244,8 @@ private static void handleCleanOutput(ByteCodeTranslator b, File[] sources, File copy(ByteCodeTranslator.class.getResourceAsStream("/xmlvm.h"), new FileOutputStream(xmlvm)); File nativeMethods = new File(srcRoot, "nativeMethods.m"); copy(ByteCodeTranslator.class.getResourceAsStream("/nativeMethods.m"), new FileOutputStream(nativeMethods)); + File javaIoFileM = new File(srcRoot, "java_io_File.m"); + copy(ByteCodeTranslator.class.getResourceAsStream("/java_io_File.m"), new FileOutputStream(javaIoFileM)); Parser.writeOutput(srcRoot); @@ -299,6 +301,8 @@ private static void handleIosOutput(ByteCodeTranslator b, File[] sources, File d copy(ByteCodeTranslator.class.getResourceAsStream("/cn1_globals.m"), new FileOutputStream(cn1GlobalsM)); File nativeMethods = new File(srcRoot, "nativeMethods.m"); copy(ByteCodeTranslator.class.getResourceAsStream("/nativeMethods.m"), new FileOutputStream(nativeMethods)); + File javaIoFileM = new File(srcRoot, "java_io_File.m"); + copy(ByteCodeTranslator.class.getResourceAsStream("/java_io_File.m"), new FileOutputStream(javaIoFileM)); if (System.getProperty("USE_RPMALLOC", "false").equals("true")) { File malloc = new File(srcRoot, "malloc.c"); diff --git a/vm/ByteCodeTranslator/src/java_io_File.m b/vm/ByteCodeTranslator/src/java_io_File.m new file mode 100644 index 0000000000..67bf757f39 --- /dev/null +++ b/vm/ByteCodeTranslator/src/java_io_File.m @@ -0,0 +1,487 @@ +#include "cn1_globals.h" +#include "java_io_File.h" +#include "java_lang_String.h" + +#if defined(__APPLE__) && defined(__OBJC__) +#import + +JAVA_BOOLEAN java_io_File_existsImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] fileExistsAtPath:p]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_isDirectoryImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL isDir = NO; + BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:p isDirectory:&isDir]; + [pool release]; + return exists && isDir; +} + +JAVA_BOOLEAN java_io_File_isFileImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL isDir = NO; + BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:p isDirectory:&isDir]; + [pool release]; + return exists && !isDir; +} + +JAVA_BOOLEAN java_io_File_isHiddenImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL hidden = [[p lastPathComponent] hasPrefix:@"."]; + [pool release]; + return hidden; +} + +JAVA_LONG java_io_File_lastModifiedImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return 0; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:p error:NULL]; + JAVA_LONG time = 0; + if (attrs) { + NSDate *date = [attrs fileModificationDate]; + time = (JAVA_LONG)([date timeIntervalSince1970] * 1000); + } + [pool release]; + return time; +} + +JAVA_LONG java_io_File_lengthImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return 0; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:p error:NULL]; + JAVA_LONG len = 0; + if (attrs) { + len = [attrs fileSize]; + } + [pool release]; + return len; +} + +JAVA_BOOLEAN java_io_File_createNewFileImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] createFileAtPath:p contents:nil attributes:nil]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_deleteImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] removeItemAtPath:p error:NULL]; + [pool release]; + return res; +} + +JAVA_OBJECT java_io_File_listImpl___java_lang_String_R_java_lang_String_1ARRAY(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_NULL; + enteringNativeAllocations(); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:p error:NULL]; + if (files == nil) { + [pool release]; + finishedNativeAllocations(); + return JAVA_NULL; + } + + JAVA_OBJECT arr = allocArray(threadStateData, [files count], &class__java_lang_String, sizeof(JAVA_OBJECT), 1); + + for (int i=0; i<[files count]; i++) { + NSString* f = [files objectAtIndex:i]; + JAVA_OBJECT s = fromNSString(CN1_THREAD_STATE_PASS_ARG f); + CN1_SET_ARRAY_ELEMENT_OBJECT(arr, i, s); + } + + [pool release]; + finishedNativeAllocations(); + return arr; +} + +JAVA_BOOLEAN java_io_File_mkdirImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] createDirectoryAtPath:p withIntermediateDirectories:NO attributes:nil error:NULL]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_renameToImpl___java_lang_String_java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_OBJECT dest) { + if(path == JAVA_NULL || dest == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSString* d = toNSString(CN1_THREAD_STATE_PASS_ARG dest); + BOOL res = [[NSFileManager defaultManager] moveItemAtPath:p toPath:d error:NULL]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_setReadOnlyImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSDictionary* attrs = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:NSFileImmutable]; + BOOL res = [[NSFileManager defaultManager] setAttributes:attrs ofItemAtPath:p error:NULL]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_setWritableImpl___java_lang_String_boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_BOOLEAN writable) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSDictionary* attrs = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:(!writable)] forKey:NSFileImmutable]; + BOOL res = [[NSFileManager defaultManager] setAttributes:attrs ofItemAtPath:p error:NULL]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_setReadableImpl___java_lang_String_boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_BOOLEAN readable) { + // Basic implementation for iOS/sandbox (mostly ignored/always true if exists) + return java_io_File_existsImpl___java_lang_String_R_boolean(threadStateData, __cn1ThisObject, path); +} + +JAVA_BOOLEAN java_io_File_setExecutableImpl___java_lang_String_boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_BOOLEAN executable) { + return java_io_File_existsImpl___java_lang_String_R_boolean(threadStateData, __cn1ThisObject, path); +} + +JAVA_BOOLEAN java_io_File_canReadImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] isReadableFileAtPath:p]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_canWriteImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] isWritableFileAtPath:p]; + [pool release]; + return res; +} + +JAVA_BOOLEAN java_io_File_canExecuteImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; +#ifdef CN1_ENABLE_FILE_SYSTEM_STATS + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + BOOL res = [[NSFileManager defaultManager] isExecutableFileAtPath:p]; + [pool release]; + return res; +#else + return java_io_File_existsImpl___java_lang_String_R_boolean(threadStateData, __cn1ThisObject, path); +#endif +} + +JAVA_LONG java_io_File_getTotalSpaceImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { +#ifdef CN1_ENABLE_FILE_SYSTEM_STATS + if(path == JAVA_NULL) return 0; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:p error:NULL]; + JAVA_LONG size = 0; + if(attrs) { + size = [[attrs objectForKey:NSFileSystemSize] longLongValue]; + } + [pool release]; + return size; +#else + return 0; +#endif +} + +JAVA_LONG java_io_File_getFreeSpaceImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { +#ifdef CN1_ENABLE_FILE_SYSTEM_STATS + if(path == JAVA_NULL) return 0; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:p error:NULL]; + JAVA_LONG size = 0; + if(attrs) { + size = [[attrs objectForKey:NSFileSystemFreeSize] longLongValue]; + } + [pool release]; + return size; +#else + return 0; +#endif +} + +JAVA_LONG java_io_File_getUsableSpaceImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + return java_io_File_getFreeSpaceImpl___java_lang_String_R_long(threadStateData, __cn1ThisObject, path); +} + +JAVA_OBJECT java_io_File_getAbsolutePathImpl___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_NULL; + enteringNativeAllocations(); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + + NSString* absPath; + if ([p isAbsolutePath]) { + absPath = p; + } else { + NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; + absPath = [cwd stringByAppendingPathComponent:p]; + } + JAVA_OBJECT res = fromNSString(CN1_THREAD_STATE_PASS_ARG absPath); + [pool release]; + finishedNativeAllocations(); + return res; +} + +JAVA_OBJECT java_io_File_getCanonicalPathImpl___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_NULL; + enteringNativeAllocations(); + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* p = toNSString(CN1_THREAD_STATE_PASS_ARG path); + NSString* absPath; + if ([p isAbsolutePath]) { + absPath = p; + } else { + NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; + absPath = [cwd stringByAppendingPathComponent:p]; + } + NSString* canon = [absPath stringByStandardizingPath]; + JAVA_OBJECT res = fromNSString(CN1_THREAD_STATE_PASS_ARG canon); + [pool release]; + finishedNativeAllocations(); + return res; +} + +#else +// POSIX implementation for non-ObjC environments (e.g. Linux CI) +#include +#include +#include +#include +#include +#include + +// Helper: assumes stringToUTF8 is available (implemented in test stubs or runtime) +extern const char* stringToUTF8(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT str); +extern JAVA_OBJECT newStringFromCString(CODENAME_ONE_THREAD_STATE, const char *str); + +JAVA_BOOLEAN java_io_File_existsImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + return access(p, F_OK) != -1; +} + +JAVA_BOOLEAN java_io_File_isDirectoryImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + struct stat s; + if (stat(p, &s) == 0) { + return S_ISDIR(s.st_mode); + } + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_isFileImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + struct stat s; + if (stat(p, &s) == 0) { + return S_ISREG(s.st_mode); + } + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_isHiddenImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + // This is a naive check, checking if filename starts with dot + // We need to find the last slash + const char* lastSlash = strrchr(p, '/'); + const char* name = lastSlash ? lastSlash + 1 : p; + return name[0] == '.'; +} + +JAVA_LONG java_io_File_lastModifiedImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return 0; + const char* p = stringToUTF8(threadStateData, path); + struct stat s; + if (stat(p, &s) == 0) { +#ifdef __APPLE__ + return (JAVA_LONG)s.st_mtimespec.tv_sec * 1000; +#else + return (JAVA_LONG)s.st_mtime * 1000; +#endif + } + return 0; +} + +JAVA_LONG java_io_File_lengthImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return 0; + const char* p = stringToUTF8(threadStateData, path); + struct stat s; + if (stat(p, &s) == 0) { + return (JAVA_LONG)s.st_size; + } + return 0; +} + +JAVA_BOOLEAN java_io_File_createNewFileImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + if (access(p, F_OK) != -1) return JAVA_FALSE; + FILE* f = fopen(p, "w"); + if (f) { + fclose(f); + return JAVA_TRUE; + } + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_deleteImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + if (remove(p) == 0) return JAVA_TRUE; + return JAVA_FALSE; +} + +JAVA_OBJECT java_io_File_listImpl___java_lang_String_R_java_lang_String_1ARRAY(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_NULL; + enteringNativeAllocations(); + const char* p = stringToUTF8(threadStateData, path); + DIR* d = opendir(p); + if (d == NULL) { + finishedNativeAllocations(); + return JAVA_NULL; + } + + // First count + int count = 0; + struct dirent *dir; + while ((dir = readdir(d)) != NULL) { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; + count++; + } + closedir(d); + + JAVA_OBJECT arr = allocArray(threadStateData, count, &class__java_lang_String, sizeof(JAVA_OBJECT), 1); + + d = opendir(p); + count = 0; + while ((dir = readdir(d)) != NULL) { + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; + JAVA_OBJECT s = newStringFromCString(threadStateData, dir->d_name); + CN1_SET_ARRAY_ELEMENT_OBJECT(arr, count, s); + count++; + } + closedir(d); + + finishedNativeAllocations(); + return arr; +} + +JAVA_BOOLEAN java_io_File_mkdirImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); +#ifdef _WIN32 + if (mkdir(p) == 0) return JAVA_TRUE; +#else + if (mkdir(p, 0755) == 0) return JAVA_TRUE; +#endif + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_renameToImpl___java_lang_String_java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_OBJECT dest) { + if(path == JAVA_NULL || dest == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + const char* d = stringToUTF8(threadStateData, dest); + if (rename(p, d) == 0) return JAVA_TRUE; + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_setReadOnlyImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + return JAVA_FALSE; // Not implemented for POSIX here +} + +JAVA_BOOLEAN java_io_File_setWritableImpl___java_lang_String_boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_BOOLEAN writable) { + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_setReadableImpl___java_lang_String_boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_BOOLEAN readable) { + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_setExecutableImpl___java_lang_String_boolean_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path, JAVA_BOOLEAN executable) { + return JAVA_FALSE; +} + +JAVA_BOOLEAN java_io_File_canReadImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + return access(p, R_OK) != -1; +} + +JAVA_BOOLEAN java_io_File_canWriteImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + return access(p, W_OK) != -1; +} + +JAVA_BOOLEAN java_io_File_canExecuteImpl___java_lang_String_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_FALSE; + const char* p = stringToUTF8(threadStateData, path); + return access(p, X_OK) != -1; +} + +JAVA_LONG java_io_File_getTotalSpaceImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + return 0; +} + +JAVA_LONG java_io_File_getFreeSpaceImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + return 0; +} + +JAVA_LONG java_io_File_getUsableSpaceImpl___java_lang_String_R_long(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + return 0; +} + +JAVA_OBJECT java_io_File_getAbsolutePathImpl___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_NULL; + const char* p = stringToUTF8(threadStateData, path); + if (p[0] == '/') return path; + char buf[PATH_MAX]; + if (getcwd(buf, sizeof(buf)) != NULL) { + strcat(buf, "/"); + strcat(buf, p); + return newStringFromCString(threadStateData, buf); + } + return path; +} + +JAVA_OBJECT java_io_File_getCanonicalPathImpl___java_lang_String_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT path) { + if(path == JAVA_NULL) return JAVA_NULL; + const char* p = stringToUTF8(threadStateData, path); + char buf[PATH_MAX]; + if (realpath(p, buf) != NULL) { + return newStringFromCString(threadStateData, buf); + } + // Fallback + return java_io_File_getAbsolutePathImpl___java_lang_String_R_java_lang_String(threadStateData, __cn1ThisObject, path); +} + +#endif diff --git a/vm/ByteCodeTranslator/src/nativeMethods.m b/vm/ByteCodeTranslator/src/nativeMethods.m index 1605962677..1ce483a5a1 100644 --- a/vm/ByteCodeTranslator/src/nativeMethods.m +++ b/vm/ByteCodeTranslator/src/nativeMethods.m @@ -1,4 +1,12 @@ #include "cn1_globals.h" +#include +#include +#include +#include + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif #include "java_lang_Object.h" #include "java_lang_Boolean.h" @@ -17,15 +25,26 @@ #include "java_util_HashMap.h" #include "java_util_HashMap_Entry.h" #include "java_lang_NullPointerException.h" +#include "java_lang_Class.h" +#include "java_lang_System.h" + +#if defined(__APPLE__) && defined(__OBJC__) #import +#endif + #include #include #include #include "java_util_Date.h" #include "java_text_DateFormat.h" +#if defined(__APPLE__) && defined(__OBJC__) #include "CodenameOne_GLViewController.h" +#endif #include "java_lang_StringToReal.h" + +#if defined(__APPLE__) && defined(__OBJC__) #import +#endif extern JAVA_BOOLEAN lowMemoryMode; @@ -87,6 +106,7 @@ JAVA_BOOLEAN compareStringToCharArray(const char* str, JAVA_ARRAY_CHAR* chrs, in } JAVA_VOID java_lang_String_releaseNSString___long(CODENAME_ONE_THREAD_STATE, JAVA_LONG ns) { +#if defined(__APPLE__) && defined(__OBJC__) if(ns != 0) { // this prevents a race condition where the string might get GC'd and the NSString is still pending // on a call in the native thread @@ -95,6 +115,7 @@ JAVA_VOID java_lang_String_releaseNSString___long(CODENAME_ONE_THREAD_STATE, JAV [n release]; }); } +#endif } JAVA_BOOLEAN java_lang_String_equals___java_lang_Object_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT __cn1Arg1) { @@ -217,6 +238,7 @@ JAVA_OBJECT java_lang_String_bytesToChars___byte_1ARRAY_int_int_java_lang_String enteringNativeAllocations(); JAVA_ARRAY_BYTE* sourceData = (JAVA_ARRAY_BYTE*)((JAVA_ARRAY)b)->data; sourceData += off; +#if defined(__APPLE__) && defined(__OBJC__) NSStringEncoding enc; struct obj__java_lang_String* encString = (struct obj__java_lang_String*)encoding; JAVA_ARRAY_CHAR* encArr = (JAVA_ARRAY_CHAR*)((JAVA_ARRAY)encString->java_lang_String_value)->data; @@ -357,6 +379,51 @@ JAVA_OBJECT java_lang_String_bytesToChars___byte_1ARRAY_int_int_java_lang_String [pool release]; finishedNativeAllocations(); return destArr; +#else + // DFA Decoder for POSIX/Test + // TODO: Handle proper encoding check if not UTF-8/ASCII + size_t count; + uint32_t codepoint; + uint32_t state = 0; + JAVA_ARRAY_BYTE* s = sourceData; + JAVA_ARRAY_BYTE* end = s + len; + for (count=0; s < end; s = s + 1) + if (!decode(&state, &codepoint, (uint8_t)*s)) { + if (codepoint > 65535) { + count +=2; + } else { + count+=1; + } + } + + if (state != UTF8_ACCEPT) { + JAVA_OBJECT ex = __NEW_java_lang_RuntimeException(CN1_THREAD_STATE_PASS_SINGLE_ARG); + java_lang_RuntimeException___INIT_____java_lang_String(CN1_THREAD_STATE_PASS_ARG ex, newStringFromCString(CN1_THREAD_STATE_PASS_ARG "Decoding Error")); + finishedNativeAllocations(); + throwException(threadStateData, ex); + return NULL; + } + JAVA_OBJECT destArr = __NEW_ARRAY_JAVA_CHAR(threadStateData, count); + JAVA_ARRAY_CHAR* dest = (JAVA_ARRAY_CHAR*)((JAVA_ARRAY)destArr)->data; + state = UTF8_ACCEPT; + codepoint = 0; + s = sourceData; + for (; s < end; s = s+1) + if (!decode(&state, &codepoint, (uint8_t)*s)) { + if (codepoint > 65535) { + *dest = 55296 + (((codepoint - 0x10000) >> 10) & 1023); + dest = dest + 1; + *dest = 56320 + ((codepoint - 0x10000) & 1023); + dest = dest+1; + } else { + *dest = (JAVA_CHAR)codepoint; + dest= dest + 1; + } + } + + finishedNativeAllocations(); + return destArr; +#endif } JAVA_OBJECT java_io_InputStreamReader_bytesToChars___byte_1ARRAY_int_int_java_lang_String_R_char_1ARRAY(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT b, JAVA_INT off, JAVA_INT len, JAVA_OBJECT encoding) { @@ -375,6 +442,7 @@ JAVA_BOOLEAN isAsciiArray(JAVA_ARRAY sourceArr) { JAVA_OBJECT java_lang_String_charsToBytes___char_1ARRAY_char_1ARRAY_R_byte_1ARRAY(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT arr, JAVA_OBJECT encoding) { JAVA_ARRAY sourceArr = (JAVA_ARRAY)arr; +#if defined(__APPLE__) && defined(__OBJC__) if(isAsciiArray(sourceArr)) { JAVA_OBJECT destArr = __NEW_ARRAY_JAVA_BYTE(threadStateData, sourceArr->length); JAVA_ARRAY_CHAR* arr = (JAVA_ARRAY_CHAR*)((JAVA_ARRAY)sourceArr)->data; @@ -420,6 +488,17 @@ JAVA_OBJECT java_lang_String_charsToBytes___char_1ARRAY_char_1ARRAY_R_byte_1ARRA [nsStr release]; [pool release]; return destArr; +#else + // Stub: Assume ASCII/UTF8 simple copy for Linux testing + // TODO: Implement proper encoding logic + JAVA_OBJECT destArr = __NEW_ARRAY_JAVA_BYTE(threadStateData, sourceArr->length); + JAVA_ARRAY_CHAR* src = (JAVA_ARRAY_CHAR*)((JAVA_ARRAY)sourceArr)->data; + JAVA_ARRAY_BYTE* dest = (JAVA_ARRAY_BYTE*)((JAVA_ARRAY)destArr)->data; + for(int iter = 0 ; iter < sourceArr->length ; iter++) { + dest[iter] = (JAVA_ARRAY_BYTE)src[iter]; + } + return destArr; +#endif } JAVA_VOID java_lang_Throwable_fillInStack__(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject) { @@ -485,6 +564,7 @@ JAVA_OBJECT java_lang_Throwable_getStack___R_java_lang_String(CODENAME_ONE_THREA } JAVA_VOID java_io_NSLogOutputStream_write___byte_1ARRAY_int_int(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT me, JAVA_OBJECT b, JAVA_INT off, JAVA_INT len) { +#if defined(__APPLE__) && defined(__OBJC__) NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; JAVA_ARRAY a = (JAVA_ARRAY)b; JAVA_ARRAY_BYTE* arr = (JAVA_ARRAY_BYTE*)(*a).data; @@ -497,6 +577,12 @@ JAVA_VOID java_io_NSLogOutputStream_write___byte_1ARRAY_int_int(CODENAME_ONE_THR // if we disable arc we will need to re-enable these [str release]; [pool release]; +#else + JAVA_ARRAY a = (JAVA_ARRAY)b; + JAVA_ARRAY_BYTE* arr = (JAVA_ARRAY_BYTE*)(*a).data; + // Just print to stdout + for(int i=0; i -1); allThreads[threadOffset] = i; unlockCriticalSection(); - //NSLog(@"Thread slot %d assigned to thread %d",threadOffset,(int)i->threadId); + //printf("Thread slot %d assigned to thread %d\n",threadOffset,(int)i->threadId); } return i; } @@ -1159,10 +1245,10 @@ JAVA_VOID monitorEnter(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { } - //NSLog(@"Locking mutex %i started from %@", (int)obj->__codenameOneMutex, [NSThread callStackSymbols]); - //NSLog(@"Locking mutex %i completed", (int)obj->__codenameOneMutex); + //printf("Locking mutex %i started from %@", (int)obj->__codenameOneMutex, [NSThread callStackSymbols]); + //printf("Locking mutex %i completed", (int)obj->__codenameOneMutex); if(err != 0) { - NSLog(@"Error with lock %i EINVAL %i, ETIMEDOUT %i, EPERM %i", err, EINVAL, ETIMEDOUT, EPERM); + printf("Error with lock %i EINVAL %i, ETIMEDOUT %i, EPERM %i\n", err, EINVAL, ETIMEDOUT, EPERM); } } @@ -1178,7 +1264,7 @@ JAVA_VOID monitorEnterBlock(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { } JAVA_VOID monitorExit(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { - //NSLog(@"Unlocked mutex %i ", (int)obj->__codenameOneMutex); + //printf("Unlocked mutex %i ", (int)obj->__codenameOneMutex); // remove the ownership of the thread ((struct CN1ThreadData*)obj->__codenameOneThreadData)->counter--; if(((struct CN1ThreadData*)obj->__codenameOneThreadData)->counter > 0) { @@ -1187,7 +1273,7 @@ JAVA_VOID monitorExit(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { ((struct CN1ThreadData*)obj->__codenameOneThreadData)->ownerThread = 0; int err = pthread_mutex_unlock(&((struct CN1ThreadData*)obj->__codenameOneThreadData)->__codenameOneMutex); if(err != 0) { - NSLog(@"Error with unlock %i EINVAL %i, ETIMEDOUT %i, EPERM %i", err, EINVAL, ETIMEDOUT, EPERM); + printf("Error with unlock %i EINVAL %i, ETIMEDOUT %i, EPERM %i\n", err, EINVAL, ETIMEDOUT, EPERM); } } @@ -1202,7 +1288,7 @@ JAVA_VOID monitorExitBlock(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { } JAVA_VOID java_lang_Object_wait___long_int(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj, JAVA_LONG timeout, JAVA_INT nanos) { - //NSLog(@"Waiting on mutex %i with timeout %i started", (int)obj->__codenameOneMutex, (int)timeout); + //printf("Waiting on mutex %i with timeout %i started", (int)obj->__codenameOneMutex, (int)timeout); threadStateData->threadActive = JAVA_FALSE; int counter; @@ -1216,7 +1302,7 @@ JAVA_VOID java_lang_Object_wait___long_int(CODENAME_ONE_THREAD_STATE, JAVA_OBJEC if(timeout == 0 && nanos == 0) { errCode = pthread_cond_wait(&((struct CN1ThreadData*)obj->__codenameOneThreadData)->__codenameOneCondition, &((struct CN1ThreadData*)obj->__codenameOneThreadData)->__codenameOneMutex); if(errCode != 0) { - NSLog(@"Error with wait %i EINVAL %i, ETIMEDOUT %i, EPERM %i", errCode, EINVAL, ETIMEDOUT, EPERM); + printf("Error with wait %i EINVAL %i, ETIMEDOUT %i, EPERM %i\n", errCode, EINVAL, ETIMEDOUT, EPERM); } } else { struct timeval tv; @@ -1249,16 +1335,16 @@ JAVA_VOID java_lang_Object_wait___long_int(CODENAME_ONE_THREAD_STATE, JAVA_OBJEC ((struct CN1ThreadData*)obj->__codenameOneThreadData)->counter = counter; threadStateData->threadActive = JAVA_TRUE; - //NSLog(@"Waiting on mutex %i with timeout %i finished", (int)obj->__codenameOneMutex, (int)timeout); + //printf("Waiting on mutex %i with timeout %i finished", (int)obj->__codenameOneMutex, (int)timeout); } JAVA_VOID java_lang_Object_notify__(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { - //NSLog(@"Notifying mutex %i", (int)obj->__codenameOneMutex); + //printf("Notifying mutex %i", (int)obj->__codenameOneMutex); pthread_cond_signal(&((struct CN1ThreadData*)obj->__codenameOneThreadData)->__codenameOneCondition); } JAVA_VOID java_lang_Object_notifyAll__(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { - //NSLog(@"Notifying all mutex threads %i", (int)obj->__codenameOneMutex); + //printf("Notifying all mutex threads %i", (int)obj->__codenameOneMutex); pthread_cond_broadcast(&((struct CN1ThreadData*)obj->__codenameOneThreadData)->__codenameOneCondition); } @@ -1317,11 +1403,11 @@ void markDeadThread(struct ThreadLocalData *d) if(found>=0) { - // NSLog(@"Deleting thread slot %i id %d", found,(int)d->threadId); + // printf("Deleting thread slot %i id %d", found,(int)d->threadId); } else { - NSLog(@"Thread %d not found !!",(int)d->threadId); + printf("Thread %d not found !!\n",(int)d->threadId); } } @@ -1333,9 +1419,9 @@ void markDeadThread(struct ThreadLocalData *d) d->threadActive = JAVA_TRUE; d->currentThreadObject = t; - // NSLog(@"launching thread %d",(int)d->threadId); + // printf("launching thread %d",(int)d->threadId); java_lang_Thread_runImpl___long(d, t, (long)d); // pass the actual structure as threadid - // NSLog(@"terminate thread %d",(int)d->threadId); + // printf("terminate thread %d",(int)d->threadId); // we remove the thread here since this is the only place we can do this // we add the thread in the getThreadLocalData() method to handle native threads @@ -1449,10 +1535,16 @@ void releaseForReturnInException(CODENAME_ONE_THREAD_STATE, int cn1LocalsBeginIn } JAVA_LONG java_lang_Runtime_totalMemoryImpl___R_long(CODENAME_ONE_THREAD_STATE) { +#if defined(__APPLE__) && defined(__OBJC__) return [NSProcessInfo processInfo].physicalMemory; +#else + // TODO: implement for other platforms + return 1024*1024*1024; +#endif } JAVA_LONG java_lang_Runtime_freeMemoryImpl___R_long(CODENAME_ONE_THREAD_STATE) { +#if defined(__APPLE__) && defined(__OBJC__) struct task_basic_info info; mach_msg_type_number_t size = sizeof(info); kern_return_t kerr = task_info(mach_task_self(), @@ -1460,6 +1552,10 @@ JAVA_LONG java_lang_Runtime_freeMemoryImpl___R_long(CODENAME_ONE_THREAD_STATE) { (task_info_t)&info, &size); return [NSProcessInfo processInfo].physicalMemory - info.resident_size; +#else + // TODO: implement for other platforms + return 1024*1024*1024; +#endif } @@ -1482,6 +1578,7 @@ JAVA_OBJECT java_util_HashMap_findNonNullKeyEntry___java_lang_Object_int_int_R_j } JAVA_OBJECT java_util_Locale_getOSLanguage___R_java_lang_String(CODENAME_ONE_THREAD_STATE) { +#if defined(__APPLE__) && defined(__OBJC__) NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSUserDefaults* defs = [NSUserDefaults standardUserDefaults]; NSArray* languages = [defs objectForKey:@"AppleLanguages"]; @@ -1489,12 +1586,16 @@ JAVA_OBJECT java_util_Locale_getOSLanguage___R_java_lang_String(CODENAME_ONE_THR JAVA_OBJECT language = fromNSString(threadStateData, language_); [pool release]; return language; +#else + return newStringFromCString(threadStateData, "en"); +#endif } /*JAVA_OBJECT java_util_Locale_getOSCountry___R_java_lang_String(CODENAME_ONE_THREAD_STATE) { }*/ JAVA_OBJECT java_text_DateFormat_format___java_util_Date_java_lang_StringBuffer_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject, JAVA_OBJECT __cn1Arg1, JAVA_OBJECT __cn1Arg2) { +#if defined(__APPLE__) && defined(__OBJC__) struct obj__java_text_DateFormat* df = (struct obj__java_text_DateFormat*)__cn1ThisObject; POOL_BEGIN(); #ifndef CN1_USE_ARC @@ -1556,6 +1657,10 @@ JAVA_OBJECT java_text_DateFormat_format___java_util_Date_java_lang_StringBuffer_ POOL_END(); return str; +#else + // TODO: Implement stub + return JAVA_NULL; // Stub +#endif } @@ -1660,6 +1765,7 @@ JAVA_VOID java_lang_String_getChars___int_int_char_1ARRAY_int(CODENAME_ONE_THREA } JAVA_OBJECT java_lang_String_toUpperCase___R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject) { +#if defined(__APPLE__) && defined(__OBJC__) enteringNativeAllocations(); NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSString *nsString = [toNSString(CN1_THREAD_STATE_PASS_ARG __cn1ThisObject) uppercaseString]; @@ -1667,9 +1773,14 @@ JAVA_OBJECT java_lang_String_toUpperCase___R_java_lang_String(CODENAME_ONE_THREA [pool release]; finishedNativeAllocations(); return jString; +#else + // TODO: Implement stub + return __cn1ThisObject; // Stub +#endif } JAVA_OBJECT java_lang_String_toLowerCase___R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT __cn1ThisObject) { +#if defined(__APPLE__) && defined(__OBJC__) enteringNativeAllocations(); NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSString *nsString = [toNSString(CN1_THREAD_STATE_PASS_ARG __cn1ThisObject) lowercaseString]; @@ -1677,9 +1788,14 @@ JAVA_OBJECT java_lang_String_toLowerCase___R_java_lang_String(CODENAME_ONE_THREA [pool release]; finishedNativeAllocations(); return jString; +#else + // TODO: Implement stub + return __cn1ThisObject; // Stub +#endif } JAVA_OBJECT java_lang_String_format___java_lang_String_java_lang_Object_1ARRAY_R_java_lang_String(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT format, JAVA_OBJECT args) { +#if defined(__APPLE__) && defined(__OBJC__) enteringNativeAllocations(); JAVA_ARRAY argsArray = (JAVA_ARRAY)args; JAVA_ARRAY_OBJECT* objs = (JAVA_ARRAY_OBJECT*)argsArray->data; @@ -1695,5 +1811,156 @@ JAVA_OBJECT java_lang_String_format___java_lang_String_java_lang_Object_1ARRAY_R JAVA_OBJECT out = fromNSString(CN1_THREAD_STATE_PASS_ARG [NSString init]); finishedNativeAllocations(); return out; - +#else + // TODO: Implement stub + return format; // Stub +#endif +} + +#ifndef __APPLE__ + +// Additional Stubs for Linking + +struct clazz class_array1__JAVA_BOOLEAN = {0}; +struct clazz class_array1__JAVA_CHAR = {0}; +struct clazz class_array1__JAVA_BYTE = {0}; +struct clazz class_array1__JAVA_SHORT = {0}; +struct clazz class_array1__JAVA_INT = {0}; +struct clazz class_array1__JAVA_LONG = {0}; +struct clazz class_array1__JAVA_FLOAT = {0}; +struct clazz class_array1__JAVA_DOUBLE = {0}; + +void gcMarkObject(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj, JAVA_BOOLEAN force) {} // TODO +void gcMarkArrayObject(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj, JAVA_BOOLEAN force) {} // TODO +void arrayFinalizerFunction(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT array) {} // TODO + +void** initVtableForInterface() { return 0; } +JAVA_OBJECT codenameOneGcMalloc(CODENAME_ONE_THREAD_STATE, int size, struct clazz* parent) { + JAVA_OBJECT o = (JAVA_OBJECT)calloc(1, size); + o->__codenameOneParentClsReference = parent; + return o; +} +void codenameOneGcFree(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT obj) { free(obj); } + +void throwException(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT exceptionArg) { + if (exceptionArg != JAVA_NULL && exceptionArg->__codenameOneParentClsReference != 0) { + printf("Exception thrown: %s\n", exceptionArg->__codenameOneParentClsReference->clsName); + } else { + printf("Exception thrown: %p\n", exceptionArg); + } + exit(1); +} +JAVA_INT throwException_R_int(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT exceptionArg) { + throwException(threadStateData, exceptionArg); + return 0; +} +JAVA_BOOLEAN throwException_R_boolean(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT exceptionArg) { + throwException(threadStateData, exceptionArg); + return 0; +} + +void throwArrayIndexOutOfBoundsException(CODENAME_ONE_THREAD_STATE, int index) { + printf("ArrayIndexOutOfBoundsException: %d\n", index); + exit(1); +} +JAVA_BOOLEAN throwArrayIndexOutOfBoundsException_R_boolean(CODENAME_ONE_THREAD_STATE, int index) { + throwArrayIndexOutOfBoundsException(threadStateData, index); + return 0; +} + +int byteSizeForArray(struct clazz* cls) { + return sizeof(JAVA_OBJECT); // Stub +} + +JAVA_OBJECT allocArray(CODENAME_ONE_THREAD_STATE, int length, struct clazz* type, int primitiveSize, int dim) { + int size = sizeof(struct JavaArrayPrototype) + (length * primitiveSize); + JAVA_ARRAY arr = (JAVA_ARRAY)calloc(1, size); + arr->__codenameOneParentClsReference = type; + arr->length = length; + arr->data = (char*)arr + sizeof(struct JavaArrayPrototype); + return (JAVA_OBJECT)arr; +} + +JAVA_OBJECT __NEW_ARRAY_JAVA_CHAR(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_CHAR, sizeof(JAVA_CHAR), 1); } +JAVA_OBJECT __NEW_ARRAY_JAVA_BYTE(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_BYTE, sizeof(JAVA_BYTE), 1); +} +JAVA_OBJECT __NEW_ARRAY_JAVA_INT(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_INT, sizeof(JAVA_INT), 1); +} +JAVA_OBJECT __NEW_ARRAY_JAVA_LONG(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_LONG, sizeof(JAVA_LONG), 1); +} +JAVA_OBJECT __NEW_ARRAY_JAVA_DOUBLE(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_DOUBLE, sizeof(JAVA_DOUBLE), 1); +} +JAVA_OBJECT __NEW_ARRAY_JAVA_FLOAT(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_FLOAT, sizeof(JAVA_FLOAT), 1); +} +JAVA_OBJECT __NEW_ARRAY_JAVA_SHORT(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_SHORT, sizeof(JAVA_SHORT), 1); +} +JAVA_OBJECT __NEW_ARRAY_JAVA_BOOLEAN(CODENAME_ONE_THREAD_STATE, JAVA_INT size) { + return allocArray(threadStateData, size, &class_array1__JAVA_BOOLEAN, sizeof(JAVA_BOOLEAN), 1); +} + +JAVA_BOOLEAN removeObjectFromHeapCollection(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT o) { return JAVA_FALSE; } +JAVA_OBJECT* constantPoolObjects = 0; + +int instanceofFunction(int sourceClass, int destId) { return 1; } + +void flushReleaseQueue() {} // TODO +void codenameOneGCMark() {} // TODO +void codenameOneGCSweep() {} // TODO +JAVA_BOOLEAN lowMemoryMode = JAVA_FALSE; +void collectThreadResources(struct ThreadLocalData *current) {} // TODO + +struct elementStruct* pop(struct elementStruct**sp) { + (*sp)--; + return *sp; +} +void popMany(CODENAME_ONE_THREAD_STATE, int count, struct elementStruct**sp) { + (*sp) -= count; +} + +extern JAVA_OBJECT __NEW_INSTANCE_java_lang_String(CODENAME_ONE_THREAD_STATE); + +JAVA_OBJECT newStringFromCString(CODENAME_ONE_THREAD_STATE, const char *str) { + if (str == NULL) return JAVA_NULL; + int len = strlen(str); + JAVA_OBJECT charArr = __NEW_ARRAY_JAVA_CHAR(threadStateData, len); + JAVA_ARRAY_CHAR* chars = (JAVA_ARRAY_CHAR*)((JAVA_ARRAY)charArr)->data; + for(int i=0; ijava_lang_String_value = charArr; + stringObj->java_lang_String_count = len; + stringObj->java_lang_String_offset = 0; + return s; +} + +const char* stringToUTF8(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT str) { + if (str == JAVA_NULL) return NULL; + struct obj__java_lang_String* s = (struct obj__java_lang_String*)str; + JAVA_ARRAY_CHAR* chars = (JAVA_ARRAY_CHAR*)((JAVA_ARRAY)s->java_lang_String_value)->data; + int len = s->java_lang_String_count; + int offset = s->java_lang_String_offset; + char* buf = (char*)malloc(len + 1); + for(int i=0; i Character.MAX_RADIX) { - throw new NumberFormatException("Invalid radix: " + radix); + throw new NumberFormatException("Invalid radix"); } if (string == null) { throw invalidInt(string); @@ -176,7 +176,7 @@ private static int parse(String string, int offset, int radix, boolean negative) } private static NumberFormatException invalidInt(String s) { - throw new NumberFormatException("Invalid int: \"" + s + "\""); + throw new NumberFormatException("Invalid int"); } diff --git a/vm/JavaAPI/src/java/lang/StringIndexOutOfBoundsException.java b/vm/JavaAPI/src/java/lang/StringIndexOutOfBoundsException.java index e9eb773f68..49603f137a 100644 --- a/vm/JavaAPI/src/java/lang/StringIndexOutOfBoundsException.java +++ b/vm/JavaAPI/src/java/lang/StringIndexOutOfBoundsException.java @@ -39,7 +39,7 @@ public StringIndexOutOfBoundsException(){ * index - the illegal index. */ public StringIndexOutOfBoundsException(int index){ - super("" + index); + super(String.valueOf(index)); } /** diff --git a/vm/JavaAPI/src/java/util/ArrayList.java b/vm/JavaAPI/src/java/util/ArrayList.java index f61bbda291..8a3c4129a1 100644 --- a/vm/JavaAPI/src/java/util/ArrayList.java +++ b/vm/JavaAPI/src/java/util/ArrayList.java @@ -119,7 +119,7 @@ private E[] newElementArray(int size) { @Override public void add(int location, E object) { if (location < 0 || location > size) { - throw new IndexOutOfBoundsException("" + location + " out of: " + size); + throw new IndexOutOfBoundsException("Index out of bounds"); } if (location == 0) { if (firstIndex == 0) { @@ -185,7 +185,7 @@ public boolean add(E object) { @Override public boolean addAll(int location, Collection collection) { if (location < 0 || location > size) { - throw new IndexOutOfBoundsException("" + location + " out of: " + size); + throw new IndexOutOfBoundsException("Index out of bounds"); } Object[] dumparray = toObjectArray(collection); @@ -325,7 +325,7 @@ public void ensureCapacity(int minimumCapacity) { @Override public E get(int location) { if (location < 0 || location >= size) { - throw new IndexOutOfBoundsException("" + location + " out of: " + size); + throw new IndexOutOfBoundsException("Index out of bounds"); } return array[firstIndex + location]; } @@ -472,7 +472,7 @@ public int lastIndexOf(Object object) { public E remove(int location) { E result; if (location < 0 || location >= size) { - throw new IndexOutOfBoundsException("" + location + " out of: " + size); + throw new IndexOutOfBoundsException("Index out of bounds"); } if (location == 0) { result = array[firstIndex]; @@ -531,11 +531,11 @@ public boolean remove(Object object) { protected void removeRange(int start, int end) { // REVIEW: does RI call this from remove(location) if (start < 0) { - throw new IndexOutOfBoundsException("" + start); + throw new IndexOutOfBoundsException("Index out of bounds"); } else if (end > size) { - throw new IndexOutOfBoundsException("" + end + " out of: " + size); + throw new IndexOutOfBoundsException("Index out of bounds"); } else if (start > end) { - throw new IndexOutOfBoundsException("" + start + " out of: " + end); + throw new IndexOutOfBoundsException("Index out of bounds"); } if (start == end) { @@ -573,7 +573,7 @@ protected void removeRange(int start, int end) { @Override public E set(int location, E object) { if (location < 0 || location >= size) { - throw new IndexOutOfBoundsException("" + location + " out of: " + size); + throw new IndexOutOfBoundsException("Index out of bounds"); } E result = array[firstIndex + location]; array[firstIndex + location] = object; diff --git a/vm/JavaAPI/src/java/util/Arrays.java b/vm/JavaAPI/src/java/util/Arrays.java index c0dfc6fa30..dc3f74a135 100644 --- a/vm/JavaAPI/src/java/util/Arrays.java +++ b/vm/JavaAPI/src/java/util/Arrays.java @@ -38,7 +38,7 @@ public class Arrays { */ public static void checkOffsetAndCount(int arrayLength, int offset, int count) { if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) { - throw new ArrayIndexOutOfBoundsException(offset); + throw new ArrayIndexOutOfBoundsException(String.valueOf(offset)); } } @@ -1902,15 +1902,15 @@ public static void sort(byte[] array, int start, int end) { private static void checkBounds(int arrLength, int start, int end) { if (start > end) { // luni.35=Start index ({0}) is greater than end index ({1}) - throw new IllegalArgumentException("" + start + " out of: " + end); + throw new IllegalArgumentException("Start index greater than end index"); } if (start < 0) { // luni.36=Array index out of range\: {0} - throw new IndexOutOfBoundsException("" + start); + throw new IndexOutOfBoundsException(String.valueOf(start)); } if (end > arrLength) { // luni.36=Array index out of range\: {0} - throw new IndexOutOfBoundsException("" + end); + throw new IndexOutOfBoundsException(String.valueOf(end)); } } diff --git a/vm/JavaAPI/src/java/util/Objects.java b/vm/JavaAPI/src/java/util/Objects.java new file mode 100644 index 0000000000..209c9a42e4 --- /dev/null +++ b/vm/JavaAPI/src/java/util/Objects.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package java.util; + +/** + * Utility methods for objects. + */ +public final class Objects { + private Objects() { + throw new AssertionError("No java.util.Objects instances for you!"); + } + + public static T requireNonNull(T obj) { + if (obj == null) + throw new NullPointerException(); + return obj; + } + + public static T requireNonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } + + public static boolean equals(Object a, Object b) { + return (a == b) || (a != null && a.equals(b)); + } + + public static int hashCode(Object o) { + return o != null ? o.hashCode() : 0; + } + + public static String toString(Object o) { + return String.valueOf(o); + } + + public static String toString(Object o, String nullDefault) { + return (o != null) ? o.toString() : nullDefault; + } + + public static int compare(Object a, Object b, Comparator c) { + return (a == b) ? 0 : c.compare(a, b); + } + + public static boolean isNull(Object obj) { + return obj == null; + } + + public static boolean nonNull(Object obj) { + return obj != null; + } +} diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/FileClassIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/FileClassIntegrationTest.java new file mode 100644 index 0000000000..d358ae4244 --- /dev/null +++ b/vm/tests/src/test/java/com/codename1/tools/translator/FileClassIntegrationTest.java @@ -0,0 +1,142 @@ +package com.codename1.tools.translator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class FileClassIntegrationTest { + + @ParameterizedTest + @org.junit.jupiter.params.provider.MethodSource("com.codename1.tools.translator.BytecodeInstructionIntegrationTest#provideCompilerConfigs") + public void testFileClassMethods(CompilerHelper.CompilerConfig config) throws Exception { + Parser.cleanup(); + + Path sourceDir = Files.createTempDirectory("file-test-sources"); + Path classesDir = Files.createTempDirectory("file-test-classes"); + Path javaFile = sourceDir.resolve("FileTestApp.java"); + + Files.write(javaFile, fileTestAppSource().getBytes(StandardCharsets.UTF_8)); + + // Use real JavaAPI sources + Path javaApiSrc = Paths.get("../JavaAPI/src"); + if (!Files.exists(javaApiSrc)) { + javaApiSrc = Paths.get("vm/JavaAPI/src"); + } + + List compileArgs = new ArrayList<>(); + double jdkVer = 1.8; + try { jdkVer = Double.parseDouble(config.jdkVersion); } catch (NumberFormatException ignored) {} + + if (jdkVer >= 9) { + if (Double.parseDouble(config.targetVersion) < 9) { + return; + } + compileArgs.add("-source"); + compileArgs.add(config.targetVersion); + compileArgs.add("-target"); + compileArgs.add(config.targetVersion); + compileArgs.add("--patch-module"); + compileArgs.add("java.base=" + javaApiSrc.toString()); + compileArgs.add("-Xlint:-module"); + } else { + compileArgs.add("-source"); + compileArgs.add(config.targetVersion); + compileArgs.add("-target"); + compileArgs.add(config.targetVersion); + compileArgs.add("-Xlint:-options"); + } + + compileArgs.add("-d"); + compileArgs.add(classesDir.toString()); + compileArgs.add(javaFile.toString()); + + // Add all JavaAPI source files + Files.walk(javaApiSrc) + .filter(p -> p.toString().endsWith(".java")) + .forEach(p -> compileArgs.add(p.toString())); + + int compileResult = CompilerHelper.compile(config.jdkHome, compileArgs); + assertEquals(0, compileResult, "FileTestApp.java compilation failed with " + config); + + Path outputDir = Files.createTempDirectory("file-test-output"); + CleanTargetIntegrationTest.runTranslator(classesDir, outputDir, "FileTestApp"); + + Path distDir = outputDir.resolve("dist"); + Path cmakeLists = distDir.resolve("CMakeLists.txt"); + assertTrue(Files.exists(cmakeLists), "Translator should emit a CMake project"); + + Path srcRoot = distDir.resolve("FileTestApp-src"); + CleanTargetIntegrationTest.patchCn1Globals(srcRoot); + + // Ensure java_io_File.m is included (ByteCodeTranslator should copy it) + assertTrue(Files.exists(srcRoot.resolve("java_io_File.m")), "java_io_File.m should exist"); + + replaceLibraryWithExecutableTarget(cmakeLists, srcRoot.getFileName().toString()); + + Path buildDir = distDir.resolve("build"); + Files.createDirectories(buildDir); + + CleanTargetIntegrationTest.runCommand(Arrays.asList( + "cmake", + "-S", distDir.toString(), + "-B", buildDir.toString(), + "-DCMAKE_C_COMPILER=clang", + "-DCMAKE_OBJC_COMPILER=clang" + ), distDir); + + CleanTargetIntegrationTest.runCommand(Arrays.asList("cmake", "--build", buildDir.toString()), distDir); + + Path executable = buildDir.resolve("FileTestApp"); + // Running the app. If it exits with 0, logic passed. + CleanTargetIntegrationTest.runCommand(Arrays.asList(executable.toString()), buildDir); + } + + private String fileTestAppSource() { + return "import java.io.File;\n" + + "public class FileTestApp {\n" + + " public static void main(String[] args) {\n" + + " try {\n" + + " // Construct string manually to avoid constant pool issues in test env\n" + + " char[] pathChars = new char[]{'t','e','s','t','f','i','l','e','.','t','x','t'};\n" + + " String path = new String(pathChars);\n" + + " File f = new File(path);\n" + + " if (f.exists()) {\n" + + " f.delete();\n" + + " }\n" + + " boolean created = f.createNewFile();\n" + + " if (!created) throw new RuntimeException(\"Create failed\");\n" + + " if (!f.exists()) throw new RuntimeException(\"Exists failed\");\n" + + " if (f.isDirectory()) throw new RuntimeException(\"IsDirectory failed\");\n" + + " if (!f.delete()) throw new RuntimeException(\"Delete failed\");\n" + + " if (f.exists()) throw new RuntimeException(\"Delete verification failed\");\n" + + " } catch (Exception e) {\n" + + " // e.printStackTrace(); // Can't print stack trace without constants\n" + + " System.exit(1);\n" + + " }\n" + + " }\n" + + "}"; + } + + private void replaceLibraryWithExecutableTarget(Path cmakeLists, String sourceDirName) throws IOException { + String content = new String(Files.readAllBytes(cmakeLists), StandardCharsets.UTF_8); + String replacement = content.replace( + "add_library(${PROJECT_NAME} ${TRANSLATOR_SOURCES} ${TRANSLATOR_HEADERS})", + "add_executable(${PROJECT_NAME} ${TRANSLATOR_SOURCES} ${TRANSLATOR_HEADERS})\ntarget_link_libraries(${PROJECT_NAME} m)" + ); + Files.write(cmakeLists, replacement.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/LambdaIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/LambdaIntegrationTest.java index cd90c36177..f9f14e931f 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/LambdaIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/LambdaIntegrationTest.java @@ -154,7 +154,6 @@ private void compileJavaAPI(Path outputDir) throws Exception { // Add stubs for java.lang.invoke Path stubsDir = Files.createTempDirectory("java-lang-invoke-stubs"); sources.addAll(generateJavaLangInvokeStubs(stubsDir)); - sources.addAll(generateJavaUtilStubs(stubsDir)); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); List args = new ArrayList<>(); @@ -177,22 +176,6 @@ private void compileJavaAPI(Path outputDir) throws Exception { assertEquals(0, result, "JavaAPI should compile"); } - private List generateJavaUtilStubs(Path stubsDir) throws IOException { - List stubFiles = new ArrayList<>(); - Path utilPkg = stubsDir.resolve("java/util"); - Files.createDirectories(utilPkg); - - // Objects - Path objs = utilPkg.resolve("Objects.java"); - Files.write(objs, ("package java.util;\n" + - "public class Objects {\n" + - " public static T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }\n" + - "}").getBytes(StandardCharsets.UTF_8)); - stubFiles.add(objs.toString()); - - return stubFiles; - } - private List generateJavaLangInvokeStubs(Path stubsDir) throws IOException { List stubFiles = new ArrayList<>(); Path invokePkg = stubsDir.resolve("java/lang/invoke");