662 lines
26 KiB
C
662 lines
26 KiB
C
#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;
|
|
}
|
|
|
|
/* Guard against accidental self-resolution causing infinite recursion. */
|
|
if (target_cf == cf && target == m) {
|
|
fprintf(stderr, "recursive invokespecial guard: %s.%s%s\n", class_name, name, desc);
|
|
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;
|
|
}
|
|
|