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

org.teavm.html4j.JavaScriptBodyDependency Maven / Gradle / Ivy

There is a newer version: 0.7.0
Show newest version
/*
 *  Copyright 2014 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.html4j;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.java.html.js.JavaScriptBody;
import org.teavm.dependency.AbstractDependencyListener;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyConsumer;
import org.teavm.dependency.DependencyNode;
import org.teavm.dependency.DependencyType;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;

public class JavaScriptBodyDependency extends AbstractDependencyListener {
    private DependencyNode allClassesNode;
    private Map> reachedMethods = new HashMap<>();

    @Override
    public void started(DependencyAgent agent) {
        allClassesNode = agent.createNode();
        allClassesNode.setTag("JavaScriptBody:global");
    }

    public String[] getClassesPassedToJavaScript() {
        return allClassesNode.getTypes();
    }

    static class OneDirectionalConnection implements DependencyConsumer {
        private DependencyNode target;
        OneDirectionalConnection(DependencyNode target) {
            this.target = target;
        }
        @Override public void consume(DependencyType type) {
            target.propagate(type);
        }
    }

    @Override
    public void classReached(DependencyAgent agent, String className, CallLocation location) {
        ClassReader cls = agent.getClassSource().get(className);
        if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT)
                && !cls.hasModifier(ElementModifier.INTERFACE)) {
            allClassesNode.propagate(agent.getType(className));
        }
    }

    @Override
    public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
        Set methodsToReach = reachedMethods.get(method.getReference());
        if (methodsToReach != null) {
            for (MethodReference methodToReach : methodsToReach) {
                agent.linkMethod(methodToReach, location);
            }
            return;
        }
        reachedMethods.put(method.getReference(), new HashSet<>());
        if (method.isMissing()) {
            return;
        }
        AnnotationReader annot = method.getMethod().getAnnotations().get(JavaScriptBody.class.getName());
        if (annot != null) {
            includeDefaultDependencies(agent, location);
            AnnotationValue javacall = annot.getValue("javacall");
            if (method.getResult() != null) {
                allClassesNode.connect(method.getResult());
                allClassesNode.addConsumer(new OneDirectionalConnection(method.getResult().getArrayItem()));
                allClassesNode.addConsumer(new OneDirectionalConnection(method.getResult().getArrayItem()
                        .getArrayItem()));
            }
            for (int i = 0; i < method.getParameterCount(); ++i) {
                method.getVariable(i).connect(allClassesNode);
                method.getVariable(i).addConsumer(type -> {
                    if (agent.getClassSource().isSuperType("java.lang.Enum", type.getName()).orElse(false)) {
                        MethodReference toStringMethod = new MethodReference(type.getName(), "toString",
                                ValueType.parse(String.class));
                        MethodDependency dependency = agent.linkMethod(toStringMethod, location);
                        dependency.getVariable(0).propagate(type);
                        dependency.use();
                    }
                });
                allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem()));
                allClassesNode.addConsumer(new OneDirectionalConnection(method.getVariable(i).getArrayItem()
                        .getArrayItem()));
            }
            if (javacall != null && javacall.getBoolean()) {
                String body = annot.getValue("body").getString();
                new GeneratorJsCallback(agent, method, location).parse(body);
            }
        }
    }

    private void includeDefaultDependencies(DependencyAgent agent, CallLocation location) {
        agent.linkMethod(JavaScriptConvGenerator.fromJsMethod, location).use();
        agent.linkMethod(JavaScriptConvGenerator.toJsMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.intValueMethod, location).propagate(0, Integer.class).use();
        agent.linkMethod(JavaScriptConvGenerator.valueOfIntMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.booleanValueMethod, location).propagate(0, Boolean.class).use();
        agent.linkMethod(JavaScriptConvGenerator.valueOfBooleanMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.doubleValueMethod, location).propagate(0, Double.class).use();
        agent.linkMethod(JavaScriptConvGenerator.valueOfDoubleMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.charValueMethod, location).propagate(0, Character.class).use();
        agent.linkMethod(JavaScriptConvGenerator.valueOfCharMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.byteValueMethod, location).propagate(0, Byte.class).use();
        agent.linkMethod(JavaScriptConvGenerator.valueOfByteMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.shortValueMethod, location).propagate(0, Short.class).use();
        agent.linkMethod(JavaScriptConvGenerator.valueOfShortMethod, location).use();

        agent.linkMethod(JavaScriptConvGenerator.valueOfLongMethod, location).use();
    }

    private static MethodReader findMethod(ClassReaderSource classSource, String clsName, MethodDescriptor desc) {
        while (clsName != null) {
            ClassReader cls = classSource.get(clsName);
            if (cls == null) {
                return null;
            }
            for (MethodReader method : cls.getMethods()) {
                if (method.getName().equals(desc.getName()) && sameParams(method.getDescriptor(), desc)) {
                    return method;
                }
            }
            clsName = cls.getParent();
        }
        return null;
    }
    private static boolean sameParams(MethodDescriptor a, MethodDescriptor b) {
        if (a.parameterCount() != b.parameterCount()) {
            return false;
        }
        for (int i = 0; i < a.parameterCount(); ++i) {
            if (!a.parameterType(i).equals(b.parameterType(i))) {
                return false;
            }
        }
        return true;
    }

    class GeneratorJsCallback extends JsCallback {
        private DependencyAgent agent;
        private MethodDependency caller;
        private CallLocation location;
        GeneratorJsCallback(DependencyAgent agent, MethodDependency caller, CallLocation location) {
            this.agent = agent;
            this.caller = caller;
            this.location = location;
        }
        @Override protected CharSequence callMethod(String ident, String fqn, String method, String params) {
            MethodDescriptor desc = MethodDescriptor.parse(method + params + "V");
            MethodReader reader = findMethod(agent.getClassSource(), fqn, desc);
            MethodReference ref = reader != null ? reader.getReference() : new MethodReference(fqn, desc);
            MethodDependency methodDep = agent.linkMethod(ref, location);
            reachedMethods.get(caller.getReference()).add(ref);
            if (!methodDep.isMissing()) {
                if (reader.hasModifier(ElementModifier.STATIC) || reader.hasModifier(ElementModifier.FINAL)) {
                    methodDep.use();
                    for (int i = 0; i < methodDep.getParameterCount(); ++i) {
                        allClassesNode.connect(methodDep.getVariable(i));
                    }
                } else {
                    allClassesNode.addConsumer(new VirtualCallbackConsumer(agent, reader, caller));
                }
            }
            return "";
        }
    }

    class VirtualCallbackConsumer implements DependencyConsumer {
        private DependencyAgent agent;
        private MethodReader superMethod;
        private ClassReader superClass;
        private MethodDependency caller;
        VirtualCallbackConsumer(DependencyAgent agent, MethodReader superMethod, MethodDependency caller) {
            this.agent = agent;
            this.superMethod = superMethod;
            this.caller = caller;
            this.superClass = agent.getClassSource().get(superMethod.getOwnerName());
        }
        @Override public void consume(DependencyType type) {
            if (!agent.getClassSource().isSuperType(superClass.getName(), type.getName()).orElse(false)) {
                return;
            }
            MethodReference methodRef = new MethodReference(type.getName(), superMethod.getDescriptor());
            MethodDependency method = agent.linkMethod(methodRef, new CallLocation(caller.getReference()));
            method.use();
            for (int i = 0; i < method.getParameterCount(); ++i) {
                allClassesNode.connect(method.getVariable(i));
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy