Refactoring DefaultGroovyTranspiler.

This commit is contained in:
JesseBrault0709 2024-05-25 14:09:35 +02:00
parent 449c83975f
commit 3f45609236
28 changed files with 761 additions and 159 deletions

View File

@ -6,17 +6,25 @@ import groowt.view.component.compiler.source.ComponentTemplateSource;
public abstract class AbstractComponentTemplateCompileUnit implements public abstract class AbstractComponentTemplateCompileUnit implements
ComponentTemplateCompileUnit { ComponentTemplateCompileUnit {
private final String descriptiveName;
private final Class<? extends ViewComponent> forClass; private final Class<? extends ViewComponent> forClass;
private final ComponentTemplateSource source; private final ComponentTemplateSource source;
public AbstractComponentTemplateCompileUnit( public AbstractComponentTemplateCompileUnit(
String descriptiveName,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
ComponentTemplateSource source ComponentTemplateSource source
) { ) {
this.descriptiveName = descriptiveName;
this.forClass = forClass; this.forClass = forClass;
this.source = source; this.source = source;
} }
@Override
public String getDescriptiveName() {
return this.descriptiveName;
}
@Override @Override
public Class<? extends ViewComponent> getForClass() { public Class<? extends ViewComponent> getForClass() {
return this.forClass; return this.forClass;

View File

@ -22,7 +22,7 @@ public class ComponentTemplateCompileException extends Exception {
@Override @Override
public String getMessage() { public String getMessage() {
final var sb = new StringBuilder("Error in ").append(compileUnit.getSource().getDescription()); final var sb = new StringBuilder("Error in ").append(compileUnit.getSource().getDescriptiveName());
final @Nullable String position = this.formatPosition(); final @Nullable String position = this.formatPosition();
if (position != null) { if (position != null) {
sb.append(" at ").append(position); sb.append(" at ").append(position);

View File

@ -5,6 +5,7 @@ import groowt.view.component.compiler.source.ComponentTemplateSource;
public interface ComponentTemplateCompileUnit { public interface ComponentTemplateCompileUnit {
String getDescriptiveName();
Class<? extends ViewComponent> getForClass(); Class<? extends ViewComponent> getForClass();
String getDefaultPackageName(); String getDefaultPackageName();
ComponentTemplateSource getSource(); ComponentTemplateSource getSource();

View File

@ -46,7 +46,7 @@ public interface ComponentTemplateSource {
} }
Reader toReader() throws Exception; Reader toReader() throws Exception;
String getDescription(); String getDescriptiveName();
boolean canReopen(); boolean canReopen();
List<String> getLines(); List<String> getLines();

View File

@ -21,7 +21,7 @@ public class FileSource implements ComponentTemplateSource {
} }
@Override @Override
public String getDescription() { public String getDescriptiveName() {
return this.templateFile.toString(); return this.templateFile.toString();
} }

View File

@ -23,7 +23,7 @@ public class InputStreamSource implements ComponentTemplateSource {
} }
@Override @Override
public String getDescription() { public String getDescriptiveName() {
return this.description != null ? this.description : "<anonymous InputStream source>"; return this.description != null ? this.description : "<anonymous InputStream source>";
} }

View File

@ -21,7 +21,7 @@ public class ReaderSource implements ComponentTemplateSource {
} }
@Override @Override
public String getDescription() { public String getDescriptiveName() {
return this.description != null ? this.description : "<anonymous Reader source>"; return this.description != null ? this.description : "<anonymous Reader source>";
} }

View File

@ -22,7 +22,7 @@ public class StringSource implements ComponentTemplateSource {
} }
@Override @Override
public String getDescription() { public String getDescriptiveName() {
return this.name != null ? this.name : "<anonymous string source>"; return this.name != null ? this.name : "<anonymous string source>";
} }

View File

@ -21,7 +21,7 @@ public class URISource implements ComponentTemplateSource {
} }
@Override @Override
public String getDescription() { public String getDescriptiveName() {
return this.templateURI.toString(); return this.templateURI.toString();
} }

View File

@ -25,7 +25,7 @@ public class URLSource implements ComponentTemplateSource {
} }
@Override @Override
public String getDescription() { public String getDescriptiveName() {
return this.url.toString(); return this.url.toString();
} }

View File

@ -65,6 +65,7 @@ public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlu
final String sourceUnitFileName = sourceUnitFullName.substring(lastSlashIndex + 1); final String sourceUnitFileName = sourceUnitFullName.substring(lastSlashIndex + 1);
if (sourceUnitFileName.endsWith(".wvc")) { if (sourceUnitFileName.endsWith(".wvc")) {
final var compileUnit = new DefaultWebViewComponentTemplateCompileUnit( final var compileUnit = new DefaultWebViewComponentTemplateCompileUnit(
sourceUnitFileName,
AnonymousWebViewComponent.class, AnonymousWebViewComponent.class,
ComponentTemplateSource.of(sourceUnit.getSource().getURI()), ComponentTemplateSource.of(sourceUnit.getSource().getURI()),
"" // default package "" // default package
@ -79,13 +80,13 @@ public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlu
} }
final var groovyTranspiler = new DefaultGroovyTranspiler(); final var groovyTranspiler = new DefaultGroovyTranspiler();
final String nameWithoutExtension = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4); final String teplateClassSimpleName = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4);
try { try {
final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile( final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(), new DefaultComponentTemplateCompilerConfiguration(),
compileUnit, compileUnit,
cuNode, cuNode,
nameWithoutExtension teplateClassSimpleName
); );
return transpiledSourceUnit.getAST(); return transpiledSourceUnit.getAST();
} catch (ComponentTemplateCompileException e) { } catch (ComponentTemplateCompileException e) {

View File

@ -24,6 +24,7 @@ import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Deprecated
public class DefaultGStringTranspiler implements GStringTranspiler { public class DefaultGStringTranspiler implements GStringTranspiler {
private final PositionSetter positionSetter; private final PositionSetter positionSetter;

View File

@ -2,6 +2,7 @@ package groowt.view.component.web.transpile;
import groovy.transform.Field; import groovy.transform.Field;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.ComponentTemplateCompileUnit;
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration; import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
import groowt.view.component.web.WebViewComponentBugError; import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.ast.node.BodyNode; import groowt.view.component.web.ast.node.BodyNode;
@ -10,7 +11,7 @@ import groowt.view.component.web.ast.node.PreambleNode;
import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsException; import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsException;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.component.web.runtime.DefaultWebViewRenderContext; import groowt.view.component.web.transpile.BodyTranspiler.AddOrAppendCallback;
import groowt.view.component.web.transpile.groovy.GroovyUtil; import groowt.view.component.web.transpile.groovy.GroovyUtil;
import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver; import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.*;
@ -19,9 +20,8 @@ import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement; import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.ErrorCollector; import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.SourceUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -30,29 +30,58 @@ import java.util.List;
import static groowt.view.component.web.transpile.TranspilerUtil.*; import static groowt.view.component.web.transpile.TranspilerUtil.*;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
/**
* Takes a Groovy {@link CompilationUnit} in the constructor and adds a {@link SourceUnit} representing the target
* Groovy code for the template represented by the AST passed to {@link #transpile}.
* <p>
* Note that while the terminology is similar, a Groovy {@link CompilationUnit} is distinct from our own
* {@link CompilationUnitNode}.
*/
public class DefaultGroovyTranspiler implements GroovyTranspiler { public class DefaultGroovyTranspiler implements GroovyTranspiler {
private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class); private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class);
private static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class); private static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class);
private static final ClassNode RENDER_CONTEXT_IMPLEMENTATION =
ClassHelper.make(DefaultWebViewRenderContext.class);
protected TranspilerConfiguration getConfiguration( protected TranspilerConfiguration getConfiguration(
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
ModuleNode moduleNode,
ClassLoader classLoader ClassLoader classLoader
) { ) {
return new DefaultTranspilerConfiguration(new ClassLoaderComponentClassNodeResolver( return new DefaultTranspilerConfiguration(new ClassLoaderComponentClassNodeResolver(compileUnit, classLoader));
compileUnit, moduleNode, classLoader }
protected void addAutomaticImports(WebViewComponentModuleNode moduleNode, TranspilerConfiguration configuration) {
configuration.getImports().forEach(moduleNode::addImport);
configuration.getStaticImports().forEach(staticImport -> moduleNode.addStaticImport(
staticImport.getV1(), staticImport.getV2(), staticImport.getV3()
)); ));
configuration.getStarImports().forEach(moduleNode::addStarImport);
configuration.getStaticStarImports().forEach(moduleNode::addStaticStarImport);
}
protected WebViewComponentModuleNode initModuleNode(
ComponentTemplateCompileUnit compileUnit,
WebViewComponentSourceUnit sourceUnit,
TranspilerConfiguration configuration
) {
final var moduleNode = new WebViewComponentModuleNode(sourceUnit);
sourceUnit.setModuleNode(moduleNode);
final String defaultPackageName = compileUnit.getDefaultPackageName();
if (!defaultPackageName.trim().isEmpty()) {
moduleNode.setPackageName(defaultPackageName);
}
this.addAutomaticImports(moduleNode, configuration);
return moduleNode;
}
protected ClassNode initMainClassNode(
ComponentTemplateCompileUnit compileUnit,
String templateClassName,
WebViewComponentModuleNode moduleNode
) {
final ClassNode mainClassNode = new ClassNode(
compileUnit.getDefaultPackageName() + templateClassName,
ACC_PUBLIC,
ClassHelper.OBJECT_TYPE
);
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
moduleNode.addClass(mainClassNode);
return mainClassNode;
} }
protected void checkPreambleClasses(String templateName, List<ClassNode> classNodes) { protected void checkPreambleClasses(String templateName, List<ClassNode> classNodes) {
@ -72,17 +101,27 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
String templateClassName, String templateClassName,
PreambleNode preambleNode, PreambleNode preambleNode,
ClassNode mainClassNode, ClassNode mainClassNode,
WebViewComponentModuleNode moduleNode WebViewComponentModuleNode moduleNode,
PositionSetter positionSetter
) { ) {
final GroovyUtil.ConvertResult convertResult = GroovyUtil.convert( final GroovyUtil.ConvertResult convertResult = GroovyUtil.convert(
preambleNode.getGroovyCode().getAsValidGroovyCode() preambleNode.getGroovyCode().getAsValidGroovyCode()
); );
final ModuleNode convertModuleNode = convertResult.moduleNode();
WebViewComponentModuleNode.copyTo(convertResult.moduleNode(), moduleNode); final PositionVisitor positionVisitor = new PositionVisitor(positionSetter, preambleNode);
convertModuleNode.getImports().forEach(moduleNode::addImport);
convertModuleNode.getStarImports().forEach(moduleNode::addStarImport);
convertModuleNode.getStaticImports().forEach(moduleNode::addStaticImport);
convertModuleNode.getStaticStarImports().forEach(moduleNode::addStaticStarImport);
positionVisitor.visitImports(moduleNode);
// if user supplied a package, use it
if (convertResult.moduleNode().hasPackage()) { if (convertResult.moduleNode().hasPackage()) {
moduleNode.setPackage(convertResult.moduleNode().getPackage()); moduleNode.setPackage(convertResult.moduleNode().getPackage());
mainClassNode.setName(moduleNode.getPackageName() + templateClassName); mainClassNode.setName(moduleNode.getPackageName() + templateClassName);
positionVisitor.visitPackage(moduleNode.getPackage());
} }
final BlockStatement preambleBlock = convertResult.blockStatement(); final BlockStatement preambleBlock = convertResult.blockStatement();
@ -100,6 +139,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
) )
.toList(); .toList();
if (declarationsWithField.size() != preambleStatements.size()) { if (declarationsWithField.size() != preambleStatements.size()) {
// TODO: figure out why we have extraneous statements sometimes when it seems otherwise not
logger.warn( logger.warn(
"{} contains script statements which are not supported. " + "{} contains script statements which are not supported. " +
"Currently, only classes, methods, and field declarations " + "Currently, only classes, methods, and field declarations " +
@ -110,13 +150,19 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
} }
declarationsWithField.forEach(declaration -> { declarationsWithField.forEach(declaration -> {
declaration.setDeclaringClass(mainClassNode); declaration.setDeclaringClass(mainClassNode);
positionVisitor.visitDeclarationExpression(declaration);
}); });
} }
// move methods from script class // move methods from script class
final ClassNode scriptClass = convertResult.scriptClass(); final ClassNode scriptClass = convertResult.scriptClass();
if (scriptClass != null) { if (scriptClass != null) {
scriptClass.getMethods().forEach(mainClassNode::addMethod); scriptClass.getMethods().stream()
.filter(method -> !(method.getName().equals("main") || method.getName().equals("run")))
.forEach(method -> {
mainClassNode.addMethod(method);
positionVisitor.visitMethod(method);
});
} }
// handle classes // handle classes
@ -124,62 +170,127 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
this.checkPreambleClasses(templateClassName, classNodes); this.checkPreambleClasses(templateClassName, classNodes);
classNodes.stream() classNodes.stream()
.filter(classNode -> classNode != convertResult.scriptClass()) .filter(classNode -> classNode != convertResult.scriptClass())
.forEach(moduleNode::addClass); .forEach(classNode -> {
moduleNode.addClass(classNode);
positionVisitor.visitClass(classNode);
});
}
protected ExpressionStatement constructRenderContext(
ClassNode renderContextImplementation,
Variable componentContextParam,
Variable writerParam,
VariableExpression renderContextVariableExpr
) {
final ConstructorCallExpression renderContextConstructor = new ConstructorCallExpression(
renderContextImplementation,
new ArgumentListExpression(
new VariableExpression(componentContextParam),
new VariableExpression(writerParam)
)
);
final BinaryExpression renderContextAssignExpr = new DeclarationExpression(
renderContextVariableExpr,
getAssignToken(),
renderContextConstructor
);
return new ExpressionStatement(renderContextAssignExpr);
}
protected ExpressionStatement assignComponentContextRenderContext(
Parameter componentContextParam,
VariableExpression renderContextVariableExpr
) {
final BinaryExpression componentContextRenderContextAssign = new BinaryExpression(
new PropertyExpression(new VariableExpression(componentContextParam), "renderContext"),
getAssignToken(),
renderContextVariableExpr
);
return new ExpressionStatement(componentContextRenderContextAssign);
}
protected ExpressionStatement assignWriterRenderContext(
Parameter writerParam,
VariableExpression renderContextVariableExpr
) {
final BinaryExpression writerRenderContextAssign = new BinaryExpression(
new PropertyExpression(new VariableExpression(writerParam), "renderContext"),
getAssignToken(),
renderContextVariableExpr
);
return new ExpressionStatement(writerRenderContextAssign);
}
protected ExpressionStatement assignWriterComponentContext(Parameter writerParam, Parameter componentContextParam) {
final BinaryExpression writerComponentContextAssign = new BinaryExpression(
new PropertyExpression(new VariableExpression(writerParam), "componentContext"),
getAssignToken(),
new VariableExpression(componentContextParam)
);
return new ExpressionStatement(writerComponentContextAssign);
}
protected Statement handleBody(
BodyNode bodyNode,
TranspilerConfiguration transpilerConfiguration,
TranspilerState state
) {
final var appendOrAddStatementFactory = transpilerConfiguration.getAppendOrAddStatementFactory();
final AddOrAppendCallback callback = (source, expr) -> appendOrAddStatementFactory.addOrAppend(
source,
state,
action -> {
if (action == AppendOrAddStatementFactory.Action.ADD) {
throw new WebViewComponentBugError(new IllegalStateException(
"Should not be adding from document root, only appending!"
));
}
return expr;
}
);
return transpilerConfiguration.getBodyTranspiler().transpileBody(bodyNode, callback, state);
} }
// Cases:
// - no preamble -> create our own class
// - some preamble, but no script -> create our own class but use imports/packageName from preamble
// - preamble with script -> use the script class from the converted preamble,
// and don't forget to call run in our render method
@Override @Override
public WebViewComponentSourceUnit transpile( public WebViewComponentSourceUnit transpile(
ComponentTemplateCompilerConfiguration compilerConfiguration, ComponentTemplateCompilerConfiguration compilerConfiguration,
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
CompilationUnitNode compilationUnitNode, CompilationUnitNode compilationUnitNode,
String templateClassName String templateClassSimpleName
) throws ComponentTemplateCompileException { ) throws ComponentTemplateCompileException {
final var groovyCompilerConfiguration = compilerConfiguration.getGroovyCompilerConfiguration(); // transpilerConfiguration and positionSetter
final var sourceUnit = new WebViewComponentSourceUnit( final var transpilerConfiguration = this.getConfiguration(
templateClassName, compileUnit, compileUnit.getGroovyCompilationUnit().getClassLoader()
);
final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter();
// prepare sourceUnit
final CompilerConfiguration groovyCompilerConfiguration =
compilerConfiguration.getGroovyCompilerConfiguration();
final WebViewComponentSourceUnit sourceUnit = new WebViewComponentSourceUnit(
compileUnit.getDescriptiveName(),
compileUnit.getGroovyReaderSource(), compileUnit.getGroovyReaderSource(),
groovyCompilerConfiguration, groovyCompilerConfiguration,
compilerConfiguration.getGroovyClassLoader(), compilerConfiguration.getGroovyClassLoader(),
new ErrorCollector(groovyCompilerConfiguration) new ErrorCollector(groovyCompilerConfiguration)
); );
final var moduleNode = new WebViewComponentModuleNode(sourceUnit); // prepare moduleNode
sourceUnit.setModuleNode(moduleNode); final WebViewComponentModuleNode moduleNode = this.initModuleNode(
compileUnit, sourceUnit, transpilerConfiguration
final String defaultPackageName = compileUnit.getDefaultPackageName();
if (!defaultPackageName.trim().isEmpty()) {
moduleNode.setPackageName(defaultPackageName);
}
moduleNode.addStarImport(GROOWT_VIEW_COMPONENT_WEB + ".lib");
moduleNode.addImport(COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE);
moduleNode.addImport(COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE);
moduleNode.addStarImport("groowt.view.component.runtime");
moduleNode.addStarImport(GROOWT_VIEW_COMPONENT_WEB + ".runtime");
final ClassNode mainClassNode = new ClassNode(
compileUnit.getDefaultPackageName() + templateClassName,
ACC_PUBLIC,
ClassHelper.OBJECT_TYPE
); );
mainClassNode.setScript(true);
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
moduleNode.addClass(mainClassNode); // prepare mainClassNode
final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode);
// preamble // handle preamble
final PreambleNode preambleNode = compilationUnitNode.getPreambleNode(); final PreambleNode preambleNode = compilationUnitNode.getPreambleNode();
if (preambleNode != null) { if (preambleNode != null) {
this.handlePreamble(templateClassName, preambleNode, mainClassNode, moduleNode); this.handlePreamble(templateClassSimpleName, preambleNode, mainClassNode, moduleNode, positionSetter);
} }
// getRenderer // getRenderer method and render closure
// params // first, getRenderer params
final Parameter componentContextParam = new Parameter(COMPONENT_CONTEXT_TYPE, COMPONENT_CONTEXT_NAME); final Parameter componentContextParam = new Parameter(COMPONENT_CONTEXT_TYPE, COMPONENT_CONTEXT_NAME);
final Parameter writerParam = new Parameter(COMPONENT_WRITER_TYPE, COMPONENT_WRITER_NAME); final Parameter writerParam = new Parameter(COMPONENT_WRITER_TYPE, COMPONENT_WRITER_NAME);
final VariableExpression renderContextVariable = new VariableExpression( final VariableExpression renderContextVariable = new VariableExpression(
@ -187,98 +298,53 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE
); );
// closure body // returned closure body
final BlockStatement renderBlock = new BlockStatement(); final BlockStatement renderBlock = new BlockStatement();
// init renderContext, componentContext, and writer properties
renderBlock.addStatement(this.constructRenderContext(
transpilerConfiguration.getRenderContextImplementation(),
componentContextParam,
writerParam,
renderContextVariable
));
renderBlock.addStatement(this.assignComponentContextRenderContext(
componentContextParam, renderContextVariable
));
renderBlock.addStatement(this.assignWriterRenderContext(writerParam, renderContextVariable));
renderBlock.addStatement(this.assignWriterComponentContext(writerParam, componentContextParam));
// init transpiler state
final TranspilerState state = TranspilerState.withRootScope( final TranspilerState state = TranspilerState.withRootScope(
componentContextParam, componentContextParam,
writerParam, writerParam,
renderContextVariable renderContextVariable
); );
renderBlock.setVariableScope(state.getCurrentScope()); renderBlock.setVariableScope(state.getCurrentScope()); // root scope
// init: construct RenderContext // body
final ConstructorCallExpression renderContextConstructor = new ConstructorCallExpression(
RENDER_CONTEXT_IMPLEMENTATION,
new ArgumentListExpression(
new VariableExpression(componentContextParam), // component context
new VariableExpression(writerParam)
)
);
final BinaryExpression renderContextAssignExpr = new DeclarationExpression(
renderContextVariable,
getAssignToken(),
renderContextConstructor
);
renderBlock.addStatement(new ExpressionStatement(renderContextAssignExpr));
// init: componentContext.renderContext = renderContext
final BinaryExpression componentContextRenderContextAssign = new BinaryExpression(
new PropertyExpression(new VariableExpression(componentContextParam), "renderContext"),
getAssignToken(),
renderContextVariable
);
renderBlock.addStatement(new ExpressionStatement(componentContextRenderContextAssign));
// init: writer.renderContext = renderContext
final BinaryExpression writerRenderContextAssign = new BinaryExpression(
new PropertyExpression(new VariableExpression(writerParam), "renderContext"),
getAssignToken(),
renderContextVariable
);
renderBlock.addStatement(new ExpressionStatement(writerRenderContextAssign));
// init: writer.componentContext = componentContext
final BinaryExpression writerComponentContextAssign = new BinaryExpression(
new PropertyExpression(new VariableExpression(writerParam), "componentContext"),
getAssignToken(),
new VariableExpression(componentContextParam)
);
renderBlock.addStatement(new ExpressionStatement(writerComponentContextAssign));
// actual rendering of body
final var configuration = this.getConfiguration(
compileUnit,
moduleNode,
compilerConfiguration.getGroovyClassLoader()
);
final BodyNode bodyNode = compilationUnitNode.getBodyNode(); final BodyNode bodyNode = compilationUnitNode.getBodyNode();
if (bodyNode != null) { if (bodyNode != null) {
final var appendOrAddStatementFactory = configuration.getAppendOrAddStatementFactory(); renderBlock.addStatement(this.handleBody(bodyNode, transpilerConfiguration, state));
renderBlock.addStatement(
configuration.getBodyTranspiler()
.transpileBody(
compilationUnitNode.getBodyNode(),
(source, expr) -> appendOrAddStatementFactory.addOrAppend(
source,
state,
action -> {
if (action == AppendOrAddStatementFactory.Action.ADD) {
throw new WebViewComponentBugError(new IllegalStateException(
"Should not be adding from document root!"
));
}
return expr;
}
),
state
)
);
} }
// return null from render closure
renderBlock.addStatement(new ReturnStatement(ConstantExpression.NULL)); renderBlock.addStatement(new ReturnStatement(ConstantExpression.NULL));
// make the closure
final ClosureExpression renderer = new ClosureExpression( final ClosureExpression renderer = new ClosureExpression(
new Parameter[] { componentContextParam, writerParam }, new Parameter[] { componentContextParam, writerParam },
renderBlock renderBlock
); );
// getRenderer() // getRenderer() return statement
final Statement returnRendererStmt = new ReturnStatement(renderer); final Statement returnRendererStmt = new ReturnStatement(renderer);
// getRenderer() return type is Closure<Void>
final var voidClosure = ClassHelper.CLOSURE_TYPE.getPlainNodeReference(); final var voidClosure = ClassHelper.CLOSURE_TYPE.getPlainNodeReference();
voidClosure.setGenericsTypes(new GenericsType[] { new GenericsType(ClassHelper.void_WRAPPER_TYPE) }); voidClosure.setGenericsTypes(new GenericsType[] { new GenericsType(ClassHelper.void_WRAPPER_TYPE) });
// getRenderer method
final var getRenderer = new MethodNode( final var getRenderer = new MethodNode(
GET_RENDERER, GET_RENDERER,
ACC_PUBLIC, ACC_PUBLIC,
@ -289,6 +355,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
); );
mainClassNode.addMethod(getRenderer); mainClassNode.addMethod(getRenderer);
// check for errors
if (state.hasErrors()) { if (state.hasErrors()) {
final List<ComponentTemplateCompileException> errors = state.getErrors(); final List<ComponentTemplateCompileException> errors = state.getErrors();
if (errors.size() == 1) { if (errors.size() == 1) {
@ -298,6 +365,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
} }
} }
// return the sourceUnit for later processing
return sourceUnit; return sourceUnit;
} }

View File

@ -1,18 +1,26 @@
package groowt.view.component.web.transpile; package groowt.view.component.web.transpile;
import groovy.lang.Tuple3;
import groowt.util.fp.provider.DefaultProvider; import groowt.util.fp.provider.DefaultProvider;
import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver; import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver;
import org.codehaus.groovy.ast.ClassNode;
import java.util.Map;
import java.util.Set;
import static groowt.view.component.web.transpile.TranspilerUtil.*;
public class DefaultTranspilerConfiguration implements TranspilerConfiguration { public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
private final PositionSetter positionSetter;
private final AppendOrAddStatementFactory appendOrAddStatementFactory = new DefaultAppendOrAddStatementFactory(); private final AppendOrAddStatementFactory appendOrAddStatementFactory = new DefaultAppendOrAddStatementFactory();
private final BodyTranspiler bodyTranspiler; private final BodyTranspiler bodyTranspiler;
private final ValueNodeTranspiler valueNodeTranspiler; private final ValueNodeTranspiler valueNodeTranspiler;
public DefaultTranspilerConfiguration(ComponentClassNodeResolver classNodeResolver) { public DefaultTranspilerConfiguration(ComponentClassNodeResolver classNodeResolver) {
final var positionSetter = new SimplePositionSetter(); this.positionSetter = new SimplePositionSetter();
final var jStringTranspiler = new DefaultJStringTranspiler(positionSetter); final var jStringTranspiler = new DefaultJStringTranspiler(this.positionSetter);
final var gStringTranspiler = new DefaultGStringTranspiler(positionSetter, jStringTranspiler); final var gStringTranspiler = new DefaultGStringTranspiler(this.positionSetter, jStringTranspiler);
final var componentTranspiler = new DefaultComponentTranspiler( final var componentTranspiler = new DefaultComponentTranspiler(
DefaultProvider.of(this.appendOrAddStatementFactory), DefaultProvider.of(this.appendOrAddStatementFactory),
DefaultProvider.of(classNodeResolver), DefaultProvider.of(classNodeResolver),
@ -23,6 +31,11 @@ public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
this.bodyTranspiler = new DefaultBodyTranspiler(gStringTranspiler, jStringTranspiler, componentTranspiler); this.bodyTranspiler = new DefaultBodyTranspiler(gStringTranspiler, jStringTranspiler, componentTranspiler);
} }
@Override
public PositionSetter getPositionSetter() {
return this.positionSetter;
}
@Override @Override
public BodyTranspiler getBodyTranspiler() { public BodyTranspiler getBodyTranspiler() {
return this.bodyTranspiler; return this.bodyTranspiler;
@ -37,4 +50,36 @@ public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
return this.valueNodeTranspiler; return this.valueNodeTranspiler;
} }
@Override
public Map<String, ClassNode> getImports() {
return Map.of(
COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE,
COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE
);
}
@Override
public Set<String> getStarImports() {
return Set.of(
GROOWT_VIEW_COMPONENT_WEB + ".lib",
"groowt.view.component.runtime",
GROOWT_VIEW_COMPONENT_WEB + ".runtime"
);
}
@Override
public Set<Tuple3<ClassNode, String, String>> getStaticImports() {
return Set.of();
}
@Override
public Map<String, ClassNode> getStaticStarImports() {
return Map.of();
}
@Override
public ClassNode getRenderContextImplementation() {
return DEFAULT_RENDER_CONTEXT_IMPLEMENTATION;
}
} }

View File

@ -3,6 +3,7 @@ package groowt.view.component.web.transpile;
import groowt.view.component.web.ast.node.GStringBodyTextNode; import groowt.view.component.web.ast.node.GStringBodyTextNode;
import org.codehaus.groovy.ast.expr.GStringExpression; import org.codehaus.groovy.ast.expr.GStringExpression;
@Deprecated
public interface GStringTranspiler { public interface GStringTranspiler {
GStringExpression createGStringExpression(GStringBodyTextNode parent); GStringExpression createGStringExpression(GStringBodyTextNode parent);
} }

View File

@ -11,7 +11,7 @@ public interface GroovyTranspiler {
ComponentTemplateCompilerConfiguration compilerConfiguration, ComponentTemplateCompilerConfiguration compilerConfiguration,
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
CompilationUnitNode compilationUnitNode, CompilationUnitNode compilationUnitNode,
String templateClassName String templateClassSimpleName
) throws ComponentTemplateCompileException; ) throws ComponentTemplateCompileException;
} }

View File

@ -5,6 +5,25 @@ import groowt.view.component.web.util.TokenRange;
import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.ASTNode;
public interface PositionSetter { public interface PositionSetter {
/**
* Sets the position of the Groovy node off-set from the start
* of the start of the Wvc node. The formula is
* thus:
* <pre>
* target.line = container.line + target.line - 1
* target.column = target.line == 1 ? container.column + target.column - 1 : target.column
* target.lastLine = container.line + target.lastLine - 1
* target.lastColumn = target.lastLine == 1 ? container.column + target.lastColumn - 1 : target.lastColumn
* </pre>
* For example, if the container has 2,1..4,1 and the target has
* 3,1..3,1 (in its source), the target will be adjusted to 4,1..4,1.
*
* @param target The (Groovy) node whose position is to be set.
* @param container The containing (Wvc) node.
*/
void setPositionOffsetInContainer(ASTNode target, Node container);
void setPosition(ASTNode target, TokenRange tokenRange); void setPosition(ASTNode target, TokenRange tokenRange);
void setPosition(ASTNode target, Node source); void setPosition(ASTNode target, Node source);
void setPosition(ASTNode target, Node start, Node end); void setPosition(ASTNode target, Node start, Node end);

View File

@ -0,0 +1,426 @@
package groowt.view.component.web.transpile;
import groowt.view.component.web.ast.node.Node;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.classgen.BytecodeExpression;
import org.codehaus.groovy.control.SourceUnit;
// TODO: create a utility walker-visitor class to make this much simpler
public class PositionVisitor extends ClassCodeVisitorSupport {
private final PositionSetter positionSetter;
private final Node container;
public PositionVisitor(PositionSetter positionSetter, Node container) {
this.positionSetter = positionSetter;
this.container = container;
}
@Override
protected SourceUnit getSourceUnit() {
throw new UnsupportedOperationException();
}
@Override
public void visitClass(ClassNode node) {
super.visitClass(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitAnnotations(AnnotatedNode node) {
super.visitAnnotations(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
protected void visitAnnotation(AnnotationNode node) {
super.visitAnnotation(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitPackage(PackageNode node) {
super.visitPackage(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitImports(ModuleNode node) {
super.visitImports(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitConstructor(ConstructorNode node) {
super.visitConstructor(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitMethod(MethodNode node) {
super.visitMethod(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
super.visitConstructorOrMethod(node, isConstructor);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitField(FieldNode node) {
super.visitField(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitProperty(PropertyNode node) {
super.visitProperty(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
protected void visitObjectInitializerStatements(ClassNode node) {
super.visitObjectInitializerStatements(node);
this.positionSetter.setPositionOffsetInContainer(node, container);
}
@Override
public void visitDeclarationExpression(DeclarationExpression expression) {
super.visitDeclarationExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitAssertStatement(AssertStatement statement) {
super.visitAssertStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitBlockStatement(BlockStatement statement) {
super.visitBlockStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitBreakStatement(BreakStatement statement) {
super.visitBreakStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitCaseStatement(CaseStatement statement) {
super.visitCaseStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitCatchStatement(CatchStatement statement) {
super.visitCatchStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitContinueStatement(ContinueStatement statement) {
super.visitContinueStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitDoWhileLoop(DoWhileStatement statement) {
super.visitDoWhileLoop(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitExpressionStatement(ExpressionStatement statement) {
super.visitExpressionStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitForLoop(ForStatement statement) {
super.visitForLoop(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitIfElse(IfStatement statement) {
super.visitIfElse(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitReturnStatement(ReturnStatement statement) {
super.visitReturnStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitSwitch(SwitchStatement statement) {
super.visitSwitch(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitSynchronizedStatement(SynchronizedStatement statement) {
super.visitSynchronizedStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitThrowStatement(ThrowStatement statement) {
super.visitThrowStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitTryCatchFinally(TryCatchStatement statement) {
super.visitTryCatchFinally(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitWhileLoop(WhileStatement statement) {
super.visitWhileLoop(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitEmptyStatement(EmptyStatement statement) {
super.visitEmptyStatement(statement);
this.positionSetter.setPositionOffsetInContainer(statement, container);
}
@Override
public void visitMethodCallExpression(MethodCallExpression call) {
super.visitMethodCallExpression(call);
this.positionSetter.setPositionOffsetInContainer(call, container);
}
@Override
public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
super.visitStaticMethodCallExpression(call);
this.positionSetter.setPositionOffsetInContainer(call, container);
}
@Override
public void visitConstructorCallExpression(ConstructorCallExpression call) {
super.visitConstructorCallExpression(call);
this.positionSetter.setPositionOffsetInContainer(call, container);
}
@Override
public void visitBinaryExpression(BinaryExpression expression) {
super.visitBinaryExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitTernaryExpression(TernaryExpression expression) {
super.visitTernaryExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitShortTernaryExpression(ElvisOperatorExpression expression) {
super.visitShortTernaryExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitPostfixExpression(PostfixExpression expression) {
super.visitPostfixExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitPrefixExpression(PrefixExpression expression) {
super.visitPrefixExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitBooleanExpression(BooleanExpression expression) {
super.visitBooleanExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitNotExpression(NotExpression expression) {
super.visitNotExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitClosureExpression(ClosureExpression expression) {
super.visitClosureExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitLambdaExpression(LambdaExpression expression) {
super.visitLambdaExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitTupleExpression(TupleExpression expression) {
super.visitTupleExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitListExpression(ListExpression expression) {
super.visitListExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitArrayExpression(ArrayExpression expression) {
super.visitArrayExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitMapExpression(MapExpression expression) {
super.visitMapExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitMapEntryExpression(MapEntryExpression expression) {
super.visitMapEntryExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitRangeExpression(RangeExpression expression) {
super.visitRangeExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitSpreadExpression(SpreadExpression expression) {
super.visitSpreadExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitSpreadMapExpression(SpreadMapExpression expression) {
super.visitSpreadMapExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitMethodPointerExpression(MethodPointerExpression expression) {
super.visitMethodPointerExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitMethodReferenceExpression(MethodReferenceExpression expression) {
super.visitMethodReferenceExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
super.visitUnaryMinusExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
super.visitUnaryPlusExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
super.visitBitwiseNegationExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitCastExpression(CastExpression expression) {
super.visitCastExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitConstantExpression(ConstantExpression expression) {
super.visitConstantExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitClassExpression(ClassExpression expression) {
super.visitClassExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitVariableExpression(VariableExpression expression) {
super.visitVariableExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitPropertyExpression(PropertyExpression expression) {
super.visitPropertyExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitAttributeExpression(AttributeExpression expression) {
super.visitAttributeExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitFieldExpression(FieldExpression expression) {
super.visitFieldExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitGStringExpression(GStringExpression expression) {
super.visitGStringExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitArgumentlistExpression(ArgumentListExpression expression) {
super.visitArgumentlistExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitClosureListExpression(ClosureListExpression expression) {
super.visitClosureListExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visitBytecodeExpression(BytecodeExpression expression) {
super.visitBytecodeExpression(expression);
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
@Override
public void visit(Statement statement) {
super.visit(statement);
}
@Override
public void visitEmptyExpression(EmptyExpression expression) {
this.positionSetter.setPositionOffsetInContainer(expression, container);
}
}

View File

@ -18,6 +18,24 @@ public class SimplePositionSetter implements PositionSetter {
this.set(target, start.line(), start.column(), end.line(), end.column()); this.set(target, start.line(), start.column(), end.line(), end.column());
} }
@Override
public void setPositionOffsetInContainer(ASTNode target, Node container) {
final SourcePosition containerStart = container.getTokenRange().getStartPosition();
final SourcePosition startPosition = new SourcePosition(
containerStart.line() + target.getLineNumber() - 1,
target.getLineNumber() == 1
? containerStart.column() + target.getColumnNumber() - 1
: target.getColumnNumber()
);
final SourcePosition endPosition = new SourcePosition(
containerStart.line() + target.getLastLineNumber() - 1,
target.getLastLineNumber() == 1
? containerStart.column() + target.getLastColumnNumber() - 1
: target.getLastColumnNumber()
);
this.set(target, startPosition, endPosition);
}
@Override @Override
public void setPosition(ASTNode target, TokenRange tokenRange) { public void setPosition(ASTNode target, TokenRange tokenRange) {
this.set(target, tokenRange.getStartPosition(), tokenRange.getEndPosition()); this.set(target, tokenRange.getStartPosition(), tokenRange.getEndPosition());

View File

@ -1,6 +1,18 @@
package groowt.view.component.web.transpile; package groowt.view.component.web.transpile;
import groovy.lang.Tuple3;
import org.codehaus.groovy.ast.ClassNode;
import java.util.Map;
import java.util.Set;
public interface TranspilerConfiguration { public interface TranspilerConfiguration {
PositionSetter getPositionSetter();
BodyTranspiler getBodyTranspiler(); BodyTranspiler getBodyTranspiler();
AppendOrAddStatementFactory getAppendOrAddStatementFactory(); AppendOrAddStatementFactory getAppendOrAddStatementFactory();
Map<String, ClassNode> getImports();
Set<String> getStarImports();
Set<Tuple3<ClassNode, String, String>> getStaticImports();
Map<String, ClassNode> getStaticStarImports();
ClassNode getRenderContextImplementation();
} }

View File

@ -5,7 +5,7 @@ import groowt.view.component.ComponentTemplate;
import groowt.view.component.compiler.ComponentTemplateCompileException; import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.context.ComponentContext; import groowt.view.component.context.ComponentContext;
import groowt.view.component.runtime.ComponentWriter; import groowt.view.component.runtime.ComponentWriter;
import groowt.view.component.web.WebViewComponent; import groowt.view.component.web.runtime.DefaultWebViewRenderContext;
import groowt.view.component.web.runtime.WebViewComponentRenderContext; import groowt.view.component.web.runtime.WebViewComponentRenderContext;
import groowt.view.component.web.util.SourcePosition; import groowt.view.component.web.util.SourcePosition;
import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.*;
@ -23,8 +23,10 @@ public final class TranspilerUtil {
public static final ClassNode COMPONENT_TEMPLATE = ClassHelper.make(ComponentTemplate.class); public static final ClassNode COMPONENT_TEMPLATE = ClassHelper.make(ComponentTemplate.class);
public static final ClassNode COMPONENT_CONTEXT_TYPE = ClassHelper.make(ComponentContext.class); public static final ClassNode COMPONENT_CONTEXT_TYPE = ClassHelper.make(ComponentContext.class);
public static final ClassNode COMPONENT_WRITER_TYPE = ClassHelper.make(ComponentWriter.class); public static final ClassNode COMPONENT_WRITER_TYPE = ClassHelper.make(ComponentWriter.class);
public static final ClassNode WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE = ClassHelper.make(WebViewComponentRenderContext.class); public static final ClassNode WEB_VIEW_COMPONENT_RENDER_CONTEXT_TYPE =
public static final ClassNode WEB_VIEW_COMPONENT_TYPE = ClassHelper.make(WebViewComponent.class); ClassHelper.make(WebViewComponentRenderContext.class);
public static final ClassNode DEFAULT_RENDER_CONTEXT_IMPLEMENTATION =
ClassHelper.make(DefaultWebViewRenderContext.class);
public static final String GROOWT_VIEW_COMPONENT_WEB = "groowt.view.component.web"; public static final String GROOWT_VIEW_COMPONENT_WEB = "groowt.view.component.web";
public static final String COMPONENT_CONTEXT_NAME = "componentContext"; public static final String COMPONENT_CONTEXT_NAME = "componentContext";

View File

@ -11,13 +11,6 @@ import java.util.*;
public class WebViewComponentModuleNode extends ModuleNode { public class WebViewComponentModuleNode extends ModuleNode {
public static void copyTo(ModuleNode from, WebViewComponentModuleNode to) {
from.getImports().forEach(to::addImport);
from.getStarImports().forEach(to::addStarImport);
from.getStaticImports().forEach(to::addStaticImport);
from.getStaticStarImports().forEach(to::addStaticStarImport);
}
protected final List<ImportNode> imports = new ArrayList<>(); protected final List<ImportNode> imports = new ArrayList<>();
protected final List<ImportNode> starImports = new ArrayList<>(); protected final List<ImportNode> starImports = new ArrayList<>();
protected final Map<String, ImportNode> staticImports = new LinkedHashMap<>(); protected final Map<String, ImportNode> staticImports = new LinkedHashMap<>();

View File

@ -3,7 +3,6 @@ package groowt.view.component.web.transpile.resolve;
import groowt.util.fp.either.Either; import groowt.util.fp.either.Either;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit; import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -15,10 +14,9 @@ public class ClassLoaderComponentClassNodeResolver extends ModuleNodeComponentCl
public ClassLoaderComponentClassNodeResolver( public ClassLoaderComponentClassNodeResolver(
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
ModuleNode moduleNode,
ClassLoader classLoader ClassLoader classLoader
) { ) {
super(compileUnit, moduleNode); super(compileUnit);
this.classLoader = classLoader; this.classLoader = classLoader;
} }

View File

@ -5,30 +5,36 @@ import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.ModuleNode;
import java.util.Objects;
public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassNodeResolver { public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassNodeResolver {
private final ModuleNode moduleNode; private ModuleNode moduleNode;
public ModuleNodeComponentClassNodeResolver( public ModuleNodeComponentClassNodeResolver(WebViewComponentTemplateCompileUnit compileUnit) {
WebViewComponentTemplateCompileUnit compileUnit,
ModuleNode moduleNode
) {
super(compileUnit); super(compileUnit);
this.moduleNode = moduleNode; }
public ModuleNode getModuleNode() {
return Objects.requireNonNull(this.moduleNode);
}
public void setModuleNode(ModuleNode moduleNode) {
this.moduleNode = Objects.requireNonNull(moduleNode);
} }
@Override @Override
public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) { public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) {
return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> { return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> {
// try regular imports first // try regular imports first
final var importedClassNode = this.moduleNode.getImportType(nameWithoutPackage); final var importedClassNode = this.getModuleNode().getImportType(nameWithoutPackage);
if (importedClassNode != null) { if (importedClassNode != null) {
this.addClassNode(importedClassNode); this.addClassNode(importedClassNode);
return Either.right(importedClassNode); return Either.right(importedClassNode);
} }
// try star imports // try star imports
final var starImports = this.moduleNode.getStarImports(); final var starImports = this.getModuleNode().getStarImports();
for (final var starImport : starImports) { for (final var starImport : starImports) {
final var packageName = starImport.getPackageName(); final var packageName = starImport.getPackageName();
final String fqn; final String fqn;
@ -44,7 +50,7 @@ public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassN
} }
// try pre-pending package and asking for fqn // try pre-pending package and asking for fqn
final String moduleNodePackageName = this.moduleNode.getPackageName(); final String moduleNodePackageName = this.getModuleNode().getPackageName();
final String packageName; final String packageName;
if (moduleNodePackageName != null) { if (moduleNodePackageName != null) {
packageName = moduleNodePackageName; packageName = moduleNodePackageName;

View File

@ -57,6 +57,7 @@ public abstract class GroovyTranspilerTests {
this.transpiler.transpile( this.transpiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(), new DefaultComponentTemplateCompilerConfiguration(),
new DefaultWebViewComponentTemplateCompileUnit( new DefaultWebViewComponentTemplateCompileUnit(
"<anonymous string source>",
AnonymousWebViewComponent.class, AnonymousWebViewComponent.class,
new StringSource(source, null), new StringSource(source, null),
"groowt.view.component.web.transpiler" "groowt.view.component.web.transpiler"

View File

@ -37,7 +37,7 @@ public abstract class AbstractWebViewComponent extends AbstractViewComponent imp
public AbstractWebViewComponent(ComponentTemplateSource source) { public AbstractWebViewComponent(ComponentTemplateSource source) {
this(selfClass -> new DefaultWebViewComponentTemplateCompileUnit( this(selfClass -> new DefaultWebViewComponentTemplateCompileUnit(
selfClass, source, selfClass.getPackageName()) source.getDescriptiveName(), selfClass, source, selfClass.getPackageName())
); );
} }

View File

@ -24,11 +24,12 @@ public class DefaultWebViewComponentTemplateCompileUnit extends AbstractComponen
private final CompilationUnit groovyCompilationUnit = new CompilationUnit(); private final CompilationUnit groovyCompilationUnit = new CompilationUnit();
public DefaultWebViewComponentTemplateCompileUnit( public DefaultWebViewComponentTemplateCompileUnit(
String descriptiveName,
Class<? extends ViewComponent> forClass, Class<? extends ViewComponent> forClass,
ComponentTemplateSource source, ComponentTemplateSource source,
String defaultPackageName String defaultPackageName
) { ) {
super(forClass, source); super(descriptiveName, forClass, source);
if (!defaultPackageName.isEmpty() && !defaultPackageName.endsWith(".")) { if (!defaultPackageName.isEmpty() && !defaultPackageName.endsWith(".")) {
this.defaultPackageName = defaultPackageName + "."; this.defaultPackageName = defaultPackageName + ".";
} else { } else {

View File

@ -26,6 +26,7 @@ public interface WebViewComponentTemplateCompiler
default ComponentTemplateCompileResult compileAnonymous(ComponentTemplateSource source, String packageName) default ComponentTemplateCompileResult compileAnonymous(ComponentTemplateSource source, String packageName)
throws ComponentTemplateCompileException { throws ComponentTemplateCompileException {
return this.compile(new DefaultWebViewComponentTemplateCompileUnit( return this.compile(new DefaultWebViewComponentTemplateCompileUnit(
source.getDescriptiveName(),
AnonymousWebViewComponent.class, AnonymousWebViewComponent.class,
source, source,
packageName packageName