Initial commit

This commit is contained in:
Bikram-ghuku committed 2026-03-30 19:20:11 +00:00
commit dc5f530210
19 files changed
+1936

No files matched your search

+41
View File
@@ -0,0 +1,41 @@
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Hello extends MIDlet {
private Display display;
private MyCanvas canvas;
public void startApp() {
canvas = new MyCanvas();
display = Display.getDisplay(this);
display.setCurrent(canvas);
System.out.println("Hello J2ME!");
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
}
class MyCanvas extends Canvas {
protected void paint(Graphics g) {
// Clear screen (black background)
g.setColor(0, 0, 0);
g.fillRect(0, 0, getWidth(), getHeight());
// Draw a red rectangle
g.setColor(255, 0, 0);
g.fillRect(20, 20, 80, 40);
// Draw a green line
g.setColor(0, 255, 0);
g.drawLine(0, 0, getWidth(), getHeight());
// Draw white text
g.setColor(255, 255, 255);
g.drawString("Hello J2ME!", 10, 80, Graphics.TOP | Graphics.LEFT);
// Draw a blue circle (approximated with drawArc)
g.setColor(0, 0, 255);
g.drawArc(100, 100, 50, 50, 0, 360);
}
}
+26
View File
@@ -0,0 +1,26 @@
CC = gcc
CFLAGS = -Wall -Wextra -g -std=c99 -ffreestanding
J2ME_JARS = lib/cldcapi10.jar:lib/midpapi20.jar
.PHONY: all clean run_parser run_exec
all: test_parser exec_code
test_parser: test_parser.c classfile.c classfile.h
$(CC) $(CFLAGS) -o test_parser test_parser.c classfile.c
exec_code: exec_code.c classfile.c classfile.h helpers.h helpers.c
$(CC) $(CFLAGS) -o exec_code exec_code.c classfile.c helpers.c -lSDL2 -lSDL2_ttf -lm
Hello.class: Hello.java
javac -classpath $(J2ME_JARS) Hello.java
run_parser: test_parser Hello.class
./test_parser Hello.class
run_exec: exec_code Hello.class
export LIBGL_ALWAYS_SOFTWARE=1 && ./exec_code Hello.class
clean:
rm -f test_parser exec_code *.class
+299
View File
@@ -0,0 +1,299 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "classfile.h"
static uint8_t read_u1(FILE *f) {
return (uint8_t)fgetc(f);
}
static uint16_t read_u2(FILE *f) {
uint16_t a = read_u1(f), b = read_u1(f);
return (a << 8) | b;
}
static uint32_t read_u4(FILE *f) {
uint32_t a = read_u2(f), b = read_u2(f);
return (a << 16) | b;
}
static uint64_t read_u8(FILE *f) {
uint64_t a = read_u4(f), b = read_u4(f);
return (a << 32) | b;
}
static void skip_attr(FILE *f) {
uint32_t len = read_u4(f);
fseek(f, len, SEEK_CUR);
}
static void parse_code_attr(FILE *f, CodeAttr *code) {
read_u4(f); /* attribute_length (ignored) */
code->max_stack = read_u2(f);
code->max_locals = read_u2(f);
code->code_len = read_u4(f);
code->code = malloc(code->code_len);
fread(code->code, 1, code->code_len, f);
code->exception_table_len = read_u2(f);
code->exception_table = NULL;
if (code->exception_table_len > 0) {
code->exception_table = malloc(
code->exception_table_len * sizeof(ExceptionHandler));
for (int i = 0; i < code->exception_table_len; i++) {
code->exception_table[i].start_pc = read_u2(f);
code->exception_table[i].end_pc = read_u2(f);
code->exception_table[i].handler_pc = read_u2(f);
code->exception_table[i].catch_type = read_u2(f);
}
}
/* skip Code sub-attributes (LineNumberTable, LocalVariableTable …) */
uint16_t attr_count = read_u2(f);
for (int i = 0; i < attr_count; i++) {
read_u2(f); /* attribute_name_index */
skip_attr(f);
}
}
static int parse_constant_pool(FILE *f, ClassFile *cf) {
cf->cp = calloc(cf->cp_count, sizeof(CPEntry));
if (!cf->cp) return 0;
/* spec: index starts at 1; long/double consume two slots */
for (int i = 1; i < cf->cp_count; i++) {
CPEntry *e = &cf->cp[i];
e->tag = read_u1(f);
switch (e->tag) {
case CP_UTF8: {
e->utf8.len = read_u2(f);
e->utf8.bytes = malloc(e->utf8.len + 1);
fread(e->utf8.bytes, 1, e->utf8.len, f);
e->utf8.bytes[e->utf8.len] = '\0';
break;
}
case CP_INTEGER:
e->integer.value = (int32_t)read_u4(f);
break;
case CP_FLOAT: {
uint32_t raw = read_u4(f);
memcpy(&e->flt.value, &raw, 4);
break;
}
case CP_LONG:
e->lng.value = (int64_t)read_u8(f);
i++; /* long takes two CP slots */
break;
case CP_DOUBLE: {
uint64_t raw = read_u8(f);
memcpy(&e->dbl.value, &raw, 8);
i++; /* double takes two CP slots */
break;
}
case CP_CLASS:
case CP_STRING:
e->cls_or_str.index = read_u2(f);
break;
case CP_FIELDREF:
case CP_METHODREF:
case CP_IFACE_METHODREF:
e->ref.class_index = read_u2(f);
e->ref.name_and_type_index = read_u2(f);
break;
case CP_NAME_AND_TYPE:
e->nat.name_index = read_u2(f);
e->nat.descriptor_index = read_u2(f);
break;
default:
fprintf(stderr, "classfile: unknown CP tag %d at index %d\n",
e->tag, i);
return 0;
}
}
return 1;
}
/* ── parse fields ───────────────────────────────────────────────────────── */
static int parse_fields(FILE *f, ClassFile *cf) {
cf->fields = calloc(cf->fields_count, sizeof(Field));
if (!cf->fields && cf->fields_count > 0) return 0;
for (int i = 0; i < cf->fields_count; i++) {
cf->fields[i].access_flags = read_u2(f);
cf->fields[i].name_index = read_u2(f);
cf->fields[i].descriptor_index = read_u2(f);
/* skip field attributes (ConstantValue, etc.) */
uint16_t ac = read_u2(f);
for (int j = 0; j < ac; j++) {
read_u2(f);
skip_attr(f);
}
}
return 1;
}
/* ── parse methods ──────────────────────────────────────────────────────── */
static int parse_methods(FILE *f, ClassFile *cf) {
cf->methods = calloc(cf->methods_count, sizeof(Method));
if (!cf->methods && cf->methods_count > 0) return 0;
for (int i = 0; i < cf->methods_count; i++) {
Method *m = &cf->methods[i];
m->access_flags = read_u2(f);
m->name_index = read_u2(f);
m->descriptor_index = read_u2(f);
m->has_code = 0;
uint16_t ac = read_u2(f);
for (int j = 0; j < ac; j++) {
uint16_t attr_name_idx = read_u2(f);
const char *attr_name = cp_utf8(cf, attr_name_idx);
if (strcmp(attr_name, "Code") == 0) {
parse_code_attr(f, &m->code);
m->has_code = 1;
} else {
skip_attr(f);
}
}
}
return 1;
}
/* ── public: load a .class file ─────────────────────────────────────────── */
ClassFile *classfile_load(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) {
perror(path);
return NULL;
}
uint32_t magic = read_u4(f);
if (magic != 0xCAFEBABE) {
fprintf(stderr, "%s: not a class file (magic=0x%08X)\n", path, magic);
fclose(f);
return NULL;
}
ClassFile *cf = calloc(1, sizeof(ClassFile));
cf->minor_version = read_u2(f);
cf->major_version = read_u2(f);
/* constant pool */
cf->cp_count = read_u2(f);
if (!parse_constant_pool(f, cf)) goto fail;
cf->access_flags = read_u2(f);
cf->this_class = read_u2(f);
cf->super_class = read_u2(f);
/* interfaces */
cf->interfaces_count = read_u2(f);
cf->interfaces = malloc(cf->interfaces_count * sizeof(uint16_t));
for (int i = 0; i < cf->interfaces_count; i++)
cf->interfaces[i] = read_u2(f);
/* fields */
cf->fields_count = read_u2(f);
if (!parse_fields(f, cf)) goto fail;
/* methods */
cf->methods_count = read_u2(f);
if (!parse_methods(f, cf)) goto fail;
/* skip class-level attributes (SourceFile, etc.) */
uint16_t ac = read_u2(f);
for (int i = 0; i < ac; i++) {
read_u2(f);
skip_attr(f);
}
fclose(f);
return cf;
fail:
fclose(f);
classfile_free(cf);
return NULL;
}
/* ── public: find a method by name + descriptor ─────────────────────────── */
Method *classfile_find_method(ClassFile *cf, const char *name, const char *descriptor) {
for (int i = 0; i < cf->methods_count; i++) {
const char *n = cp_utf8(cf, cf->methods[i].name_index);
const char *d = cp_utf8(cf, cf->methods[i].descriptor_index);
if (strcmp(n, name) == 0 && strcmp(d, descriptor) == 0)
return &cf->methods[i];
}
return NULL;
}
/* ── public: free everything ────────────────────────────────────────────── */
void classfile_free(ClassFile *cf) {
if (!cf) return;
if (cf->cp) {
for (int i = 1; i < cf->cp_count; i++)
if (cf->cp[i].tag == CP_UTF8)
free(cf->cp[i].utf8.bytes);
free(cf->cp);
}
if (cf->interfaces) free(cf->interfaces);
if (cf->fields) free(cf->fields);
if (cf->methods) {
for (int i = 0; i < cf->methods_count; i++) {
if (cf->methods[i].has_code) {
free(cf->methods[i].code.code);
free(cf->methods[i].code.exception_table);
}
}
free(cf->methods);
}
free(cf);
}
/* ── public: debug dump ─────────────────────────────────────────────────── */
void classfile_dump(const ClassFile *cf) {
printf("Class file version: %d.%d\n",
cf->major_version, cf->minor_version);
printf("This class : %s\n",
cp_utf8(cf, cf->cp[cf->this_class].cls_or_str.index));
printf("Super class: %s\n",
cf->super_class
? cp_utf8(cf, cf->cp[cf->super_class].cls_or_str.index)
: "(none)");
printf("\nConstant pool (%d entries):\n", cf->cp_count - 1);
for (int i = 1; i < cf->cp_count; i++) {
CPEntry *e = &cf->cp[i];
printf(" #%-3d ", i);
switch (e->tag) {
case CP_UTF8: printf("Utf8 \"%s\"\n", e->utf8.bytes); break;
case CP_INTEGER: printf("Integer %d\n", e->integer.value); break;
case CP_FLOAT: printf("Float %f\n", e->flt.value); break;
case CP_LONG: printf("Long %lld\n", (long long)e->lng.value); break;
case CP_DOUBLE: printf("Double %f\n", e->dbl.value); break;
case CP_CLASS: printf("Class #%d\n", e->cls_or_str.index); break;
case CP_STRING: printf("String #%d\n", e->cls_or_str.index); break;
case CP_FIELDREF:
printf("Fieldref #%d.#%d\n",
e->ref.class_index, e->ref.name_and_type_index); break;
case CP_METHODREF:
printf("Methodref #%d.#%d\n",
e->ref.class_index, e->ref.name_and_type_index); break;
case CP_NAME_AND_TYPE:
printf("NameAndType #%d:#%d\n",
e->nat.name_index, e->nat.descriptor_index); break;
default: printf("(tag %d)\n", e->tag);
}
}
printf("\nMethods (%d):\n", cf->methods_count);
for (int i = 0; i < cf->methods_count; i++) {
Method *m = &cf->methods[i];
printf(" %s %s [code=%s, max_stack=%d, max_locals=%d, len=%d]\n",
cp_utf8(cf, m->name_index),
cp_utf8(cf, m->descriptor_index),
m->has_code ? "yes" : "no",
m->has_code ? m->code.max_stack : 0,
m->has_code ? m->code.max_locals : 0,
m->has_code ? (int)m->code.code_len : 0);
}
}
+222
View File
@@ -0,0 +1,222 @@
#ifndef CLASSFILE_H
#define CLASSFILE_H
#include <stdint.h>
#include <stddef.h>
#include <string.h>
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.htmls table 4.3
#define CP_UTF8 1
#define CP_INTEGER 3
#define CP_FLOAT 4
#define CP_LONG 5
#define CP_DOUBLE 6
#define CP_CLASS 7
#define CP_STRING 8
#define CP_FIELDREF 9
#define CP_METHODREF 10
#define CP_IFACE_METHODREF 11
#define CP_NAME_AND_TYPE 12
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html table 4.1
#define ACC_PUBLIC 0x0001
#define ACC_PRIVATE 0x0002
#define ACC_PROTECTED 0x0004
#define ACC_STATIC 0x0008
#define ACC_FINAL 0x0010
#define ACC_SYNCHRONIZED 0x0020
#define ACC_NATIVE 0x0100
#define ACC_ABSTRACT 0x0400
/* ── opcodes we handle ──────────────────────────────────────────────────── */
#define OP_NOP 0x00
#define OP_ACONST_NULL 0x01
#define OP_ICONST_M1 0x02
#define OP_ICONST_0 0x03
#define OP_ICONST_1 0x04
#define OP_ICONST_2 0x05
#define OP_ICONST_3 0x06
#define OP_ICONST_4 0x07
#define OP_ICONST_5 0x08
#define OP_LCONST_0 0x09
#define OP_LCONST_1 0x0A
#define OP_BIPUSH 0x10
#define OP_SIPUSH 0x11
#define OP_LDC 0x12
#define OP_LDC_W 0x13
#define OP_LDC2_W 0x14
#define OP_ILOAD 0x15
#define OP_ALOAD 0x19
#define OP_ILOAD_0 0x1A
#define OP_ILOAD_1 0x1B
#define OP_ILOAD_2 0x1C
#define OP_ILOAD_3 0x1D
#define OP_ALOAD_0 0x2A
#define OP_ALOAD_1 0x2B
#define OP_ALOAD_2 0x2C
#define OP_ALOAD_3 0x2D
#define OP_IALOAD 0x2E
#define OP_ISTORE 0x36
#define OP_ASTORE 0x3A
#define OP_ISTORE_0 0x3B
#define OP_ISTORE_1 0x3C
#define OP_ISTORE_2 0x3D
#define OP_ISTORE_3 0x3E
#define OP_ASTORE_0 0x4B
#define OP_ASTORE_1 0x4C
#define OP_ASTORE_2 0x4D
#define OP_ASTORE_3 0x4E
#define OP_IASTORE 0x4F
#define OP_POP 0x57
#define OP_POP2 0x58
#define OP_DUP 0x59
#define OP_DUP_X1 0x5A
#define OP_SWAP 0x5F
#define OP_IADD 0x60
#define OP_LADD 0x61
#define OP_ISUB 0x64
#define OP_IMUL 0x68
#define OP_IDIV 0x6C
#define OP_IREM 0x70
#define OP_INEG 0x74
#define OP_ISHL 0x78
#define OP_ISHR 0x7A
#define OP_IUSHR 0x7C
#define OP_IAND 0x7E
#define OP_IOR 0x80
#define OP_IXOR 0x82
#define OP_IINC 0x84
#define OP_I2L 0x85
#define OP_I2C 0x92
#define OP_LCMP 0x94
#define OP_IFEQ 0x99
#define OP_IFNE 0x9A
#define OP_IFLT 0x9B
#define OP_IFGE 0x9C
#define OP_IFGT 0x9D
#define OP_IFLE 0x9E
#define OP_IF_ICMPEQ 0x9F
#define OP_IF_ICMPNE 0xA0
#define OP_IF_ICMPLT 0xA1
#define OP_IF_ICMPGE 0xA2
#define OP_IF_ICMPGT 0xA3
#define OP_IF_ICMPLE 0xA4
#define OP_IF_ACMPEQ 0xA5
#define OP_IF_ACMPNE 0xA6
#define OP_GOTO 0xA7
#define OP_IRETURN 0xAC
#define OP_LRETURN 0xAD
#define OP_ARETURN 0xB0
#define OP_RETURN 0xB1
#define OP_GETSTATIC 0xB2
#define OP_PUTSTATIC 0xB3
#define OP_GETFIELD 0xB4
#define OP_PUTFIELD 0xB5
#define OP_INVOKEVIRTUAL 0xB6
#define OP_INVOKESPECIAL 0xB7
#define OP_INVOKESTATIC 0xB8
#define OP_NEW 0xBB
#define OP_NEWARRAY 0xBC
#define OP_ANEWARRAY 0xBD
#define OP_ARRAYLENGTH 0xBE
#define OP_ATHROW 0xBF
#define OP_CHECKCAST 0xC0
#define OP_INSTANCEOF 0xC1
#define OP_IFNULL 0xC6
#define OP_IFNONNULL 0xC7
/* ── constant pool entry ────────────────────────────────────────────────── */
typedef struct {
uint8_t tag;
union {
/* CP_UTF8 */
struct { uint16_t len; char *bytes; } utf8;
/* CP_INTEGER */
struct { int32_t value; } integer;
/* CP_FLOAT */
struct { float value; } flt;
/* CP_LONG */
struct { int64_t value; } lng;
/* CP_DOUBLE */
struct { double value; } dbl;
/* CP_CLASS, CP_STRING */
struct { uint16_t index; } cls_or_str;
/* CP_FIELDREF, CP_METHODREF, CP_IFACE_METHODREF */
struct { uint16_t class_index; uint16_t name_and_type_index; } ref;
/* CP_NAME_AND_TYPE */
struct { uint16_t name_index; uint16_t descriptor_index; } nat;
};
} CPEntry;
/* ── exception handler table entry ─────────────────────────────────────── */
typedef struct {
uint16_t start_pc;
uint16_t end_pc;
uint16_t handler_pc;
uint16_t catch_type; /* CP index to Class, or 0 = catch all */
} ExceptionHandler;
/* ── Code attribute (lives inside a Method) ─────────────────────────────── */
typedef struct {
uint16_t max_stack;
uint16_t max_locals;
uint32_t code_len;
uint8_t *code;
uint16_t exception_table_len;
ExceptionHandler *exception_table;
} CodeAttr;
/* ── field ──────────────────────────────────────────────────────────────── */
typedef struct {
uint16_t access_flags;
uint16_t name_index; /* → CP_UTF8 */
uint16_t descriptor_index; /* → CP_UTF8 */
} Field;
/* ── method ─────────────────────────────────────────────────────────────── */
typedef struct {
uint16_t access_flags;
uint16_t name_index; /* → CP_UTF8 */
uint16_t descriptor_index; /* → CP_UTF8 */
CodeAttr code; /* zeroed if native/abstract */
int has_code;
} Method;
/* ── top-level class file ───────────────────────────────────────────────── */
typedef struct {
uint16_t minor_version;
uint16_t major_version; /* 46 = Java 1.2 … 50 = Java 6 */
uint16_t cp_count; /* constant_pool_count from spec (1-indexed) */
CPEntry *cp; /* cp[1..cp_count-1], cp[0] unused */
uint16_t access_flags;
uint16_t this_class; /* CP index */
uint16_t super_class; /* CP index (0 = java/lang/Object) */
uint16_t interfaces_count;
uint16_t *interfaces; /* array of CP indices */
uint16_t fields_count;
Field *fields;
uint16_t methods_count;
Method *methods;
} ClassFile;
/* ── helper: resolve UTF8 string from CP ───────────────────────────────── */
static inline const char *cp_utf8(const ClassFile *cf, uint16_t idx) {
if (idx == 0 || idx >= cf->cp_count) return "";
if (cf->cp[idx].tag != CP_UTF8) return "";
return cf->cp[idx].utf8.bytes;
}
/* ── function declarations ──────────────────────────────────────────────── */
ClassFile *classfile_load(const char *path);
void classfile_free(ClassFile *cf);
Method *classfile_find_method(ClassFile *cf, const char *name, const char *descriptor);
void classfile_dump(const ClassFile *cf); /* debug print */
#endif /* CLASSFILE_H */
+652
View File
@@ -0,0 +1,652 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include "classfile.h"
#include "helpers.h"
#include <stdlib.h>
#include <limits.h>
#ifndef PATH_MAX
#include <linux/limits.h>
#endif
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <ctype.h>
static int ends_with(const char *s, const char *suffix) {
size_t ls = strlen(s), lq = strlen(suffix);
if (lq > ls) return 0;
return strcmp(s + ls - lq, suffix) == 0;
}
static void trim_inplace(char *s) {
char *p = s;
while (*p && isspace((unsigned char)*p)) p++;
if (p != s) memmove(s, p, strlen(p) + 1);
size_t n = strlen(s);
while (n > 0 && isspace((unsigned char)s[n - 1])) s[--n] = '\0';
}
static void class_name_to_path(char *name) {
for (char *p = name; *p; p++) if (*p == '.') *p = '/';
if (!ends_with(name, ".class")) strcat(name, ".class");
}
static int shell_quote(const char *in, char *out, size_t out_sz) {
size_t k = 0;
if (out_sz < 3) return 0;
out[k++] = '\'';
for (const char *p = in; *p; p++) {
if (*p == '\'') {
if (k + 4 >= out_sz) return 0;
out[k++] = '\'';
out[k++] = '\\';
out[k++] = '\'';
out[k++] = '\'';
} else {
if (k + 1 >= out_sz) return 0;
out[k++] = *p;
}
}
if (k + 2 > out_sz) return 0;
out[k++] = '\'';
out[k] = '\0';
return 1;
}
static int find_first_class_file(const char *root, char *out_path, size_t out_sz) {
DIR *dir = opendir(root);
if (!dir) return 0;
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
char full[PATH_MAX];
snprintf(full, sizeof(full), "%s/%s", root, ent->d_name);
struct stat st;
if (stat(full, &st) != 0) continue;
if (S_ISDIR(st.st_mode)) {
if (find_first_class_file(full, out_path, out_sz)) {
closedir(dir);
return 1;
}
} else if (S_ISREG(st.st_mode) && ends_with(ent->d_name, ".class")) {
strncpy(out_path, full, out_sz - 1);
out_path[out_sz - 1] = '\0';
closedir(dir);
return 1;
}
}
closedir(dir);
return 0;
}
static int resolve_jar_entry_class(const char *extract_dir, char *out_class_file, size_t out_sz) {
char manifest[PATH_MAX];
snprintf(manifest, sizeof(manifest), "%s/META-INF/MANIFEST.MF", extract_dir);
FILE *fp = fopen(manifest, "r");
if (!fp) return find_first_class_file(extract_dir, out_class_file, out_sz);
char line[1024];
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "MIDlet-1:", 9) == 0) {
char value[512];
strncpy(value, line + 9, sizeof(value) - 1);
value[sizeof(value) - 1] = '\0';
trim_inplace(value);
char *a = strrchr(value, ',');
if (a) {
a++;
trim_inplace(a);
class_name_to_path(a);
snprintf(out_class_file, out_sz, "%s/%s", extract_dir, a);
fclose(fp);
return access(out_class_file, R_OK) == 0;
}
}
if (strncmp(line, "Main-Class:", 11) == 0) {
char value[512];
strncpy(value, line + 11, sizeof(value) - 1);
value[sizeof(value) - 1] = '\0';
trim_inplace(value);
class_name_to_path(value);
snprintf(out_class_file, out_sz, "%s/%s", extract_dir, value);
fclose(fp);
return access(out_class_file, R_OK) == 0;
}
}
fclose(fp);
return find_first_class_file(extract_dir, out_class_file, out_sz);
}
typedef struct {
intptr_t locals[256];
intptr_t stack[256];
int sp;
uint8_t *code;
int pc;
} Frame;
static intptr_t g_static_fields[65536];
static const char *resolve_class_name(ClassFile *cf, CPEntry *methodref) {
uint16_t cls_idx = methodref->ref.class_index;
uint16_t utf_idx = cf->cp[cls_idx].cls_or_str.index;
return cp_utf8(cf, utf_idx);
}
static int resolve_field_info(ClassFile *cf, uint16_t fieldref_idx, const char **class_name, const char **field_name, const char **field_desc) {
if (!cf || fieldref_idx == 0 || fieldref_idx >= cf->cp_count) return 0;
CPEntry *ref = &cf->cp[fieldref_idx];
if (ref->tag != CP_FIELDREF) return 0;
uint16_t cls_idx = ref->ref.class_index;
uint16_t nat_idx = ref->ref.name_and_type_index;
if (cls_idx == 0 || cls_idx >= cf->cp_count) return 0;
if (nat_idx == 0 || nat_idx >= cf->cp_count) return 0;
if (cf->cp[cls_idx].tag != CP_CLASS) return 0;
if (cf->cp[nat_idx].tag != CP_NAME_AND_TYPE) return 0;
CPEntry *nat = &cf->cp[nat_idx];
if (class_name) *class_name = cp_utf8(cf, cf->cp[cls_idx].cls_or_str.index);
if (field_name) *field_name = cp_utf8(cf, nat->nat.name_index);
if (field_desc) *field_desc = cp_utf8(cf, nat->nat.descriptor_index);
return 1;
}
static const char *current_class_name(ClassFile *cf) {
if (!cf || cf->this_class == 0 || cf->this_class >= cf->cp_count) return "";
if (cf->cp[cf->this_class].tag != CP_CLASS) return "";
return cp_utf8(cf, cf->cp[cf->this_class].cls_or_str.index);
}
static int field_slot(ClassFile *cf, uint16_t fieldref_idx) {
CPEntry *ref = &cf->cp[fieldref_idx];
CPEntry *nat = &cf->cp[ref->ref.name_and_type_index];
const char *fname = cp_utf8(cf, nat->nat.name_index);
for (int i = 0; i < cf->fields_count; i++)
if (strcmp(cp_utf8(cf, cf->fields[i].name_index), fname) == 0)
return i;
fprintf(stderr, "field not found: %s\n", fname);
return -1;
}
int execute(ClassFile *cf, Method *m, intptr_t *args, int argc) {
Frame f = {0};
f.code = m->code.code;
/* copy arguments into locals */
for (int i = 0; i < argc; i++)
f.locals[i] = args[i];
while (1) {
uint8_t op = f.code[f.pc++];
switch (op) {
case OP_ALOAD_0: f.stack[f.sp++] = f.locals[0]; break;
case OP_ALOAD_1: f.stack[f.sp++] = f.locals[1]; break;
case OP_ALOAD_2: f.stack[f.sp++] = f.locals[2]; break;
case OP_ALOAD_3: f.stack[f.sp++] = f.locals[3]; break;
case OP_NEW: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
CPEntry *cls = &cf->cp[idx];
Object *obj = heap_alloc(cls, 0);
f.stack[f.sp++] = OBJ_TO_SLOT(obj);
break;
}
case OP_DUP: f.stack[f.sp] = f.stack[f.sp - 1]; f.sp++; break;
case OP_ICONST_0: f.stack[f.sp++] = 0; break;
case OP_ICONST_1: f.stack[f.sp++] = 1; break;
case OP_ICONST_2: f.stack[f.sp++] = 2; break;
case OP_ICONST_3: f.stack[f.sp++] = 3; break;
case OP_ICONST_4: f.stack[f.sp++] = 4; break;
case OP_ICONST_5: f.stack[f.sp++] = 5; break;
case OP_BIPUSH: f.stack[f.sp++] = (int8_t)f.code[f.pc++]; break;
case OP_SIPUSH: {
int16_t v = (int16_t)((f.code[f.pc] << 8) | f.code[f.pc + 1]);
f.pc += 2;
f.stack[f.sp++] = v;
break;
}
case OP_ISTORE_0: f.locals[0] = f.stack[--f.sp]; break;
case OP_ISTORE_1: f.locals[1] = f.stack[--f.sp]; break;
case OP_ISTORE_2: f.locals[2] = f.stack[--f.sp]; break;
case OP_ISTORE_3: f.locals[3] = f.stack[--f.sp]; break;
case OP_ILOAD_0: f.stack[f.sp++] = f.locals[0]; break;
case OP_ILOAD_1: f.stack[f.sp++] = f.locals[1]; break;
case OP_ILOAD_2: f.stack[f.sp++] = f.locals[2]; break;
case OP_ILOAD_3: f.stack[f.sp++] = f.locals[3]; break;
case OP_IADD: {
int b = f.stack[--f.sp];
int a = f.stack[--f.sp];
f.stack[f.sp++] = a + b;
break;
}
case OP_ISUB:{
int b = f.stack[--f.sp];
int a = f.stack[--f.sp];
f.stack[f.sp++] = a - b;
break;
}
case OP_IF_ICMPGT: {
int16_t offset = (f.code[f.pc] << 8) | f.code[f.pc+1];
f.pc += 2;
int b = f.stack[--f.sp];
int a = f.stack[--f.sp];
if (a > b) f.pc += offset - 3;
break;
}
case OP_IFNULL: {
int16_t offset = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
intptr_t ref = f.stack[--f.sp];
if (ref == 0) f.pc += offset - 3;
break;
}
case OP_IFNONNULL: {
int16_t offset = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
intptr_t ref = f.stack[--f.sp];
if (ref != 0) f.pc += offset - 3;
break;
}
case OP_IRETURN: return f.stack[--f.sp];
case OP_RETURN: return 0;
case OP_GETSTATIC: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
const char *cls = NULL, *name = NULL, *desc = NULL;
if (resolve_field_info(cf, idx, &cls, &name, &desc) &&
strcmp(cls, "java/lang/System") == 0 &&
strcmp(name, "out") == 0 &&
strcmp(desc, "Ljava/io/PrintStream;") == 0) {
f.stack[f.sp++] = 0x57D0; /* fake PrintStream/static handle */
break;
}
f.stack[f.sp++] = g_static_fields[idx];
break;
}
case OP_PUTSTATIC: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
g_static_fields[idx] = f.stack[--f.sp];
break;
}
case OP_INVOKEVIRTUAL: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc+1];
f.pc += 2;
CPEntry *methodref = &cf->cp[idx];
CPEntry *nat = &cf->cp[methodref->ref.name_and_type_index];
const char *name = cp_utf8(cf, nat->nat.name_index);
const char *desc = cp_utf8(cf, nat->nat.descriptor_index);
const char *class_name = resolve_class_name(cf, methodref);
if (midp_native_call(cf, class_name, name, desc, f.stack, &f.sp, op)) break;
int nargs = 1; /* +1 for 'this' */
const char *d = desc + 1;
while (*d && *d != ')') {
switch (*d) {
case 'I': case 'Z': case 'B': case 'C': case 'S': case 'F':
nargs++; d++; break;
case 'J': case 'D': nargs += 2; d++; break;
case 'L': nargs++; while (*d && *d != ';') d++; d++; break;
case '[': nargs++;
while (*d == '[') d++;
if (*d == 'L') while (*d && *d != ';') d++;
d++; break;
default: d++; break;
}
}
ClassFile *target_cf = cf;
ClassFile *loaded_cf = NULL;
Method *target = classfile_find_method(cf, name, desc);
if (!target && class_name && *class_name) {
char class_path[PATH_MAX];
snprintf(class_path, sizeof(class_path), "%s.class", class_name);
loaded_cf = classfile_load(class_path);
if (loaded_cf) {
target = classfile_find_method(loaded_cf, name, desc);
if (target) target_cf = loaded_cf;
}
}
if (!target) {
if (loaded_cf) classfile_free(loaded_cf);
fprintf(stderr, "method not found: %s.%s%s\n", class_name, name, desc);
return -1;
}
intptr_t vargs[32];
for (int i = nargs - 1; i >= 0; i--) vargs[i] = f.stack[--f.sp];
int result = execute(target_cf, target, vargs, nargs);
if (loaded_cf) classfile_free(loaded_cf);
const char *ret = strchr(desc, ')') + 1;
if (*ret != 'V') f.stack[f.sp++] = result;
break;
}
case OP_LDC: {
uint8_t idx = f.code[f.pc++];
CPEntry *e = &cf->cp[idx];
switch (e->tag) {
case CP_INTEGER: f.stack[f.sp++] = e->integer.value; break;
case CP_FLOAT: { int b; memcpy(&b, &e->flt.value, 4); f.stack[f.sp++] = b; break; }
case CP_STRING: f.stack[f.sp++] = (int)idx; break;
default: fprintf(stderr, "ldc: unhandled tag %d\n", e->tag); return -1;
}
break;
}
case OP_LDC_W:
case OP_LDC2_W: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc+1];
f.pc += 2;
CPEntry *e = &cf->cp[idx];
switch (e->tag) {
case CP_INTEGER: f.stack[f.sp++] = e->integer.value; break;
case CP_LONG:
f.stack[f.sp++] = (int)(e->lng.value & 0xFFFFFFFF);
f.stack[f.sp++] = (int)(e->lng.value >> 32);
break;
case CP_STRING: f.stack[f.sp++] = (int)idx; break;
default: fprintf(stderr, "ldc_w: unhandled tag %d\n", e->tag); return -1;
}
break;
}
case OP_INVOKESTATIC: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc+1];
f.pc += 2;
CPEntry *methodref = &cf->cp[idx];
CPEntry *nat = &cf->cp[methodref->ref.name_and_type_index];
const char *name = cp_utf8(cf, nat->nat.name_index);
const char *desc = cp_utf8(cf, nat->nat.descriptor_index);
const char *class_name = resolve_class_name(cf, methodref);
if (midp_native_call(cf, class_name, name, desc, f.stack, &f.sp, op)) break;
ClassFile *target_cf = cf;
ClassFile *loaded_cf = NULL;
Method *target = classfile_find_method(cf, name, desc);
if (!target && class_name && *class_name) {
char class_path[PATH_MAX];
snprintf(class_path, sizeof(class_path), "%s.class", class_name);
loaded_cf = classfile_load(class_path);
if (loaded_cf) {
target = classfile_find_method(loaded_cf, name, desc);
if (target) target_cf = loaded_cf;
}
}
if (!target) {
if (loaded_cf) classfile_free(loaded_cf);
fprintf(stderr, "method not found: %s%s\n", name, desc);
return -1;
}
int nargs = 0;
const char *d = desc + 1; /* skip '(' */
while (*d && *d != ')') {
switch (*d) {
case 'I': case 'Z': case 'B':
case 'C': case 'S': case 'F':
nargs++; d++;
break;
case 'J': case 'D': /* long/double take 2 slots */
nargs += 2; d++;
break;
case 'L': /* object ref */
nargs++;
while (*d && *d != ';') d++;
d++; /* skip ';' */
break;
case '[': /* array */
nargs++;
while (*d == '[') d++; /* skip dimensions */
if (*d == 'L') while (*d && *d != ';') d++;
d++;
break;
}
}
intptr_t args[16];
for (int i = nargs - 1; i >= 0; i--) args[i] = f.stack[--f.sp];
int result = execute(target_cf, target, args, nargs);
if (loaded_cf) classfile_free(loaded_cf);
const char *ret = strchr(desc, ')') + 1;
if (*ret != 'V') f.stack[f.sp++] = result;
break;
}
case OP_ASTORE_0: f.locals[0] = f.stack[--f.sp]; break;
case OP_ASTORE_1: f.locals[1] = f.stack[--f.sp]; break;
case OP_ASTORE_2: f.locals[2] = f.stack[--f.sp]; break;
case OP_ASTORE_3: f.locals[3] = f.stack[--f.sp]; break;
case OP_INVOKESPECIAL: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc+1];
f.pc += 2;
CPEntry *methodref = &cf->cp[idx];
CPEntry *nat = &cf->cp[methodref->ref.name_and_type_index];
const char *name = cp_utf8(cf, nat->nat.name_index);
const char *desc = cp_utf8(cf, nat->nat.descriptor_index);
const char *class_name = resolve_class_name(cf, methodref);
const char *this_name = current_class_name(cf);
/* count args + implicit 'this' */
int nargs = 1;
const char *d = desc + 1;
while (*d && *d != ')') {
switch (*d) {
case 'I': case 'Z': case 'B': case 'C': case 'S': case 'F':
nargs++; d++; break;
case 'J': case 'D': nargs += 2; d++; break;
case 'L': nargs++; while (*d && *d != ';') d++; d++; break;
case '[': nargs++;
while (*d == '[') d++;
if (*d == 'L') while (*d && *d != ';') d++;
d++; break;
default: d++; break;
}
}
if (midp_native_call(cf, class_name, name, desc, f.stack, &f.sp, op)) break;
ClassFile *target_cf = cf;
ClassFile *loaded_cf = NULL;
Method *target = NULL;
if (class_name && *class_name && strcmp(class_name, this_name) != 0) {
char class_path[PATH_MAX];
snprintf(class_path, sizeof(class_path), "%s.class", class_name);
loaded_cf = classfile_load(class_path);
if (loaded_cf) {
target = classfile_find_method(loaded_cf, name, desc);
if (target) target_cf = loaded_cf;
}
} else {
target = classfile_find_method(cf, name, desc);
}
if (!target) {
/* super/<init> we don't have — just pop args+this */
f.sp -= nargs;
if (loaded_cf) classfile_free(loaded_cf);
break;
}
intptr_t sargs[32];
for (int i = nargs - 1; i >= 0; i--) sargs[i] = f.stack[--f.sp];
execute(target_cf, target, sargs, nargs);
if (loaded_cf) classfile_free(loaded_cf);
break;
}
case OP_PUTFIELD: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
intptr_t val = f.stack[--f.sp];
Object *obj = SLOT_TO_OBJ(f.stack[--f.sp]);
if (!obj) { fprintf(stderr, "NPE: putfield at pc=%d\n", f.pc - 3); return -1; }
int slot = field_slot(cf, idx);
if (slot < 0) return -1;
if (slot >= obj->fields_count) {
obj->fields = realloc(obj->fields, (slot + 1) * sizeof(intptr_t));
for (int i = obj->fields_count; i <= slot; i++) obj->fields[i] = 0;
obj->fields_count = slot + 1;
}
obj->fields[slot] = val;
break;
}
case OP_GETFIELD: {
uint16_t idx = (f.code[f.pc] << 8) | f.code[f.pc + 1];
f.pc += 2;
Object *obj = SLOT_TO_OBJ(f.stack[--f.sp]);
if (!obj) { fprintf(stderr, "NPE: getfield at pc=%d\n", f.pc - 3); return -1; }
int slot = field_slot(cf, idx);
if (slot < 0) return -1;
if (slot >= obj->fields_count) {
obj->fields = realloc(obj->fields, (slot + 1) * sizeof(intptr_t));
for (int i = obj->fields_count; i <= slot; i++) obj->fields[i] = 0;
obj->fields_count = slot + 1;
}
f.stack[f.sp++] = obj->fields[slot];
break;
}
default:
printf("unknown opcode 0x%02X at pc=%d\n", op, f.pc-1);
return -1;
}
}
}
int main(int argc, char *argv[]) {
int exit_code = 0;
char input_path[PATH_MAX];
char original_cwd[PATH_MAX];
char jar_temp_dir[PATH_MAX] = {0};
int changed_dir = 0;
if (argc < 2) {
fprintf(stderr, "usage: %s <file.class|file.jar>\n", argv[0]);
return 1;
}
strncpy(input_path, argv[1], sizeof(input_path) - 1);
input_path[sizeof(input_path) - 1] = '\0';
if (ends_with(input_path, ".jar")) {
if (!getcwd(original_cwd, sizeof(original_cwd))) {
perror("getcwd");
return 1;
}
char tmpl[] = "/tmp/kolibrios-jar-XXXXXX";
char *tmp = mkdtemp(tmpl);
if (!tmp) {
perror("mkdtemp");
return 1;
}
strncpy(jar_temp_dir, tmp, sizeof(jar_temp_dir) - 1);
char qjar[PATH_MAX * 2], qtmp[PATH_MAX * 2], cmd[PATH_MAX * 5];
if (!shell_quote(input_path, qjar, sizeof(qjar)) ||
!shell_quote(jar_temp_dir, qtmp, sizeof(qtmp))) {
fprintf(stderr, "path too long for jar extraction\n");
return 1;
}
snprintf(cmd, sizeof(cmd), "unzip -oq %s -d %s >/dev/null", qjar, qtmp);
if (system(cmd) != 0) {
fprintf(stderr, "failed to extract jar: %s\n", input_path);
return 1;
}
if (!resolve_jar_entry_class(jar_temp_dir, input_path, sizeof(input_path))) {
fprintf(stderr, "could not resolve entry class from jar manifest: %s\n", argv[1]);
return 1;
}
if (chdir(jar_temp_dir) != 0) {
perror("chdir");
return 1;
}
changed_dir = 1;
}
midp_sdl_init(240, 320);
/* step 1 - load the class file */
ClassFile *cf = classfile_load(input_path);
if (!cf) {
midp_sdl_destroy();
if (changed_dir) chdir(original_cwd);
return 1;
}
/* step 2 - execute entry point */
Method *m = classfile_find_method(cf, "main", "([Ljava/lang/String;)V");
if (!m) {
Method *start = classfile_find_method(cf, "startApp", "()V");
if (!start) {
fprintf(stderr, "no main or startApp method found\n");
exit_code = 1;
goto cleanup;
}
Object *self = heap_alloc(&cf->cp[cf->this_class], cf->fields_count);
if (!self) {
fprintf(stderr, "failed to allocate MIDlet instance\n");
exit_code = 1;
goto cleanup;
}
Method *ctor = classfile_find_method(cf, "<init>", "()V");
if (ctor) {
intptr_t cargs[1] = { OBJ_TO_SLOT(self) };
if (execute(cf, ctor, cargs, 1) < 0) {
exit_code = 1;
goto cleanup;
}
}
intptr_t sargs[1] = { OBJ_TO_SLOT(self) };
if (execute(cf, start, sargs, 1) < 0) {
exit_code = 1;
goto cleanup;
}
} else {
if (execute(cf, m, NULL, 0) < 0) {
exit_code = 1;
goto cleanup;
}
}
/* Keep emulator window visible after startup work. */
midp_sdl_wait(5000);
cleanup:
classfile_free(cf);
midp_sdl_destroy();
if (changed_dir) {
char qtmp[PATH_MAX * 2], cmd[PATH_MAX * 3];
chdir(original_cwd);
if (shell_quote(jar_temp_dir, qtmp, sizeof(qtmp))) {
snprintf(cmd, sizeof(cmd), "rm -rf %s", qtmp);
system(cmd);
}
}
return exit_code;
}
+439
View File
@@ -0,0 +1,439 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "helpers.h"
#define HEAP_POOL_SIZE 4096
static Object pool[HEAP_POOL_SIZE];
static Object *free_list = NULL;
static int initialized = 0;
static void heap_init(void) {
for (int i = 0; i < HEAP_POOL_SIZE - 1; i++)
pool[i].next_free = &pool[i + 1];
pool[HEAP_POOL_SIZE - 1].next_free = NULL;
free_list = &pool[0];
initialized = 1;
}
Object *heap_alloc(CPEntry *cls, uint16_t fields_count) {
if (!initialized) heap_init();
if (!free_list) {
fprintf(stderr, "heap: out of memory\n");
return NULL;
}
Object *obj = free_list;
free_list = obj->next_free;
obj->next_free = NULL;
obj->cls = cls;
obj->fields_count = fields_count;
obj->fields = calloc(fields_count, sizeof(intptr_t));
return obj;
}
void heap_free(Object *obj) {
if (!obj) return;
free(obj->fields);
obj->fields = NULL;
obj->next_free = free_list;
free_list = obj;
}
void heap_destroy(void) {
for (int i = 0; i < HEAP_POOL_SIZE; i++)
if (pool[i].next_free == NULL && pool[i].fields)
free(pool[i].fields);
}
/* SDL2 globals */
static SDL_Window *g_win = NULL;
static SDL_Renderer *g_ren = NULL;
static TTF_Font *g_font = NULL;
extern int execute(ClassFile *cf, Method *m, intptr_t *args, int argc);
/* Fake "Display" object slot — just a sentinel int, not a real Object */
#define DISPLAY_SENTINEL 0xD15F1A7
void midp_sdl_init(int w, int h) {
SDL_Init(SDL_INIT_VIDEO);
if (TTF_Init() != 0) {
fprintf(stderr, "SDL_ttf init failed: %s\n", TTF_GetError());
}
g_win = SDL_CreateWindow("MIDlet", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, 0);
g_ren = SDL_CreateRenderer(g_win, -1, SDL_RENDERER_ACCELERATED);
if (!g_font) {
const char *font_candidates[] = {
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf",
"/usr/share/fonts/truetype/freefont/FreeSans.ttf"
};
for (size_t i = 0; i < sizeof(font_candidates) / sizeof(font_candidates[0]); i++) {
g_font = TTF_OpenFont(font_candidates[i], 16);
if (g_font) break;
}
if (!g_font) {
fprintf(stderr, "SDL_ttf font load failed: %s\n", TTF_GetError());
}
}
SDL_SetRenderDrawColor(g_ren, 0, 0, 0, 255);
SDL_RenderClear(g_ren);
SDL_RenderPresent(g_ren);
}
void midp_sdl_destroy(void) {
if (g_font) {
TTF_CloseFont(g_font);
g_font = NULL;
}
if (TTF_WasInit()) TTF_Quit();
SDL_DestroyRenderer(g_ren);
SDL_DestroyWindow(g_win);
SDL_Quit();
}
void midp_sdl_wait(int milliseconds) {
Uint32 start = SDL_GetTicks();
SDL_Event ev;
while ((int)(SDL_GetTicks() - start) < milliseconds) {
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT) {
return;
}
}
SDL_Delay(16);
}
}
/* helper: match class/name/desc triple */
static int is(const char *a, const char *b) { return strcmp(a, b) == 0; }
static void draw_text_ttf(const char *text, int x, int y) {
if (!text || !*text || !g_font || !g_ren) return;
Uint8 r = 255, g = 255, b = 255, a = 255;
SDL_GetRenderDrawColor(g_ren, &r, &g, &b, &a);
SDL_Color color = { r, g, b, a };
SDL_Surface *surface = TTF_RenderUTF8_Blended(g_font, text, color);
if (!surface) return;
SDL_Texture *texture = SDL_CreateTextureFromSurface(g_ren, surface);
if (!texture) {
SDL_FreeSurface(surface);
return;
}
SDL_Rect dst = { x, y, surface->w, surface->h };
SDL_FreeSurface(surface);
SDL_RenderCopy(g_ren, texture, NULL, &dst);
SDL_DestroyTexture(texture);
}
static void draw_arc_outline(int cx, int cy, int rx, int ry, int start_deg, int arc_deg) {
int end_deg = start_deg + arc_deg;
for (int deg = start_deg; deg <= end_deg; deg++) {
double rad = deg * 3.14159265358979323846 / 180.0;
int x = cx + (int)(rx * cos(rad));
int y = cy + (int)(ry * sin(rad));
SDL_RenderDrawPoint(g_ren, x, y);
}
}
static int invoke_canvas_paint(ClassFile *caller_cf, intptr_t canvas_slot) {
Object *canvas = SLOT_TO_OBJ(canvas_slot);
if (!canvas || !canvas->cls || canvas->cls->tag != CP_CLASS) return 0;
const char *canvas_class = cp_utf8(caller_cf, canvas->cls->cls_or_str.index);
if (!canvas_class || !canvas_class[0]) return 0;
char class_path[512];
snprintf(class_path, sizeof(class_path), "%s.class", canvas_class);
ClassFile *canvas_cf = classfile_load(class_path);
if (!canvas_cf) {
fprintf(stderr, "cannot load canvas class file: %s\n", class_path);
return 0;
}
Method *paint = classfile_find_method(canvas_cf, "paint",
"(Ljavax/microedition/lcdui/Graphics;)V");
if (!paint) {
classfile_free(canvas_cf);
return 0;
}
intptr_t args[2] = { canvas_slot, 0xC0FFEE };
int rc = execute(canvas_cf, paint, args, 2);
classfile_free(canvas_cf);
SDL_RenderPresent(g_ren);
return rc >= 0;
}
int midp_native_call(ClassFile *cf, const char *class_name, const char *name, const char *desc, intptr_t *stack, int *sp, int opcode __attribute__((unused)))
{
if (is(desc, "()I") && is(name, "getWidth")) {
int w, h;
(void)h;
SDL_GetWindowSize(g_win, &w, &h);
(*sp)--; /* pop 'this' */
stack[(*sp)++] = w;
return 1;
}
if (is(desc, "()I") && is(name, "getHeight")) {
int w, h;
(void)w;
SDL_GetWindowSize(g_win, &w, &h);
(*sp)--; /* pop 'this' */
stack[(*sp)++] = h;
return 1;
}
/* javax.microedition.lcdui.Display */
if (is(class_name, "javax/microedition/lcdui/Display")) {
/* Display.getDisplay(MIDlet) → Display */
if (is(name, "getDisplay") &&
is(desc, "(Ljavax/microedition/midlet/MIDlet;)Ljavax/microedition/lcdui/Display;")) {
(*sp)--; /* pop MIDlet ref */
stack[(*sp)++] = DISPLAY_SENTINEL;
return 1;
}
/* Display.setCurrent(Displayable) → void */
if (is(name, "setCurrent") &&
is(desc, "(Ljavax/microedition/lcdui/Displayable;)V")) {
intptr_t displayable = stack[--(*sp)];
intptr_t display_ref = stack[--(*sp)]; /* 'this' for virtual */
(void)displayable; (void)display_ref;
/* Clear and show a frame so emulator window is visibly active. */
SDL_SetRenderDrawColor(g_ren, 0, 0, 0, 255);
SDL_RenderClear(g_ren);
/* Try to run original Canvas.paint(Graphics). */
invoke_canvas_paint(cf, displayable);
SDL_RenderPresent(g_ren);
return 1;
}
}
/* javax.microedition.lcdui.Graphics ─ */
if (is(class_name, "javax/microedition/lcdui/Graphics")) {
/* setColor(int) */
if (is(name, "setColor") && is(desc, "(I)V")) {
int rgb = stack[--(*sp)];
(*sp)--; /* pop 'this' */
SDL_SetRenderDrawColor(g_ren,
(rgb >> 16) & 0xFF,
(rgb >> 8) & 0xFF,
rgb & 0xFF, 255);
return 1;
}
/* setColor(r, g, b) */
if (is(name, "setColor") && is(desc, "(III)V")) {
int b = stack[--(*sp)] & 0xFF;
int g = stack[--(*sp)] & 0xFF;
int r = stack[--(*sp)] & 0xFF;
(*sp)--; /* pop 'this' */
SDL_SetRenderDrawColor(g_ren, r, g, b, 255);
return 1;
}
/* fillRect(x, y, w, h) */
if (is(name, "fillRect") && is(desc, "(IIII)V")) {
int h2 = stack[--(*sp)];
int w2 = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
(*sp)--; /* pop 'this' */
SDL_Rect r = { x, y, w2, h2 };
SDL_RenderFillRect(g_ren, &r);
return 1;
}
/* drawRect(x, y, w, h) */
if (is(name, "drawRect") && is(desc, "(IIII)V")) {
int h2 = stack[--(*sp)];
int w2 = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
(*sp)--;
SDL_Rect r = { x, y, w2, h2 };
SDL_RenderDrawRect(g_ren, &r);
return 1;
}
/* drawLine(x1, y1, x2, y2) */
if (is(name, "drawLine") && is(desc, "(IIII)V")) {
int y2 = stack[--(*sp)];
int x2 = stack[--(*sp)];
int y1 = stack[--(*sp)];
int x1 = stack[--(*sp)];
(*sp)--;
SDL_RenderDrawLine(g_ren, x1, y1, x2, y2);
return 1;
}
/* drawString(String, x, y, anchor) - anchor ignored for now */
if (is(name, "drawString") && is(desc, "(Ljava/lang/String;III)V")) {
int anchor = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
int sidx = stack[--(*sp)];
(*sp)--;
(void)anchor;
if (sidx > 0 && sidx < cf->cp_count && cf->cp[sidx].tag == CP_STRING) {
const char *text = cp_utf8(cf, cf->cp[sidx].cls_or_str.index);
draw_text_ttf(text, x, y);
}
return 1;
}
if (is(name, "drawArc") && is(desc, "(IIIIII)V")) {
int arc_deg = stack[--(*sp)];
int start = stack[--(*sp)];
int h2 = stack[--(*sp)];
int w2 = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
(*sp)--;
int cx = x + (w2 / 2);
int cy = y + (h2 / 2);
int rx = w2 / 2;
int ry = h2 / 2;
draw_arc_outline(cx, cy, rx, ry, start, arc_deg);
return 1;
}
/* flushGraphics / repaint stubs — just present */
if (is(name, "flushGraphics") || is(name, "repaint")) {
/* pop whatever args there are, then 'this' */
/* for safety just drain back to a known depth — caller handles */
SDL_RenderPresent(g_ren);
return 1;
}
}
/* javax.microedition.lcdui.Canvas */
if (is(class_name, "javax/microedition/lcdui/Canvas")) {
/* Canvas.repaint() */
if (is(name, "repaint") && is(desc, "()V")) {
(*sp)--; /* pop 'this' */
SDL_RenderPresent(g_ren);
return 1;
}
/* Canvas.getGraphics() → Graphics (stub: return sentinel) */
if (is(name, "getGraphics") &&
is(desc, "()Ljavax/microedition/lcdui/Graphics;")) {
(*sp)--; /* pop 'this' */
stack[(*sp)++] = 0xC0FFEE; /* fake Graphics handle */
return 1;
}
}
/* java.io.PrintStream ─ */
if (is(class_name, "java/io/PrintStream")) {
if (is(name, "println")) {
if (is(desc, "(I)V")) {
int val = stack[--(*sp)]; (*sp)--;
printf("%d\n", val);
return 1;
}
if (is(desc, "(Ljava/lang/String;)V")) {
int str_idx = stack[--(*sp)]; (*sp)--;
CPEntry *e = &cf->cp[str_idx];
if (e->tag == CP_STRING)
printf("%s\n", cp_utf8(cf, e->cls_or_str.index));
return 1;
}
if (is(desc, "(Z)V")) {
int val = stack[--(*sp)]; (*sp)--;
printf("%s\n", val ? "true" : "false");
return 1;
}
if (is(desc, "()V")) { (*sp)--; printf("\n"); return 1; }
}
if (is(name, "print")) {
if (is(desc, "(I)V")) {
int val = stack[--(*sp)]; (*sp)--;
printf("%d", val);
return 1;
}
if (is(desc, "(Ljava/lang/String;)V")) {
int str_idx = stack[--(*sp)]; (*sp)--;
CPEntry *e = &cf->cp[str_idx];
if (e->tag == CP_STRING)
printf("%s", cp_utf8(cf, e->cls_or_str.index));
return 1;
}
}
}
/* java.lang.Thread (minimal stubs) */
if (is(class_name, "java/lang/Thread")) {
if (is(name, "sleep")) {
long long ms = stack[--(*sp)];
(*sp)--; /* long takes 2 slots */
SDL_Delay((uint32_t)ms);
return 1;
}
if (is(name, "start") || is(name, "join")) {
(*sp)--;
return 1;
}
}
/* javax.microedition.midlet.MIDlet (common stubs) */
if (is(name, "getAppProperty") && is(desc, "(Ljava/lang/String;)Ljava/lang/String;")) {
/* this, key -> null */
(*sp) -= 2;
stack[(*sp)++] = 0;
return 1;
}
if (is(name, "notifyDestroyed") && is(desc, "()V")) {
(*sp)--; /* this */
return 1;
}
if (is(name, "notifyPaused") && is(desc, "()V")) {
(*sp)--; /* this */
return 1;
}
if (is(name, "platformRequest") && is(desc, "(Ljava/lang/String;)Z")) {
(*sp) -= 2; /* this, url */
stack[(*sp)++] = 0;
return 1;
}
if (is(name, "checkPermission") && is(desc, "(Ljava/lang/String;)I")) {
(*sp) -= 2; /* this, permission */
stack[(*sp)++] = 1; /* allowed */
return 1;
}
return 0; /* not handled */
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef HEALPER_H
#define HEALPER_H
#include <stdint.h>
#include "classfile.h"
typedef struct Object {
CPEntry *cls;
uint16_t fields_count;
intptr_t *fields;
struct Object *next_free;
} Object;
#define OBJ_TO_SLOT(o) ((intptr_t)(uintptr_t)(o))
#define SLOT_TO_OBJ(s) ((Object *)(uintptr_t)(intptr_t)(s))
int midp_native_call(ClassFile *cf, const char *class_name, const char *name, const char *desc, intptr_t *stack, int *sp, int opcode);
void midp_sdl_init(int w, int h);
void midp_sdl_wait(int milliseconds);
void midp_sdl_destroy(void);
Object *heap_alloc(CPEntry *cls, uint16_t fields_count);
void heap_free(Object *obj);
void heap_destroy(void);
#endif
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
#include <stdio.h>
#include "classfile.h"
int execute(ClassFile *cf, Method *m, int *args, int argc);
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "usage: %s <file.class>\n", argv[0]);
return 1;
}
/* step 1 - load the class file */
ClassFile *cf = classfile_load(argv[1]);
if (!cf) return 1;
/* step 2 - find the main method */
Method *m = classfile_find_method(cf, "main", "([Ljava/lang/String;)V");
if (!m) m = classfile_find_method(cf, "startApp", "()V");
if (!m) {
fprintf(stderr, "no main or startApp method found\n");
return 1;
}
/* step 3 - execute it */
execute(cf, m, NULL, 0);
classfile_free(cf);
return 0;
}