Package com.redhat.ceylon.compiler.java.runtime.serialization

Source Code of com.redhat.ceylon.compiler.java.runtime.serialization.DeserializingReference

package com.redhat.ceylon.compiler.java.runtime.serialization;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import ceylon.language.AssertionError;
import ceylon.language.Collection;
import ceylon.language.Finished;
import ceylon.language.Tuple;
import ceylon.language.meta.model.ClassModel;
import ceylon.language.meta.model.Type;
import ceylon.language.serialization.Deconstructed;
import ceylon.language.serialization.DeserializableReference;
import ceylon.language.serialization.RealizableReference;
import ceylon.language.serialization.Reference;

import com.redhat.ceylon.compiler.java.Util;
import com.redhat.ceylon.compiler.java.metadata.Ceylon;
import com.redhat.ceylon.compiler.java.metadata.SatisfiedTypes;
import com.redhat.ceylon.compiler.java.metadata.TypeParameter;
import com.redhat.ceylon.compiler.java.metadata.TypeParameters;
import com.redhat.ceylon.compiler.java.runtime.metamodel.AppliedClass;
import com.redhat.ceylon.compiler.java.runtime.metamodel.AppliedMemberClass;
import com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel;
import com.redhat.ceylon.compiler.java.runtime.model.ReifiedType;
import com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor;

@Ceylon(major=7, minor=1)
@com.redhat.ceylon.compiler.java.metadata.Class
@TypeParameters(@TypeParameter("Instance"))
@SatisfiedTypes({"ceylon.language.serialization::DeserializableReference<Instance>",
        "ceylon.language.serialization::RealizableReference<Instance>"})
