blob: 4951746a2590ff4ebb5bbcf98ae02f42afed8f21 [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.sling.scripting.sightly.java.compiler.impl;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.sling.scripting.sightly.compiler.expression.ExpressionNode;
import org.apache.sling.scripting.sightly.compiler.expression.NodeVisitor;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.ArrayLiteral;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperation;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.BooleanConstant;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.Identifier;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.MapLiteral;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.NullLiteral;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.NumericConstant;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.PropertyAccess;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.RuntimeCall;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.StringConstant;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.TernaryOperator;
import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation;
import org.apache.sling.scripting.sightly.java.compiler.JavaImportsAnalyzer;
import org.apache.sling.scripting.sightly.java.compiler.impl.operator.BinaryOpGen;
import org.apache.sling.scripting.sightly.java.compiler.impl.operator.Operators;
import org.apache.sling.scripting.sightly.java.compiler.impl.operator.UnaryOpGen;
/**
* Expression translator which uses type information
*/
public final class TypeInference implements NodeVisitor<Type> {
private final VariableAnalyzer analyzer;
private final Map<ExpressionNode, Type> inferMap = new IdentityHashMap<>();
private final Set<String> imports;
private final JavaImportsAnalyzer importsAnalyzer;
private static final Pattern FQCN_PATTERN = Pattern.compile("([[\\p{L}&&[^\\p{Lu}]]_$][\\p{L}\\p{N}_$]*\\.)+[\\p{Lu}_$][\\p{L}\\p{N}_$]*");
public static TypeInfo inferTypes(ExpressionNode node, VariableAnalyzer analyzer, Set<String> imports,
JavaImportsAnalyzer importsAnalyzer) {
TypeInference typeInference = new TypeInference(analyzer, imports, importsAnalyzer);
typeInference.infer(node);
return new TypeInfo(typeInference.inferMap);
}
private TypeInference(VariableAnalyzer analyzer, Set<String> imports, JavaImportsAnalyzer importsAnalyzer) {
this.analyzer = analyzer;
this.imports = imports;
this.importsAnalyzer = importsAnalyzer;
}
private Type infer(ExpressionNode node) {
Type type = node.accept(this);
inferMap.put(node, type);
return type;
}
@Override
public Type evaluate(PropertyAccess propertyAccess) {
infer(propertyAccess.getTarget());
infer(propertyAccess.getProperty());
return Type.UNKNOWN;
}
@Override
public Type evaluate(Identifier identifier) {
return analyzer.descriptor(identifier.getName()).getType();
}
@Override
public Type evaluate(StringConstant text) {
return Type.STRING;
}
@Override
public Type evaluate(BinaryOperation binaryOperation) {
Type leftType = infer(binaryOperation.getLeftOperand());
Type rightType = infer(binaryOperation.getRightOperand());
BinaryOpGen opGen = Operators.generatorFor(binaryOperation.getOperator());
return opGen.returnType(leftType, rightType);
}
@Override
public Type evaluate(BooleanConstant booleanConstant) {
return Type.BOOLEAN;
}
@Override
public Type evaluate(NumericConstant numericConstant) {
Number number = numericConstant.getValue();
if (number instanceof Integer || number instanceof Long) {
return Type.LONG;
}
if (number instanceof Float || number instanceof Double) {
return Type.DOUBLE;
}
return Type.UNKNOWN;
}
@Override
public Type evaluate(UnaryOperation unaryOperation) {
infer(unaryOperation.getTarget());
UnaryOpGen opGen = Operators.generatorFor(unaryOperation.getOperator());
return opGen.returnType(infer(unaryOperation.getTarget()));
}
@Override
public Type evaluate(TernaryOperator ternaryOperator) {
infer(ternaryOperator.getCondition());
Type thenType = infer(ternaryOperator.getThenBranch());
Type elseType = infer(ternaryOperator.getElseBranch());
if (thenType.equals(elseType)) {
return thenType;
}
return Type.UNKNOWN;
}
@Override
public Type evaluate(RuntimeCall runtimeCall) {
inferAll(runtimeCall.getArguments());
if (runtimeCall.getFunctionName().equals(RuntimeCall.USE)) {
ExpressionNode identifier = runtimeCall.getArguments().get(0);
if (identifier instanceof StringConstant) {
String objectType = ((StringConstant) identifier).getText();
if (FQCN_PATTERN.matcher(objectType).matches() && importsAnalyzer != null && importsAnalyzer.allowImport(objectType)) {
imports.add(objectType);
return Type.dynamic(objectType);
}
}
}
return Type.UNKNOWN;
}
@Override
public Type evaluate(MapLiteral mapLiteral) {
inferAll(mapLiteral.getMap().values());
return Type.MAP;
}
@Override
public Type evaluate(ArrayLiteral arrayLiteral) {
inferAll(arrayLiteral.getItems());
return Type.UNKNOWN;
}
@Override
public Type evaluate(NullLiteral nullLiteral) {
return Type.UNKNOWN;
}
private void inferAll(Iterable<ExpressionNode> nodes) {
for (ExpressionNode node : nodes) {
infer(node);
}
}
}