|
Java example source code file (Subclassing.m)
The Subclassing.m Java example source code/* * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "com_apple_jobjc_Subclassing.h" #include <math.h> #include <assert.h> #include <errno.h> #include <objc/runtime.h> #include <objc/message.h> #include <ffi/ffi.h> #include <sys/mman.h> #include <JavaNativeFoundation/JavaNativeFoundation.h> // Subclassing of Obj-C classes in Java // // See: // - Objective-C Runtime documentation // - man ffi_prep_closure // - Subclassing.java #pragma mark Accessing object in IVar #define JOBJ_IVAR_NAME "jObjWrapper" static jobject getJObjectFromIVar(id obj); jobject getJObjectFromIVar(id obj) { JNFJObjectWrapper *wrapper = NULL; object_getInstanceVariable(obj, JOBJ_IVAR_NAME, (void**) &wrapper); return wrapper ? [wrapper jObject] : NULL; } JNIEXPORT jobject JNICALL Java_com_apple_jobjc_Subclassing_getJObjectFromIVar (JNIEnv *env, jclass jClass, jlong jPtr) { id obj = (id) jlong_to_ptr(jPtr); if(obj == NULL){ (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/NullPointerException"), "obj"); return NULL; } JNFJObjectWrapper *wrapper; if(!object_getInstanceVariable(obj, JOBJ_IVAR_NAME, (void**) &wrapper)){ NSLog(@"IVar '%s' not found. obj: %@", JOBJ_IVAR_NAME, obj); (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Could not find instance variable that holds Java object."); return NULL; } return wrapper ? [wrapper jObject] : NULL; } JNIEXPORT void JNICALL Java_com_apple_jobjc_Subclassing_initJObjectToIVar (JNIEnv *env, jclass jClass, jlong jPtr, jobject jObject) { id obj = (id) jlong_to_ptr(jPtr); JNFJObjectWrapper *wrapper = [[JNFJObjectWrapper alloc] initWithJObject:jObject withEnv:env]; [wrapper retain]; if(!object_setInstanceVariable(obj, JOBJ_IVAR_NAME, wrapper)){ NSLog(@"IVar '%s' not found. obj: %@", JOBJ_IVAR_NAME, obj); (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), "Could not find instance variable that holds Java object."); return; } } #pragma mark Registering class JNIEXPORT jlong JNICALL Java_com_apple_jobjc_Subclassing_allocateClassPair (JNIEnv *env, jclass clazz, jlong jSuperClass, jstring jName) { const Class superClass = (Class)jlong_to_ptr(jSuperClass); assert(superClass); const char *name = (*env)->GetStringUTFChars(env, jName, JNI_FALSE); const Class newClass = objc_allocateClassPair(superClass, name, 0); (*env)->ReleaseStringUTFChars(env, jName, name); return ptr_to_jlong(newClass); } JNIEXPORT jboolean JNICALL Java_com_apple_jobjc_Subclassing_addIVarForJObj (JNIEnv *env, jclass clazz, jlong jSynthClass) { return class_addIvar( jlong_to_ptr(jSynthClass), JOBJ_IVAR_NAME, sizeof(id), (uint8_t)log2((double)sizeof(id)), "@"); } JNIEXPORT void JNICALL Java_com_apple_jobjc_Subclassing_registerClassPair (JNIEnv *env, jclass clazz, jlong jClass) { Class c = jlong_to_ptr(jClass); // NSLog(@"Registering class pair %p / %s", c, class_getName(c)); objc_registerClassPair(c); } #pragma mark Patching +alloc static id patchedAllocIMP(id obj, SEL sel); static void addJavaInstance(id obj); JNIEXPORT jboolean JNICALL Java_com_apple_jobjc_Subclassing_patchAlloc (JNIEnv *env, jclass clazz, jlong jNativeClass) { Class metaClass = object_getClass(jlong_to_ptr(jNativeClass)); return class_addMethod(metaClass, sel_registerName("alloc"), (IMP) patchedAllocIMP, "@@:"); } static id patchedAllocIMP(id cls, SEL sel){ id inst = class_createInstance(cls, 0); addJavaInstance(inst); return inst; } static void addJavaInstance(id obj){ // NSLog(@"addJavaInstance %p", obj); // NSLog(@"... calling up to Java"); static JNF_CLASS_CACHE(jc_Subclassing, "com/apple/jobjc/Subclassing"); static JNF_STATIC_MEMBER_CACHE(jm_Subclassing_initJObject, jc_Subclassing, "initJObject", "(J)V"); JNFThreadContext threadWasAttached = JNFThreadDetachOnThreadDeath; JNIEnv *env = JNFObtainEnv(&threadWasAttached); JNFCallStaticVoidMethod(env, jm_Subclassing_initJObject, ptr_to_jlong(obj)); JNFReleaseEnv(env, &threadWasAttached); } #pragma mark Adding methods static ffi_closure *make_closure(ffi_cif *cif, void *user_data); static void sel_closure_call(ffi_cif* cif, void* result, void** args, void* user_data); typedef struct closure_data_t{ JNFJObjectWrapper *jMethod; JNFJObjectWrapper *jCIF; } closure_data_t; static ffi_closure *make_closure(ffi_cif *cif, void *user_data){ // Allocate a page to hold the closure with read and write permissions. ffi_closure *closure; if ((closure = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, (off_t) 0)) == (void*)-1) { fprintf(stderr, "mmap failed with errno: %d", errno); return NULL; } // Prepare the ffi_closure structure. ffi_status status; if ((status = ffi_prep_closure(closure, cif, sel_closure_call, (void *)user_data)) != FFI_OK) { fprintf(stderr, "ffi_prep_closure failed with ffi_status: %d", status); munmap(closure, sizeof(ffi_closure)); return NULL; } // Ensure that the closure will execute on all architectures. if (mprotect(closure, sizeof(closure), PROT_READ | PROT_EXEC) == -1) { fprintf(stderr, "mprotect failed with errno: %d", errno); munmap(closure, sizeof(ffi_closure)); return NULL; } return closure; } JNIEXPORT jboolean JNICALL Java_com_apple_jobjc_Subclassing_addMethod (JNIEnv *env, jclass clazz, jlong jClass, jstring jSelName, jobject jMethod, jobject jCIF, jlong jCIFPtr, jstring jObjCEncodedType) { ffi_cif *cif = jlong_to_ptr(jCIFPtr); closure_data_t *user_data = malloc(sizeof(closure_data_t)); user_data->jMethod = [[JNFJObjectWrapper alloc] initWithJObject:jMethod withEnv:env]; user_data->jCIF = [[JNFJObjectWrapper alloc] initWithJObject:jCIF withEnv:env]; ffi_closure *closure;; if(!(closure = make_closure(cif, user_data))){ [user_data->jMethod release]; [user_data->jCIF release]; free(user_data); return NO; } const Class objcClass = (Class)jlong_to_ptr(jClass); const char *selName = (*env)->GetStringUTFChars(env, jSelName, JNI_FALSE); const char *objCEncodedType = (*env)->GetStringUTFChars(env, jObjCEncodedType, JNI_FALSE); // NSLog(@"Adding method '%s' :: '%s' to '%s' / %p", // selName, // objCEncodedType, // class_getName(objcClass), // objcClass); BOOL ret = class_addMethod(objcClass, sel_registerName(selName), (IMP) closure, objCEncodedType); (*env)->ReleaseStringUTFChars(env, jSelName, selName); (*env)->ReleaseStringUTFChars(env, jObjCEncodedType, objCEncodedType); if(!ret){ NSLog(@"class_addMethod failed"); munmap(closure, sizeof(ffi_closure)); [user_data->jMethod release]; [user_data->jCIF release]; free(user_data); return NO; } return ret; } static void sel_closure_call(ffi_cif* cif, void* result, void** args, void* user_data) { id obj = *(id*) args[0]; // SEL sel = *(SEL*) args[1]; // NSLog(@"Subclassing: sel_closure_call: %p %p", obj, sel); // NSLog(@"Subclassing: sel_closure_call: obj class: %@ sel name: %s", object_getClass(obj), sel_getName(sel)); jobject jObj = getJObjectFromIVar(obj); if(!jObj){ addJavaInstance(obj); jObj = getJObjectFromIVar(obj); } closure_data_t *jmeta = user_data; jobject jMethod = [jmeta->jMethod jObject]; jobject jCIF = [jmeta->jCIF jObject]; JNFThreadContext threadWasAttached = JNFThreadDetachOnThreadDeath; JNIEnv *env = JNFObtainEnv(&threadWasAttached); if((*env)->ExceptionOccurred(env)) goto bail; static JNF_CLASS_CACHE(jc, "com/apple/jobjc/Subclassing"); static JNF_STATIC_MEMBER_CACHE(jm_invokeFromJNI, jc, "invokeFromJNI", "(Lcom/apple/jobjc/ID;Ljava/lang/reflect/Method;Lcom/apple/jobjc/CIF;JJ)V"); JNFCallStaticVoidMethod(env, jm_invokeFromJNI, jObj, jMethod, jCIF, ptr_to_jlong(result), ptr_to_jlong(args)); bail: JNFReleaseEnv(env, &threadWasAttached); if((*env)->ExceptionOccurred(env)){ NSLog(@"Exception!"); (*env)->ExceptionDescribe(env); } JNFReleaseEnv(env, &threadWasAttached); } Other Java examples (source code examples)Here is a short list of links related to this Java Subclassing.m source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.