public class DeserializingReference<Instance>
    implements DeserializableReference<Instance>, RealizableReference<Instance>, $InstanceLeaker$<Instance>, ReifiedType {
   
    private final TypeDescriptor reified$Instance;
    /**
     * The reference has not state because
     * {@link #deserialize(Deconstructed)} has not yet been called.
     */
    static final int ST_STATELESS = 0;
    /**
     * The reference has state but is not
     * yet initialized because {@link Serializable#$deserialize$(Deconstructed)}
     * has not yet been called
     */
    static final int ST_UNINITIALIZED = 1;
    /**
     * The reference has been initialized but has
     * dependent references which have not yet been queued for
     * initialization.
     */
    static final int ST_UNINITIALIZED_REFS = 2;
    /**
     * The reference has been initialized and all its direct
     * dependent references have been queued for initialization.
     */
    static final int ST_INITIALIZED = 3;
   
    static final int ST_ERROR = 4;
    /** The current state of this reference */
    private int state;
    /** The id of this reference */
    private final Object id;
    /** The possibly partially initialized instance */
    private final Instance instance;
    @SuppressWarnings("rawtypes")
    /** The class model of this reference */
    private final ClassModel classModel;
    /** The state associated with this reference. May be null */
    private Deconstructed deconstructed;
   
    DeserializingReference(TypeDescriptor reified$Instance,
            DeserializationContextImpl context,
            Object id,
            ClassModel classModel, Reference<?> outerReference) {
        this(reified$Instance,
                id,
                classModel,
                (Instance)createInstance(context, id, classModel, outerReference));
    }
   
    DeserializingReference(TypeDescriptor reified$Instance,
            Object id,
            @SuppressWarnings("rawtypes") ClassModel classModel, Instance instance) {
        this.reified$Instance = reified$Instance;
        this.id = id;
        this.classModel = classModel;
        this.state = ST_STATELESS;
        this.instance = instance;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static <Instance> Instance createInstance(DeserializationContextImpl context, Object id,
            ClassModel classModel, Reference<?> outerReference)
            throws AssertionError {
        java.lang.Class<Instance> clazz;
        Class outerClass;
        Object outer;
        TypeDescriptor[] typeArguments = ((TypeDescriptor.Class)((ReifiedType)classModel).$getType$()).getTypeArguments();
        if (classModel instanceof AppliedClass) {
            // Class<Type, Arguments>
            clazz = (java.lang.Class)((TypeDescriptor.Class)typeArguments[0]).getKlass();
            outerClass = null;
            outer = null;
        } else if (classModel instanceof AppliedMemberClass) {
            // MemberClass<Container, Type, Arguments>
            clazz = (java.lang.Class)((TypeDescriptor.Class)typeArguments[1]).getKlass();
            outerClass = ((TypeDescriptor.Class)typeArguments[0]).getKlass();
            outer = context.leakReferred(outerReference.getId());
        } else {
            throw new AssertionError("unexpected class model: "
                    + (classModel != null ? classModel.getClass().getName() : "null"));
        }
        // Construct arrays for types and arguments for reflective instantiation
        // of the serialization constructor
        Collection<?> typeArgs = classModel.getTypeArguments().getItems();
        Class<?>[] types = new Class[(outerClass != null ? 2 : 1) + Util.toInt(typeArgs.getSize())];
        Object[] args = new Object[(outer != null ? 2 : 1) + Util.toInt(typeArgs.getSize())];
        int ii = 0;
        if (outerClass != null) {
            types[ii] = outerClass;
            args[ii] = outer;
            ii++;
        }
        types[ii] = $Serialization$.class;
        args[ii] = null;
        ii++;
        for (int jj = 0 ; jj < typeArgs.getSize(); ii++, jj++) {
            types[ii] = TypeDescriptor.class;
            args[ii] = Metamodel.getTypeDescriptor((Type)typeArgs.getFromFirst(jj));
        }
       
        try {
            Constructor<Instance> ctor = clazz.getDeclaredConstructor(types);
            ctor.setAccessible(true);
            // Actually we need to pass something equivalent to the type descriptors here
            // because the companion instances can require those. But we don't have the deconstructed yet!
            // This means we have to obtain the type descriptors from the class model
            return ctor.newInstance(args);// Pass a null $Serialization$
        } catch (NoSuchMethodException e) {
            throw new AssertionError("class is not serializable " + classModel);
        } catch (InvocationTargetException e) {
            throw new AssertionError("error thrown during instantiation of " + classModel+ (e.getMessage() != null ? ": " + e.getMessage() : ""));
        } catch (SecurityException e) {
            // Should never happen
            throw new RuntimeException(e);
        } catch (InstantiationException|IllegalAccessException|IllegalArgumentException e) {
            // Should never happen
            throw new RuntimeException(e);
        }
    }
   
    /**
     * Instantiates and returns a {@link DeserializingStatefulReference}
     */
    @Override
    public RealizableReference<Instance> deserialize(Deconstructed deconstructed) {
        this.deconstructed = deconstructed;
        if (getState() != ST_STATELESS) {
            throw new AssertionError("reference has already been deserialized: " + this);
        }
        this.state = ST_UNINITIALIZED;
        return this;
    }
   

    @Override
    public TypeDescriptor $getType$() {
        return TypeDescriptor.klass(DeserializingReference.class, reified$Instance);
    }

    @Override
    public Instance $leakInstance$() {
        return instance;
    }
   
    @Override
    public String toString() {
        switch (getState()) {
        case ST_STATELESS:
            return "DeserializableReference(" + id + ")";
        case ST_UNINITIALIZED:
            return "RealizableReference(" + id + "~>~" + deconstructed + ")";
        case ST_UNINITIALIZED_REFS:
            return "RealizableReference(" + id + "~>" + deconstructed + ")";
        case ST_INITIALIZED:
            return "RealizableReference(" + id + "->" + instance + ")";
        }
        throw new AssertionError("Illegal state " + getState());
    }
   
    @Override
    public Instance instance() {
        // !!!!! XXX MUST NOT LEAK PARTIALLY BUILT OBJECTS
        // XXX HERE we must ensure that this.instance has been
        // reconstructed, but also that everything it references
        // (transitively) has been reconstructed too.
        // We can do this by inspecting this.deconstructed finding the
        // references (from this.context) and ensuring those are
        // reconstructed.
        reconstruct();
        return instance;
    }
   
    /**
     * Reconstructs this instance
     */
    @Override
    public Object reconstruct() {
        if (getState() == ST_ERROR) {
            throw new AssertionError("broken graph");
        }
        IdentityHashMap<DeserializingReference<?>, Object> i = new IdentityHashMap<DeserializingReference<?>, Object>();
        try {
            if (getState() != ST_INITIALIZED) {
                LinkedList<DeserializingReference<?>> queue = new LinkedList<DeserializingReference<?>>();
                queue.addLast(this);
                while (!queue.isEmpty()) {
                    DeserializingReference<?> r = queue.removeFirst();
                    i.put(r,  null);
                    if (r.getState() == ST_UNINITIALIZED) {
                        ((Serializable)r.instance).$deserialize$(r.deconstructed);
                        r.state = ST_UNINITIALIZED_REFS;
                    }
                    if (r.getState() == ST_UNINITIALIZED_REFS) {
                        for (DeserializingReference<Object> referred : r.references()) {
                            if (referred.getState() == ST_STATELESS) {
                                throw new AssertionError("reference " + referred.getId() + " has not been deserialized");
                            }
                            DeserializingReference<?> statefulReferred = (DeserializingReference<?>)referred;
                            if (statefulReferred.getState() != ST_INITIALIZED) {
                                queue.addLast(statefulReferred);
                            }
                        }
                        r.state = ST_INITIALIZED;
                        r.deconstructed = null;
                    }
                }
            }
            return null;
        } catch (Exception|AssertionError e) {
            // If anything went wrong mark every object we encountered as
            // being broken. Otherwise it's possible to call instance() a
            // second time and encounter a partially constructed object
            // in the returned object graph.
           
            // This isn't ideal, as it marks as broken
            // objects whose subgraph is fine, just because they're reachable
            // from a broken graph. But it saves having to track the
            // reverse dependencies
            for (DeserializingReference<?> r : i.keySet()) {
                r.state = ST_ERROR;
            }
            throw e;
        }
    }
   
    /**
     * The references in the Deconstructed
     * @return
     */
    private Iterable<DeserializingReference<Object>> references() {
        return new Iterable<DeserializingReference<Object>>() {

            @Override
            public java.util.Iterator<DeserializingReference<Object>> iterator() {
                return new java.util.Iterator<DeserializingReference<Object>>() {
                    ceylon.language.Iterator it = deconstructed.iterator();
                    Object next = null;
                   
                    @Override
                    public boolean hasNext() {
                        if (next == null) {
                            Object vdValue = it.next();
                            while (true) {
                                if (vdValue instanceof Finished) {
                                    next = vdValue;
                                    break;
                                }
                                Object valueOrRef = ((Tuple)vdValue).getFromFirst(1);
                                if (valueOrRef instanceof Reference) {
                                    next = valueOrRef;
                                    break;
                                }
                                vdValue = it.next();
                            }
                        }
                        return !(next instanceof Finished);
                    }
                    @Override
                    public DeserializingReference<Object> next() {
                        if (next == null) {
                            hasNext();
                        }
                        if (next instanceof Finished) {
                            throw new NoSuchElementException();
                        }
                        DeserializingReference<Object> result = (DeserializingReference)next;
                        next = null;
                        return result;
                    }
                   
                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                   
                };
            }
           
        };
    }

    @Override
    public Object getId() {
        return id;
    }
   
    @SuppressWarnings("rawtypes")
    @Override
    public ClassModel getClazz() {
        // note: we cannot call type() because the instance might not have
        // had it's reified type arguments set yet.
        return classModel;
        //type_.type(reified$Instance, instance);
    }

    int getState() {
        return state;
    }
   
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.runtime.serialization.DeserializingReference

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.