All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.objc.ObjCBlock Maven / Gradle / Ivy

There is a newer version: 2.3.21
Show newest version
/*
 * Copyright (C) 2013 RoboVM AB
 *
 * 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.
 */
package org.robovm.objc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.IdentityHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.robovm.objc.annotation.TypeEncoding;
import org.robovm.objc.block.VoidBooleanBlock;
import org.robovm.rt.VM;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.annotation.MachineSizedUInt;
import org.robovm.rt.bro.annotation.Pointer;
import org.robovm.rt.bro.annotation.StructMember;
import org.robovm.rt.bro.ptr.BytePtr;

/**
 * {@link Struct} mapping the {@code Block_literal} struct used internally by
 * the Objective-C runtime. This class is only for internal use. See the
 * source code of the block interfaces in the {@link org.robovm.objc.block}
 * package (e.g. {@link VoidBooleanBlock} for examples on how to marshal
 * Objective-C block types.
 * The Block ABI
 * documentation has more information on how blocks are implemented in 
 * Objective-C.
 */
public final class ObjCBlock extends Struct {
    @StructMember(0)
    public native @Pointer long isa();
    
    @StructMember(0)
    public native ObjCBlock isa(@Pointer long isa);
    
    @StructMember(1)
    public native int flags();
    
    @StructMember(1)
    public native ObjCBlock flags(int flags);

    @StructMember(2)
    public native int reserved();
    
    @StructMember(2)
    public native ObjCBlock reserved(int reserved);

    @StructMember(3)
    public native @Pointer long invoke();
    
    @StructMember(3)
    public native ObjCBlock invoke(@Pointer long invoke);

    @StructMember(4)
    public native Descriptor descriptor();
    
    @StructMember(4)
    public native ObjCBlock descriptor(Descriptor descriptor);
    
    @StructMember(5)
    public native @Pointer long object_addr();
    
    @StructMember(5)
    public native ObjCBlock object_addr(@Pointer long object_addr);
    
    @StructMember(6)
    public native @Pointer long wrapper_addr();
    
    @StructMember(6)
    public native ObjCBlock wrapper_addr(@Pointer long wrapper_addr);

    public static void setHandle(ObjCBlock block, long handle) {
        block.setHandle(handle);
    }
    
    public Object object() {
        return VM.castAddressToObject(object_addr());
    }

    public ObjCBlock object(Object o) {
        object_addr(VM.getObjectAddress(o));
        return this;
    }

    public Wrapper wrapper() {
        return (Wrapper) VM.castAddressToObject(wrapper_addr());
    }

    public ObjCBlock wrapper(Wrapper o) {
        wrapper_addr(VM.getObjectAddress(o));
        return this;
    }
    
    public boolean hasObject() {
        return descriptor().getHandle() == Wrapper.DESCRIPTOR.getHandle();
    }
    
    public static final class Descriptor extends Struct {
        @StructMember(0)
        public native @MachineSizedUInt long reserved();
        
        @StructMember(0)
        public native Descriptor reserved(@MachineSizedUInt long reserved);

        @StructMember(1)
        public native @MachineSizedUInt long literal_size();
        
        @StructMember(1)
        public native Descriptor literal_size(@MachineSizedUInt long literal_size);

        @StructMember(2)
        public native @Pointer long copy_helper();
        
        @StructMember(2)
        public native Descriptor copy_helper(@Pointer long copy_helper);
        
        @StructMember(3)
        public native @Pointer long dispose_helper();
        
        @StructMember(3)
        public native Descriptor dispose_helper(@Pointer long dispose_helper);

        @StructMember(4)
        public native BytePtr signature();

        @StructMember(4)
        public native Descriptor signature(BytePtr value);
    }
    
    public static final class Wrapper {
        private static final Descriptor DESCRIPTOR;
        private static final long NSStackBlock;
        private static final int BLOCK_HAS_COPY_DISPOSE = (1 << 25);
        private static final int BLOCK_HAS_STRET = (1 << 29);
        private static final int BLOCK_HAS_SIGNATURE = (1 << 30);
        
        static {
            try {
                long copyImpl = VM.getCallbackMethodImpl(
                        Wrapper.class.getDeclaredMethod("copy", ObjCBlock.class, ObjCBlock.class));
                long disposeImpl = VM.getCallbackMethodImpl(
                        Wrapper.class.getDeclaredMethod("dispose", ObjCBlock.class));
                DESCRIPTOR = new Descriptor()
                    .literal_size(ObjCBlock.sizeOf())
                    .copy_helper(copyImpl)
                    .dispose_helper(disposeImpl)
                    .signature(null);
            } catch (Exception e) {
                throw new Error(e);
            }
            NSStackBlock = 
                    ObjCRuntime.objc_getClass(VM.getStringUTFChars("__NSStackBlock__"));
            if (NSStackBlock == 0L) {
                throw new Error("Objective-C class __NSStackBlock__ not found");
            }
        }
        
