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

net.oneandone.mork.compiler.GenericCompiler Maven / Gradle / Ivy

/**
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 *
 * 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 net.oneandone.mork.compiler;

import net.oneandone.mork.classfile.Bytecodes;
import net.oneandone.mork.classfile.ClassRef;
import net.oneandone.mork.classfile.Code;
import net.oneandone.mork.classfile.MethodRef;
import net.oneandone.mork.reflect.Field;
import net.oneandone.mork.reflect.Function;
import net.oneandone.mork.reflect.Method;
import net.oneandone.mork.reflect.Selection;

import java.lang.reflect.InvocationTargetException;

/**
 * Compiling an object compiles it fields. The reverse process
 * creates objects from fields.   This is like serializing into a class file instead of an
 * ObjectOutputStream. Instantiate the resulting class to "deserialize" an
 * "bytecode"-serialized object.
 */

public class GenericCompiler extends CustomCompiler implements Bytecodes {
    static {
        if (ObjectCompiler.MIN_INSTRUCTIONS < 3) {
            // I need three statements
            throw new IllegalArgumentException();
        }
    }
    private final Class type;

    /**
     * Reader methods to obtain a field value. Methods have
     * to take one argument of type "type".
     */
    private final Function[] fields;

    private final Class[] fieldTypes;

    /** Reference to constructor function. */
    private final MethodRef constr;

    /** Bytecode to invoke constructor. */
    private final int constrType;

    public GenericCompiler(Class typeInit, String[] fieldNames) {
        this(typeInit, fieldNames, null);
    }

    /**
     * @param constrName  name of constructor function or null for
     *        real constructor.
     */
    public GenericCompiler(Class typeInit, String[] fieldNames, String constrName) {
        int i;
        ClassRef[] tmp;

        type = typeInit;
        fields = new Function[fieldNames.length];
        fieldTypes = new Class[fields.length];
        for (i = 0; i < fields.length; i++) {
            fields[i] = findField(fieldNames[i]);
            fieldTypes[i] = fields[i].getReturnType();
        }

        if (constrName == null) {
            tmp = new ClassRef[fields.length];
            for (i = 0; i < fields.length; i++) {
                tmp[i] = new ClassRef(fields[i].getReturnType());
            }
            constr = MethodRef.constr(new ClassRef(type), tmp);
            constrType = INVOKESPECIAL;
        } else {
            constr = new MethodRef(findConstr(constrName));
            constrType = INVOKESTATIC;
        }
    }

    @Override
    public boolean matches(Class c) {
        return type.equals(c);
    }

    @Override
    public Class[] getFieldTypes() {
        return fieldTypes;
    }

    @Override
    public Object[] getFieldObjects(Object obj) {
        Object[] result;
        int i;

        result = new Object[fieldTypes.length];
        for (i = 0; i < result.length; i++) {
            try {
                result[i] = fields[i].invokeN(obj);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("can't get field: " + fields[i] + ": " + e);
            }
        }
        return result;
    }


    //--

    // helper method for constructor
    private Function findField(String name) {
        Selection slkt;
        Function f;

        if (name.indexOf('.') == -1) {
            name = type.getName() + "." + name;
        }
        slkt = Method.forName(name);
        if (slkt.size() == 0) {
            f = Field.forName(name);
            if (f != null) {
                slkt = slkt.add(new Selection(f));
            }
        }
        slkt = slkt.restrictArgumentCount(1);
        slkt = slkt.restrictArgumentType(0, type);
        switch (slkt.size()) {
        case 0:
            throw new RuntimeException("no such field: " + name);
        case 1:
            return slkt.getFunction();
        default:
            throw new RuntimeException("ambiguous field: " + name);
        }
    }

    /** Helper method for the constructor. */
    private java.lang.reflect.Method findConstr(String name) {
        Selection slkt;
        int i;

        slkt = Method.forName(name);
        slkt = slkt.restrictArgumentCount(fields.length);
        for (i = 0; i < fields.length; i++) {
            slkt.restrictArgumentType(i, fields[i].getReturnType());
        }
        switch (slkt.size()) {
        case 0:
            throw new RuntimeException("no such constructor: " + name);
        case 1:
            return ((Method) slkt.getFunction()).getRaw();
        default:
            throw new RuntimeException("constructor ambiguous: " + name);
        }
    }

    /** called before prepareing the arguments. **/
    @Override
    public void beginTranslation(Object obj, Code dest) {
        if (constrType == INVOKESPECIAL) {
            dest.emit(NEW, new ClassRef(type));
            dest.emit(DUP);
            // save reference that remains on the stack
            // when object() is finished
        }
    }

    /** actually invoke. **/
    @Override
    public void endTranslation(Object obj, Code dest) {
        dest.emit(constrType, constr);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy