org.codehaus.groovy.ast.builder.AstBuilder.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of groovy-eclipse-batch Show documentation
Show all versions of groovy-eclipse-batch Show documentation
Groovy Eclipse Compiler wrapped for Batch Use from Maven
/*
* 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.codehaus.groovy.ast.builder
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.control.CompilePhase
/**
* The AstBuilder provides several ways to build an abstract syntax tree (AST) of Groovy code.
*
* You can convert a String into AST using the buildFromString method.
* You can convert code into AST using the buildFromCode method.
* You can use the AST DSL with the buildFromSpec method.
*
* For more information, see the resources on the Groovy wiki pages.
*/
@CompileStatic
class AstBuilder {
/**
* Builds AST based on the code within the {@link Closure} parameter.
*
* This method must be invoked at compile time and never at runtime, because
* an ASTTransformation must be run to support it. If you receive an IllegalStateException then
* you most likely need to add stronger typing. For instance, this will not work:
*
* def builder = new AstBuilder()
* builder.buildFromCode {
* // some code
* }
*
* While this code will:
*
* new AstBuilder().buildFromCode {
* // some code
* }
*
*
* The compiler rewrites buildFromCode invocations into {@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
* invocations. An exception raised during AST generation will show a stack trace from {@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
* and not from {@link AstBuilder#buildFromCode(CompilePhase, boolean, Closure)} .
*
* The compiler saves the source code of the closure as a String within the Java class file. The String source
* of the closure will be visible and un-obfuscated within the class file. If your Closure parameter contains
* sensitive data such as a hard-coded password then that data is free to be seen by anyone with the class file.
* Do not store sensitive data within the closure parameter.
*
* @param phase
* the {@link CompilePhase} the AST will be targeted towards. Default is {@link CompilePhase#CLASS_GENERATION}
* @param statementsOnly
* when true, only the script statements are returned. WHen false, you will
* receive back a Script class node also. Default is true.
* @param block
* the code that will be converted
* @returns a List of {@link ASTNode}
* @throws IllegalStateException
* this method may not be invoked at runtime. It works via a compile-time transformation
* of the closure source code into a String, which is sent to the {@link AstBuilder#buildFromString(CompilePhase, boolean, String)}
* method. The buildFromCode() method must be invoked against a strongly typed AstBuilder.
*/
List buildFromCode(CompilePhase ignoredPhase = CompilePhase.CLASS_GENERATION, boolean ignoredStatementsOnly = true, Closure ignoredBlock) {
throw new IllegalStateException('''AstBuilder.build(CompilePhase, boolean, Closure):List should never be called at runtime.
Are you sure you are using it correctly?''')
}
/**
* Builds AST based on the code within the String parameter.
*
* @param phase
* the {@link CompilePhase} the AST will be targeted towards. Default is {@link CompilePhase#CLASS_GENERATION}
* @param statementsOnly
* when true, only the script statements are returned. WHen false, you will
* receive back a Script class node also. Default is true.
* @param source
* The source code String that will be compiled.
* @returns a List of {@link ASTNode} .
* @throws IllegalArgumentException
* if source is null or empty
*/
List buildFromString(CompilePhase phase = CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, String source) {
if (!source || '' == source.trim()) throw new IllegalArgumentException('A source must be specified')
new AstStringCompiler().compile(source, phase, statementsOnly)
}
/**
* Builds AST based on the code within the String parameter. The parameter is assumed to be
* a code block which is not legal Groovy code. A goto label is affixed to the block, compiled,
* and the resulting BlockStatement wrapper is removed before returning a result.
* @param phase
* the {@link CompilePhase} the AST will be targeted towards. Default is {@link CompilePhase#CLASS_GENERATION}
* @param statementsOnly
* when true, only the script statements are returned. WHen false, you will
* receive back a Script class node also. Default is true.
* @param source
* The source code String that will be compiled. The string must be a block wrapped in curly braces.
* @returns a List of {@link ASTNode}.
* @throws IllegalArgumentException if source is null or empty
*/
@SuppressWarnings('Instanceof')
private List buildFromBlock(CompilePhase phase = CompilePhase.CLASS_GENERATION, boolean statementsOnly = true, String source) {
if (!source || '' == source.trim()) throw new IllegalArgumentException('A source must be specified')
def labelledSource = "__synthesized__label__${System.currentTimeMillis()}__:" + source
List result = new AstStringCompiler().compile(labelledSource, phase, statementsOnly)
// find the block statement from the result, and unwrap it from one level.
result.collect { node ->
if (node instanceof BlockStatement) {
((BlockStatement) node).statements[0] //unwrap the artifact of pre-pending the goto label
} else {
node
}
}
}
/**
* Builds AST based on the DSL data within the Closure parameter.
* @param specification
* the contents to create
*/
List buildFromSpec(@DelegatesTo(AstSpecificationCompiler) Closure specification) {
if (specification == null) throw new IllegalArgumentException('Null: specification')
def properties = new AstSpecificationCompiler(specification)
properties.expression
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy