Skip to content

Commit

Permalink
Merge branch 'master' into wagon-gitsite
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed Aug 8, 2017
2 parents 09fcbae + b5b7d4b commit 61efc2c
Show file tree
Hide file tree
Showing 13 changed files with 441 additions and 44 deletions.
2 changes: 1 addition & 1 deletion dgm-builder/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>com.cloudbees</groupId>
<artifactId>groovy-cps-parent</artifactId>
<version>1.17-SNAPSHOT</version>
<version>1.19-SNAPSHOT</version>
</parent>

<artifactId>groovy-cps-dgm-builder</artifactId>
Expand Down
19 changes: 6 additions & 13 deletions lib/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>com.cloudbees</groupId>
<artifactId>groovy-cps-parent</artifactId>
<version>1.17-SNAPSHOT</version>
<version>1.19-SNAPSHOT</version>
</parent>

<artifactId>groovy-cps</artifactId>
Expand Down Expand Up @@ -71,8 +71,6 @@
<executions>
<execution>
<goals>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>testGenerateStubs</goal>
<goal>testCompile</goal>
</goals>
Expand Down Expand Up @@ -106,6 +104,9 @@
</plugins>
</build>

<properties>
<groovy-sandbox.version>1.13</groovy-sandbox.version>
</properties>
<dependencies>
<dependency>
<groupId>com.cloudbees</groupId>
Expand All @@ -118,13 +119,13 @@
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>groovy-sandbox</artifactId>
<version>1.11</version>
<version>${groovy-sandbox.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>groovy-sandbox</artifactId>
<version>1.11</version>
<version>${groovy-sandbox.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
Expand Down Expand Up @@ -163,14 +164,6 @@
</dependency>
</dependencies>

<reporting>
<plugins>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</reporting>

<profiles>
<profile>
<id>cloudbees-oss-release</id>
Expand Down
15 changes: 14 additions & 1 deletion lib/src/main/java/com/cloudbees/groovy/cps/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@

import static com.cloudbees.groovy.cps.Block.*;
import static java.util.Arrays.*;
import org.codehaus.groovy.ast.expr.CastExpression;
import org.kohsuke.groovy.sandbox.impl.Checker;

/**
* Builder pattern for constructing {@link Block}s into a tree.
Expand Down Expand Up @@ -125,7 +127,7 @@ public Block constant(Object o) {
}

public Block methodPointer(int line, Block lhs, Block methodName) {
return new MethodPointerBlock(loc(line),lhs,methodName);
return new MethodPointerBlock(loc(line),lhs,methodName, tags);
}

public Block zero() {
Expand Down Expand Up @@ -587,6 +589,17 @@ public Block cast(int line, Block block, Class type, boolean coerce) {
block,constant(type));
}

/**
* Cast to type when {@link CastExpression#isCoerce} from {@link SandboxCpsTransformer}.
*/
// TODO should ideally be defined in some sandbox-specific subtype of Builder
public Block sandboxCast(int line, Block block, Class<?> type, boolean ignoreAutoboxing, boolean strict) {
return staticCall(line, Checker.class,
"checkedCast",
constant(type), block,
constant(ignoreAutoboxing), constant(true), constant(strict));
}

public Block instanceOf(int line, Block value, Block type) {
return functionCall(line,type,"isInstance",value);
}
Expand Down
59 changes: 41 additions & 18 deletions lib/src/main/java/com/cloudbees/groovy/cps/CpsTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.cloudbees.groovy.cps.impl.CpsFunction;
import com.cloudbees.groovy.cps.sandbox.Trusted;
import com.cloudbees.groovy.cps.sandbox.Untrusted;
import com.google.common.annotations.VisibleForTesting;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
Expand Down Expand Up @@ -95,7 +96,8 @@ public class CpsTransformer extends CompilationCustomizer implements GroovyCodeV

private static final Logger LOGGER = Logger.getLogger(CpsTransformer.class.getName());

private static final AtomicLong iota = new AtomicLong();
@VisibleForTesting
public static final AtomicLong iota = new AtomicLong();

private SourceUnit sourceUnit;

Expand All @@ -120,15 +122,16 @@ public void call(SourceUnit source, GeneratorContext context, ClassNode classNod
this.classNode = classNode;
try {

// copy(source.ast.methods)?.each { visitMethod(it) }
// classNode?.declaredConstructors?.each { visitMethod(it) } // can't transform constructor
if (classNode != null) {
for (MethodNode method : new ArrayList<>(classNode.getMethods())) {
visitMethod(method);
}
for (MethodNode method : new ArrayList<>(classNode.getMethods())) {
visitMethod(method);
}
processConstructors(classNode);
for (FieldNode field : new ArrayList<>(classNode.getFields())) {
visitNontransformedField(field);
}
for (Statement statement : new ArrayList<>(classNode.getObjectInitializerStatements())) {
visitNontransformedStatement(statement);
}
// classNode?.objectInitializerStatements?.each { it.visit(visitor) }
// classNode?.fields?.each { visitor.visitField(it) }

// groovy puts timestamp of compilation into a class file, causing serialVersionUID to change.
// this tends to be undesirable for CPS involving persistence.
Expand All @@ -149,6 +152,12 @@ public void call(SourceUnit source, GeneratorContext context, ClassNode classNod
}
}

protected void processConstructors(ClassNode classNode) {
for (ConstructorNode constructor : new ArrayList<>(classNode.getDeclaredConstructors())) {
visitNontransformedMethod(constructor);
}
}

/**
* Should this method be transformed?
*/
Expand Down Expand Up @@ -198,9 +207,6 @@ public void visitMethod(final MethodNode m) {
@Override
public void call(Expression e) {
body.set(e);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "in {0} transformed {1} to {2}", new Object[] {classNode.getName(), m.getTypeDescriptor(), e.getText()});
}
}
};
visitWithSafepoint(m.getCode());
Expand All @@ -218,11 +224,10 @@ CpsFunction ___cps___N() {
*/
String cpsName = "___cps___" + iota.getAndIncrement();

DeclarationExpression builderDeclaration = new DeclarationExpression(BUILDER, new Token(ASSIGN, "=", -1, -1), makeBuilder(m));
ReturnStatement returnStatement = new ReturnStatement(new ConstructorCallExpression(FUNCTION_TYPE, new TupleExpression(params, body.get())));
MethodNode builderMethod = m.getDeclaringClass().addMethod(cpsName, PRIVATE_STATIC_FINAL, FUNCTION_TYPE, new Parameter[0], new ClassNode[0],
new BlockStatement(Arrays.asList(
new ExpressionStatement(new DeclarationExpression(BUILDER, new Token(ASSIGN, "=", -1, -1), makeBuilder(m))),
new ReturnStatement(new ConstructorCallExpression(FUNCTION_TYPE, new TupleExpression(params, body.get())))
), new VariableScope())
new BlockStatement(Arrays.asList(new ExpressionStatement(builderDeclaration), returnStatement), new VariableScope())
);
builderMethod.addAnnotation(new AnnotationNode(WORKFLOW_TRANSFORMED_TYPE));

Expand All @@ -237,9 +242,20 @@ CpsFunction ___cps___N() {
ArrayExpression paramArray = new ArrayExpression(ClassHelper.OBJECT_TYPE, paramExpressions);
TupleExpression args = new TupleExpression(new VariableExpression(f), THIS, paramArray);

m.setCode(new ThrowStatement(new ConstructorCallExpression(CPSCALLINVK_TYPE, args)));
ConstructorCallExpression cce = new ConstructorCallExpression(CPSCALLINVK_TYPE, args);
m.setCode(new ThrowStatement(cce));

m.addAnnotation(new AnnotationNode(WORKFLOW_TRANSFORMED_TYPE));

if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "in {0} transformed {1} to {2}: throw {3} plus {4}: {5}; {6}", new Object[] {
classNode.getName(), m.getTypeDescriptor(),
// TODO https://github.com/apache/groovy/pull/574 m.getCode().getText() does not work well
m.getText(), cce.getText(),
// TODO ditto for builderMethod.getCode().getText()
builderMethod.getText(), builderDeclaration.getText(), returnStatement.getText()
});
}
}

/**
Expand Down Expand Up @@ -290,6 +306,12 @@ protected Class getTrustTag() {
protected void visitNontransformedMethod(MethodNode m) {
}

protected void visitNontransformedField(FieldNode f) {
}

protected void visitNontransformedStatement(Statement s) {
}

// TODO Java 8 @FunctionalInterface, or switch to Consumer<Expression>
protected interface ParentClosure {
void call(Expression e);
Expand Down Expand Up @@ -1099,7 +1121,7 @@ public void run() {
}
});
} else {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException(exp.getText());
}
}

Expand Down Expand Up @@ -1166,6 +1188,7 @@ public void run() {
visit(exp.getExpression());
literal(exp.getType());
literal(exp.isCoerce());
// TODO what about ignoreAutoboxing & strict?
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
import com.cloudbees.groovy.cps.sandbox.Untrusted;
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.SourceUnit;
import org.kohsuke.groovy.sandbox.SandboxTransformer;

/**
* {@link CpsTransformer} + {@link org.kohsuke.groovy.sandbox.SandboxTransformer}
* {@link CpsTransformer} + {@link SandboxTransformer}
*
* @author Kohsuke Kawaguchi
*/
Expand All @@ -20,19 +25,91 @@ public class SandboxCpsTransformer extends CpsTransformer {

@Override
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
stv = st.createVisitor(source);
stv = st.createVisitor(source, classNode);
super.call(source, context, classNode);
}

@Override
protected void processConstructors(ClassNode classNode) {
st.processConstructors(stv, classNode);
}

/**
* If the method is not CPS transformed, we need to sandbox-transform that
* method to intercept calls that happen in these methods.
* This includes all constructor bodies.
* @see SandboxTransformer#call
*/
@Override
protected void visitNontransformedMethod(MethodNode m) {
stv.visitMethod(m);
}

/**
* Field initializers are never transformed, but we still need to run the sandbox transformer on them.
* @see SandboxTransformer#call
*/
@Override
protected void visitNontransformedField(FieldNode f) {
stv.visitField(f);
}

/**
* Miscellaneous statements like object initializers are never transformed, but we still need to run the sandbox transformer on them.
* @see SandboxTransformer#call
*/
@Override
protected void visitNontransformedStatement(Statement s) {
s.visit(stv);
}

@Override
public void visitCastExpression(final CastExpression exp) {
if (exp.isCoerce()) {
makeNode("sandboxCast", new Runnable() {
@Override
public void run() {
loc(exp);
visit(exp.getExpression());
literal(exp.getType());
literal(exp.isIgnoringAutoboxing());
literal(exp.isStrict());
}
});
} else {
super.visitCastExpression(exp);
}
}

@Override
public void visitDeclarationExpression(final DeclarationExpression exp) {
if (exp.isMultipleAssignmentDeclaration()) {
throw new UnsupportedOperationException("multiple assignments not supported"); // TODO
} else if (SandboxTransformer.mightBePositionalArgumentConstructor(exp.getVariableExpression())) {
makeNode("declareVariable", new Runnable() {
@Override
public void run() {
VariableExpression v = exp.getVariableExpression();
loc(exp);
literal(v.getType());
literal(v.getName());
makeNode("sandboxCast", new Runnable() {
@Override
public void run() {
loc(exp);
visit(exp.getRightExpression());
literal(exp.getVariableExpression().getType());
literal(false);
literal(false);
}
});
}
});
} else {
super.visitDeclarationExpression(exp);
}
}

@Override
protected Class getTrustTag() {
return Untrusted.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;

import static java.util.Arrays.*;
import java.util.Collections;

/**
* When an CPS-interpreted method is invoked, it immediately throws this error
Expand All @@ -32,7 +33,7 @@ public class CpsCallableInvocation extends Error/*not really an error but we wan
public CpsCallableInvocation(CpsCallable call, Object receiver, Object... arguments) {
this.call = call;
this.receiver = receiver;
this.arguments = asList(arguments);
this.arguments = arguments != null ? asList(arguments) : Collections.emptyList();
}

public Next invoke(Env caller, SourceLocation loc, Continuation k) {
Expand Down
Loading

0 comments on commit 61efc2c

Please sign in to comment.