        private final long callbackImpl;
        private final int flags;
        private final IdentityHashMap refCounts = 
                new IdentityHashMap();
        private final Descriptor descriptor;
        
        public Wrapper(Class callbacks) {
            this(findCallback(callbacks), false);
        }

        public Wrapper(Method method) {
            this(method, true);
        }

        public Wrapper(Class cls, String methodName) {
            this(findCallback(cls, methodName), true);
        }

        private Wrapper(Method method, boolean validate) {
            if (validate && method.getAnnotation(Callback.class) == null) {
                throw new IllegalArgumentException("Method " + method 
                        + " is not a @Callback method");
            }

            TypeEncoding typeEncoding = method.getAnnotation(TypeEncoding.class);
            if (typeEncoding != null) {
                descriptor = new Descriptor()
                        .literal_size(ObjCBlock.sizeOf())
                        .copy_helper(DESCRIPTOR.copy_helper())
                        .dispose_helper(DESCRIPTOR.dispose_helper())
                        .signature(BytePtr.toBytePtrAsciiZ(typeEncoding.value()));
            } else {
                descriptor = DESCRIPTOR;
            }

            callbackImpl = VM.getCallbackMethodImpl(method);
            int flags = BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_SIGNATURE;
            if (ObjCRuntime.isStret(method)) {
                flags |= BLOCK_HAS_STRET;
            }
            this.flags = flags;
        }

        private static Method findCallback(Class cls, String methodName) {
            for (Method m : cls.getDeclaredMethods()) {
                if (m.getName().equals(methodName)) {
                    return m;
                }
            }
            throw new NoSuchMethodError(methodName);
        }
        
        private static Method findCallback(Class cls) {
            Method method = null;
            for (Method m : cls.getDeclaredMethods()) {
                if (m.getAnnotation(Callback.class) != null) {
                    if (method != null) {
                        throw new IllegalArgumentException("Several @Callback " 
                                + "methods found in class " + cls.getName());
                    }
                    method = m;
                }
            }
            if (method == null) {
                throw new IllegalArgumentException("No @Callback method found " 
                            + "in class " + cls.getName());
            }
            return method;
        }
        
        public static void initWrappers(Class cls) {
            // The ObjCBlockPlugin generates @Callback methods named $cb$block$
            // which each have a matching Wrapper static field named 
            // $cb$block$$wrapper.
            try {
                for (Method m : cls.getDeclaredMethods()) {
                    if (m.getName().startsWith("$cb$block$")) {
                        Field f = cls.getDeclaredField(m.getName() + "$wrapper");
                        f.setAccessible(true);
                        f.set(null, new Wrapper(m, true));
                    }
                }
            } catch (Throwable t) {
                throw new Error(t);
            }
        }
        
        public Object toObject(long handle) {
            ObjCBlock block = Struct.toStruct(ObjCBlock.class, handle);
            if (block == null) {
                return null;
            }
            return block.object();
        }
        
        public ObjCBlock toObjCBlock(Object o) {
            if (o == null) {
                return null;
            }
            // Create an Objective-C block struct which looks like it was
            // allocated on the stack. The struct will be GC reachable during
            // the duration of the call to the Objective-C side. The Objective-C
            // side will copy the block if it needs to call it later which will
            // trigger a call to the copy() method below. The GC will reclaim 
            // the struct memory area some time after the call has finished but
            // the Java block instance (e.g. VoidBlock) will be retained on 
            // every copy and only GCed after all copies have been deallocated.
            ObjCBlock block = new ObjCBlock()
                .isa(NSStackBlock)
                .flags(flags)
                .invoke(callbackImpl)
                .descriptor(descriptor)
                .object(o)
                .wrapper(this);
            return block;
        }
        
        @Callback
        private static void copy(ObjCBlock dst, ObjCBlock src) {
            // Called from the Objective-C side every time the block is copied.
            // Increase the ref count of the block object on the Java side to
            // prevent it from being GCed.
            Object blockObj = src.object();
            Wrapper wrapper = src.wrapper();
            IdentityHashMap refCounts = wrapper.refCounts;
            synchronized (refCounts) {
                AtomicInteger count = refCounts.get(blockObj);
                if (count == null) {
                    count = new AtomicInteger(0);
                    refCounts.put(blockObj, count);
                }
                count.incrementAndGet();
            }
        }
        
        @Callback
        private static void dispose(ObjCBlock block) {
            // Called from the Objective-C side when a previously copied block
            // is released and about to be deallocated. Decrease the ref count
            // of the object on the Java side.
            Object blockObj = block.object();
            Wrapper wrapper = block.wrapper();
            IdentityHashMap refCounts = wrapper.refCounts;
            synchronized (refCounts) {
                if (refCounts.get(blockObj).decrementAndGet() == 0) {
                    refCounts.remove(blockObj);
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy