You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:28 UTC

[17/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
new file mode 100644
index 0000000..98ad092
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -0,0 +1,888 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.classgen.asm.CallSiteWriter;
+import org.codehaus.groovy.classgen.asm.CompileStack;
+import org.codehaus.groovy.classgen.asm.OperandStack;
+import org.codehaus.groovy.classgen.asm.TypeChooser;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.codehaus.groovy.ast.ClassHelper.BigDecimal_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.BigInteger_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.Boolean_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.CLASS_Type;
+import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.GROOVY_OBJECT_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.Integer_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.Iterator_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.LIST_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.Long_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.MAP_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.Number_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
+import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
+import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.ast.ClassHelper.make;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.chooseBestMethod;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isClassClassNodeWrappingConcreteType;
+
+/**
+ * A call site writer which replaces call site caching with static calls. This means that the generated code
+ * looks more like Java code than dynamic Groovy code. Best effort is made to use JVM instructions instead of
+ * calls to helper methods.
+ *
+ * @author Cedric Champeau
+ */
+public class StaticTypesCallSiteWriter extends CallSiteWriter implements Opcodes {
+
+    private static final ClassNode INVOKERHELPER_TYPE = ClassHelper.make(InvokerHelper.class);
+    private static final MethodNode GROOVYOBJECT_GETPROPERTY_METHOD = GROOVY_OBJECT_TYPE.getMethod("getProperty", new Parameter[]{new Parameter(STRING_TYPE, "propertyName")});
+    private static final MethodNode INVOKERHELPER_GETPROPERTY_METHOD = INVOKERHELPER_TYPE.getMethod("getProperty", new Parameter[]{new Parameter(OBJECT_TYPE, "object"), new Parameter(STRING_TYPE, "propertyName")});
+    private static final MethodNode INVOKERHELPER_GETPROPERTYSAFE_METHOD = INVOKERHELPER_TYPE.getMethod("getPropertySafe", new Parameter[]{new Parameter(OBJECT_TYPE, "object"), new Parameter(STRING_TYPE, "propertyName")});
+    private static final MethodNode CLOSURE_GETTHISOBJECT_METHOD = CLOSURE_TYPE.getMethod("getThisObject", new Parameter[0]);
+    private static final ClassNode COLLECTION_TYPE = make(Collection.class);
+    private static final MethodNode COLLECTION_SIZE_METHOD = COLLECTION_TYPE.getMethod("size", Parameter.EMPTY_ARRAY);
+    private static final MethodNode MAP_GET_METHOD = MAP_TYPE.getMethod("get", new Parameter[] { new Parameter(OBJECT_TYPE, "key")});
+
+
+    private final StaticTypesWriterController controller;
+
+    public StaticTypesCallSiteWriter(final StaticTypesWriterController controller) {
+        super(controller);
+        this.controller = controller;
+    }
+
+    @Override
+    public void generateCallSiteArray() {
+        CallSiteWriter regularCallSiteWriter = controller.getRegularCallSiteWriter();
+        if (regularCallSiteWriter.hasCallSiteUse()) {
+            regularCallSiteWriter.generateCallSiteArray();
+        }
+    }
+
+    @Override
+    public void makeCallSite(final Expression receiver, final String message, final Expression arguments, final boolean safe, final boolean implicitThis, final boolean callCurrent, final boolean callStatic) {
+    }
+
+    @Override
+    public void makeGetPropertySite(Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) {
+        Object dynamic = receiver.getNodeMetaData(StaticCompilationMetadataKeys.RECEIVER_OF_DYNAMIC_PROPERTY);
+        if (dynamic !=null) {
+            makeDynamicGetProperty(receiver, methodName, safe);
+            return;
+        }
+        TypeChooser typeChooser = controller.getTypeChooser();
+        ClassNode classNode = controller.getClassNode();
+        ClassNode receiverType = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
+        if (receiverType==null) {
+            receiverType = typeChooser.resolveType(receiver, classNode);
+        }
+        Object type = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+        if (type==null && receiver instanceof VariableExpression) {
+            Variable variable = ((VariableExpression) receiver).getAccessedVariable();
+            if (variable instanceof Expression) {
+                type = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+            }
+        }
+        if (type!=null) {
+            // in case a "flow type" is found, it is preferred to use it instead of
+            // the declaration type
+            receiverType = (ClassNode) type;
+        }
+        boolean isClassReceiver = false;
+        if (isClassClassNodeWrappingConcreteType(receiverType)) {
+            isClassReceiver = true;
+            receiverType = receiverType.getGenericsTypes()[0].getType();
+        }
+
+        if (isPrimitiveType(receiverType)) {
+            // GROOVY-6590: wrap primitive types
+            receiverType = getWrapper(receiverType);
+        }
+
+        MethodVisitor mv = controller.getMethodVisitor();
+
+        if (receiverType.isArray() && methodName.equals("length")) {
+            receiver.visit(controller.getAcg());
+            ClassNode arrayGetReturnType = typeChooser.resolveType(receiver, classNode);
+            controller.getOperandStack().doGroovyCast(arrayGetReturnType);
+            mv.visitInsn(ARRAYLENGTH);
+            controller.getOperandStack().replace(int_TYPE);
+            return;
+        } else if (
+                (receiverType.implementsInterface(COLLECTION_TYPE)
+                        || COLLECTION_TYPE.equals(receiverType)) && ("size".equals(methodName) || "length".equals(methodName))) {
+            MethodCallExpression expr = new MethodCallExpression(
+                    receiver,
+                    "size",
+                    ArgumentListExpression.EMPTY_ARGUMENTS
+            );
+            expr.setMethodTarget(COLLECTION_SIZE_METHOD);
+            expr.setImplicitThis(implicitThis);
+            expr.setSafe(safe);
+            expr.visit(controller.getAcg());
+            return;
+        }
+
+        boolean isStaticProperty = receiver instanceof ClassExpression
+                && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType()));
+
+        if (!isStaticProperty && (receiverType.implementsInterface(MAP_TYPE) || MAP_TYPE.equals(receiverType))) {
+            // for maps, replace map.foo with map.get('foo')
+            writeMapDotProperty(receiver, methodName, mv, safe);
+            return;
+        }
+        if (makeGetPropertyWithGetter(receiver, receiverType, methodName, safe, implicitThis)) return;
+        if (makeGetField(receiver, receiverType, methodName, safe, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return;
+        if (receiver instanceof ClassExpression) {
+            if (makeGetField(receiver, receiver.getType(), methodName, safe, implicitThis, samePackages(receiver.getType().getPackageName(), classNode.getPackageName()))) return;
+            if (makeGetPropertyWithGetter(receiver, receiver.getType(), methodName, safe, implicitThis)) return;
+            if (makeGetPrivateFieldWithBridgeMethod(receiver, receiver.getType(), methodName, safe, implicitThis)) return;
+        }
+        if (isClassReceiver) {
+            // we are probably looking for a property of the class
+            if (makeGetPropertyWithGetter(receiver, CLASS_Type, methodName, safe, implicitThis)) return;
+            if (makeGetField(receiver, CLASS_Type, methodName, safe, false, true)) return;
+        }
+        if (receiverType.isEnum()) {
+            mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(receiverType), methodName, BytecodeHelper.getTypeDescription(receiverType));
+            controller.getOperandStack().push(receiverType);
+            return;
+        }
+        if (makeGetPrivateFieldWithBridgeMethod(receiver, receiverType, methodName, safe, implicitThis)) return;
+
+        // GROOVY-5580, it is still possible that we're calling a superinterface property
+        String getterName = "get" + MetaClassHelper.capitalize(methodName);
+        String altGetterName = "is" + MetaClassHelper.capitalize(methodName);
+        if (receiverType.isInterface()) {
+            Set<ClassNode> allInterfaces = receiverType.getAllInterfaces();
+            MethodNode getterMethod = null;
+            for (ClassNode anInterface : allInterfaces) {
+                getterMethod = anInterface.getGetterMethod(getterName);
+                if (getterMethod == null) getterMethod = anInterface.getGetterMethod(altGetterName);
+                if (getterMethod != null) break;
+            }
+            // GROOVY-5585
+            if (getterMethod == null) {
+                getterMethod = OBJECT_TYPE.getGetterMethod(getterName);
+            }
+
+            if (getterMethod != null) {
+                MethodCallExpression call = new MethodCallExpression(
+                        receiver,
+                        getterName,
+                        ArgumentListExpression.EMPTY_ARGUMENTS
+                );
+                call.setMethodTarget(getterMethod);
+                call.setImplicitThis(false);
+                call.setSourcePosition(receiver);
+                call.setSafe(safe);
+                call.visit(controller.getAcg());
+                return;
+            }
+
+        }
+
+        // GROOVY-5568, we would be facing a DGM call, but instead of foo.getText(), have foo.text
+        List<MethodNode> methods = findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), receiverType, getterName, ClassNode.EMPTY_ARRAY);
+        for (MethodNode m: findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), receiverType, altGetterName, ClassNode.EMPTY_ARRAY)) {
+            if (Boolean_TYPE.equals(getWrapper(m.getReturnType()))) methods.add(m);
+        }
+        if (!methods.isEmpty()) {
+            List<MethodNode> methodNodes = chooseBestMethod(receiverType, methods, ClassNode.EMPTY_ARRAY);
+            if (methodNodes.size() == 1) {
+                MethodNode getter = methodNodes.get(0);
+                MethodCallExpression call = new MethodCallExpression(
+                        receiver,
+                        getter.getName(),
+                        ArgumentListExpression.EMPTY_ARGUMENTS
+                );
+                call.setMethodTarget(getter);
+                call.setImplicitThis(false);
+                call.setSafe(safe);
+                call.setSourcePosition(receiver);
+                call.visit(controller.getAcg());
+                return;
+            }
+        }
+
+        if (!isStaticProperty && (receiverType.implementsInterface(LIST_TYPE) || LIST_TYPE.equals(receiverType))) {
+            writeListDotProperty(receiver, methodName, mv, safe);
+            return;
+        }
+
+        controller.getSourceUnit().addError(
+                new SyntaxException("Access to "+
+                                                (receiver instanceof ClassExpression ?receiver.getType():receiverType).toString(false)
+                                                +"#"+methodName+" is forbidden", receiver.getLineNumber(), receiver.getColumnNumber(), receiver.getLastLineNumber(), receiver.getLastColumnNumber())
+        );
+        controller.getMethodVisitor().visitInsn(ACONST_NULL);
+        controller.getOperandStack().push(OBJECT_TYPE);
+    }
+
+    private void makeDynamicGetProperty(final Expression receiver, final String methodName, final boolean safe) {
+        MethodNode target = safe?INVOKERHELPER_GETPROPERTYSAFE_METHOD:INVOKERHELPER_GETPROPERTY_METHOD;
+        MethodCallExpression mce = new MethodCallExpression(
+                new ClassExpression(INVOKERHELPER_TYPE),
+                target.getName(),
+                new ArgumentListExpression(receiver, new ConstantExpression(methodName))
+        );
+        mce.setSafe(false);
+        mce.setImplicitThis(false);
+        mce.setMethodTarget(target);
+        mce.visit(controller.getAcg());
+    }
+
+    private void writeMapDotProperty(final Expression receiver, final String methodName, final MethodVisitor mv, final boolean safe) {
+        receiver.visit(controller.getAcg()); // load receiver
+
+        Label exit = new Label();
+        if (safe) {
+            Label doGet = new Label();
+            mv.visitJumpInsn(IFNONNULL, doGet);
+            controller.getOperandStack().remove(1);
+            mv.visitInsn(ACONST_NULL);
+            mv.visitJumpInsn(GOTO, exit);
+            mv.visitLabel(doGet);
+            receiver.visit(controller.getAcg());
+        }
+
+        mv.visitLdcInsn(methodName); // load property name
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
+        if (safe) {
+            mv.visitLabel(exit);
+        }
+        controller.getOperandStack().replace(OBJECT_TYPE);
+    }
+
+    private void writeListDotProperty(final Expression receiver, final String methodName, final MethodVisitor mv, final boolean safe) {
+        ClassNode componentType = receiver.getNodeMetaData(StaticCompilationMetadataKeys.COMPONENT_TYPE);
+        if (componentType==null) {
+            componentType = OBJECT_TYPE;
+        }
+        // for lists, replace list.foo with:
+        // def result = new ArrayList(list.size())
+        // for (e in list) { result.add (e.foo) }
+        // result
+        CompileStack compileStack = controller.getCompileStack();
+
+        Label exit = new Label();
+        if (safe) {
+            receiver.visit(controller.getAcg());
+            Label doGet = new Label();
+            mv.visitJumpInsn(IFNONNULL, doGet);
+            controller.getOperandStack().remove(1);
+            mv.visitInsn(ACONST_NULL);
+            mv.visitJumpInsn(GOTO, exit);
+            mv.visitLabel(doGet);
+        }
+
+        Variable tmpList = new VariableExpression("tmpList", make(ArrayList.class));
+        int var = compileStack.defineTemporaryVariable(tmpList, false);
+        Variable iterator = new VariableExpression("iterator", Iterator_TYPE);
+        int it = compileStack.defineTemporaryVariable(iterator, false);
+        Variable nextVar = new VariableExpression("next", componentType);
+        final int next = compileStack.defineTemporaryVariable(nextVar, false);
+
+        mv.visitTypeInsn(NEW, "java/util/ArrayList");
+        mv.visitInsn(DUP);
+        receiver.visit(controller.getAcg());
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I", true);
+        controller.getOperandStack().remove(1);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "(I)V", false);
+        mv.visitVarInsn(ASTORE, var);
+        Label l1 = new Label();
+        mv.visitLabel(l1);
+        receiver.visit(controller.getAcg());
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;", true);
+        controller.getOperandStack().remove(1);
+        mv.visitVarInsn(ASTORE, it);
+        Label l2 = new Label();
+        mv.visitLabel(l2);
+        mv.visitVarInsn(ALOAD, it);
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);
+        Label l3 = new Label();
+        mv.visitJumpInsn(IFEQ, l3);
+        mv.visitVarInsn(ALOAD, it);
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
+        mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(componentType));
+        mv.visitVarInsn(ASTORE, next);
+        Label l4 = new Label();
+        mv.visitLabel(l4);
+        mv.visitVarInsn(ALOAD, var);
+        final ClassNode finalComponentType = componentType;
+        PropertyExpression pexp = new PropertyExpression(new BytecodeExpression() {
+            @Override
+            public void visit(final MethodVisitor mv) {
+                mv.visitVarInsn(ALOAD, next);
+            }
+
+            @Override
+            public ClassNode getType() {
+                return finalComponentType;
+            }
+        }, methodName);
+        pexp.visit(controller.getAcg());
+        controller.getOperandStack().box();
+        controller.getOperandStack().remove(1);
+        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true);
+        mv.visitInsn(POP);
+        Label l5 = new Label();
+        mv.visitLabel(l5);
+        mv.visitJumpInsn(GOTO, l2);
+        mv.visitLabel(l3);
+        mv.visitVarInsn(ALOAD, var);
+        if (safe) {
+            mv.visitLabel(exit);
+        }
+        controller.getOperandStack().push(make(ArrayList.class));
+        controller.getCompileStack().removeVar(next);
+        controller.getCompileStack().removeVar(it);
+        controller.getCompileStack().removeVar(var);
+    }
+
+    @SuppressWarnings("unchecked")
+    private boolean makeGetPrivateFieldWithBridgeMethod(final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean safe, final boolean implicitThis) {
+        FieldNode field = receiverType.getField(fieldName);
+        ClassNode outerClass = receiverType.getOuterClass();
+        if (field==null && implicitThis && outerClass !=null && !receiverType.isStaticClass()) {
+            Expression pexp;
+            if (controller.isInClosure()) {
+                MethodCallExpression mce = new MethodCallExpression(
+                        new VariableExpression("this"),
+                        "getThisObject",
+                        ArgumentListExpression.EMPTY_ARGUMENTS
+                );
+                mce.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, controller.getOutermostClass());
+                mce.setImplicitThis(true);
+                mce.setMethodTarget(CLOSURE_GETTHISOBJECT_METHOD);
+                pexp = new CastExpression(controller.getOutermostClass(),mce);
+            } else {
+                pexp = new PropertyExpression(
+                        new ClassExpression(outerClass),
+                        "this"
+                );
+                ((PropertyExpression)pexp).setImplicitThis(true);
+            }
+            pexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, outerClass);
+            pexp.setSourcePosition(receiver);
+            return makeGetPrivateFieldWithBridgeMethod(pexp, outerClass, fieldName, safe, true);
+        }
+        ClassNode classNode = controller.getClassNode();
+        if (field!=null && Modifier.isPrivate(field.getModifiers())
+                && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode,receiverType))
+                && !receiverType.equals(classNode)) {
+            Map<String, MethodNode> accessors = receiverType.redirect().getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS);
+            if (accessors!=null) {
+                MethodNode methodNode = accessors.get(fieldName);
+                if (methodNode!=null) {
+                    MethodCallExpression mce = new MethodCallExpression(receiver, methodNode.getName(),
+                            new ArgumentListExpression(field.isStatic()?new ConstantExpression(null):receiver));
+                    mce.setMethodTarget(methodNode);
+                    mce.setSafe(safe);
+                    mce.setImplicitThis(implicitThis);
+                    mce.visit(controller.getAcg());
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void makeGroovyObjectGetPropertySite(final Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) {
+        TypeChooser typeChooser = controller.getTypeChooser();
+        ClassNode classNode = controller.getClassNode();
+        ClassNode receiverType = typeChooser.resolveType(receiver, classNode);
+        if (receiver instanceof VariableExpression && ((VariableExpression) receiver).isThisExpression() && !controller.isInClosure()) {
+            receiverType = classNode;
+        }
+        
+        String property = methodName;
+        if (implicitThis) {
+            if (controller.getInvocationWriter() instanceof StaticInvocationWriter) {
+                MethodCallExpression currentCall = ((StaticInvocationWriter) controller.getInvocationWriter()).getCurrentCall();
+                if (currentCall != null && currentCall.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER) != null) {
+                    property = currentCall.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
+                    String[] props = property.split("\\.");
+                    BytecodeExpression thisLoader = new BytecodeExpression() {
+                        @Override
+                        public void visit(final MethodVisitor mv) {
+                            mv.visitVarInsn(ALOAD, 0); // load this
+                        }
+                    };
+                    thisLoader.setType(CLOSURE_TYPE);
+                    Expression pexp = new PropertyExpression(thisLoader, new ConstantExpression(props[0]), safe);
+                    for (int i = 1, propsLength = props.length; i < propsLength; i++) {
+                        final String prop = props[i];
+                        pexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, CLOSURE_TYPE);
+                        pexp = new PropertyExpression(pexp, prop);
+                    }
+                    pexp.visit(controller.getAcg());
+                    return;
+                }
+            }
+        }
+
+        if (makeGetPropertyWithGetter(receiver, receiverType, property, safe, implicitThis)) return;
+        if (makeGetPrivateFieldWithBridgeMethod(receiver, receiverType, property, safe, implicitThis)) return;
+        if (makeGetField(receiver, receiverType, property, safe, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return;
+
+        MethodCallExpression call = new MethodCallExpression(
+                receiver,
+                "getProperty",
+                new ArgumentListExpression(new ConstantExpression(property))
+        );
+        call.setImplicitThis(implicitThis);
+        call.setSafe(safe);
+        call.setMethodTarget(GROOVYOBJECT_GETPROPERTY_METHOD);
+        call.visit(controller.getAcg());
+        return;
+    }
+
+    @Override
+    public void makeCallSiteArrayInitializer() {
+    }
+
+    private boolean makeGetPropertyWithGetter(final Expression receiver, final ClassNode receiverType, final String methodName, final boolean safe, final boolean implicitThis) {
+        // does a getter exists ?
+        String getterName = "get" + MetaClassHelper.capitalize(methodName);
+        MethodNode getterNode = receiverType.getGetterMethod(getterName);
+        if (getterNode==null) {
+            getterName = "is" + MetaClassHelper.capitalize(methodName);
+            getterNode = receiverType.getGetterMethod(getterName);
+        }
+        if (getterNode!=null && receiver instanceof ClassExpression && !CLASS_Type.equals(receiverType) && !getterNode.isStatic()) {
+            return false;
+        }
+
+        // GROOVY-5561: if two files are compiled in the same source unit
+        // and that one references the other, the getters for properties have not been
+        // generated by the compiler yet (generated by the Verifier)
+        PropertyNode propertyNode = receiverType.getProperty(methodName);
+        if (getterNode == null && propertyNode != null) {
+            // it is possible to use a getter
+            String prefix = "get";
+            if (boolean_TYPE.equals(propertyNode.getOriginType())) {
+                prefix = "is";
+            }
+            getterName = prefix + MetaClassHelper.capitalize(methodName);
+            getterNode = new MethodNode(
+                    getterName,
+                    ACC_PUBLIC,
+                    propertyNode.getOriginType(),
+                    Parameter.EMPTY_ARRAY,
+                    ClassNode.EMPTY_ARRAY,
+                    EmptyStatement.INSTANCE);
+            getterNode.setDeclaringClass(receiverType);
+            if (propertyNode.isStatic()) getterNode.setModifiers(ACC_PUBLIC + ACC_STATIC);
+        }
+        if (getterNode!=null) {
+            MethodCallExpression call = new MethodCallExpression(
+                    receiver,
+                    getterName,
+                    ArgumentListExpression.EMPTY_ARGUMENTS
+            );
+            call.setSourcePosition(receiver);
+            call.setMethodTarget(getterNode);
+            call.setImplicitThis(implicitThis);
+            call.setSafe(safe);
+            call.visit(controller.getAcg());
+            return true;
+        }
+
+        if (receiverType instanceof InnerClassNode && !receiverType.isStaticClass()) {
+            if (makeGetPropertyWithGetter(receiver,  receiverType.getOuterClass(), methodName,  safe, implicitThis)) {
+                return true;
+            }
+        }
+
+        // check direct interfaces (GROOVY-7149)
+        for (ClassNode node : receiverType.getInterfaces()) {
+            if (makeGetPropertyWithGetter(receiver, node, methodName, safe, implicitThis)) {
+                return true;
+            }
+        }
+        // go upper level
+        ClassNode superClass = receiverType.getSuperClass();
+        if (superClass !=null) {
+            return makeGetPropertyWithGetter(receiver, superClass, methodName, safe, implicitThis);
+        }
+
+        return false;
+    }
+
+    boolean makeGetField(final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean safe, final boolean implicitThis, final boolean samePackage) {
+        FieldNode field = receiverType.getField(fieldName);
+        // direct access is allowed if we are in the same class as the declaring class
+        // or we are in an inner class
+        if (field !=null 
+                && isDirectAccessAllowed(field, controller.getClassNode(), samePackage)) {
+            CompileStack compileStack = controller.getCompileStack();
+            MethodVisitor mv = controller.getMethodVisitor();
+            ClassNode replacementType = field.getOriginType();
+            OperandStack operandStack = controller.getOperandStack();
+            if (field.isStatic()) {
+                mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(replacementType));
+                operandStack.push(replacementType);
+            } else {
+                if (implicitThis) {
+                    compileStack.pushImplicitThis(implicitThis);
+                }
+                receiver.visit(controller.getAcg());
+                if (implicitThis) compileStack.popImplicitThis();
+                Label exit = new Label();
+                if (safe) {
+                    mv.visitInsn(DUP);
+                    Label doGet = new Label();
+                    mv.visitJumpInsn(IFNONNULL, doGet);
+                    mv.visitInsn(POP);
+                    mv.visitInsn(ACONST_NULL);
+                    mv.visitJumpInsn(GOTO, exit);
+                    mv.visitLabel(doGet);
+                }
+                if (!operandStack.getTopOperand().isDerivedFrom(field.getOwner())) {
+                    mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(field.getOwner()));
+                }
+                mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(replacementType));
+                if (safe) {
+                    if (ClassHelper.isPrimitiveType(replacementType)) {
+                        operandStack.replace(replacementType);
+                        operandStack.box();
+                        replacementType = operandStack.getTopOperand();
+                    }
+                    mv.visitLabel(exit);
+                }
+            }
+            operandStack.replace(replacementType);
+            return true;
+        }
+
+        for (ClassNode intf : receiverType.getInterfaces()) {
+            // GROOVY-7039
+            if (intf!=receiverType && makeGetField(receiver, intf, fieldName, safe, implicitThis, false)) {
+                return true;
+            }
+        }
+
+        ClassNode superClass = receiverType.getSuperClass();
+        if (superClass !=null) {
+            return makeGetField(receiver, superClass, fieldName, safe, implicitThis, false);
+        }
+        return false;
+    }
+
+    private static boolean samePackages(final String pkg1, final String pkg2) {
+        return (
+                (pkg1 ==null && pkg2 ==null)
+                || pkg1 !=null && pkg1.equals(pkg2)
+                );
+    }
+
+    private static boolean isDirectAccessAllowed(FieldNode a, ClassNode receiver, boolean isSamePackage) {
+        ClassNode declaringClass = a.getDeclaringClass().redirect();
+        ClassNode receiverType = receiver.redirect();
+
+        // first, direct access from within the class or inner class nodes
+        if (declaringClass.equals(receiverType)) return true;
+        if (receiverType instanceof InnerClassNode) {
+            while (receiverType!=null && receiverType instanceof InnerClassNode) {
+                if (declaringClass.equals(receiverType)) return true;
+                receiverType = receiverType.getOuterClass();
+            }
+        }
+
+        // no getter
+        return a.isPublic() || (a.isProtected() && isSamePackage);
+    }
+
+    @Override
+    public void makeSiteEntry() {
+    }
+
+    @Override
+    public void prepareCallSite(final String message) {
+    }
+
+    @Override
+    public void makeSingleArgumentCall(final Expression receiver, final String message, final Expression arguments, boolean safe) {
+        TypeChooser typeChooser = controller.getTypeChooser();
+        ClassNode classNode = controller.getClassNode();
+        ClassNode rType = typeChooser.resolveType(receiver, classNode);
+        ClassNode aType = typeChooser.resolveType(arguments, classNode);
+        if (trySubscript(receiver, message, arguments, rType, aType, safe)) {
+            return;
+        }
+        // now try with flow type instead of declaration type
+        rType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+        if (receiver instanceof VariableExpression && rType == null) {
+            // TODO: can STCV be made smarter to avoid this check?
+            VariableExpression ve = (VariableExpression) ((VariableExpression)receiver).getAccessedVariable();
+            rType = ve.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+        }
+        if (rType!=null && trySubscript(receiver, message, arguments, rType, aType, safe)) {
+            return;
+        }
+        // todo: more cases
+        throw new GroovyBugError(
+                "At line " + receiver.getLineNumber() + " column " + receiver.getColumnNumber() + "\n" +
+                "On receiver: " + receiver.getText() + " with message: " + message + " and arguments: " + arguments.getText() + "\n" +
+                "This method should not have been called. Please try to create a simple example reproducing\n" +
+                "this error and file a bug report at https://issues.apache.org/jira/browse/GROOVY");
+    }
+
+    private boolean trySubscript(final Expression receiver, final String message, final Expression arguments, ClassNode rType, final ClassNode aType, boolean safe) {
+        if (getWrapper(rType).isDerivedFrom(Number_TYPE)
+                && getWrapper(aType).isDerivedFrom(Number_TYPE)) {
+            if ("plus".equals(message) || "minus".equals(message) || "multiply".equals(message) || "div".equals(message)) {
+                writeNumberNumberCall(receiver, message, arguments);
+                return true;
+            } else if ("power".equals(message)) {
+                writePowerCall(receiver, arguments, rType, aType);
+                return true;
+            } else if ("mod".equals(message) || "leftShift".equals(message) || "rightShift".equals(message) || "rightShiftUnsigned".equals(message)
+                    || "and".equals(message) || "or".equals(message) || "xor".equals(message)) {
+                writeOperatorCall(receiver, arguments, message);
+                return true;
+            }
+        } else if (STRING_TYPE.equals(rType) && "plus".equals(message)) {
+            writeStringPlusCall(receiver, message, arguments);
+            return true;
+        } else if ("getAt".equals(message)) {
+            if (rType.isArray() && getWrapper(aType).isDerivedFrom(Number_TYPE) && !safe) {
+                writeArrayGet(receiver, arguments, rType, aType);
+                return true;
+            } else {
+                // check if a getAt method can be found on the receiver
+                ClassNode current = rType;
+                MethodNode getAtNode = null;
+                while (current!=null && getAtNode==null) {
+                    getAtNode = current.getDeclaredMethod("getAt", new Parameter[]{new Parameter(aType, "index")});
+                    if (getAtNode == null) {
+                        getAtNode = getCompatibleMethod(current, "getAt", aType);
+                    }
+                    if (getAtNode==null && isPrimitiveType(aType)) {
+                        getAtNode = current.getDeclaredMethod("getAt", new Parameter[]{new Parameter(getWrapper(aType), "index")});
+                        if (getAtNode == null) {
+                            getAtNode = getCompatibleMethod(current, "getAt", getWrapper(aType));
+                        }
+                    } else if (getAtNode==null && aType.isDerivedFrom(Number_TYPE)) {
+                        getAtNode = current.getDeclaredMethod("getAt", new Parameter[]{new Parameter(getUnwrapper(aType), "index")});
+                        if (getAtNode == null) {
+                            getAtNode = getCompatibleMethod(current, "getAt", getUnwrapper(aType));
+                        }
+                    }
+                    current = current.getSuperClass();
+                }
+                if (getAtNode!=null) {
+                    MethodCallExpression call = new MethodCallExpression(
+                            receiver,
+                            "getAt",
+                            arguments
+                    );
+
+                    call.setSafe(safe);
+                    call.setSourcePosition(arguments);
+                    call.setImplicitThis(false);
+                    call.setMethodTarget(getAtNode);
+                    call.visit(controller.getAcg());
+                    return true;
+                }
+
+                // make sure Map#getAt() and List#getAt handled with the bracket syntax are properly compiled
+                ClassNode[] args = {aType};
+                boolean acceptAnyMethod =
+                        MAP_TYPE.equals(rType) || rType.implementsInterface(MAP_TYPE)
+                        || LIST_TYPE.equals(rType) || rType.implementsInterface(LIST_TYPE);
+                List<MethodNode> nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), rType, message, args);
+                if (nodes.isEmpty()) {
+                    // retry with raw types
+                    rType = rType.getPlainNodeReference();
+                    nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), rType, message, args);
+                }
+                nodes = StaticTypeCheckingSupport.chooseBestMethod(rType, nodes, args);
+                if (nodes.size()==1 || nodes.size()>1 && acceptAnyMethod) {
+                    MethodNode methodNode = nodes.get(0);
+                    MethodCallExpression call = new MethodCallExpression(
+                            receiver,
+                            message,
+                            arguments
+                    );
+
+                    call.setSafe(safe);
+                    call.setSourcePosition(arguments);
+                    call.setImplicitThis(false);
+                    call.setMethodTarget(methodNode);
+                    call.visit(controller.getAcg());
+                    return true;
+                }
+                if (implementsInterfaceOrIsSubclassOf(rType, MAP_TYPE)) {
+                    // fallback to Map#get
+                    MethodCallExpression call = new MethodCallExpression(
+                            receiver,
+                            "get",
+                            arguments
+                    );
+
+                    call.setSafe(safe);
+                    call.setMethodTarget(MAP_GET_METHOD);
+                    call.setSourcePosition(arguments);
+                    call.setImplicitThis(false);
+                    call.visit(controller.getAcg());
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private MethodNode getCompatibleMethod(ClassNode current, String getAt, ClassNode aType) {
+        // TODO this really should find "best" match or find all matches and complain about ambiguity if more than one
+        // TODO handle getAt with more than one parameter
+        // TODO handle default getAt methods on Java 8 interfaces
+        for (MethodNode methodNode : current.getDeclaredMethods("getAt")) {
+            if (methodNode.getParameters().length == 1) {
+                ClassNode paramType = methodNode.getParameters()[0].getType();
+                if (aType.isDerivedFrom(paramType) || aType.declaresInterface(paramType)) {
+                    return methodNode;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void writeArrayGet(final Expression receiver, final Expression arguments, final ClassNode rType, final ClassNode aType) {
+        OperandStack operandStack = controller.getOperandStack();
+        int m1 = operandStack.getStackLength();
+        // visit receiver
+        receiver.visit(controller.getAcg());
+        // visit arguments as array index
+        arguments.visit(controller.getAcg());
+        operandStack.doGroovyCast(int_TYPE);
+        int m2 = operandStack.getStackLength();
+        // array access
+        controller.getMethodVisitor().visitInsn(AALOAD);
+        operandStack.replace(rType.getComponentType(), m2-m1);
+    }
+
+    private void writeOperatorCall(Expression receiver, Expression arguments, String operator) {
+        prepareSiteAndReceiver(receiver, operator, false, controller.getCompileStack().isLHS());
+        controller.getOperandStack().doGroovyCast(Number_TYPE);
+        visitBoxedArgument(arguments);
+        controller.getOperandStack().doGroovyCast(Number_TYPE);
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/typehandling/NumberMath", operator, "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;", false);
+        controller.getOperandStack().replace(Number_TYPE, 2);
+    }
+
+    private void writePowerCall(Expression receiver, Expression arguments, final ClassNode rType, ClassNode aType) {
+        OperandStack operandStack = controller.getOperandStack();
+        int m1 = operandStack.getStackLength();
+        //slow Path
+        prepareSiteAndReceiver(receiver, "power", false, controller.getCompileStack().isLHS());
+        operandStack.doGroovyCast(getWrapper(rType));
+        visitBoxedArgument(arguments);
+        operandStack.doGroovyCast(getWrapper(aType));
+        int m2 = operandStack.getStackLength();
+        MethodVisitor mv = controller.getMethodVisitor();
+        if (BigDecimal_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) {
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/math/BigDecimal;Ljava/lang/Integer;)Ljava/lang/Number;", false);
+        } else if (BigInteger_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) {
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/math/BigInteger;Ljava/lang/Integer;)Ljava/lang/Number;", false);
+        } else if (Long_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) {
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/lang/Long;Ljava/lang/Integer;)Ljava/lang/Number;", false);
+        } else if (Integer_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) {
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Number;", false);
+        } else {
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;", false);
+        }
+        controller.getOperandStack().replace(Number_TYPE, m2 - m1);
+    }
+
+    private void writeStringPlusCall(final Expression receiver, final String message, final Expression arguments) {
+        // todo: performance would be better if we created a StringBuilder
+        OperandStack operandStack = controller.getOperandStack();
+        int m1 = operandStack.getStackLength();
+        //slow Path
+        prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
+        visitBoxedArgument(arguments);
+        int m2 = operandStack.getStackLength();
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "plus", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;", false);
+        controller.getOperandStack().replace(STRING_TYPE, m2-m1);
+    }
+
+    private void writeNumberNumberCall(final Expression receiver, final String message, final Expression arguments) {
+        OperandStack operandStack = controller.getOperandStack();
+        int m1 = operandStack.getStackLength();
+        //slow Path
+        prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
+        controller.getOperandStack().doGroovyCast(Number_TYPE);
+        visitBoxedArgument(arguments);
+        controller.getOperandStack().doGroovyCast(Number_TYPE);
+        int m2 = operandStack.getStackLength();
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/dgmimpl/NumberNumber" + MetaClassHelper.capitalize(message), message, "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;", false);
+        controller.getOperandStack().replace(Number_TYPE, m2 - m1);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
new file mode 100644
index 0000000..fc26486
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
@@ -0,0 +1,135 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.classgen.asm.ClosureWriter;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.Opcodes;
+
+import java.util.List;
+
+/**
+ * Writer responsible for generating closure classes in statically compiled mode.
+ *
+ * @author Cedric Champeau
+ */
+public class StaticTypesClosureWriter extends ClosureWriter {
+    public StaticTypesClosureWriter(WriterController wc) {
+        super(wc);
+    }
+
+    @Override
+    protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) {
+        ClassNode closureClass = super.createClosureClass(expression, mods);
+        List<MethodNode> methods = closureClass.getDeclaredMethods("call");
+        List<MethodNode> doCall = closureClass.getMethods("doCall");
+        if (doCall.size() != 1) {
+            throw new GroovyBugError("Expected to find one (1) doCall method on generated closure, but found " + doCall.size());
+        }
+        MethodNode doCallMethod = doCall.get(0);
+        if (methods.isEmpty() && doCallMethod.getParameters().length == 1) {
+            createDirectCallMethod(closureClass, doCallMethod);
+        }
+        MethodTargetCompletionVisitor visitor = new MethodTargetCompletionVisitor(doCallMethod);
+        Object dynamic = expression.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION);
+        if (dynamic != null) {
+            doCallMethod.putNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION, dynamic);
+        }
+        for (MethodNode method : methods) {
+            visitor.visitMethod(method);
+        }
+        closureClass.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
+        return closureClass;
+    }
+
+    private static void createDirectCallMethod(final ClassNode closureClass, final MethodNode doCallMethod) {
+        // in case there is no "call" method on the closure, we can create a "fast invocation" paths
+        // to avoid going through ClosureMetaClass by call(Object...) method
+
+        // we can't have a specialized version of call(Object...) because the dispatch logic in ClosureMetaClass
+        // is too complex!
+
+        // call(Object)
+        Parameter args = new Parameter(ClassHelper.OBJECT_TYPE, "args");
+        MethodCallExpression doCall1arg = new MethodCallExpression(
+                new VariableExpression("this", closureClass),
+                "doCall",
+                new ArgumentListExpression(new VariableExpression(args))
+        );
+        doCall1arg.setImplicitThis(true);
+        doCall1arg.setMethodTarget(doCallMethod);
+        closureClass.addMethod(
+                new MethodNode("call",
+                        Opcodes.ACC_PUBLIC,
+                        ClassHelper.OBJECT_TYPE,
+                        new Parameter[]{args},
+                        ClassNode.EMPTY_ARRAY,
+                        new ReturnStatement(doCall1arg)));
+
+        // call()
+        MethodCallExpression doCallNoArgs = new MethodCallExpression(new VariableExpression("this", closureClass), "doCall", new ArgumentListExpression(new ConstantExpression(null)));
+        doCallNoArgs.setImplicitThis(true);
+        doCallNoArgs.setMethodTarget(doCallMethod);
+        closureClass.addMethod(
+                new MethodNode("call",
+                        Opcodes.ACC_PUBLIC,
+                        ClassHelper.OBJECT_TYPE,
+                        Parameter.EMPTY_ARRAY,
+                        ClassNode.EMPTY_ARRAY,
+                        new ReturnStatement(doCallNoArgs)));
+    }
+
+    private static final class MethodTargetCompletionVisitor extends ClassCodeVisitorSupport {
+
+        private final MethodNode doCallMethod;
+
+        private MethodTargetCompletionVisitor(final MethodNode doCallMethod) {
+            this.doCallMethod = doCallMethod;
+        }
+
+        @Override
+        protected SourceUnit getSourceUnit() {
+            return null;
+        }
+
+        @Override
+        public void visitMethodCallExpression(final MethodCallExpression call) {
+            super.visitMethodCallExpression(call);
+            MethodNode mn = call.getMethodTarget();
+            if (mn == null) {
+                call.setMethodTarget(doCallMethod);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java
new file mode 100644
index 0000000..cabc3c4
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesStatementWriter.java
@@ -0,0 +1,299 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.asm.BytecodeVariable;
+import org.codehaus.groovy.classgen.asm.CompileStack;
+import org.codehaus.groovy.classgen.asm.MethodCaller;
+import org.codehaus.groovy.classgen.asm.OperandStack;
+import org.codehaus.groovy.classgen.asm.StatementWriter;
+import org.codehaus.groovy.classgen.asm.TypeChooser;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.Enumeration;
+
+import static org.objectweb.asm.Opcodes.AALOAD;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ARRAYLENGTH;
+import static org.objectweb.asm.Opcodes.BALOAD;
+import static org.objectweb.asm.Opcodes.CALOAD;
+import static org.objectweb.asm.Opcodes.DALOAD;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.FALOAD;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.IALOAD;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.IFNULL;
+import static org.objectweb.asm.Opcodes.IF_ICMPGE;
+import static org.objectweb.asm.Opcodes.ILOAD;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.LALOAD;
+import static org.objectweb.asm.Opcodes.SALOAD;
+
+/**
+ * A class to write out the optimized statements
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class StaticTypesStatementWriter extends StatementWriter {
+
+    private static final ClassNode ITERABLE_CLASSNODE = ClassHelper.make(Iterable.class);
+    private static final ClassNode ENUMERATION_CLASSNODE = ClassHelper.make(Enumeration.class);
+    private static final MethodCaller ENUMERATION_NEXT_METHOD = MethodCaller.newInterface(Enumeration.class, "nextElement");
+    private static final MethodCaller ENUMERATION_HASMORE_METHOD = MethodCaller.newInterface(Enumeration.class, "hasMoreElements");
+
+    private final StaticTypesWriterController controller;
+
+    public StaticTypesStatementWriter(StaticTypesWriterController controller) {
+        super(controller);
+        this.controller = controller;
+    }
+    
+    @Override
+    public void writeBlockStatement(BlockStatement statement) {
+        controller.switchToFastPath();
+        super.writeBlockStatement(statement);
+        controller.switchToSlowPath();
+    }
+
+    @Override
+    protected void writeForInLoop(final ForStatement loop) {
+        controller.getAcg().onLineNumber(loop,"visitForLoop");
+        writeStatementLabel(loop);
+
+        CompileStack compileStack = controller.getCompileStack();
+        MethodVisitor mv = controller.getMethodVisitor();
+        OperandStack operandStack = controller.getOperandStack();
+
+        compileStack.pushLoop(loop.getVariableScope(), loop.getStatementLabels());
+
+        // Identify type of collection
+        TypeChooser typeChooser = controller.getTypeChooser();
+        Expression collectionExpression = loop.getCollectionExpression();
+        ClassNode collectionType = typeChooser.resolveType(collectionExpression, controller.getClassNode());
+        Parameter loopVariable = loop.getVariable();
+        int size = operandStack.getStackLength();
+        if (collectionType.isArray() && loopVariable.getOriginType().equals(collectionType.getComponentType())) {
+            writeOptimizedForEachLoop(compileStack, operandStack, mv, loop, collectionExpression, collectionType, loopVariable);
+        } else if (ENUMERATION_CLASSNODE.equals(collectionType)) {
+            writeEnumerationBasedForEachLoop(compileStack, operandStack, mv, loop, collectionExpression, collectionType, loopVariable);
+        } else {
+            writeIteratorBasedForEachLoop(compileStack, operandStack, mv, loop, collectionExpression, collectionType, loopVariable);
+        }
+        operandStack.popDownTo(size);
+        compileStack.pop();
+    }
+
+    private void writeOptimizedForEachLoop(
+            CompileStack compileStack,
+            OperandStack operandStack,
+            MethodVisitor mv,
+            ForStatement loop,
+            Expression collectionExpression,
+            ClassNode collectionType,
+            Parameter loopVariable) {
+        BytecodeVariable variable = compileStack.defineVariable(loopVariable, false);
+
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        AsmClassGenerator acg = controller.getAcg();
+
+        // load array on stack
+        collectionExpression.visit(acg);
+        mv.visitInsn(DUP);
+        int array = compileStack.defineTemporaryVariable("$arr", collectionType, true);
+        mv.visitJumpInsn(IFNULL, breakLabel);
+
+        // $len = array.length
+        mv.visitVarInsn(ALOAD, array);
+        mv.visitInsn(ARRAYLENGTH);
+        operandStack.push(ClassHelper.int_TYPE);
+        int arrayLen = compileStack.defineTemporaryVariable("$len", ClassHelper.int_TYPE, true);
+
+        // $idx = 0
+        mv.visitInsn(ICONST_0);
+        operandStack.push(ClassHelper.int_TYPE);
+        int loopIdx = compileStack.defineTemporaryVariable("$idx", ClassHelper.int_TYPE, true);
+
+        mv.visitLabel(continueLabel);
+        // $idx<$len?
+        mv.visitVarInsn(ILOAD, loopIdx);
+        mv.visitVarInsn(ILOAD, arrayLen);
+        mv.visitJumpInsn(IF_ICMPGE, breakLabel);
+
+        // get array element
+        loadFromArray(mv, variable, array, loopIdx);
+
+        // $idx++
+        mv.visitIincInsn(loopIdx, 1);
+
+        // loop body
+        loop.getLoopBlock().visit(acg);
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+
+        mv.visitLabel(breakLabel);
+
+        compileStack.removeVar(loopIdx);
+        compileStack.removeVar(arrayLen);
+        compileStack.removeVar(array);
+    }
+
+    private void loadFromArray(MethodVisitor mv, BytecodeVariable variable, int array, int iteratorIdx) {
+        OperandStack os = controller.getOperandStack();
+        mv.visitVarInsn(ALOAD, array);
+        mv.visitVarInsn(ILOAD, iteratorIdx);
+
+        ClassNode varType = variable.getType();
+        boolean primitiveType = ClassHelper.isPrimitiveType(varType);
+        boolean isByte = ClassHelper.byte_TYPE.equals(varType);
+        boolean isShort = ClassHelper.short_TYPE.equals(varType);
+        boolean isInt = ClassHelper.int_TYPE.equals(varType);
+        boolean isLong = ClassHelper.long_TYPE.equals(varType);
+        boolean isFloat = ClassHelper.float_TYPE.equals(varType);
+        boolean isDouble = ClassHelper.double_TYPE.equals(varType);
+        boolean isChar = ClassHelper.char_TYPE.equals(varType);
+        boolean isBoolean = ClassHelper.boolean_TYPE.equals(varType);
+
+        if (primitiveType) {
+            if (isByte) {
+                mv.visitInsn(BALOAD);
+            }
+            if (isShort) {
+                mv.visitInsn(SALOAD);
+            }
+            if (isInt || isChar || isBoolean) {
+                mv.visitInsn(isChar ? CALOAD : isBoolean ? BALOAD : IALOAD);
+            }
+            if (isLong) {
+                mv.visitInsn(LALOAD);
+            }
+            if (isFloat) {
+                mv.visitInsn(FALOAD);
+            }
+            if (isDouble) {
+                mv.visitInsn(DALOAD);
+            }
+        } else {
+            mv.visitInsn(AALOAD);
+        }
+        os.push(varType);
+        os.storeVar(variable);
+    }
+
+    private void writeIteratorBasedForEachLoop(
+            CompileStack compileStack,
+            OperandStack operandStack,
+            MethodVisitor mv,
+            ForStatement loop,
+            Expression collectionExpression,
+            ClassNode collectionType,
+            Parameter loopVariable) {
+        // Declare the loop counter.
+        BytecodeVariable variable = compileStack.defineVariable(loopVariable, false);
+
+        if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(collectionType, ITERABLE_CLASSNODE)) {
+            MethodCallExpression iterator = new MethodCallExpression(collectionExpression, "iterator", new ArgumentListExpression());
+            iterator.setMethodTarget(collectionType.getMethod("iterator", Parameter.EMPTY_ARRAY));
+            iterator.setImplicitThis(false);
+            iterator.visit(controller.getAcg());
+        } else {
+            collectionExpression.visit(controller.getAcg());
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "iterator", "(Ljava/lang/Object;)Ljava/util/Iterator;", false);
+            operandStack.replace(ClassHelper.Iterator_TYPE);
+        }
+
+        // Then get the iterator and generate the loop control
+
+        int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.Iterator_TYPE, true);
+
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+        mv.visitVarInsn(ALOAD, iteratorIdx);
+        writeIteratorHasNext(mv);
+        // note: ifeq tests for ==0, a boolean is 0 if it is false
+        mv.visitJumpInsn(IFEQ, breakLabel);
+
+        mv.visitVarInsn(ALOAD, iteratorIdx);
+        writeIteratorNext(mv);
+        operandStack.push(ClassHelper.OBJECT_TYPE);
+        operandStack.storeVar(variable);
+
+        // Generate the loop body
+        loop.getLoopBlock().visit(controller.getAcg());
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+        compileStack.removeVar(iteratorIdx);
+    }
+
+    private void writeEnumerationBasedForEachLoop(
+            CompileStack compileStack,
+            OperandStack operandStack,
+            MethodVisitor mv,
+            ForStatement loop,
+            Expression collectionExpression,
+            ClassNode collectionType,
+            Parameter loopVariable) {
+        // Declare the loop counter.
+        BytecodeVariable variable = compileStack.defineVariable(loopVariable, false);
+
+        collectionExpression.visit(controller.getAcg());
+
+        // Then get the iterator and generate the loop control
+
+        int enumIdx = compileStack.defineTemporaryVariable("$enum", ENUMERATION_CLASSNODE, true);
+
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+        mv.visitVarInsn(ALOAD, enumIdx);
+        ENUMERATION_HASMORE_METHOD.call(mv);
+        // note: ifeq tests for ==0, a boolean is 0 if it is false
+        mv.visitJumpInsn(IFEQ, breakLabel);
+
+        mv.visitVarInsn(ALOAD, enumIdx);
+        ENUMERATION_NEXT_METHOD.call(mv);
+        operandStack.push(ClassHelper.OBJECT_TYPE);
+        operandStack.storeVar(variable);
+
+        // Generate the loop body
+        loop.getLoopBlock().visit(controller.getAcg());
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java
new file mode 100644
index 0000000..a3e4551
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesTypeChooser.java
@@ -0,0 +1,75 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.classgen.asm.StatementMetaTypeChooser;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+
+/**
+ * A {@link org.codehaus.groovy.classgen.asm.TypeChooser} which reads type information from node metadata
+ * generated by the {@link groovy.transform.CompileStatic} annotation.
+ *
+ * @author Cedric Champeau
+ */
+public class StaticTypesTypeChooser extends StatementMetaTypeChooser {
+    @Override
+    public ClassNode resolveType(final Expression exp, final ClassNode current) {
+        ASTNode target = exp instanceof VariableExpression ? getTarget((VariableExpression) exp) : exp;
+        ClassNode inferredType = target.getNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE);
+        if (inferredType == null) {
+            inferredType = target.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
+            if (inferredType == null && target instanceof VariableExpression && ((VariableExpression) target).getAccessedVariable() instanceof Parameter) {
+                target = (Parameter) ((VariableExpression) target).getAccessedVariable();
+                inferredType = ((Parameter) target).getOriginType();
+            }
+        }
+        if (inferredType != null) {
+            if (ClassHelper.VOID_TYPE == inferredType) {
+                // we are in a case of a type inference failure, probably because code was generated
+                // it is better to avoid using this
+                inferredType = super.resolveType(exp, current);
+            }
+            return inferredType;
+        }
+        if (target instanceof VariableExpression && ((VariableExpression) target).isThisExpression()) {
+            // AsmClassGenerator may create "this" expressions that the type checker knows nothing about
+            return current;
+        }
+        return super.resolveType(exp, current);
+    }
+
+    /**
+     * The inferred type, in case of a variable expression, can be set on the accessed variable, so we take it instead
+     * of the facade one.
+     *
+     * @param ve the variable expression for which to return the target expression
+     * @return the target variable expression
+     */
+    private static VariableExpression getTarget(VariableExpression ve) {
+        if (ve.getAccessedVariable() == null || ve.getAccessedVariable() == ve || (!(ve.getAccessedVariable() instanceof VariableExpression)))
+            return ve;
+        return getTarget((VariableExpression) ve.getAccessedVariable());
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java
new file mode 100644
index 0000000..73adb10
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java
@@ -0,0 +1,178 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.NotExpression;
+import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
+import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.codehaus.groovy.classgen.asm.TypeChooser;
+import org.codehaus.groovy.classgen.asm.UnaryExpressionHelper;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
+import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
+
+/**
+ * An unary expression helper which generates optimized bytecode depending on
+ * the current type on top of the operand stack.
+ *
+ * @author Cedric Champeau
+ */
+public class StaticTypesUnaryExpressionHelper extends UnaryExpressionHelper implements Opcodes {
+    private static final UnaryMinusExpression EMPTY_UNARY_MINUS = new UnaryMinusExpression(EmptyExpression.INSTANCE);
+    private static final UnaryPlusExpression EMPTY_UNARY_PLUS = new UnaryPlusExpression(EmptyExpression.INSTANCE);
+    private static final BitwiseNegationExpression EMPTY_BITWISE_NEGATE = new BitwiseNegationExpression(EmptyExpression.INSTANCE);
+
+    private final WriterController controller;
+
+    public StaticTypesUnaryExpressionHelper(final WriterController controller) {
+        super(controller);
+        this.controller = controller;
+    }
+
+    @Override
+    public void writeBitwiseNegate(final BitwiseNegationExpression expression) {
+        expression.getExpression().visit(controller.getAcg());
+        if (isPrimitiveOnTop()) {
+            final ClassNode top = getTopOperand();
+            if (top==int_TYPE || top==short_TYPE || top==byte_TYPE || top==char_TYPE || top==long_TYPE) {
+                BytecodeExpression bytecodeExpression = new BytecodeExpression() {
+                    @Override
+                    public void visit(final MethodVisitor mv) {
+                        if (long_TYPE==top) {
+                            mv.visitLdcInsn(-1);
+                            mv.visitInsn(LXOR);
+                        } else {
+                            mv.visitInsn(ICONST_M1);
+                            mv.visitInsn(IXOR);
+                            if (byte_TYPE==top) {
+                                mv.visitInsn(I2B);
+                            } else if (char_TYPE==top) {
+                                mv.visitInsn(I2C);
+                            } else if (short_TYPE==top) {
+                                mv.visitInsn(I2S);
+                            }
+                        }
+                    }
+                };
+                bytecodeExpression.visit(controller.getAcg());
+                controller.getOperandStack().remove(1);
+                return;
+            }
+        }
+        super.writeBitwiseNegate(EMPTY_BITWISE_NEGATE);
+    }
+
+    @Override
+    public void writeNotExpression(final NotExpression expression) {
+        TypeChooser typeChooser = controller.getTypeChooser();
+        Expression subExpression = expression.getExpression();
+        ClassNode classNode = controller.getClassNode();
+        if (typeChooser.resolveType(subExpression, classNode) == boolean_TYPE) {
+            subExpression.visit(controller.getAcg());
+            controller.getOperandStack().doGroovyCast(boolean_TYPE);
+            BytecodeExpression bytecodeExpression = new BytecodeExpression() {
+                @Override
+                public void visit(final MethodVisitor mv) {
+                    Label ne = new Label();
+                    mv.visitJumpInsn(IFNE, ne);
+                    mv.visitInsn(ICONST_1);
+                    Label out = new Label();
+                    mv.visitJumpInsn(GOTO, out);
+                    mv.visitLabel(ne);
+                    mv.visitInsn(ICONST_0);
+                    mv.visitLabel(out);
+                }
+            };
+            bytecodeExpression.visit(controller.getAcg());
+            controller.getOperandStack().remove(1);
+            return;
+        }
+        super.writeNotExpression(expression);
+    }
+
+    @Override
+    public void writeUnaryMinus(final UnaryMinusExpression expression) {
+        expression.getExpression().visit(controller.getAcg());
+        if (isPrimitiveOnTop()) {
+            final ClassNode top = getTopOperand();
+            if (top!=boolean_TYPE) {
+                BytecodeExpression bytecodeExpression = new BytecodeExpression() {
+                    @Override
+                    public void visit(final MethodVisitor mv) {
+                        if (int_TYPE == top || short_TYPE == top || byte_TYPE==top || char_TYPE==top) {
+                            mv.visitInsn(INEG);
+                            if (byte_TYPE==top) {
+                                mv.visitInsn(I2B);
+                            } else if (char_TYPE==top) {
+                                mv.visitInsn(I2C);
+                            } else if (short_TYPE==top) {
+                                mv.visitInsn(I2S);
+                            }
+                        } else if (long_TYPE == top) {
+                            mv.visitInsn(LNEG);
+                        } else if (float_TYPE == top) {
+                            mv.visitInsn(FNEG);
+                        } else if (double_TYPE == top) {
+                            mv.visitInsn(DNEG);
+                        }
+                    }
+                };
+                bytecodeExpression.visit(controller.getAcg());
+                controller.getOperandStack().remove(1);
+                return;
+            }
+        }
+        // we already visited the sub expression
+        super.writeUnaryMinus(EMPTY_UNARY_MINUS);
+    }
+
+    @Override
+    public void writeUnaryPlus(final UnaryPlusExpression expression) {
+        expression.getExpression().visit(controller.getAcg());
+        if (isPrimitiveOnTop()) {
+            // only visit the expression
+            return;
+        }
+        super.writeUnaryPlus(EMPTY_UNARY_PLUS);
+    }
+
+    private boolean isPrimitiveOnTop() {
+        return isPrimitiveType(getTopOperand());
+    }
+
+    private ClassNode getTopOperand() {
+        return controller.getOperandStack().getTopOperand();
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
new file mode 100644
index 0000000..47ef3d4
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterController.java
@@ -0,0 +1,186 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.classgen.asm.BinaryExpressionHelper;
+import org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher;
+import org.codehaus.groovy.classgen.asm.CallSiteWriter;
+import org.codehaus.groovy.classgen.asm.ClosureWriter;
+import org.codehaus.groovy.classgen.asm.DelegatingController;
+import org.codehaus.groovy.classgen.asm.InvocationWriter;
+import org.codehaus.groovy.classgen.asm.StatementWriter;
+import org.codehaus.groovy.classgen.asm.TypeChooser;
+import org.codehaus.groovy.classgen.asm.UnaryExpressionHelper;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.classgen.asm.indy.sc.IndyStaticTypesMultiTypeDispatcher;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.sc.StaticCompilationVisitor;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.ClassVisitor;
+
+
+/**
+ * An alternative {@link org.codehaus.groovy.classgen.asm.WriterController} which handles static types and method
+ * dispatch. In case of a "mixed mode" where only some methods are annotated with {@link groovy.transform.TypeChecked}
+ * then this writer will delegate to the classic writer controller.
+ *
+ * @author Cedric Champeau
+ */
+public class StaticTypesWriterController extends DelegatingController {
+
+    protected boolean isInStaticallyCheckedMethod;
+    private StaticTypesCallSiteWriter callSiteWriter;
+    private StaticTypesStatementWriter statementWriter;
+    private StaticTypesTypeChooser typeChooser;
+    private StaticInvocationWriter invocationWriter;
+    private BinaryExpressionMultiTypeDispatcher binaryExprHelper;
+    private UnaryExpressionHelper unaryExpressionHelper;
+    private ClosureWriter closureWriter;
+
+    public StaticTypesWriterController(WriterController normalController) {
+        super(normalController);
+        isInStaticallyCheckedMethod = false;
+    }
+
+    @Override
+    public void init(final AsmClassGenerator asmClassGenerator, final GeneratorContext gcon, final ClassVisitor cv, final ClassNode cn) {
+        super.init(asmClassGenerator, gcon, cv, cn);
+        this.callSiteWriter = new StaticTypesCallSiteWriter(this);
+        this.statementWriter = new StaticTypesStatementWriter(this);
+        this.typeChooser = new StaticTypesTypeChooser();
+        this.invocationWriter = new StaticInvocationWriter(this);
+        this.closureWriter = new StaticTypesClosureWriter(this);
+        this.unaryExpressionHelper = new StaticTypesUnaryExpressionHelper(this);
+
+        CompilerConfiguration config = cn.getCompileUnit().getConfig();
+        this.binaryExprHelper = config.isIndyEnabled()
+                ? new IndyStaticTypesMultiTypeDispatcher(this)
+                : new StaticTypesBinaryExpressionMultiTypeDispatcher(this);
+    }
+
+    @Override
+    public void setMethodNode(final MethodNode mn) {
+        updateStaticCompileFlag(mn);
+        super.setMethodNode(mn);
+    }
+
+    private void updateStaticCompileFlag(final MethodNode mn) {
+        ClassNode classNode = getClassNode();
+        AnnotatedNode node = mn;
+        if (classNode.implementsInterface(ClassHelper.GENERATED_CLOSURE_Type)) {
+            node = classNode.getOuterClass();
+        }
+
+        isInStaticallyCheckedMethod = mn != null && (
+                StaticCompilationVisitor.isStaticallyCompiled(node)
+                        || classNode.implementsInterface(ClassHelper.GENERATED_CLOSURE_Type)&&classNode.getNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE)!=null);
+    }
+
+    @Override
+    public void setConstructorNode(final ConstructorNode cn) {
+        updateStaticCompileFlag(cn);
+        super.setConstructorNode(cn);
+    }
+    
+    @Override
+    public boolean isFastPath() {
+        if (isInStaticallyCheckedMethod) return true;
+        return super.isFastPath();
+    }
+    
+    @Override
+    public CallSiteWriter getCallSiteWriter() {
+        MethodNode methodNode = getMethodNode();
+        if (methodNode !=null && methodNode.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION)==Boolean.TRUE) {
+            return super.getCallSiteWriter();
+        }
+        if (isInStaticallyCheckedMethod) {
+            return callSiteWriter;
+        }
+        return super.getCallSiteWriter();
+    }
+
+    public CallSiteWriter getRegularCallSiteWriter() {
+        return super.getCallSiteWriter();
+    }
+
+    @Override
+    public StatementWriter getStatementWriter() {
+        if (isInStaticallyCheckedMethod) {
+            return statementWriter;
+        } else {
+            return super.getStatementWriter();            
+        }
+    }
+    
+    @Override
+    public TypeChooser getTypeChooser() {
+        if (isInStaticallyCheckedMethod) {
+            return typeChooser;
+        } else {
+            return super.getTypeChooser();
+        }
+    }
+
+    @Override
+    public InvocationWriter getInvocationWriter() {
+        if (isInStaticallyCheckedMethod) {
+            return invocationWriter;
+        } else {
+            return super.getInvocationWriter();
+        }
+    }
+
+    public InvocationWriter getRegularInvocationWriter() {
+        return super.getInvocationWriter();
+    }
+
+    @Override
+    public BinaryExpressionHelper getBinaryExpressionHelper() {
+        if (isInStaticallyCheckedMethod) {
+            return binaryExprHelper;
+        } else {
+            return super.getBinaryExpressionHelper();
+        }
+    }
+
+    @Override
+    public UnaryExpressionHelper getUnaryExpressionHelper() {
+        if (isInStaticallyCheckedMethod) {
+            return unaryExpressionHelper;
+        }
+        return super.getUnaryExpressionHelper();
+    }
+
+    @Override
+    public ClosureWriter getClosureWriter() {
+        if (isInStaticallyCheckedMethod) {
+            return closureWriter;
+        }
+        return super.getClosureWriter();
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterControllerFactoryImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterControllerFactoryImpl.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterControllerFactoryImpl.java
new file mode 100644
index 0000000..c68f635
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesWriterControllerFactoryImpl.java
@@ -0,0 +1,33 @@
+/*
+ *  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.classgen.asm.sc;
+
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.classgen.asm.WriterControllerFactory;
+
+/**
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class StaticTypesWriterControllerFactoryImpl implements WriterControllerFactory {
+
+    public WriterController makeController(WriterController normalController) {
+        return new StaticTypesWriterController(normalController);
+    }
+
+}