1 /* 2 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.apple.jobjc; 26 27 import java.lang.ref.WeakReference; 28 import java.lang.reflect.Constructor; 29 import java.util.LinkedHashMap; 30 import java.util.Map; 31 32 import javax.tools.annotation.GenerateNativeHeader; 33 34 /* No native methods here, but the constants are needed in the supporting JNI code */ 35 @GenerateNativeHeader 36 public class ID extends Pointer<Void>{ 37 static native String getNativeDescription(final long objPtr); 38 39 final JObjCRuntime runtime; 40 41 static final Class[] CTOR_ARGS = { long.class, JObjCRuntime.class }; 42 protected ID(final long objPtr, final JObjCRuntime runtime) { 43 super(objPtr); 44 runtime.assertOK(); 45 this.runtime = runtime; 46 } 47 48 protected ID(final ID obj, final JObjCRuntime runtime) { 49 this(obj.ptr, runtime); 50 } 51 52 @Override protected NativeObjectLifecycleManager getNativeObjectLifecycleManager() { 53 return NativeObjectLifecycleManager.CFRetainRelease.INST; 54 } 55 56 protected final JObjCRuntime getRuntime() { return runtime; } 57 58 @Override public String toString(){ 59 String s = super.toString(); 60 return s + " (ObjC: " + ptr + " / " + Long.toHexString(ptr) + ")"; 61 } 62 63 // 64 65 public static <T extends ID> T getInstance(final long ptr, final JObjCRuntime runtime){ 66 return (T) getObjCObjectFor(runtime, ptr); 67 } 68 69 static <T extends ID> T getObjCObjectFor(final JObjCRuntime runtime, final long objPtr){ 70 if (objPtr == 0) return null; 71 72 final WeakReference cachedObj = objectCache.get().get(objPtr); 73 if(cachedObj != null && cachedObj.get() != null) return (T) cachedObj.get(); 74 75 final long clsPtr = NSClass.getClass(objPtr); 76 77 final T newObj = (T) (runtime.subclassing.isUserClass(clsPtr) ? 78 Subclassing.getJObjectFromIVar(objPtr) 79 : createNewObjCObjectFor(runtime, objPtr, clsPtr)); 80 81 objectCache.get().put(objPtr, new WeakReference(newObj)); 82 return newObj; 83 } 84 85 static <T extends ID> T createNewObjCObjectFor(final JObjCRuntime runtime, final long objPtr, final long clsPtr) { 86 final Constructor<T> ctor = getConstructorForClassPtr(runtime, clsPtr); 87 return (T) createNewObjCObjectForConstructor(ctor, objPtr, runtime); 88 } 89 90 @SuppressWarnings("unchecked") 91 static <T extends ID> Constructor<T> getConstructorForClassPtr(final JObjCRuntime runtime, final long clazzPtr){ 92 final Constructor<T> cachedCtor = (Constructor<T>) constructorCache.get().get(clazzPtr); 93 if(cachedCtor != null) return cachedCtor; 94 95 final Class<T> clazz = getClassForClassPtr(runtime, clazzPtr); 96 Constructor<T> ctor; 97 try { 98 ctor = clazz.getDeclaredConstructor(CTOR_ARGS); 99 } catch (SecurityException e) { 100 throw new RuntimeException(e); 101 } catch (NoSuchMethodException e) { 102 throw new RuntimeException(e); 103 } 104 ctor.setAccessible(true); 105 constructorCache.get().put(clazzPtr, (Constructor<ID>) ctor); 106 return ctor; 107 } 108 109 @SuppressWarnings("unchecked") 110 static <T extends ID> Class<T> getClassForClassPtr(final JObjCRuntime runtime, final long clazzPtr){ 111 final String className = NSClass.getClassNameOfClass(clazzPtr); 112 final Class<T> clazz = (Class<T>) runtime.getClassForNativeClassName(className); 113 if(clazz == null){ 114 final long superClazzPtr = NSClass.getSuperClassOfClass(clazzPtr); 115 if(superClazzPtr != 0) 116 return getClassForClassPtr(runtime, superClazzPtr); 117 } 118 return clazz; 119 } 120 121 static <T extends ID> T createNewObjCObjectForConstructor(final Constructor ctor, final long objPtr, final JObjCRuntime runtime) { 122 try { 123 final T newInstance = (T) ctor.newInstance(new Object[] { Long.valueOf(objPtr), runtime }); 124 objectCache.get().put(objPtr, new WeakReference(newInstance)); 125 return newInstance; 126 } catch (final Exception e) { 127 throw new RuntimeException(e); 128 } 129 } 130 131 static <T extends ID> T createNewObjCObjectForClass(final Class<T> clazz, final long objPtr, final JObjCRuntime runtime) { 132 try { 133 final Constructor<T> constructor = clazz.getDeclaredConstructor(CTOR_ARGS); 134 constructor.setAccessible(true); 135 return (T) createNewObjCObjectForConstructor(constructor, objPtr, runtime); 136 } catch (final Exception e) { 137 throw new RuntimeException(e); 138 } 139 } 140 141 // 142 143 static final ThreadLocal<LinkedHashMap<Long, Constructor>> constructorCache = new ThreadLocal<LinkedHashMap<Long, Constructor>>(){ 144 @Override protected LinkedHashMap<Long, Constructor> initialValue(){ 145 final int MAX_ENTRIES = 1000; 146 final float LOAD_FACTOR = 0.75f; 147 return new LinkedHashMap<Long, Constructor>((int) (MAX_ENTRIES/LOAD_FACTOR), LOAD_FACTOR, true) { 148 @Override protected boolean removeEldestEntry(Map.Entry<Long, Constructor> eldest) { 149 return size() > MAX_ENTRIES; 150 } 151 }; 152 } 153 }; 154 155 static final ThreadLocal<LinkedHashMap<Long, WeakReference>> objectCache = new ThreadLocal<LinkedHashMap<Long, WeakReference>>(){ 156 @Override protected LinkedHashMap<Long, WeakReference> initialValue(){ 157 final int MAX_ENTRIES = 1000; 158 final float LOAD_FACTOR = 0.75f; 159 return new LinkedHashMap<Long, WeakReference>((int) (MAX_ENTRIES/LOAD_FACTOR), LOAD_FACTOR, true) { 160 @Override protected boolean removeEldestEntry(Map.Entry<Long, WeakReference> eldest) { 161 return size() > MAX_ENTRIES || eldest.getValue().get() == null; 162 } 163 }; 164 } 165 }; 166 }