/* * Copyright 2009-2010 MBTE Sweden AB. * * Licensed 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.mbte.groovypp.compiler; import groovy.lang.TypePolicy; import groovy.lang.Use; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.classgen.BytecodeHelper; import org.codehaus.groovy.classgen.BytecodeSequence; import org.codehaus.groovy.control.MultipleCompilationErrorsException; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.messages.SyntaxErrorMessage; import org.codehaus.groovy.syntax.SyntaxException; import org.codehaus.groovy.syntax.Token; import org.codehaus.groovy.syntax.Types; import org.codehaus.groovy.util.FastArray; import org.mbte.groovypp.compiler.bytecode.BytecodeExpr; import org.mbte.groovypp.compiler.bytecode.LocalVarTypeInferenceState; import org.mbte.groovypp.compiler.bytecode.StackAwareMethodAdapter; import org.mbte.groovypp.compiler.transformers.ExprTransformer; import org.mbte.groovypp.compiler.transformers.ListExpressionTransformer; import org.mbte.groovypp.compiler.transformers.MapExpressionTransformer; import org.mbte.groovypp.compiler.transformers.TernaryExpressionTransformer; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.LinkedList; import java.util.List; import static org.mbte.groovypp.compiler.transformers.ExprTransformer.transformExpression; public abstract class CompilerTransformer extends ReturnsAdder implements Opcodes, LocalVarTypeInferenceState { public final CompilerStack compileStack; public final ClassNode classNode; protected final StackAwareMethodAdapter mv; public final int debug; public final TypePolicy policy; private static final ClassNode USE = ClassHelper.make(Use.class); private int nestedLevel; LinkedList<CompiledClosureBytecodeExpr> pendingClosures = new LinkedList<CompiledClosureBytecodeExpr> (); private int nextClosureIndex = 1; private final String baseClosureName; public SourceUnitContext context; public CompilerTransformer(SourceUnit source, ClassNode classNode, MethodNode methodNode, StackAwareMethodAdapter mv, CompilerStack compileStack, int debug, TypePolicy policy, String baseClosureName, SourceUnitContext context) { super(source, methodNode); this.classNode = classNode; this.mv = mv; this.debug = debug; this.policy = policy; this.baseClosureName = baseClosureName; this.compileStack = new CompilerStack(compileStack); this.context = context; } public void addError(String msg, ASTNode expr) { int line = expr.getLineNumber(); int col = expr.getColumnNumber(); SourceUnit source = getSourceUnit(); source.getErrorCollector().addError( new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source), true ); } @Override public Expression transform(Expression exp) { nestedLevel++; try { Expression result = transformExpression(exp, this); processPendingClosures(); return result; } catch (MultipleCompilationErrorsException err) { throw err; } catch (Throwable e) { e.printStackTrace(); addError(e.getMessage(), exp); return null; } finally { nestedLevel--; } } public Expression transformToGround(Expression expr) { return transformSynthetic((BytecodeExpr) transform(expr)); } public BytecodeExpr transformSynthetic(BytecodeExpr res) { if (res instanceof ListExpressionTransformer.UntransformedListExpr) return ((ListExpressionTransformer.UntransformedListExpr) res).transform(TypeUtil.ARRAY_LIST_TYPE, this); else if (res instanceof MapExpressionTransformer.UntransformedMapExpr) return ((MapExpressionTransformer.UntransformedMapExpr) res).transform(this); else if (res instanceof TernaryExpressionTransformer.UntransformedTernaryExpr) return ((TernaryExpressionTransformer.UntransformedTernaryExpr) res).transform(this); else if (res.getType().declaresInterface(TypeUtil.TTHIS)) { res.setType(res.getType().getOuterClass()); } return res; } private void processPendingClosures() { if (nestedLevel == 1) { if (!pendingClosures.isEmpty()) { for (CompiledClosureBytecodeExpr pendingClosure : pendingClosures) { processPendingClosure(pendingClosure); } pendingClosures.clear(); } } } public void processPendingClosure(CompiledClosureBytecodeExpr pendingClosure) { ClosureClassNode type = (ClosureClassNode) pendingClosure.getType(); ClosureMethodNode doCallMethod = type.getDoCallMethod(); Statement code = doCallMethod.getCode(); if (!(code instanceof BytecodeSequence)) { ClosureUtil.improveClosureType(type, ClassHelper.CLOSURE_TYPE); StaticMethodBytecode.replaceMethodCode(su, context, doCallMethod, compileStack, debug == -1 ? -1 : debug+1, policy, type.getName()); } } public BytecodeExpr transformLogical(Expression exp, Label label, boolean onTrue) { nestedLevel++; try { final BytecodeExpr result = ExprTransformer.transformLogicalExpression(exp, this, label, onTrue); processPendingClosures(); return result; } catch (MultipleCompilationErrorsException err) { throw err; } catch (Throwable e) { e.printStackTrace(); addError(e.getMessage(), exp); return null; } finally { nestedLevel--; } } public Expression transformImpl(Expression exp) { if (exp instanceof SpreadMapExpression) { addError("Spread expressions are not supported by static compiler", exp); return null; } if (exp instanceof StaticMethodCallExpression) { StaticMethodCallExpression smce = (StaticMethodCallExpression) exp; MethodCallExpression mce = new MethodCallExpression( new ClassExpression(smce.getOwnerType()), new ConstantExpression(smce.getMethod()), smce.getArguments()); mce.setSourcePosition(smce); mce.getMethod().setSourcePosition(smce); return transform(mce); } return super.transform(exp); } public FieldNode findField(ClassNode type, String fieldName) { Object fields = ClassNodeCache.getFields(type, fieldName); return (FieldNode) fields; } private Object findCategoryMethod(ClassNode category, String methodName, ClassNode objectType, ClassNode [] args, Object candidates) { final Object o = ClassNodeCache.getMethods(category, methodName); if (o instanceof MethodNode) { MethodNode mn = (MethodNode) o; if (mn.isStatic()) { final Parameter[] parameters = mn.getParameters(); if (parameters.length > 0 && TypeUtil.isDirectlyAssignableFrom(parameters[0].getType(), objectType)) { candidates = ClassNodeCache.createDGM(mn); } } } else { FastArray ms = (FastArray) o; if (ms == null) return candidates; for (int i = 0; i != ms.size(); ++i) { MethodNode mn = (MethodNode) ms.get(i); if (mn.isStatic()) { final Parameter[] parameters = mn.getParameters(); if (parameters.length > 0) { if (TypeUtil.isDirectlyAssignableFrom(parameters[0].getType(), objectType)) { if (candidates == null) candidates = ClassNodeCache.createDGM(mn); else if (candidates instanceof FastArray) { ((FastArray) candidates).add(ClassNodeCache.createDGM(mn)); } else { MethodNode _1st = (MethodNode) candidates; candidates = new FastArray(2); ((FastArray) candidates).add(_1st); ((FastArray) candidates).add(ClassNodeCache.createDGM(mn)); } } } } } } return candidates; } public MethodNode findMethod(ClassNode type, String methodName, ClassNode[] args, boolean staticOnly) { Object methods = staticOnly ? ClassNodeCache.getStaticMethods(type, methodName) : ClassNodeCache.getMethods(type, methodName); final Object res = MethodSelection.chooseMethod(methodName, methods, type, args, classNode); if (res instanceof MethodNode) return (MethodNode) res; if (!staticOnly) { Object candidates = findCategoryMethod(type, methodName, type, args, null); if (candidates == null) { candidates = findCategoryMethod(classNode, methodName, type, args, candidates); final List<AnnotationNode> list = classNode.getAnnotations(USE); for (AnnotationNode annotationNode : list) { final Expression member = annotationNode.getMember("value"); if (member instanceof ClassExpression) { ClassExpression expression = (ClassExpression) member; final ClassNode category = expression.getType(); candidates = findCategoryMethod(category, methodName, type, args, candidates); } } } if (candidates == null) { final CompileUnit compileUnit = classNode.getCompileUnit(); if (compileUnit != null) for (ModuleNode moduleNode : compileUnit.getModules()) { for (ClassNode category : moduleNode.getClasses()) { candidates = findCategoryMethod(category, methodName, type, args, candidates); } } } if (candidates != null) { final Object r = MethodSelection.chooseMethod(methodName, candidates, type, args, classNode); if (r instanceof MethodNode) return (MethodNode) r; } } return null; } public PropertyNode findProperty(ClassNode type, String property) { for (; type != null; type = type.getSuperClass()) { PropertyNode propertyNode = type.getProperty(property); if (propertyNode != null) return propertyNode; } return null; } public MethodNode findConstructor(ClassNode type, ClassNode[] args, ClassNode contextClass) { FastArray methods = ClassNodeCache.getConstructors(type); // if (type.redirect() instanceof InnerClassNode && (type.getModifiers() & ACC_STATIC) == 0) { // ClassNode newArgs [] = new ClassNode[args.length+1]; // // for (ClassNode tp = classNode ; tp != null && !tp.equals(type.redirect().getOuterClass()); ) { // final ClassNode outerTp = tp.getOuterClass(); // // tp = outerTp; // } // // newArgs [0] = type.getOuterClass(); // System.arraycopy(args, 0, newArgs, 1, args.length); // args = newArgs; // } final Object res = MethodSelection.chooseMethod("<init>", methods, type, args, contextClass == null ? classNode : contextClass); if (res instanceof MethodNode) return (MethodNode) res; return null; } public ClassNode[] exprToTypeArray(Expression args) { final List list = ((TupleExpression) args).getExpressions(); final ClassNode[] nodes = new ClassNode[list.size()]; for (int i = 0; i < nodes.length; i++) { ClassNode type = ((Expression) list.get(i)).getType(); if (type == TypeUtil.NULL_TYPE) nodes[i] = null; else nodes[i] = type; } return nodes; } public void mathOp(ClassNode type, Token op, BinaryExpression be) { switch (op.getType()) { case Types.PLUS: if (type == ClassHelper.int_TYPE) mv.visitInsn(IADD); else if (type == ClassHelper.double_TYPE) mv.visitInsn(DADD); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LADD); else throw new RuntimeException("Internal Error"); break; case Types.COMPARE_NOT_EQUAL: { Label _true = new Label(); if (type == ClassHelper.int_TYPE) mv.visitJumpInsn(IF_ICMPEQ, _true); else if (type == ClassHelper.double_TYPE) { mv.visitInsn(DCMPG); mv.visitJumpInsn(IFEQ, _true); } else if (type == ClassHelper.float_TYPE) { mv.visitInsn(FCMPG); mv.visitJumpInsn(IFEQ, _true); } else if (type == ClassHelper.long_TYPE) { mv.visitInsn(LCMP); mv.visitJumpInsn(IFEQ, _true); } else throw new RuntimeException("Internal Error"); mv.visitInsn(ICONST_1); Label _false = new Label(); mv.visitJumpInsn(GOTO, _false); mv.visitLabel(_true); mv.visitInsn(ICONST_0); mv.visitLabel(_false); break; } case Types.MULTIPLY: if (type == ClassHelper.int_TYPE) mv.visitInsn(IMUL); else if (type == ClassHelper.double_TYPE) mv.visitInsn(DMUL); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LMUL); else throw new RuntimeException("Internal Error"); break; case Types.MINUS: if (type == ClassHelper.int_TYPE) mv.visitInsn(ISUB); else if (type == ClassHelper.double_TYPE) mv.visitInsn(DSUB); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LSUB); else throw new RuntimeException("Internal Error"); break; case Types.DIVIDE: if (type == ClassHelper.int_TYPE) mv.visitInsn(IDIV); else if (type == ClassHelper.double_TYPE) mv.visitInsn(DDIV); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LDIV); else throw new RuntimeException("Internal Error"); break; case Types.BITWISE_XOR: if (type == ClassHelper.int_TYPE) mv.visitInsn(IXOR); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LXOR); else throw new RuntimeException("Internal Error"); break; case Types.BITWISE_AND: if (type == ClassHelper.int_TYPE) mv.visitInsn(IAND); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LAND); else throw new RuntimeException("Internal Error"); break; case Types.INTDIV: if (type == ClassHelper.int_TYPE) mv.visitInsn(IDIV); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LDIV); else throw new RuntimeException("Internal Error"); break; case Types.LEFT_SHIFT: if (type == ClassHelper.int_TYPE) mv.visitInsn(ISHL); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LSHL); else throw new RuntimeException("Internal Error"); break; case Types.RIGHT_SHIFT: if (type == ClassHelper.int_TYPE) mv.visitInsn(ISHR); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LSHR); else throw new RuntimeException("Internal Error"); break; case Types.RIGHT_SHIFT_UNSIGNED: if (type == ClassHelper.int_TYPE) mv.visitInsn(IUSHR); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LUSHR); else throw new RuntimeException("Internal Error"); break; case Types.MOD: if (type == ClassHelper.int_TYPE) mv.visitInsn(IREM); else if (type == ClassHelper.double_TYPE) mv.visitInsn(DREM); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LREM); else throw new RuntimeException("Internal Error"); break; case Types.BITWISE_OR: if (type == ClassHelper.int_TYPE) mv.visitInsn(IOR); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LOR); else throw new RuntimeException("Internal Error"); break; default: addError("Operation " + op.getDescription() + " doesn't supported", be); } } public BytecodeExpr cast(final Expression be, final ClassNode type) { if (be instanceof TernaryExpression) { TernaryExpression ternaryExpression = (TernaryExpression) be; TernaryExpression cast = new TernaryExpression(ternaryExpression.getBooleanExpression(), cast(ternaryExpression.getTrueExpression(), type), cast(ternaryExpression.getFalseExpression(), type)); cast.setSourcePosition(be); return (BytecodeExpr) transform(cast); } final CastExpression cast = new CastExpression(type, be); cast.setSourcePosition(be); return (BytecodeExpr) transform(cast); } public BytecodeExpr castToBoolean(final BytecodeExpr be, final ClassNode type) { if (be.getType().equals(ClassHelper.boolean_TYPE)) return be; if (ClassHelper.isPrimitiveType(be.getType())) { return new BytecodeExpr(be, type) { protected void compile(MethodVisitor mv) { ClassNode btype = be.getType(); if (btype == ClassHelper.long_TYPE) { be.visit(mv); mv.visitInsn(L2I); } else if (btype == ClassHelper.float_TYPE) { mv.visitInsn(ICONST_0); be.visit(mv); mv.visitInsn(FCONST_0); mv.visitInsn(FCMPG); final Label falseL = new Label(); mv.visitJumpInsn(IFEQ, falseL); mv.visitInsn(POP); mv.visitInsn(ICONST_1); mv.visitLabel(falseL); } else if (btype == ClassHelper.double_TYPE) { mv.visitInsn(ICONST_0); be.visit(mv); mv.visitInsn(DCONST_0); mv.visitInsn(DCMPG); final Label falseL = new Label(); mv.visitJumpInsn(IFEQ, falseL); mv.visitInsn(POP); mv.visitInsn(ICONST_1); mv.visitLabel(falseL); } else { be.visit(mv); } if (type.equals(ClassHelper.Boolean_TYPE)) box(ClassHelper.boolean_TYPE, mv); } }; } else { MethodCallExpression safeCall = new MethodCallExpression(new BytecodeExpr(be, be.getType()) { protected void compile(MethodVisitor mv) { } }, "asBoolean", ArgumentListExpression.EMPTY_ARGUMENTS); safeCall.setSourcePosition(be); final BytecodeExpr call = (BytecodeExpr) transform(safeCall); if (!call.getType().equals(ClassHelper.boolean_TYPE)) addError("method asBoolean () should return 'boolean'", be); return new BytecodeExpr(be, type) { protected void compile(MethodVisitor mv) { be.visit(mv); mv.visitInsn(DUP); Label nullLabel = new Label(), endLabel = new Label (); mv.visitJumpInsn(IFNULL, nullLabel); call.visit(mv); mv.visitJumpInsn(GOTO, endLabel); mv.visitLabel(nullLabel); mv.visitInsn(POP); mv.visitInsn(ICONST_0); mv.visitLabel(endLabel); if (type.equals(ClassHelper.Boolean_TYPE)) box(ClassHelper.boolean_TYPE, mv); } }; } } public BytecodeExpr castToString(final BytecodeExpr be) { if (be.getType().equals(TypeUtil.NULL_TYPE) || be.getType().equals(ClassHelper.STRING_TYPE)) return be; MethodCallExpression safeCall = new MethodCallExpression(new BytecodeExpr(be, TypeUtil.wrapSafely(be.getType())) { protected void compile(MethodVisitor mv) { } }, "toString", ArgumentListExpression.EMPTY_ARGUMENTS); safeCall.setSourcePosition(be); final BytecodeExpr call = (BytecodeExpr) transform(safeCall); if (!call.getType().equals(ClassHelper.STRING_TYPE)) addError("method toString () should return 'java.lang.String'", be); return new BytecodeExpr(be, ClassHelper.STRING_TYPE) { protected void compile(MethodVisitor mv) { be.visit(mv); box(be.getType(), mv); mv.visitInsn(DUP); Label nullLabel = new Label(), endLabel = new Label (); mv.visitJumpInsn(IFNULL, nullLabel); call.visit(mv); mv.visitJumpInsn(GOTO, endLabel); mv.visitLabel(nullLabel); mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(ClassHelper.STRING_TYPE)); mv.visitLabel(endLabel); } }; } public ClassNode getCollectionType(ClassNode type) { final GenericsType[] generics = TypeUtil.getSubstitutedType(TypeUtil.ITERABLE, TypeUtil.ITERABLE, type).getGenericsTypes(); if (generics == null) return ClassHelper.OBJECT_TYPE; ClassNode substitutedType = generics[0].getType(); return getCollOrMapGenericType(substitutedType); } public ClassNode getMapKeyType(ClassNode type) { final GenericsType[] generics = TypeUtil.getSubstitutedType(ClassHelper.MAP_TYPE, ClassHelper.MAP_TYPE, type).getGenericsTypes(); if (generics == null) return ClassHelper.OBJECT_TYPE; ClassNode substitutedType = generics[0].getType(); return getCollOrMapGenericType(substitutedType); } public ClassNode getMapValueType(ClassNode type) { final GenericsType[] generics = TypeUtil.getSubstitutedType(ClassHelper.MAP_TYPE, ClassHelper.MAP_TYPE, type).getGenericsTypes(); if (generics == null) return ClassHelper.OBJECT_TYPE; ClassNode substitutedType = generics[1].getType(); return getCollOrMapGenericType(substitutedType); } public ClassNode getCollOrMapGenericType(ClassNode substitutedType) { while (substitutedType.equals(ClassHelper.OBJECT_TYPE) && !substitutedType.isGenericsPlaceHolder() && substitutedType.getGenericsTypes() != null && substitutedType.getGenericsTypes().length != 0) { GenericsType genericsType = substitutedType.getGenericsTypes()[0]; if (genericsType.isWildcard()) { substitutedType = genericsType.getUpperBounds()[0]; if (substitutedType.equals(ClassHelper.OBJECT_TYPE) && substitutedType.getGenericsTypes() != null && substitutedType.getGenericsTypes().length != 0) { genericsType = substitutedType.getGenericsTypes()[0]; } } else { substitutedType = genericsType.getType(); } } return substitutedType; } public String getNextClosureName() { while (true) { String name = baseClosureName + "$" + (nextClosureIndex++); if (checkNotExist(name)) return name; } } private boolean checkNotExist (String name) { for (ClassNode node : classNode.getModule().getClasses()) { if (name.equals(node.getName())) { return false; } } return true; } }