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

foundation.icon.ee.struct.StructDB Maven / Gradle / Ivy

There is a newer version: 0.9.6
Show newest version
/*
 * Copyright 2020 ICON Foundation
 *
 * 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 foundation.icon.ee.struct;

import foundation.icon.ee.score.EEPType;
import foundation.icon.ee.types.Method;
import org.objectweb.asm.Type;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class StructDB {
    private final Map cpiMap;

    // readable struct to properties map including parent class properties.
    private final Map> rPropsMap = new HashMap<>();

    // writable struct to properties map including parent class properties.
    private final Map> wPropsMap = new HashMap<>();

    // TODO: separate
    private final Set parameterStructs = new HashSet<>();
    private final Set returnStructs = new HashSet<>();

    public StructDB(Map classMap) {
        this.cpiMap = ClassPropertyMemberInfo.map(classMap);
        updatePropsMap();
    }

    private void requireNoCyclicInheritance(ClassPropertyMemberInfo cpi) {
        List visited = new ArrayList<>();
        while (cpi != null) {
            if (visited.contains(cpi.getType())) {
                throw new IllegalArgumentException("cyclic inheritance "
                        + cpi.getType().getClassName());
            }
            visited.add(cpi.getType());
            cpi = cpiMap.get(cpi.getSuperType());
        }
    }

    private void uniteByName(List list, List r) {
        next:
        for (var sp : r) {
            for (var wp : list) {
                if (sp.getName().equals(wp.getName())) {
                    continue next;
                }
            }
            list.add(sp);
        }
    }

    private List getRProps(ClassPropertyMemberInfo cpi) {
        List rProps = new ArrayList<>();
        var cur = cpi;
        while (cur != null) {
            uniteByName(rProps, cur.getGetters());
            cur = cpiMap.get(cur.getSuperType());
        }
        cur = cpi;
        while (cur != null) {
            uniteByName(rProps, cur.getFields());
            cur = cpiMap.get(cur.getSuperType());
        }
        if (rProps.isEmpty()) {
            return null;
        }
        return rProps;
    }

    private List getWProps(ClassPropertyMemberInfo cpi) {
        if (!cpi.isCreatable()) {
            return null;
        }
        List wProps = new ArrayList<>();
        var cur = cpi;
        while (cur != null) {
            nextSetter:
            for (var sp : cur.getSetters()) {
                for (var wp : wProps) {
                    if (sp.getName().equals(wp.getName())) {
                        if (!sp.getType().equals(wp.getType())) {
                            // conflicting. do not add to map
                            return null;
                        }
                        continue nextSetter;
                    }
                }
                wProps.add(sp);
            }
            cur = cpiMap.get(cur.getSuperType());
        }
        cur = cpi;
        while (cur != null) {
            uniteByName(wProps, cur.getFields());
            cur = cpiMap.get(cur.getSuperType());
        }
        if (wProps.isEmpty()) {
            return null;
        }
        return wProps;
    }

    private boolean isValidStruct(Type t, Map> propsMap) {
        try {
            new TypeDetailCreator(propsMap).getTypeDetail(t);
        } catch (IllegalArgumentException e) {
            return false;
        }
        return true;
    }

    private void updatePropsMap() {
        var lwPropsMap = new HashMap>();
        var lrPropsMap = new HashMap>();
        for (var ce : cpiMap.entrySet()) {
            var c = ce.getValue();
            requireNoCyclicInheritance(c);

            if (c.isCreatable()) {
                var props = getWProps(c);
                if (props != null) {
                    lwPropsMap.put(c.getType(), props);
                }
            }
            var props = getRProps(c);
            if (props != null) {
                lrPropsMap.put(c.getType(), props);
            }
        }
        for (var wpe : lwPropsMap.entrySet()) {
            if (isValidStruct(wpe.getKey(), lwPropsMap)) {
                wPropsMap.put(wpe.getKey(), wpe.getValue());
            }
        }
        for (var rpe : lrPropsMap.entrySet()) {
            if (isValidStruct(rpe.getKey(), lrPropsMap)) {
                rPropsMap.put(rpe.getKey(), rpe.getValue());
            }
        }
    }

    public boolean isWritableStruct(Type t) {
        return wPropsMap.containsKey(t);
    }

    public boolean isReadableStruct(Type t) {
        return rPropsMap.containsKey(t);
    }

    private static class TypeDetailCreator {
        private final Map> propsMap;
        private final List visiting = new ArrayList<>();

        public TypeDetailCreator(Map> propsMap) {
            this.propsMap = propsMap;
        }

        Method.TypeDetail getTypeDetail(Type t) {
            if (t.getSort() == Type.ARRAY) {
                var elemType = t.getElementType();
                Method.TypeDetail elemTD;
                int elemEEPType;
                if (elemType.getSort() == Type.BYTE) {
                    elemTD = new Method.TypeDetail(Method.DataType.BYTES);
                    elemEEPType = ((t.getDimensions()-1) << Method.DataType.DIMENSION_SHIFT)
                            | elemTD.getType();
                } else {
                    elemTD = getTypeDetail(elemType);
                    elemEEPType = (t.getDimensions() << Method.DataType.DIMENSION_SHIFT)
                                    | elemTD.getType();
                }
                return new Method.TypeDetail(elemEEPType,
                        elemTD.getStructFields());
            }
            var eepType = EEPType.fromBasicFieldType.get(t);
            if (eepType != null) {
                return new Method.TypeDetail(eepType);
            }
            var props = propsMap.get(t);
            if (props == null) {
                throw new IllegalArgumentException("invalid t "
                        + t.getClassName());
            }
            if (visiting.contains(t)) {
                throw new IllegalArgumentException("cyclic property in "
                        + t.getClassName());
            }
            visiting.add(t);
            var fields = new ArrayList();
            for (var p : props) {
                var ftd = getTypeDetail(p.getType());
                fields.add(new Method.Field(p.getName(), ftd.getType(),
                        ftd.getStructFields()));
            }
            visiting.remove(visiting.size() - 1);
            return new Method.TypeDetail(Method.DataType.STRUCT,
                    fields.toArray(new Method.Field[0]));
        }
    }

    private static class ReferredStructCollector {
        private final Map> propsMap;

        public ReferredStructCollector(Map> propsMap) {
            this.propsMap = propsMap;
        }

        void visit(Set rs, Type t) {
            if (t.getSort() == Type.ARRAY) {
                visit(rs, t.getElementType());
            }
            var props = propsMap.get(t);
            if (props == null) {
                return;
            }
            rs.add(t);
            for (var p : props) {
                visit(rs, p.getType());
            }
        }

        Set getReferredStructs(Type t) {
            Set rs = new HashSet<>();
            visit(rs, t);
            return rs;
        }
    }

    public List getReadableProperties(Type type) {
        return rPropsMap.get(type);
    }

    public List getWritableProperties(Type type) {
        return wPropsMap.get(type);
    }

    public boolean isValidParamTypeElement(Type type) {
        return EEPType.fromBasicParamType.containsKey(type)
                || isWritableStruct(type);
    }

    public boolean isValidParamType(Type type) {
        if (type.getSort() == Type.ARRAY) {
            return isValidParamTypeElement(type.getElementType());
        }
        return isValidParamTypeElement(type);
    }

    public boolean isValidReturnTypeElement(Type type) {
        return EEPType.fromBasicReturnType.containsKey(type)
                || isReadableStruct(type);
    }

    public boolean isValidReturnType(Type type) {
        if (type.getSort() == Type.ARRAY) {
            return isValidReturnTypeElement(type.getElementType());
        }
        return isValidReturnTypeElement(type);
    }

    public Method.TypeDetail getDetailFromParameterType(Type t) {
        var eepType = EEPType.fromBasicParamType.get(t);
        if (eepType != null) {
            return new Method.TypeDetail(eepType);
        }
        return new TypeDetailCreator(wPropsMap).getTypeDetail(t);
    }

    public void addParameterType(Type t) {
        var rtc = new ReferredStructCollector(wPropsMap);
        parameterStructs.addAll(rtc.getReferredStructs(t));
    }

    public Set getParameterStructs() {
        return parameterStructs;
    }

    public int getEEPTypeFromReturnType(Type t) {
        // handles byte[] here
        Integer eepType = EEPType.fromBasicReturnType.get(t);
        if (eepType != null) {
            return eepType;
        }
        if (t.getSort() == Type.ARRAY
                && isValidReturnTypeElement(t.getElementType())) {
            return Method.DataType.LIST;
        }
        if (isReadableStruct(t)) {
            return Method.DataType.DICT;
        }
        throw new IllegalArgumentException();
    }

    public void addReturnType(Type t) {
        var rtc = new ReferredStructCollector(rPropsMap);
        returnStructs.addAll(rtc.getReferredStructs(t));
    }

    public Set getReturnStructs() {
        return returnStructs;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy