| /******************************************************************************* |
| * 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.HashSet; |
| import java.util.Set; |
| import java.util.Stack; |
| |
| import org.apache.sling.scripting.sightly.compiler.commands.CommandVisitor; |
| import org.apache.sling.scripting.sightly.compiler.commands.Conditional; |
| import org.apache.sling.scripting.sightly.compiler.commands.Loop; |
| import org.apache.sling.scripting.sightly.compiler.commands.OutText; |
| import org.apache.sling.scripting.sightly.compiler.commands.OutputVariable; |
| import org.apache.sling.scripting.sightly.compiler.commands.Procedure; |
| import org.apache.sling.scripting.sightly.compiler.commands.StatefulVisitor; |
| import org.apache.sling.scripting.sightly.compiler.commands.VariableBinding; |
| |
| /** |
| * Java code generator |
| */ |
| public class CodeGenVisitor implements CommandVisitor { |
| |
| private final JavaSource source; |
| private final UnitBuilder unitBuilder; |
| private final Stack<String> loopStatusStack = new Stack<>(); |
| private final VariableAnalyzer analyzer = new VariableAnalyzer(); |
| private final StatefulVisitor.StateControl control; |
| private final Set<String> unitParameters; |
| |
| public CodeGenVisitor(UnitBuilder unitBuilder, StatefulVisitor.StateControl control) { |
| this.unitBuilder = unitBuilder; |
| this.source = unitBuilder.getSource(); |
| this.control = control; |
| this.unitParameters = new HashSet<>(); |
| for (String param : unitBuilder.getParameters()) { |
| this.unitParameters.add(param.toLowerCase()); |
| } |
| } |
| |
| /** |
| * Complete building the source |
| */ |
| public void finish() { |
| source.prepend(initializations()); |
| } |
| |
| private String initializations() { |
| JavaSource initSource = new JavaSource(); |
| for (VariableDescriptor descriptor : analyzer.allVariables()) { |
| initVariable(descriptor, initSource); |
| } |
| return initSource.toString(); |
| } |
| |
| private void initVariable(VariableDescriptor descriptor, JavaSource initSource) { |
| VariableScope scope = descriptor.getScope(); |
| if (scope == VariableScope.DYNAMIC) { |
| initSource.beginAssignment(descriptor.getAssignedName()); |
| if (descriptor.isTemplateVariable()) { |
| initSource.startCall(SourceGenConstants.RECORD_GET_VALUE); |
| } else if (unitParameters.contains(descriptor.getOriginalName().toLowerCase())) { |
| initSource.startMethodCall(SourceGenConstants.ARGUMENTS_FIELD, SourceGenConstants.BINDINGS_GET_METHOD); |
| } else { |
| initSource.startMethodCall(SourceGenConstants.BINDINGS_FIELD, SourceGenConstants.BINDINGS_GET_METHOD); |
| } |
| initSource.stringLiteral(descriptor.getOriginalName()) |
| .endCall() |
| .endStatement(); |
| } else if (scope == VariableScope.GLOBAL) { |
| initSource.beginAssignment(descriptor.getAssignedName()) |
| .nullLiteral() |
| .endStatement(); |
| } |
| String listCoercionVar = descriptor.getListCoercion(); |
| if (listCoercionVar != null) { |
| //need to initialize the list coercion to null |
| initSource.beginAssignment(listCoercionVar, SourceGenConstants.COLLECTION_TYPE) |
| .nullLiteral().endStatement(); |
| } |
| } |
| |
| @Override |
| public void visit(Conditional.Start conditional) { |
| VariableDescriptor descriptor = analyzer.descriptor(conditional.getVariable()); |
| boolean negate = !conditional.getExpectedTruthValue(); |
| source.beginIf(); |
| if (negate) { |
| source.negation(); |
| } |
| if (descriptor.getType() == Type.BOOLEAN) { |
| source.append(descriptor.getAssignedName()); |
| } else { |
| source.objectModel().startCall(SourceGenConstants.ROM_TO_BOOLEAN, true).append(descriptor.getAssignedName()).endCall(); |
| } |
| source.completeIf(); |
| } |
| |
| @Override |
| public void visit(Conditional.End conditionalEnd) { |
| source.endIf(); |
| } |
| |
| @Override |
| public void visit(VariableBinding.Start variableBinding) { |
| source.startBlock(); |
| TypeInfo typeInfo = TypeInference |
| .inferTypes(variableBinding.getExpression(), analyzer, unitBuilder.getImports(), unitBuilder.getJavaImportsAnalyzer()); |
| Type type = typeInfo.typeOf(variableBinding.getExpression()); |
| String properName = declare(variableBinding.getVariableName(), type); |
| source.beginAssignment(properName, type.getNativeClass()); |
| ExpressionTranslator.buildExpression( |
| variableBinding.getExpression(), |
| source, |
| analyzer, |
| typeInfo, |
| unitBuilder.getImports()); |
| source.endStatement(); |
| } |
| |
| @Override |
| public void visit(VariableBinding.End variableBindingEnd) { |
| VariableDescriptor descriptor = analyzer.endVariable(); |
| String listCoercionVar = descriptor.getListCoercion(); |
| if (listCoercionVar != null) { |
| //this variable was coerced to list at some point |
| generateCoercionClearing(listCoercionVar); |
| } |
| source.endBlock(); |
| } |
| |
| @Override |
| public void visit(VariableBinding.Global globalAssignment) { |
| TypeInfo typeInfo = TypeInference |
| .inferTypes(globalAssignment.getExpression(), analyzer, unitBuilder.getImports(), unitBuilder.getJavaImportsAnalyzer()); |
| VariableDescriptor descriptor = analyzer.declareGlobal(globalAssignment.getVariableName()); |
| String name = descriptor.getAssignedName(); |
| source.append(name).assign(); |
| ExpressionTranslator.buildExpression( |
| globalAssignment.getExpression(), |
| source, |
| analyzer, |
| typeInfo, |
| unitBuilder.getImports()); |
| source.endStatement(); |
| String listCoercionVar = descriptor.getListCoercion(); |
| if (listCoercionVar != null) { |
| //variable was used for list coercion. Generating a coercion clearing |
| generateCoercionClearing(listCoercionVar); |
| } |
| } |
| |
| @Override |
| public void visit(OutputVariable outputVariable) { |
| String variable = analyzer.assignedName(outputVariable.getVariableName()); |
| source.startStatement() |
| .startMethodCall(SourceGenConstants.OUT_BUFFER, SourceGenConstants.WRITE_METHOD) |
| .objectModel().startCall(SourceGenConstants.ROM_TO_STRING, true) |
| .append(variable) |
| .endCall() |
| .endCall() |
| .endStatement(); |
| } |
| |
| @Override |
| public void visit(OutText outText) { |
| source.startStatement() |
| .startMethodCall(SourceGenConstants.OUT_BUFFER, SourceGenConstants.WRITE_METHOD) |
| .stringLiteral(outText.getText()) |
| .endCall() |
| .endStatement(); |
| } |
| |
| @Override |
| public void visit(Loop.Start loop) { |
| VariableDescriptor descriptor = analyzer.descriptor(loop.getListVariable()); |
| String listVariable = descriptor.getAssignedName(); |
| String collectionVar = descriptor.requireListCoercion(); |
| source.beginIf().append(collectionVar).equality().nullLiteral().completeIf() |
| .startStatement() |
| .append(collectionVar) |
| .assign() |
| .objectModel().startCall(SourceGenConstants.ROM_TO_COLLECTION, true) |
| .append(listVariable) |
| .endCall() |
| .endStatement() |
| .endIf(); |
| String indexVar = declare(loop.getIndexVariable(), Type.LONG); |
| source.beginAssignment(indexVar, Type.LONG.getNativeClass()).number(0).endStatement(); |
| String itemVar = declare(loop.getItemVariable(), Type.UNKNOWN); |
| source.beginFor(itemVar, collectionVar); |
| loopStatusStack.push(indexVar); |
| } |
| |
| @Override |
| public void visit(Loop.End loopEnd) { |
| String indexVar = loopStatusStack.pop(); |
| source.startStatement().append(indexVar).increment().endStatement(); |
| source.endFor(); |
| analyzer.endVariable(); |
| analyzer.endVariable(); |
| } |
| |
| @Override |
| public void visit(Procedure.Start startProcedure) { |
| UnitBuilder subTemplateUnit = unitBuilder.newSubBuilder(startProcedure.getName(), startProcedure.getParameters()); |
| analyzer.declareTemplate(startProcedure.getName()); |
| control.push(new CodeGenVisitor(subTemplateUnit, control)); |
| } |
| |
| @Override |
| public void visit(Procedure.End endProcedure) { |
| CodeGenVisitor previous = (CodeGenVisitor) control.pop(); |
| previous.finish(); |
| } |
| |
| @Override |
| public void visit(Procedure.Call procedureCall) { |
| String templateVar = analyzer.assignedName(procedureCall.getTemplateVariable()); |
| String argVar = analyzer.assignedName(procedureCall.getArgumentsVariable()); |
| source.startStatement() |
| .startCall(SourceGenConstants.CALL_UNIT_METHOD, false) |
| .append(SourceGenConstants.OUT_BUFFER) |
| .separateArgument() |
| .append(SourceGenConstants.RENDER_CONTEXT_INSTANCE) |
| .separateArgument() |
| .append(templateVar) |
| .separateArgument() |
| .append(argVar) |
| .endCall() |
| .endStatement(); |
| } |
| |
| private String declare(String originalName, Type type) { |
| return analyzer.declareVariable(originalName, type).getAssignedName(); |
| } |
| |
| private void generateCoercionClearing(String coercionVariableName) { |
| source.startStatement().append(coercionVariableName).assign().nullLiteral().endStatement(); |
| } |
| } |