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

org.teavm.jso.impl.JSAnnotationCache Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2024 Alexey Andreev.
 *
 *  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.teavm.jso.impl;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.AccessLevel;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;

abstract class JSAnnotationCache {
    private ClassReaderSource classes;
    protected Diagnostics diagnostics;
    private Map> data = new HashMap<>();

    JSAnnotationCache(ClassReaderSource classes, Diagnostics diagnostics) {
        this.classes = classes;
        this.diagnostics = diagnostics;
    }

    T get(MethodReference methodReference, CallLocation location) {
        var result = getValue(methodReference, location);
        return result != null ? result.annotation : null;
    }

    private Value getValue(MethodReference methodReference, CallLocation location) {
        var result = data.get(methodReference);
        if (result == null) {
            result = extract(methodReference, location);
            data.put(methodReference, result);
        }
        return result;
    }

    private Value extract(MethodReference methodReference, CallLocation location) {
        var cls = classes.get(methodReference.getClassName());
        if (cls == null) {
            return new Value<>(null, methodReference);
        }
        var method = cls.getMethod(methodReference.getDescriptor());
        if (method == null || method.hasModifier(ElementModifier.STATIC)
                || method.getLevel() == AccessLevel.PRIVATE) {
            for (var candidateMethod : cls.getMethods()) {
                if (candidateMethod.getName().equals(methodReference.getName())
                        && !candidateMethod.hasModifier(ElementModifier.STATIC)
                        && !candidateMethod.hasModifier(ElementModifier.FINAL)
                        && candidateMethod.getLevel() != AccessLevel.PRIVATE
                        && Arrays.equals(candidateMethod.getParameterTypes(), methodReference.getParameterTypes())) {
                    method = candidateMethod;
                    break;
                }
            }
        }

        if (method != null) {
            methodReference = method.getReference();
            var annotation = take(method, location);
            if (annotation != null) {
                return new Value<>(annotation, methodReference);
            }
        }

        var candidates = new HashMap();
        if (cls.getParent() != null) {
            var value = getValue(new MethodReference(cls.getParent(), methodReference.getDescriptor()), location);
            if (value.annotation != null) {
                candidates.put(value.source, value.annotation);
            }
        }
        for (var itf : cls.getInterfaces()) {
            var value = getValue(new MethodReference(itf, methodReference.getDescriptor()), location);
            if (value != null) {
                candidates.put(value.source, value.annotation);
            }
        }
        if (candidates.isEmpty()) {
            return new Value<>(null, methodReference);
        }
        if (candidates.size() == 1) {
            var entry = candidates.entrySet().iterator().next();
            return new Value<>(entry.getValue(), entry.getKey());
        }

        T annot = null;
        MethodReference lastMethod = null;
        for (var entry : candidates.entrySet()) {
            if (annot != null && !annot.equals(entry.getValue())) {
                diagnostics.error(location, "Method '{{m0}}' has inconsistent JS annotations from overridden "
                        + "methods '{{m1}}' and '{{m2}}', so it should be annotated explicitly",
                        methodReference, lastMethod, entry.getKey());
                return new Value<>(null, methodReference);
            }
            annot = entry.getValue();
            lastMethod = entry.getKey();
        }
        return new Value<>(annot, methodReference);
    }

    protected abstract T take(MethodReader method, CallLocation location);

    private static class Value {
        final T annotation;
        final MethodReference source;

        Value(T annotation, MethodReference source) {
            this.annotation = annotation;
            this.source = source;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy