// Copyright (c) Corporation for National Research Initiatives package org.python.compiler; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.ListIterator; import java.util.Map; import java.util.Stack; import java.util.Vector; import org.python.antlr.ParseException; import org.python.antlr.PythonTree; import org.python.antlr.Visitor; import org.python.antlr.ast.Assert; import org.python.antlr.ast.Assign; import org.python.antlr.ast.Attribute; import org.python.antlr.ast.AugAssign; import org.python.antlr.ast.BinOp; import org.python.antlr.ast.BoolOp; import org.python.antlr.ast.Break; import org.python.antlr.ast.Call; import org.python.antlr.ast.ClassDef; import org.python.antlr.ast.Compare; import org.python.antlr.ast.Continue; import org.python.antlr.ast.Delete; import org.python.antlr.ast.Dict; import org.python.antlr.ast.Ellipsis; import org.python.antlr.ast.ExceptHandler; import org.python.antlr.ast.Exec; import org.python.antlr.ast.Expr; import org.python.antlr.ast.Expression; import org.python.antlr.ast.ExtSlice; import org.python.antlr.ast.For; import org.python.antlr.ast.FunctionDef; import org.python.antlr.ast.GeneratorExp; import org.python.antlr.ast.Global; import org.python.antlr.ast.If; import org.python.antlr.ast.IfExp; import org.python.antlr.ast.Import; import org.python.antlr.ast.ImportFrom; import org.python.antlr.ast.Index; import org.python.antlr.ast.Interactive; import org.python.antlr.ast.Lambda; import org.python.antlr.ast.List; import org.python.antlr.ast.ListComp; import org.python.antlr.ast.Name; import org.python.antlr.ast.Num; import org.python.antlr.ast.Pass; import org.python.antlr.ast.Print; import org.python.antlr.ast.Raise; import org.python.antlr.ast.Repr; import org.python.antlr.ast.Return; import org.python.antlr.ast.Slice; import org.python.antlr.ast.Str; import org.python.antlr.ast.Subscript; import org.python.antlr.ast.Suite; import org.python.antlr.ast.TryExcept; import org.python.antlr.ast.TryFinally; import org.python.antlr.ast.Tuple; import org.python.antlr.ast.UnaryOp; import org.python.antlr.ast.While; import org.python.antlr.ast.With; import org.python.antlr.ast.Yield; import org.python.antlr.ast.alias; import org.python.antlr.ast.cmpopType; import org.python.antlr.ast.comprehension; import org.python.antlr.ast.expr_contextType; import org.python.antlr.ast.keyword; import org.python.antlr.ast.operatorType; import org.python.antlr.base.expr; import org.python.antlr.base.mod; import org.python.antlr.base.stmt; import org.python.core.CompilerFlags; import org.python.core.ContextGuard; import org.python.core.ContextManager; import org.python.core.imp; import org.python.core.Py; import org.python.core.PyCode; import org.python.core.PyComplex; import org.python.core.PyDictionary; import org.python.core.PyException; import org.python.core.PyFloat; import org.python.core.PyFrame; import org.python.core.PyFunction; import org.python.core.PyInteger; import org.python.core.PyList; import org.python.core.PyLong; import org.python.core.PyObject; import org.python.core.PySlice; import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyUnicode; import org.python.core.ThreadState; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; import static org.python.util.CodegenUtils.*; public class CodeCompiler extends Visitor implements Opcodes, ClassConstants { private static final Object Exit = new Integer(1); private static final Object NoExit = null; private Module module; private Code code; private CompilerFlags cflags; private int temporary; private expr_contextType augmode; private int augtmp1; private int augtmp2; private int augtmp3; private int augtmp4; private boolean fast_locals, print_results; private Map<String, SymInfo> tbl; private ScopeInfo my_scope; private boolean optimizeGlobals = true; private String className; private Stack<Label> continueLabels, breakLabels; private Stack<ExceptionHandler> exceptionHandlers; private Vector<Label> yields = new Vector<Label>(); /* * break/continue finally's level. This is the lowest level in the * exceptionHandlers which should be executed at break or continue. It is * saved/updated/restored when compiling loops. A similar level for * returns is not needed because a new CodeCompiler is used for each * PyCode, in other words: each 'function'. When returning through * finally's all the exceptionHandlers are executed. */ private int bcfLevel = 0; private int yield_count = 0; private Stack<String> stack = new Stack<String>(); public CodeCompiler(Module module, boolean print_results) { this.module = module; this.print_results = print_results; continueLabels = new Stack<Label>(); breakLabels = new Stack<Label>(); exceptionHandlers = new Stack<ExceptionHandler>(); } public void getNone() throws IOException { code.getstatic(p(Py.class), "None", ci(PyObject.class)); } public void loadFrame() throws Exception { code.aload(1); } public void loadThreadState() throws Exception { code.aload(2); } public void setLastI(int idx) throws Exception { loadFrame(); code.iconst(idx); code.putfield(p(PyFrame.class), "f_lasti", "I"); } private void loadf_back() throws Exception { code.getfield(p(PyFrame.class), "f_back", ci(PyFrame.class)); } public int storeTop() throws Exception { int tmp = code.getLocal(p(PyObject.class)); code.astore(tmp); return tmp; } public void setline(int line) throws Exception { if (module.linenumbers) { code.setline(line); loadFrame(); code.iconst(line); code.invokevirtual(p(PyFrame.class), "setline", sig(Void.TYPE, Integer.TYPE)); } } public void setline(PythonTree node) throws Exception { setline(node.getLine()); } public void set(PythonTree node) throws Exception { int tmp = storeTop(); set(node, tmp); code.aconst_null(); code.astore(tmp); code.freeLocal(tmp); } public void set(PythonTree node, int tmp) throws Exception { temporary = tmp; visit(node); } private void saveAugTmps(PythonTree node, int count) throws Exception { if (count >= 4) { augtmp4 = code.getLocal(ci(PyObject.class)); code.astore(augtmp4); } if (count >= 3) { augtmp3 = code.getLocal(ci(PyObject.class)); code.astore(augtmp3); } if (count >= 2) { augtmp2 = code.getLocal(ci(PyObject.class)); code.astore(augtmp2); } augtmp1 = code.getLocal(ci(PyObject.class)); code.astore(augtmp1); code.aload(augtmp1); if (count >= 2) { code.aload(augtmp2); } if (count >= 3) { code.aload(augtmp3); } if (count >= 4) { code.aload(augtmp4); } } private void restoreAugTmps(PythonTree node, int count) throws Exception { code.aload(augtmp1); code.freeLocal(augtmp1); if (count == 1) { return; } code.aload(augtmp2); code.freeLocal(augtmp2); if (count == 2) { return; } code.aload(augtmp3); code.freeLocal(augtmp3); if (count == 3) { return; } code.aload(augtmp4); code.freeLocal(augtmp4); } static boolean checkOptimizeGlobals(boolean fast_locals, ScopeInfo scope) { return fast_locals && !scope.exec && !scope.from_import_star; } void parse(mod node, Code code, boolean fast_locals, String className, Str classDoc, boolean classBody, ScopeInfo scope, CompilerFlags cflags) throws Exception { this.fast_locals = fast_locals; this.className = className; this.code = code; this.cflags = cflags; this.my_scope = scope; this.tbl = scope.tbl; //BEGIN preparse if (classBody) { // Set the class's __module__ to __name__. fails when there's no __name__ loadFrame(); code.ldc("__module__"); loadFrame(); code.ldc("__name__"); code.invokevirtual(p(PyFrame.class), "getname", sig(PyObject.class, String.class)); code.invokevirtual(p(PyFrame.class), "setlocal", sig(Void.TYPE, String.class, PyObject.class)); if (classDoc != null) { loadFrame(); code.ldc("__doc__"); visit(classDoc); code.invokevirtual(p(PyFrame.class), "setlocal", sig(Void.TYPE, String.class, PyObject.class)); } } Label genswitch = new Label(); if (my_scope.generator) { code.goto_(genswitch); } Label start = new Label(); code.label(start); int nparamcell = my_scope.jy_paramcells.size(); if (nparamcell > 0) { java.util.List<String> paramcells = my_scope.jy_paramcells; for (int i = 0; i < nparamcell; i++) { code.aload(1); SymInfo syminf = tbl.get(paramcells.get(i)); code.iconst(syminf.locals_index); code.iconst(syminf.env_index); code.invokevirtual(p(PyFrame.class), "to_cell", sig(Void.TYPE, Integer.TYPE, Integer.TYPE)); } } //END preparse optimizeGlobals = checkOptimizeGlobals(fast_locals, my_scope); if (my_scope.max_with_count > 0) { // allocate for all the with-exits we will have in the frame; // this allows yield and with to happily co-exist loadFrame(); code.iconst(my_scope.max_with_count); code.anewarray(p(PyObject.class)); code.putfield(p(PyFrame.class), "f_exits", ci(PyObject[].class)); } Object exit = visit(node); if (classBody) { loadFrame(); code.invokevirtual(p(PyFrame.class), "getf_locals", sig(PyObject.class)); code.areturn(); } else { if (exit == null) { setLastI(-1); getNone(); code.areturn(); } } //BEGIN postparse // similar to visitResume code in pyasm.py if (my_scope.generator) { code.label(genswitch); code.aload(1); code.getfield(p(PyFrame.class), "f_lasti", "I"); Label[] y = new Label[yields.size() + 1]; y[0] = start; for (int i = 1; i < y.length; i++) { y[i] = yields.get(i - 1); } code.tableswitch(0, y.length - 1, start, y); } //END postparse } @Override public Object visitInteractive(Interactive node) throws Exception { traverse(node); return null; } @Override public Object visitModule(org.python.antlr.ast.Module suite) throws Exception { Str docStr = getDocStr(suite.getInternalBody()); if (docStr != null) { loadFrame(); code.ldc("__doc__"); visit(docStr); code.invokevirtual(p(PyFrame.class), "setglobal", sig(Void.TYPE, String.class, PyObject.class)); } traverse(suite); return null; } @Override public Object visitExpression(Expression node) throws Exception { if (my_scope.generator && node.getInternalBody() != null) { module.error("'return' with argument inside generator", true, node); } return visitReturn(new Return(node, node.getInternalBody()), true); } public int makeArray(java.util.List<? extends PythonTree> nodes) throws Exception { // XXX: This should produce an array on the stack (if possible) instead of a local // the caller is responsible for freeing. int n; if (nodes == null) { n = 0; } else { n = nodes.size(); } int array = code.getLocal(ci(PyObject[].class)); if (n == 0) { code.getstatic(p(Py.class), "EmptyObjects", ci(PyObject[].class)); code.astore(array); } else { code.iconst(n); code.anewarray(p(PyObject.class)); code.astore(array); for (int i = 0; i < n; i++) { visit(nodes.get(i)); code.aload(array); code.swap(); code.iconst(i); code.swap(); code.aastore(); } } return array; } // nulls out an array of references public void freeArray(int array) { code.aload(array); code.aconst_null(); code.invokestatic(p(Arrays.class), "fill", sig(Void.TYPE, Object[].class, Object.class)); code.freeLocal(array); } public void freeArrayRef(int array) { code.aconst_null(); code.astore(array); code.freeLocal(array); } public Str getDocStr(java.util.List<stmt> suite) { if (suite.size() > 0) { stmt stmt = suite.get(0); if (stmt instanceof Expr && ((Expr) stmt).getInternalValue() instanceof Str) { return (Str) ((Expr) stmt).getInternalValue(); } } return null; } public boolean makeClosure(ScopeInfo scope) throws Exception { if (scope == null || scope.freevars == null) { return false; } int n = scope.freevars.size(); if (n == 0) { return false; } int tmp = code.getLocal(ci(PyObject[].class)); code.iconst(n); code.anewarray(p(PyObject.class)); code.astore(tmp); Map<String, SymInfo> upTbl = scope.up.tbl; for (int i = 0; i < n; i++) { code.aload(tmp); code.iconst(i); loadFrame(); for (int j = 1; j < scope.distance; j++) { loadf_back(); } SymInfo symInfo = upTbl.get(scope.freevars.elementAt(i)); code.iconst(symInfo.env_index); code.invokevirtual(p(PyFrame.class), "getclosure", sig(PyObject.class, Integer.TYPE)); code.aastore(); } code.aload(tmp); code.freeLocal(tmp); return true; } @Override public Object visitFunctionDef(FunctionDef node) throws Exception { String name = getName(node.getInternalName()); setline(node); ScopeInfo scope = module.getScopeInfo(node); // NOTE: this is attached to the constructed PyFunction, so it cannot be nulled out // with freeArray, unlike other usages of makeArray here int defaults = makeArray(scope.ac.getDefaults()); code.new_(p(PyFunction.class)); code.dup(); loadFrame(); code.getfield(p(PyFrame.class), "f_globals", ci(PyObject.class)); code.aload(defaults); code.freeLocal(defaults); scope.setup_closure(); scope.dump(); module.codeConstant(new Suite(node, node.getInternalBody()), name, true, className, false, false, node.getLine(), scope, cflags).get(code); Str docStr = getDocStr(node.getInternalBody()); if (docStr != null) { visit(docStr); } else { code.aconst_null(); } if (!makeClosure(scope)) { code.invokespecial(p(PyFunction.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject[].class, PyCode.class, PyObject.class)); } else { code.invokespecial(p(PyFunction.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject[].class, PyCode.class, PyObject.class, PyObject[].class)); } applyDecorators(node.getInternalDecorator_list()); set(new Name(node, node.getInternalName(), expr_contextType.Store)); return null; } private void applyDecorators(java.util.List<expr> decorators) throws Exception { if (decorators != null && !decorators.isEmpty()) { int res = storeTop(); for (expr decorator : decorators) { visit(decorator); stackProduce(); } for (int i = decorators.size(); i > 0; i--) { stackConsume(); loadThreadState(); code.aload(res); code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class)); code.astore(res); } code.aload(res); code.freeLocal(res); } } @Override public Object visitExpr(Expr node) throws Exception { setline(node); visit(node.getInternalValue()); if (print_results) { code.invokestatic(p(Py.class), "printResult", sig(Void.TYPE, PyObject.class)); } else { code.pop(); } return null; } @Override public Object visitAssign(Assign node) throws Exception { setline(node); visit(node.getInternalValue()); if (node.getInternalTargets().size() == 1) { set(node.getInternalTargets().get(0)); } else { int tmp = storeTop(); for (expr target : node.getInternalTargets()) { set(target, tmp); } code.freeLocal(tmp); } return null; } @Override public Object visitPrint(Print node) throws Exception { setline(node); int tmp = -1; if (node.getInternalDest() != null) { visit(node.getInternalDest()); tmp = storeTop(); } if (node.getInternalValues() == null || node.getInternalValues().size() == 0) { if (node.getInternalDest() != null) { code.aload(tmp); code.invokestatic(p(Py.class), "printlnv", sig(Void.TYPE, PyObject.class)); } else { code.invokestatic(p(Py.class), "println", sig(Void.TYPE)); } } else { for (int i = 0; i < node.getInternalValues().size(); i++) { if (node.getInternalDest() != null) { code.aload(tmp); visit(node.getInternalValues().get(i)); if (node.getInternalNl() && i == node.getInternalValues().size() - 1) { code.invokestatic(p(Py.class), "println", sig(Void.TYPE, PyObject.class, PyObject.class)); } else { code.invokestatic(p(Py.class), "printComma", sig(Void.TYPE, PyObject.class, PyObject.class)); } } else { visit(node.getInternalValues().get(i)); if (node.getInternalNl() && i == node.getInternalValues().size() - 1) { code.invokestatic(p(Py.class), "println", sig(Void.TYPE, PyObject.class)); } else { code.invokestatic(p(Py.class), "printComma", sig(Void.TYPE, PyObject.class)); } } } } if (node.getInternalDest() != null) { code.freeLocal(tmp); } return null; } @Override public Object visitDelete(Delete node) throws Exception { setline(node); traverse(node); return null; } @Override public Object visitPass(Pass node) throws Exception { setline(node); return null; } @Override public Object visitBreak(Break node) throws Exception { //setline(node); Not needed here... if (breakLabels.empty()) { throw new ParseException("'break' outside loop", node); } doFinallysDownTo(bcfLevel); code.goto_(breakLabels.peek()); return null; } @Override public Object visitContinue(Continue node) throws Exception { //setline(node); Not needed here... if (continueLabels.empty()) { throw new ParseException("'continue' not properly in loop", node); } doFinallysDownTo(bcfLevel); code.goto_(continueLabels.peek()); return Exit; } @Override public Object visitYield(Yield node) throws Exception { setline(node); if (!fast_locals) { throw new ParseException("'yield' outside function", node); } int stackState = saveStack(); if (node.getInternalValue() != null) { visit(node.getInternalValue()); } else { getNone(); } setLastI(++yield_count); saveLocals(); code.areturn(); Label restart = new Label(); yields.addElement(restart); code.label(restart); restoreLocals(); restoreStack(stackState); loadFrame(); code.invokevirtual(p(PyFrame.class), "getGeneratorInput", sig(Object.class)); code.dup(); code.instanceof_(p(PyException.class)); Label done2 = new Label(); code.ifeq(done2); code.checkcast(p(Throwable.class)); code.athrow(); code.label(done2); code.checkcast(p(PyObject.class)); return null; } private void stackProduce() { stackProduce(p(PyObject.class)); } private void stackProduce(String signature) { stack.push(signature); } private void stackConsume() { stackConsume(1); } private void stackConsume(int numItems) { for (int i = 0; i < numItems; i++) { stack.pop(); } } private int saveStack() throws Exception { if (stack.size() > 0) { int array = code.getLocal(ci(Object[].class)); code.iconst(stack.size()); code.anewarray(p(Object.class)); code.astore(array); ListIterator<String> content = stack.listIterator(stack.size()); for (int i = 0; content.hasPrevious(); i++) { String signature = content.previous(); if (p(ThreadState.class).equals(signature)) { // Stack: ... threadstate code.pop(); // Stack: ... } else { code.aload(array); // Stack: |- ... value array code.swap(); code.iconst(i++); code.swap(); // Stack: |- ... array index value code.aastore(); // Stack: |- ... } } return array; } else { return -1; } } private void restoreStack(int array) throws Exception { if (stack.size() > 0) { int i = stack.size() - 1; for (String signature : stack) { if (p(ThreadState.class).equals(signature)) { loadThreadState(); } else { code.aload(array); // Stack: |- ... array code.iconst(i--); code.aaload(); // Stack: |- ... value code.checkcast(signature); } } code.freeLocal(array); } } private void restoreLocals() throws Exception { endExceptionHandlers(); Vector<String> v = code.getActiveLocals(); loadFrame(); code.getfield(p(PyFrame.class), "f_savedlocals", ci(Object[].class)); int locals = code.getLocal(ci(Object[].class)); code.astore(locals); for (int i = 0; i < v.size(); i++) { String type = v.elementAt(i); if (type == null) { continue; } code.aload(locals); code.iconst(i); code.aaload(); code.checkcast(type); code.astore(i); } code.freeLocal(locals); restartExceptionHandlers(); } /** * Close all the open exception handler ranges. This should be paired * with restartExceptionHandlers to delimit internal code that * shouldn't be handled by user handlers. This allows us to set * variables without the verifier thinking we might jump out of our * handling with an exception. */ private void endExceptionHandlers() { Label end = new Label(); code.label(end); for (int i = 0; i < exceptionHandlers.size(); ++i) { ExceptionHandler handler = exceptionHandlers.elementAt(i); handler.exceptionEnds.addElement(end); } } private void restartExceptionHandlers() { Label start = new Label(); code.label(start); for (int i = 0; i < exceptionHandlers.size(); ++i) { ExceptionHandler handler = exceptionHandlers.elementAt(i); handler.exceptionStarts.addElement(start); } } private void saveLocals() throws Exception { Vector<String> v = code.getActiveLocals(); code.iconst(v.size()); code.anewarray(p(Object.class)); int locals = code.getLocal(ci(Object[].class)); code.astore(locals); for (int i = 0; i < v.size(); i++) { String type = v.elementAt(i); if (type == null) { continue; } code.aload(locals); code.iconst(i); //code.checkcast(code.pool.Class(p(Object.class))); if (i == 2222) { code.aconst_null(); } else { code.aload(i); } code.aastore(); } loadFrame(); code.aload(locals); code.putfield(p(PyFrame.class), "f_savedlocals", ci(Object[].class)); code.freeLocal(locals); } @Override public Object visitReturn(Return node) throws Exception { return visitReturn(node, false); } public Object visitReturn(Return node, boolean inEval) throws Exception { setline(node); if (!inEval && !fast_locals) { throw new ParseException("'return' outside function", node); } int tmp = 0; if (node.getInternalValue() != null) { if (my_scope.generator) { throw new ParseException("'return' with argument " + "inside generator", node); } visit(node.getInternalValue()); tmp = code.getReturnLocal(); code.astore(tmp); } doFinallysDownTo(0); setLastI(-1); if (node.getInternalValue() != null) { code.aload(tmp); } else { getNone(); } code.areturn(); return Exit; } @Override public Object visitRaise(Raise node) throws Exception { setline(node); if (node.getInternalType() != null) { visit(node.getInternalType()); stackProduce(); } if (node.getInternalInst() != null) { visit(node.getInternalInst()); stackProduce(); } if (node.getInternalTback() != null) { visit(node.getInternalTback()); stackProduce(); } if (node.getInternalType() == null) { code.invokestatic(p(Py.class), "makeException", sig(PyException.class)); } else if (node.getInternalInst() == null) { stackConsume(); code.invokestatic(p(Py.class), "makeException", sig(PyException.class, PyObject.class)); } else if (node.getInternalTback() == null) { stackConsume(2); code.invokestatic(p(Py.class), "makeException", sig(PyException.class, PyObject.class, PyObject.class)); } else { stackConsume(3); code.invokestatic(p(Py.class), "makeException", sig(PyException.class, PyObject.class, PyObject.class, PyObject.class)); } code.athrow(); return Exit; } /** * Push the import level <code>0</code> or <code>-1</code>. */ private void defaultImportLevel() { // already prepared for a future change of DEFAULT_LEVEL if (module.getFutures().isAbsoluteImportOn() || imp.DEFAULT_LEVEL == 0) { code.iconst_0(); } else { code.iconst_m1(); } } @Override public Object visitImport(Import node) throws Exception { setline(node); for (alias a : node.getInternalNames()) { String asname = null; if (a.getInternalAsname() != null) { String name = a.getInternalName(); asname = a.getInternalAsname(); code.ldc(name); loadFrame(); defaultImportLevel(); code.invokestatic(p(imp.class), "importOneAs", sig(PyObject.class, String.class, PyFrame.class, Integer.TYPE)); } else { String name = a.getInternalName(); asname = name; if (asname.indexOf('.') > 0) { asname = asname.substring(0, asname.indexOf('.')); } code.ldc(name); loadFrame(); defaultImportLevel(); code.invokestatic(p(imp.class), "importOne", sig(PyObject.class, String.class, PyFrame.class, Integer.TYPE)); } set(new Name(a, asname, expr_contextType.Store)); } return null; } @Override public Object visitImportFrom(ImportFrom node) throws Exception { Future.checkFromFuture(node); // future stmt support setline(node); code.ldc(node.getInternalModule()); java.util.List<alias> aliases = node.getInternalNames(); if (aliases == null || aliases.size() == 0) { throw new ParseException("Internel parser error", node); } else if (aliases.size() == 1 && aliases.get(0).getInternalName().equals("*")) { if (node.getInternalLevel() > 0) { throw new ParseException("'import *' not allowed with 'from .'", node); } if (my_scope.func_level > 0) { module.error("import * only allowed at module level", false, node); if (my_scope.contains_ns_free_vars) { module.error("import * is not allowed in function '" + my_scope.scope_name + "' because it contains a nested function with free variables", true, node); } } if (my_scope.func_level > 1) { module.error("import * is not allowed in function '" + my_scope.scope_name + "' because it is a nested function", true, node); } loadFrame(); defaultImportLevel(); code.invokestatic(p(imp.class), "importAll", sig(Void.TYPE, String.class, PyFrame.class, Integer.TYPE)); } else { java.util.List<String> fromNames = new ArrayList<String>();//[names.size()]; java.util.List<String> asnames = new ArrayList<String>();//[names.size()]; for (int i = 0; i < aliases.size(); i++) { fromNames.add(aliases.get(i).getInternalName()); asnames.add(aliases.get(i).getInternalAsname()); if (asnames.get(i) == null) { asnames.set(i, fromNames.get(i)); } } int strArray = makeStrings(code, fromNames); code.aload(strArray); code.freeLocal(strArray); loadFrame(); if (node.getInternalLevel() == 0) { defaultImportLevel(); } else { code.iconst(node.getInternalLevel()); } code.invokestatic(p(imp.class), "importFrom", sig(PyObject[].class, String.class, String[].class, PyFrame.class, Integer.TYPE)); int tmp = storeTop(); for (int i = 0; i < aliases.size(); i++) { code.aload(tmp); code.iconst(i); code.aaload(); set(new Name(aliases.get(i), asnames.get(i), expr_contextType.Store)); } code.freeLocal(tmp); } return null; } @Override public Object visitGlobal(Global node) throws Exception { return null; } @Override public Object visitExec(Exec node) throws Exception { setline(node); visit(node.getInternalBody()); stackProduce(); if (node.getInternalGlobals() != null) { visit(node.getInternalGlobals()); } else { code.aconst_null(); } stackProduce(); if (node.getInternalLocals() != null) { visit(node.getInternalLocals()); } else { code.aconst_null(); } stackProduce(); //do the real work here stackConsume(3); code.invokestatic(p(Py.class), "exec", sig(Void.TYPE, PyObject.class, PyObject.class, PyObject.class)); return null; } @Override public Object visitAssert(Assert node) throws Exception { setline(node); Label end_of_assert = new Label(); /* First do an if __debug__: */ loadFrame(); emitGetGlobal("__debug__"); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); code.ifeq(end_of_assert); /* Now do the body of the assert. If PyObject.__nonzero__ is true, then the assertion succeeded, the message portion should not be processed. Otherwise, the message will be processed. */ visit(node.getInternalTest()); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); /* If evaluation is false, then branch to end of method */ code.ifne(end_of_assert); /* Visit the message part of the assertion, or pass Py.None */ if (node.getInternalMsg() != null) { visit(node.getInternalMsg()); } else { getNone(); } /* Push exception type onto stack(AssertionError) */ loadFrame(); emitGetGlobal("AssertionError"); code.swap(); // The type is the first argument, but the message could be a yield code.invokestatic(p(Py.class), "makeException", sig(PyException.class, PyObject.class, PyObject.class)); /* Raise assertion error. Only executes this logic if assertion failed */ code.athrow(); /* And finally set the label for the end of it all */ code.label(end_of_assert); return null; } public Object doTest(Label end_of_if, If node, int index) throws Exception { Label end_of_suite = new Label(); setline(node.getInternalTest()); visit(node.getInternalTest()); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); code.ifeq(end_of_suite); Object exit = suite(node.getInternalBody()); if (end_of_if != null && exit == null) { code.goto_(end_of_if); } code.label(end_of_suite); if (node.getInternalOrelse() != null) { return suite(node.getInternalOrelse()) != null ? exit : null; } else { return null; } } @Override public Object visitIf(If node) throws Exception { Label end_of_if = null; if (node.getInternalOrelse() != null) { end_of_if = new Label(); } Object exit = doTest(end_of_if, node, 0); if (end_of_if != null) { code.label(end_of_if); } return exit; } @Override public Object visitIfExp(IfExp node) throws Exception { setline(node.getInternalTest()); Label end = new Label(); Label end_of_else = new Label(); visit(node.getInternalTest()); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); code.ifeq(end_of_else); visit(node.getInternalBody()); code.goto_(end); code.label(end_of_else); visit(node.getInternalOrelse()); code.label(end); return null; } public int beginLoop() { continueLabels.push(new Label()); breakLabels.push(new Label()); int savebcf = bcfLevel; bcfLevel = exceptionHandlers.size(); return savebcf; } public void finishLoop(int savebcf) { continueLabels.pop(); breakLabels.pop(); bcfLevel = savebcf; } @Override public Object visitWhile(While node) throws Exception { int savebcf = beginLoop(); Label continue_loop = continueLabels.peek(); Label break_loop = breakLabels.peek(); Label start_loop = new Label(); code.goto_(continue_loop); code.label(start_loop); //Do suite suite(node.getInternalBody()); code.label(continue_loop); setline(node); //Do test visit(node.getInternalTest()); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); code.ifne(start_loop); finishLoop(savebcf); if (node.getInternalOrelse() != null) { //Do else suite(node.getInternalOrelse()); } code.label(break_loop); // Probably need to detect "guaranteed exits" return null; } @Override public Object visitFor(For node) throws Exception { int savebcf = beginLoop(); Label continue_loop = continueLabels.peek(); Label break_loop = breakLabels.peek(); Label start_loop = new Label(); Label next_loop = new Label(); setline(node); //parse the list visit(node.getInternalIter()); int iter_tmp = code.getLocal(p(PyObject.class)); int expr_tmp = code.getLocal(p(PyObject.class)); //set up the loop iterator code.invokevirtual(p(PyObject.class), "__iter__", sig(PyObject.class)); code.astore(iter_tmp); //do check at end of loop. Saves one opcode ;-) code.goto_(next_loop); code.label(start_loop); //set iter variable to current entry in list set(node.getInternalTarget(), expr_tmp); //evaluate for body suite(node.getInternalBody()); code.label(continue_loop); code.label(next_loop); setline(node); //get the next element from the list code.aload(iter_tmp); code.invokevirtual(p(PyObject.class), "__iternext__", sig(PyObject.class)); code.astore(expr_tmp); code.aload(expr_tmp); //if no more elements then fall through code.ifnonnull(start_loop); finishLoop(savebcf); if (node.getInternalOrelse() != null) { //Do else clause if provided suite(node.getInternalOrelse()); } code.label(break_loop); code.freeLocal(iter_tmp); code.freeLocal(expr_tmp); // Probably need to detect "guaranteed exits" return null; } public void exceptionTest(int exc, Label end_of_exceptions, TryExcept node, int index) throws Exception { for (int i = 0; i < node.getInternalHandlers().size(); i++) { ExceptHandler handler = (ExceptHandler) node.getInternalHandlers().get(i); //setline(name); Label end_of_self = new Label(); if (handler.getInternalType() != null) { code.aload(exc); //get specific exception visit(handler.getInternalType()); code.invokevirtual(p(PyException.class), "match", sig(Boolean.TYPE, PyObject.class)); code.ifeq(end_of_self); } else { if (i != node.getInternalHandlers().size() - 1) { throw new ParseException( "default 'except:' must be last", handler); } } if (handler.getInternalName() != null) { code.aload(exc); code.getfield(p(PyException.class), "value", ci(PyObject.class)); set(handler.getInternalName()); } //do exception body suite(handler.getInternalBody()); code.goto_(end_of_exceptions); code.label(end_of_self); } code.aload(exc); code.athrow(); } @Override public Object visitTryFinally(TryFinally node) throws Exception { Label start = new Label(); Label end = new Label(); Label handlerStart = new Label(); Label finallyEnd = new Label(); Object ret; ExceptionHandler inFinally = new ExceptionHandler(node); // Do protected suite exceptionHandlers.push(inFinally); int excLocal = code.getLocal(p(Throwable.class)); code.aconst_null(); code.astore(excLocal); code.label(start); inFinally.exceptionStarts.addElement(start); ret = suite(node.getInternalBody()); code.label(end); inFinally.exceptionEnds.addElement(end); inFinally.bodyDone = true; exceptionHandlers.pop(); if (ret == NoExit) { inlineFinally(inFinally); code.goto_(finallyEnd); } // Handle any exceptions that get thrown in suite code.label(handlerStart); code.astore(excLocal); code.aload(excLocal); loadFrame(); code.invokestatic(p(Py.class), "addTraceback", sig(Void.TYPE, Throwable.class, PyFrame.class)); inlineFinally(inFinally); code.aload(excLocal); code.checkcast(p(Throwable.class)); code.athrow(); code.label(finallyEnd); code.freeLocal(excLocal); inFinally.addExceptionHandlers(handlerStart); // According to any JVM verifiers, this code block might not return return null; } private void inlineFinally(ExceptionHandler handler) throws Exception { if (!handler.bodyDone) { // end the previous exception block so inlined finally code doesn't // get covered by our exception handler. Label end = new Label(); code.label(end); handler.exceptionEnds.addElement(end); // also exiting the try: portion of this particular finally } if (handler.isFinallyHandler()) { handler.finalBody(this); } } private void reenterProtectedBody(ExceptionHandler handler) throws Exception { // restart exception coverage Label restart = new Label(); code.label(restart); handler.exceptionStarts.addElement(restart); } /** * Inline the finally handling code for levels down to the levelth parent * (0 means all). This takes care to avoid having more nested finallys * catch exceptions throw by the parent finally code. This also pops off * all the handlers above level temporarily. */ private void doFinallysDownTo(int level) throws Exception { Stack<ExceptionHandler> poppedHandlers = new Stack<ExceptionHandler>(); while (exceptionHandlers.size() > level) { ExceptionHandler handler = exceptionHandlers.pop(); inlineFinally(handler); poppedHandlers.push(handler); } while (poppedHandlers.size() > 0) { ExceptionHandler handler = poppedHandlers.pop(); reenterProtectedBody(handler); exceptionHandlers.push(handler); } } @Override public Object visitTryExcept(TryExcept node) throws Exception { Label start = new Label(); Label end = new Label(); Label handler_start = new Label(); Label handler_end = new Label(); ExceptionHandler handler = new ExceptionHandler(); code.label(start); handler.exceptionStarts.addElement(start); exceptionHandlers.push(handler); //Do suite Object exit = suite(node.getInternalBody()); exceptionHandlers.pop(); code.label(end); handler.exceptionEnds.addElement(end); if (exit == null) { code.goto_(handler_end); } code.label(handler_start); loadFrame(); code.invokestatic(p(Py.class), "setException", sig(PyException.class, Throwable.class, PyFrame.class)); int exc = code.getFinallyLocal(p(Throwable.class)); code.astore(exc); if (node.getInternalOrelse() == null) { //No else clause to worry about exceptionTest(exc, handler_end, node, 1); code.label(handler_end); } else { //Have else clause Label else_end = new Label(); exceptionTest(exc, else_end, node, 1); code.label(handler_end); //do else clause suite(node.getInternalOrelse()); code.label(else_end); } code.freeFinallyLocal(exc); handler.addExceptionHandlers(handler_start); return null; } @Override public Object visitSuite(Suite node) throws Exception { return suite(node.getInternalBody()); } public Object suite(java.util.List<stmt> stmts) throws Exception { for (stmt s : stmts) { Object exit = visit(s); if (exit != null) { return Exit; } } return null; } @Override public Object visitBoolOp(BoolOp node) throws Exception { Label end = new Label(); visit(node.getInternalValues().get(0)); for (int i = 1; i < node.getInternalValues().size(); i++) { code.dup(); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); switch (node.getInternalOp()) { case Or: code.ifne(end); break; case And: code.ifeq(end); break; } code.pop(); visit(node.getInternalValues().get(i)); } code.label(end); return null; } @Override public Object visitCompare(Compare node) throws Exception { int last = code.getLocal(p(PyObject.class)); int result = code.getLocal(p(PyObject.class)); Label end = new Label(); visit(node.getInternalLeft()); code.astore(last); int n = node.getInternalOps().size(); for (int i = 0; i < n - 1; i++) { visit(node.getInternalComparators().get(i)); code.aload(last); code.swap(); code.dup(); code.astore(last); visitCmpop(node.getInternalOps().get(i)); code.dup(); code.astore(result); code.invokevirtual(p(PyObject.class), "__nonzero__", sig(Boolean.TYPE)); code.ifeq(end); } visit(node.getInternalComparators().get(n - 1)); code.aload(last); code.swap(); visitCmpop(node.getInternalOps().get(n - 1)); if (n > 1) { code.astore(result); code.label(end); code.aload(result); } code.aconst_null(); code.astore(last); code.freeLocal(last); code.freeLocal(result); return null; } public void visitCmpop(cmpopType op) throws Exception { String name = null; switch (op) { case Eq: name = "_eq"; break; case NotEq: name = "_ne"; break; case Lt: name = "_lt"; break; case LtE: name = "_le"; break; case Gt: name = "_gt"; break; case GtE: name = "_ge"; break; case Is: name = "_is"; break; case IsNot: name = "_isnot"; break; case In: name = "_in"; break; case NotIn: name = "_notin"; break; } code.invokevirtual(p(PyObject.class), name, sig(PyObject.class, PyObject.class)); } @Override public Object visitBinOp(BinOp node) throws Exception { visit(node.getInternalLeft()); stackProduce(); visit(node.getInternalRight()); stackConsume(); String name = null; switch (node.getInternalOp()) { case Add: name = "_add"; break; case Sub: name = "_sub"; break; case Mult: name = "_mul"; break; case Div: name = "_div"; break; case Mod: name = "_mod"; break; case Pow: name = "_pow"; break; case LShift: name = "_lshift"; break; case RShift: name = "_rshift"; break; case BitOr: name = "_or"; break; case BitXor: name = "_xor"; break; case BitAnd: name = "_and"; break; case FloorDiv: name = "_floordiv"; break; } if (node.getInternalOp() == operatorType.Div && module.getFutures().areDivisionOn()) { name = "_truediv"; } code.invokevirtual(p(PyObject.class), name, sig(PyObject.class, PyObject.class)); return null; } @Override public Object visitUnaryOp(UnaryOp node) throws Exception { visit(node.getInternalOperand()); String name = null; switch (node.getInternalOp()) { case Invert: name = "__invert__"; break; case Not: name = "__not__"; break; case UAdd: name = "__pos__"; break; case USub: name = "__neg__"; break; } code.invokevirtual(p(PyObject.class), name, sig(PyObject.class)); return null; } @Override public Object visitAugAssign(AugAssign node) throws Exception { setline(node); augmode = expr_contextType.Load; visit(node.getInternalTarget()); int target = storeTop(); visit(node.getInternalValue()); code.aload(target); code.swap(); String name = null; switch (node.getInternalOp()) { case Add: name = "_iadd"; break; case Sub: name = "_isub"; break; case Mult: name = "_imul"; break; case Div: name = "_idiv"; break; case Mod: name = "_imod"; break; case Pow: name = "_ipow"; break; case LShift: name = "_ilshift"; break; case RShift: name = "_irshift"; break; case BitOr: name = "_ior"; break; case BitXor: name = "_ixor"; break; case BitAnd: name = "_iand"; break; case FloorDiv: name = "_ifloordiv"; break; } if (node.getInternalOp() == operatorType.Div && module.getFutures().areDivisionOn()) { name = "_itruediv"; } code.invokevirtual(p(PyObject.class), name, sig(PyObject.class, PyObject.class)); code.freeLocal(target); temporary = storeTop(); augmode = expr_contextType.Store; visit(node.getInternalTarget()); code.freeLocal(temporary); return null; } static int makeStrings(Code c, Collection<String> names) throws IOException { if (names != null) { c.iconst(names.size()); } else { c.iconst_0(); } c.anewarray(p(String.class)); int strings = c.getLocal(ci(String[].class)); c.astore(strings); if (names != null) { int i = 0; for (String name : names) { c.aload(strings); c.iconst(i); c.ldc(name); c.aastore(); i++; } } return strings; } public Object invokeNoKeywords(Attribute node, java.util.List<expr> values) throws Exception { String name = getName(node.getInternalAttr()); visit(node.getInternalValue()); stackProduce(); code.ldc(name); code.invokevirtual(p(PyObject.class), "__getattr__", sig(PyObject.class, String.class)); loadThreadState(); stackProduce(p(ThreadState.class)); switch (values.size()) { case 0: stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class)); break; case 1: visit(values.get(0)); stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class)); break; case 2: visit(values.get(0)); stackProduce(); visit(values.get(1)); stackConsume(3); // target + ts + arguments code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class, PyObject.class)); break; case 3: visit(values.get(0)); stackProduce(); visit(values.get(1)); stackProduce(); visit(values.get(2)); stackConsume(4); // target + ts + arguments code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class, PyObject.class, PyObject.class)); break; case 4: visit(values.get(0)); stackProduce(); visit(values.get(1)); stackProduce(); visit(values.get(2)); stackProduce(); visit(values.get(3)); stackConsume(5); // target + ts + arguments code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class, PyObject.class, PyObject.class, PyObject.class)); break; default: int argArray = makeArray(values); code.aload(argArray); code.freeLocal(argArray); stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject[].class)); break; } return null; } @Override public Object visitCall(Call node) throws Exception { java.util.List<String> keys = new ArrayList<String>(); java.util.List<expr> values = new ArrayList<expr>(); for (int i = 0; i < node.getInternalArgs().size(); i++) { values.add(node.getInternalArgs().get(i)); } for (int i = 0; i < node.getInternalKeywords().size(); i++) { keys.add(node.getInternalKeywords().get(i).getInternalArg()); values.add(node.getInternalKeywords().get(i).getInternalValue()); } if ((node.getInternalKeywords() == null || node.getInternalKeywords().size() == 0) && node.getInternalStarargs() == null && node.getInternalKwargs() == null && node.getInternalFunc() instanceof Attribute) { return invokeNoKeywords((Attribute) node.getInternalFunc(), values); } visit(node.getInternalFunc()); stackProduce(); if (node.getInternalStarargs() != null || node.getInternalKwargs() != null) { int argArray = makeArray(values); int strArray = makeStrings(code, keys); if (node.getInternalStarargs() == null) { code.aconst_null(); } else { visit(node.getInternalStarargs()); } stackProduce(); if (node.getInternalKwargs() == null) { code.aconst_null(); } else { visit(node.getInternalKwargs()); } stackProduce(); code.aload(argArray); code.aload(strArray); code.freeLocal(strArray); code.dup2_x2(); code.pop2(); stackConsume(3); // target + starargs + kwargs code.invokevirtual(p(PyObject.class), "_callextra", sig(PyObject.class, PyObject[].class, String[].class, PyObject.class, PyObject.class)); freeArrayRef(argArray); } else if (keys.size() > 0) { loadThreadState(); stackProduce(p(ThreadState.class)); int argArray = makeArray(values); int strArray = makeStrings(code, keys); code.aload(argArray); code.aload(strArray); code.freeLocal(strArray); stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject[].class, String[].class)); freeArrayRef(argArray); } else { loadThreadState(); stackProduce(p(ThreadState.class)); switch (values.size()) { case 0: stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class)); break; case 1: visit(values.get(0)); stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class)); break; case 2: visit(values.get(0)); stackProduce(); visit(values.get(1)); stackConsume(3); // target + ts + arguments code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class, PyObject.class)); break; case 3: visit(values.get(0)); stackProduce(); visit(values.get(1)); stackProduce(); visit(values.get(2)); stackConsume(4); // target + ts + arguments code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class, PyObject.class, PyObject.class)); break; case 4: visit(values.get(0)); stackProduce(); visit(values.get(1)); stackProduce(); visit(values.get(2)); stackProduce(); visit(values.get(3)); stackConsume(5); // target + ts + arguments code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class, PyObject.class, PyObject.class, PyObject.class)); break; default: int argArray = makeArray(values); code.aload(argArray); code.freeLocal(argArray); stackConsume(2); // target + ts code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject[].class)); break; } } return null; } public Object Slice(Subscript node, Slice slice) throws Exception { expr_contextType ctx = node.getInternalCtx(); if (ctx == expr_contextType.AugStore && augmode == expr_contextType.Store) { restoreAugTmps(node, 4); ctx = expr_contextType.Store; } else { visit(node.getInternalValue()); stackProduce(); if (slice.getInternalLower() != null) { visit(slice.getInternalLower()); } else { code.aconst_null(); } stackProduce(); if (slice.getInternalUpper() != null) { visit(slice.getInternalUpper()); } else { code.aconst_null(); } stackProduce(); if (slice.getInternalStep() != null) { visit(slice.getInternalStep()); } else { code.aconst_null(); } stackProduce(); if (node.getInternalCtx() == expr_contextType.AugStore && augmode == expr_contextType.Load) { saveAugTmps(node, 4); ctx = expr_contextType.Load; } stackConsume(4); } switch (ctx) { case Del: code.invokevirtual(p(PyObject.class), "__delslice__", sig(Void.TYPE, PyObject.class, PyObject.class, PyObject.class)); return null; case Load: code.invokevirtual(p(PyObject.class), "__getslice__", sig(PyObject.class, PyObject.class, PyObject.class, PyObject.class)); return null; case Param: case Store: code.aload(temporary); code.invokevirtual(p(PyObject.class), "__setslice__", sig(Void.TYPE, PyObject.class, PyObject.class, PyObject.class, PyObject.class)); return null; } return null; } @Override public Object visitSubscript(Subscript node) throws Exception { if (node.getInternalSlice() instanceof Slice) { return Slice(node, (Slice) node.getInternalSlice()); } int value = temporary; expr_contextType ctx = node.getInternalCtx(); if (node.getInternalCtx() == expr_contextType.AugStore && augmode == expr_contextType.Store) { restoreAugTmps(node, 2); ctx = expr_contextType.Store; } else { visit(node.getInternalValue()); stackProduce(); visit(node.getInternalSlice()); stackConsume(); if (node.getInternalCtx() == expr_contextType.AugStore && augmode == expr_contextType.Load) { saveAugTmps(node, 2); ctx = expr_contextType.Load; } } switch (ctx) { case Del: code.invokevirtual(p(PyObject.class), "__delitem__", sig(Void.TYPE, PyObject.class)); return null; case Load: code.invokevirtual(p(PyObject.class), "__getitem__", sig(PyObject.class, PyObject.class)); return null; case Param: case Store: code.aload(value); code.invokevirtual(p(PyObject.class), "__setitem__", sig(Void.TYPE, PyObject.class, PyObject.class)); return null; } return null; } @Override public Object visitIndex(Index node) throws Exception { traverse(node); return null; } @Override public Object visitExtSlice(ExtSlice node) throws Exception { int dims = makeArray(node.getInternalDims()); code.new_(p(PyTuple.class)); code.dup(); code.aload(dims); code.invokespecial(p(PyTuple.class), "<init>", sig(Void.TYPE, PyObject[].class)); freeArray(dims); return null; } @Override public Object visitAttribute(Attribute node) throws Exception { expr_contextType ctx = node.getInternalCtx(); if (node.getInternalCtx() == expr_contextType.AugStore && augmode == expr_contextType.Store) { restoreAugTmps(node, 2); ctx = expr_contextType.Store; } else { visit(node.getInternalValue()); code.ldc(getName(node.getInternalAttr())); if (node.getInternalCtx() == expr_contextType.AugStore && augmode == expr_contextType.Load) { saveAugTmps(node, 2); ctx = expr_contextType.Load; } } switch (ctx) { case Del: code.invokevirtual(p(PyObject.class), "__delattr__", sig(Void.TYPE, String.class)); return null; case Load: code.invokevirtual(p(PyObject.class), "__getattr__", sig(PyObject.class, String.class)); return null; case Param: case Store: code.aload(temporary); code.invokevirtual(p(PyObject.class), "__setattr__", sig(Void.TYPE, String.class, PyObject.class)); return null; } return null; } public Object seqSet(java.util.List<expr> nodes) throws Exception { code.aload(temporary); code.iconst(nodes.size()); code.invokestatic(p(Py.class), "unpackSequence", sig(PyObject[].class, PyObject.class, Integer.TYPE)); int tmp = code.getLocal("[org/python/core/PyObject"); code.astore(tmp); for (int i = 0; i < nodes.size(); i++) { code.aload(tmp); code.iconst(i); code.aaload(); set(nodes.get(i)); } code.freeLocal(tmp); return null; } public Object seqDel(java.util.List<expr> nodes) throws Exception { for (expr e : nodes) { visit(e); } return null; } @Override public Object visitTuple(Tuple node) throws Exception { if (node.getInternalCtx() == expr_contextType.Store) { return seqSet(node.getInternalElts()); } if (node.getInternalCtx() == expr_contextType.Del) { return seqDel(node.getInternalElts()); } int content = makeArray(node.getInternalElts()); code.new_(p(PyTuple.class)); code.dup(); code.aload(content); code.invokespecial(p(PyTuple.class), "<init>", sig(Void.TYPE, PyObject[].class)); freeArray(content); return null; } @Override public Object visitList(List node) throws Exception { if (node.getInternalCtx() == expr_contextType.Store) { return seqSet(node.getInternalElts()); } if (node.getInternalCtx() == expr_contextType.Del) { return seqDel(node.getInternalElts()); } int content = makeArray(node.getInternalElts()); code.new_(p(PyList.class)); code.dup(); code.aload(content); code.invokespecial(p(PyList.class), "<init>", sig(Void.TYPE, PyObject[].class)); freeArray(content); return null; } @Override public Object visitListComp(ListComp node) throws Exception { code.new_(p(PyList.class)); code.dup(); code.invokespecial(p(PyList.class), "<init>", sig(Void.TYPE)); code.dup(); code.ldc("append"); code.invokevirtual(p(PyObject.class), "__getattr__", sig(PyObject.class, String.class)); String tmp_append = "_[" + node.getLine() + "_" + node.getCharPositionInLine() + "]"; set(new Name(node, tmp_append, expr_contextType.Store)); java.util.List<expr> args = new ArrayList<expr>(); args.add(node.getInternalElt()); stmt n = new Expr(node, new Call(node, new Name(node, tmp_append, expr_contextType.Load), args, new ArrayList<keyword>(), null, null)); for (int i = node.getInternalGenerators().size() - 1; i >= 0; i--) { comprehension lc = node.getInternalGenerators().get(i); for (int j = lc.getInternalIfs().size() - 1; j >= 0; j--) { java.util.List<stmt> body = new ArrayList<stmt>(); body.add(n); n = new If(lc.getInternalIfs().get(j), lc.getInternalIfs().get(j), body, new ArrayList<stmt>()); } java.util.List<stmt> body = new ArrayList<stmt>(); body.add(n); n = new For(lc, lc.getInternalTarget(), lc.getInternalIter(), body, new ArrayList<stmt>()); } visit(n); java.util.List<expr> targets = new ArrayList<expr>(); targets.add(new Name(n, tmp_append, expr_contextType.Del)); visit(new Delete(n, targets)); return null; } @Override public Object visitDict(Dict node) throws Exception { java.util.List<PythonTree> elts = new ArrayList<PythonTree>(); for (int i = 0; i < node.getInternalKeys().size(); i++) { elts.add(node.getInternalKeys().get(i)); elts.add(node.getInternalValues().get(i)); } int content = makeArray(elts); code.new_(p(PyDictionary.class)); code.dup(); code.aload(content); code.invokespecial(p(PyDictionary.class), "<init>", sig(Void.TYPE, PyObject[].class)); freeArray(content); return null; } @Override public Object visitRepr(Repr node) throws Exception { visit(node.getInternalValue()); code.invokevirtual(p(PyObject.class), "__repr__", sig(PyString.class)); return null; } @Override public Object visitLambda(Lambda node) throws Exception { String name = "<lambda>"; //Add a return node onto the outside of suite; java.util.List<stmt> bod = new ArrayList<stmt>(); bod.add(new Return(node, node.getInternalBody())); mod retSuite = new Suite(node, bod); setline(node); ScopeInfo scope = module.getScopeInfo(node); int defaultsArray = makeArray(scope.ac.getDefaults()); code.new_(p(PyFunction.class)); code.dup(); code.aload(defaultsArray); code.freeLocal(defaultsArray); loadFrame(); code.getfield(p(PyFrame.class), "f_globals", ci(PyObject.class)); code.swap(); scope.setup_closure(); scope.dump(); module.codeConstant(retSuite, name, true, className, false, false, node.getLine(), scope, cflags).get(code); if (!makeClosure(scope)) { code.invokespecial(p(PyFunction.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject[].class, PyCode.class)); } else { code.invokespecial(p(PyFunction.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject[].class, PyCode.class, PyObject[].class)); } return null; } @Override public Object visitEllipsis(Ellipsis node) throws Exception { code.getstatic(p(Py.class), "Ellipsis", ci(PyObject.class)); return null; } @Override public Object visitSlice(Slice node) throws Exception { if (node.getInternalLower() == null) { getNone(); } else { visit(node.getInternalLower()); } stackProduce(); if (node.getInternalUpper() == null) { getNone(); } else { visit(node.getInternalUpper()); } stackProduce(); if (node.getInternalStep() == null) { getNone(); } else { visit(node.getInternalStep()); } int step = storeTop(); stackConsume(2); code.new_(p(PySlice.class)); code.dup(); code.dup2_x2(); code.pop2(); code.aload(step); code.freeLocal(step); code.invokespecial(p(PySlice.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject.class, PyObject.class)); return null; } @Override public Object visitClassDef(ClassDef node) throws Exception { setline(node); int baseArray = makeArray(node.getInternalBases()); //Get class name String name = getName(node.getInternalName()); code.ldc(name); code.aload(baseArray); ScopeInfo scope = module.getScopeInfo(node); scope.setup_closure(); scope.dump(); //Make code object out of suite module.codeConstant(new Suite(node, node.getInternalBody()), name, false, name, getDocStr(node.getInternalBody()), true, false, node.getLine(), scope, cflags).get(code); //Make class out of name, bases, and code if (!makeClosure(scope)) { code.invokestatic(p(Py.class), "makeClass", sig(PyObject.class, String.class, PyObject[].class, PyCode.class)); } else { code.invokestatic(p(Py.class), "makeClass", sig(PyObject.class, String.class, PyObject[].class, PyCode.class, PyObject[].class)); } applyDecorators(node.getInternalDecorator_list()); //Assign this new class to the given name set(new Name(node, node.getInternalName(), expr_contextType.Store)); freeArray(baseArray); return null; } @Override public Object visitNum(Num node) throws Exception { if (node.getInternalN() instanceof PyInteger) { module.integerConstant(((PyInteger) node.getInternalN()).getValue()).get(code); } else if (node.getInternalN() instanceof PyLong) { module.longConstant(((PyObject) node.getInternalN()).__str__().toString()).get(code); } else if (node.getInternalN() instanceof PyFloat) { module.floatConstant(((PyFloat) node.getInternalN()).getValue()).get(code); } else if (node.getInternalN() instanceof PyComplex) { module.complexConstant(((PyComplex) node.getInternalN()).imag).get(code); } return null; } private String getName(String name) { if (className != null && name.startsWith("__") && !name.endsWith("__")) { //remove leading '_' from classname int i = 0; while (className.charAt(i) == '_') { i++; } return "_" + className.substring(i) + name; } return name; } void emitGetGlobal(String name) throws Exception { code.ldc(name); code.invokevirtual(p(PyFrame.class), "getglobal", sig(PyObject.class, String.class)); } @Override public Object visitName(Name node) throws Exception { String name; if (fast_locals) { name = node.getInternalId(); } else { name = getName(node.getInternalId()); } SymInfo syminf = tbl.get(name); expr_contextType ctx = node.getInternalCtx(); if (ctx == expr_contextType.AugStore) { ctx = augmode; } switch (ctx) { case Load: loadFrame(); if (syminf != null) { int flags = syminf.flags; if ((flags & ScopeInfo.GLOBAL) != 0 || optimizeGlobals && (flags & (ScopeInfo.BOUND | ScopeInfo.CELL | ScopeInfo.FREE)) == 0) { emitGetGlobal(name); return null; } if (fast_locals) { if ((flags & ScopeInfo.CELL) != 0) { code.iconst(syminf.env_index); code.invokevirtual(p(PyFrame.class), "getderef", sig(PyObject.class, Integer.TYPE)); return null; } if ((flags & ScopeInfo.BOUND) != 0) { code.iconst(syminf.locals_index); code.invokevirtual(p(PyFrame.class), "getlocal", sig(PyObject.class, Integer.TYPE)); return null; } } if ((flags & ScopeInfo.FREE) != 0 && (flags & ScopeInfo.BOUND) == 0) { code.iconst(syminf.env_index); code.invokevirtual(p(PyFrame.class), "getderef", sig(PyObject.class, Integer.TYPE)); return null; } } code.ldc(name); code.invokevirtual(p(PyFrame.class), "getname", sig(PyObject.class, String.class)); return null; case Param: case Store: loadFrame(); if (syminf != null && (syminf.flags & ScopeInfo.GLOBAL) != 0) { code.ldc(name); code.aload(temporary); code.invokevirtual(p(PyFrame.class), "setglobal", sig(Void.TYPE, String.class, PyObject.class)); } else { if (!fast_locals) { code.ldc(name); code.aload(temporary); code.invokevirtual(p(PyFrame.class), "setlocal", sig(Void.TYPE, String.class, PyObject.class)); } else { if (syminf == null) { throw new ParseException("internal compiler error", node); } if ((syminf.flags & ScopeInfo.CELL) != 0) { code.iconst(syminf.env_index); code.aload(temporary); code.invokevirtual(p(PyFrame.class), "setderef", sig(Void.TYPE, Integer.TYPE, PyObject.class)); } else { code.iconst(syminf.locals_index); code.aload(temporary); code.invokevirtual(p(PyFrame.class), "setlocal", sig(Void.TYPE, Integer.TYPE, PyObject.class)); } } } return null; case Del: { loadFrame(); if (syminf != null && (syminf.flags & ScopeInfo.GLOBAL) != 0) { code.ldc(name); code.invokevirtual(p(PyFrame.class), "delglobal", sig(Void.TYPE, String.class)); } else { if (!fast_locals) { code.ldc(name); code.invokevirtual(p(PyFrame.class), "dellocal", sig(Void.TYPE, String.class)); } else { if (syminf == null) { throw new ParseException("internal compiler error", node); } if ((syminf.flags & ScopeInfo.CELL) != 0) { module.error("can not delete variable '" + name + "' referenced in nested scope", true, node); } code.iconst(syminf.locals_index); code.invokevirtual(p(PyFrame.class), "dellocal", sig(Void.TYPE, Integer.TYPE)); } } return null; } } return null; } @Override public Object visitStr(Str node) throws Exception { PyString s = (PyString) node.getInternalS(); if (s instanceof PyUnicode) { module.unicodeConstant(s.asString()).get(code); } else { module.stringConstant(s.asString()).get(code); } return null; } @Override public Object visitGeneratorExp(GeneratorExp node) throws Exception { String bound_exp = "_(x)"; setline(node); code.new_(p(PyFunction.class)); code.dup(); loadFrame(); code.getfield(p(PyFrame.class), "f_globals", ci(PyObject.class)); ScopeInfo scope = module.getScopeInfo(node); int emptyArray = makeArray(new ArrayList<expr>()); code.aload(emptyArray); scope.setup_closure(); scope.dump(); stmt n = new Expr(node, new Yield(node, node.getInternalElt())); expr iter = null; for (int i = node.getInternalGenerators().size() - 1; i >= 0; i--) { comprehension comp = node.getInternalGenerators().get(i); for (int j = comp.getInternalIfs().size() - 1; j >= 0; j--) { java.util.List<stmt> bod = new ArrayList<stmt>(); bod.add(n); n = new If(comp.getInternalIfs().get(j), comp.getInternalIfs().get(j), bod, new ArrayList<stmt>()); } java.util.List<stmt> bod = new ArrayList<stmt>(); bod.add(n); if (i != 0) { n = new For(comp, comp.getInternalTarget(), comp.getInternalIter(), bod, new ArrayList<stmt>()); } else { n = new For(comp, comp.getInternalTarget(), new Name(node, bound_exp, expr_contextType.Load), bod, new ArrayList<stmt>()); iter = comp.getInternalIter(); } } java.util.List<stmt> bod = new ArrayList<stmt>(); bod.add(n); module.codeConstant(new Suite(node, bod), "<genexpr>", true, className, false, false, node.getLine(), scope, cflags).get(code); code.aconst_null(); if (!makeClosure(scope)) { code.invokespecial(p(PyFunction.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject[].class, PyCode.class, PyObject.class)); } else { code.invokespecial(p(PyFunction.class), "<init>", sig(Void.TYPE, PyObject.class, PyObject[].class, PyCode.class, PyObject.class, PyObject[].class)); } int genExp = storeTop(); visit(iter); code.aload(genExp); code.freeLocal(genExp); code.swap(); code.invokevirtual(p(PyObject.class), "__iter__", sig(PyObject.class)); loadThreadState(); code.swap(); code.invokevirtual(p(PyObject.class), "__call__", sig(PyObject.class, ThreadState.class, PyObject.class)); freeArray(emptyArray); return null; } @Override public Object visitWith(With node) throws Exception { if (!module.getFutures().withStatementSupported()) { throw new ParseException("'with' will become a reserved keyword in Python 2.6", node); } final Label label_body_start = new Label(); final Label label_body_end = new Label(); final Label label_catch = new Label(); final Label label_end = new Label(); final Method contextGuard_getManager = Method.getMethod( "org.python.core.ContextManager getManager (org.python.core.PyObject)"); final Method __enter__ = Method.getMethod( "org.python.core.PyObject __enter__ (org.python.core.ThreadState)"); final Method __exit__ = Method.getMethod( "boolean __exit__ (org.python.core.ThreadState,org.python.core.PyException)"); // mgr = (EXPR) visit(node.getInternalContext_expr()); // wrap the manager with the ContextGuard (or get it directly if it // supports the ContextManager interface) code.invokestatic(Type.getType(ContextGuard.class).getInternalName(), contextGuard_getManager.getName(), contextGuard_getManager.getDescriptor()); code.dup(); final int mgr_tmp = code.getLocal(Type.getType(ContextManager.class).getInternalName()); code.astore(mgr_tmp); // value = mgr.__enter__() loadThreadState(); code.invokeinterface(Type.getType(ContextManager.class).getInternalName(), __enter__.getName(), __enter__.getDescriptor()); int value_tmp = code.getLocal(p(PyObject.class)); code.astore(value_tmp); // exc = True # not necessary, since we don't exec finally if exception // FINALLY (preparation) // ordinarily with a finally, we need to duplicate the code. that's not the case // here // # The normal and non-local-goto cases are handled here // if exc: # implicit // exit(None, None, None) ExceptionHandler normalExit = new ExceptionHandler() { @Override public boolean isFinallyHandler() { return true; } @Override public void finalBody(CodeCompiler compiler) throws Exception { compiler.code.aload(mgr_tmp); loadThreadState(); compiler.code.aconst_null(); compiler.code.invokeinterface(Type.getType(ContextManager.class).getInternalName(), __exit__.getName(), __exit__.getDescriptor()); compiler.code.pop(); } }; exceptionHandlers.push(normalExit); // try-catch block here ExceptionHandler handler = new ExceptionHandler(); exceptionHandlers.push(handler); handler.exceptionStarts.addElement(label_body_start); // VAR = value # Only if "as VAR" is present code.label(label_body_start); if (node.getInternalOptional_vars() != null) { set(node.getInternalOptional_vars(), value_tmp); } code.freeLocal(value_tmp); // BLOCK + FINALLY if non-local-goto Object blockResult = suite(node.getInternalBody()); normalExit.bodyDone = true; exceptionHandlers.pop(); exceptionHandlers.pop(); code.label(label_body_end); handler.exceptionEnds.addElement(label_body_end); // FINALLY if *not* non-local-goto if (blockResult == NoExit) { // BLOCK would have generated FINALLY for us if it exited (due to a break, // continue or return) inlineFinally(normalExit); code.goto_(label_end); } // CATCH code.label(label_catch); loadFrame(); code.invokestatic(p(Py.class), "setException", sig(PyException.class, Throwable.class, PyFrame.class)); code.aload(mgr_tmp); code.swap(); loadThreadState(); code.swap(); code.invokeinterface(Type.getType(ContextManager.class).getInternalName(), __exit__.getName(), __exit__.getDescriptor()); // # The exceptional case is handled here // exc = False # implicit // if not exit(*sys.exc_info()): code.ifne(label_end); // raise // # The exception is swallowed if exit() returns true code.invokestatic(p(Py.class), "makeException", sig(PyException.class)); code.checkcast(p(Throwable.class)); code.athrow(); code.label(label_end); code.freeLocal(mgr_tmp); handler.addExceptionHandlers(label_catch); return null; } @Override protected Object unhandled_node(PythonTree node) throws Exception { throw new Exception("Unhandled node " + node); } /** * Data about a given exception range whether a try:finally: or a * try:except:. The finally needs to inline the finally block for * each exit of the try: section, so we carry around that data for it. * * Both of these need to stop exception coverage of an area that is either * the inlined fin ally of a parent try:finally: or the reentry block after * a yield. Thus we keep around a set of exception ranges that the * catch block will eventually handle. */ class ExceptionHandler { /** * Each handler gets several exception ranges, this is because inlined * finally exit code shouldn't be covered by the exception handler of * that finally block. Thus each time we inline the finally code, we * stop one range and then enter a new one. * * We also need to stop coverage for the recovery of the locals after * a yield. */ public Vector<Label> exceptionStarts = new Vector<Label>(); public Vector<Label> exceptionEnds = new Vector<Label>(); public boolean bodyDone = false; public PythonTree node = null; public ExceptionHandler() { } public ExceptionHandler(PythonTree n) { node = n; } public boolean isFinallyHandler() { return node != null; } public void addExceptionHandlers(Label handlerStart) throws Exception { for (int i = 0; i < exceptionStarts.size(); ++i) { Label start = exceptionStarts.elementAt(i); Label end = exceptionEnds.elementAt(i); //FIXME: not at all sure that getOffset() test is correct or necessary. if (start.getOffset() != end.getOffset()) { code.trycatch( exceptionStarts.elementAt(i), exceptionEnds.elementAt(i), handlerStart, p(Throwable.class)); } } } public void finalBody(CodeCompiler compiler) throws Exception { if (node instanceof TryFinally) { suite(((TryFinally) node).getInternalFinalbody()); } } } }