def combine_try_nodes(node): def visit(n): if type(n.parent) == TryNode: return sl = n.parent i = sl.children.index(n) i -= 1 #we remove n here, since we may have to ascend through #several layers of StatementList nodes sl.children.remove(n) while 1: while i >= 0: if type(sl[i]) == TryNode: break i -= 1 if i >= 0 or null_node(sl.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: sys.stderr.write("%s:(%d): error: orphaned catch block\n" % (n.file, n.line)) sys.exit(-1) tn = sl[i] tn.add(n) traverse(node, CatchNode, visit, copy_children=True)
def AssignNode(self, node, traverse): self.out("push ") traverse(node[0]) self.out("\npush ") traverse(node[1]) self.out("\n") self.out("set\n")
def unpack_for_c_loops(node): start = node.children[0] if node.parent.parent == None: return p = node.parent.parent i = node.parent.parent.children.index(node.parent) node.replace(start, ExprNode([])) node.parent.parent.insert(i, start) start._c_loop_node = node if type(node.parent[1]) != StatementList: sl = StatementList() sl.add(node.parent[1]) node.parent.replace(node.parent[1], sl) sl = node.parent[1] inc = node[2] node.replace(inc, ExprNode([])) sl.add(inc) def handle_continues(n): if type(n.parent) != StatementList: sl = StatementList() n.parent.replace(n, sl) sl.add(n) n.parent.insert(n.parent.index(n), inc.copy()) traverse(node.parent, ContinueNode, handle_continues, exclude=[FunctionNode], copy_children=True) node.parent[1]
def combine_try_nodes(node): def visit(n): if type(n.parent) == TryNode: return sl = n.parent i = sl.children.index(n) i -= 1 # we remove n here, since we may have to ascend through # several layers of StatementList nodes sl.children.remove(n) while 1: while i >= 0: if type(sl[i]) == TryNode: break i -= 1 if i >= 0 or null_node(sl.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: sys.stderr.write("%s:(%d): error: orphaned catch block\n" % (n.file, n.line)) sys.exit(-1) tn = sl[i] tn.add(n) traverse(node, CatchNode, visit, copy_children=True)
def ArrayRefNode(self, node, traverse): self.out("push object ") traverse(node[0]) self.out("\n") self.out("push ") traverse(node[1]) self.out("\n")
def IfWithElse(self, node, traverse): endlabel = self.templabel("if") self.ifstack.append(endlabel) self.IfWithoutElse(node, traverse) for c in node[2:]: traverse(c) self.out(";end of if chain\n") self.out(endlabel + ":\n")
def IfWithElse(self, node, traverse): endlabel = self.templabel("if") self.ifstack.append(endlabel) self.IfWithoutElse(node, traverse) for c in node[2:]: traverse(c) self.out(";end of if chain\n"); self.out(endlabel + ":\n")
def addIterEnds(node): def visit(n): sn = StatementList() sn.force_block = True n.parent.replace(n, sn) sn.add(js_parse(onIterEnd)) sn.add(n) traverse(node, ReturnNode, visit)
def inside_generator(node): p = node while p.parent != None and not isinstance(p, FunctionNode): p = p.parent found = [0] def findyield(n): found[0] = 1 traverse(p, YieldNode, findyield) return found[0]
def FuncCallNode(self, node, traverse): #self.tlevel += 1 self.out(";BEGIN_CALL calc & push function pointer\n") if type(node[0]) == IdentNode: self.out("push " + node[0].val + "\n") else: traverse(node[0]) self.out(";push this\n"); #see if we have a this on the stack if type(node[0]) == BinOpNode and node[0].op == ".": #so, we should have this value on stack already #actually, stack should be: # | # V # function # this value # #transform to: # # function # this value # function # this value self.out("dup2;\n") #remove extraneous stack entry # # function # this value # this value self.out("drop;\n") self.out("swap;\n") #stack should now have this is right place (hopefully) else: self.out("push undefined\n") self.out("swap\n") self.out(";push arguments\n"); for i in range(len(node[1])): self.out("push ") traverse(node[1][i]) self.out("\n") self.out("call %i\n" % len(node[1])) #self.tlevel -= 1 pass
def FuncCallNode(self, node, traverse): #self.tlevel += 1 self.out(";BEGIN_CALL calc & push function pointer\n") if type(node[0]) == IdentNode: self.out("push " + node[0].val + "\n") else: traverse(node[0]) self.out(";push this\n") #see if we have a this on the stack if type(node[0]) == BinOpNode and node[0].op == ".": #so, we should have this value on stack already #actually, stack should be: # | # V # function # this value # #transform to: # # function # this value # function # this value self.out("dup2;\n") #remove extraneous stack entry # # function # this value # this value self.out("drop;\n") self.out("swap;\n") #stack should now have this is right place (hopefully) else: self.out("push undefined\n") self.out("swap\n") self.out(";push arguments\n") for i in range(len(node[1])): self.out("push ") traverse(node[1][i]) self.out("\n") self.out("call %i\n" % len(node[1])) #self.tlevel -= 1 pass
def IfWithoutElse(self, node, traverse): self.out("\n;if (" + node[0].gen_js(0) + ")\n") traverse(node[0]) label = self.templabel("if") self.out("jz " + label + "\n\n") traverse(node[1]) if type(node.parent) == ElseIfNode: print(self.ifstack) self.out(";escape if chain\n") self.out("jmp " + self.ifstack[-1] + "\n") self.out("\n" + label + ":\n")
def IfWithoutElse(self, node, traverse): self.out("\n;if (" + node[0].gen_js(0) + ")\n") traverse(node[0]) label = self.templabel("if") self.out("jz " + label + "\n\n") traverse(node[1]) if type(node.parent) == ElseIfNode: print(self.ifstack) self.out(";escape if chain\n"); self.out("jmp " + self.ifstack[-1] + "\n") self.out("\n" + label + ":\n");
def combine_if_else_nodes(node): vset = set() found = [False] def visit(n): if type(n.parent) == IfNode: return if n in vset: return vset.add(n) sl = n.parent i = sl.children.index(n) # i -= 1 # we remove n here, since we may have to ascend through # several layers of StatementList nodes sl.children.remove(n) # clamp i i = max(min(i, len(sl) - 1), 0) # print(len(sl), i) while 1: while i >= 0 and i < len(sl): if type(sl[i]) == IfNode: break i -= 1 if i >= 0 or null_node(sl.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: sys.stderr.write("%s:(%d): error: orphaned else block\n" % (n.file, n.line)) sys.exit(-1) tn = sl[i] while len(tn) > 2: tn = tn[2][0] tn.add(n) found[0] = True traverse(node, ElseNode, visit, copy_children=True) while found[0]: found[0] = False traverse(node, ElseNode, visit, copy_children=True)
def combine_if_else_nodes(node): vset = set() found = [False] def visit(n): if type(n.parent) == IfNode: return if n in vset: return vset.add(n) sl = n.parent i = sl.children.index(n) #i -= 1 #we remove n here, since we may have to ascend through #several layers of StatementList nodes sl.children.remove(n) #clamp i i = max(min(i, len(sl) - 1), 0) #print(len(sl), i) while 1: while i >= 0 and i < len(sl): if type(sl[i]) == IfNode: break i -= 1 if i >= 0 or null_node(sl.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: sys.stderr.write("%s:(%d): error: orphaned else block\n" % (n.file, n.line)) sys.exit(-1) tn = sl[i] while len(tn) > 2: tn = tn[2][0] tn.add(n) found[0] = True traverse(node, ElseNode, visit, copy_children=True) while found[0]: found[0] = False traverse(node, ElseNode, visit, copy_children=True)
def BinOpNode(self, node, traverse): #self.tlevel += 1 print("eek!", node.op) if node.op == ".": if type(node[0]) == IdentNode: self.out("gv " + node[0].val + "\n") else: traverse(node[0]) if type(node[1]) == IdentNode: self.out("push " + node[1].val) self.out("\n") else: traverse(node[1]) self.out("get\n") else: if type(node[0]) in [IdentNode, NumLitNode, StrLitNode]: self.out("push " + str(node[0].val)) self.out("\n") else: traverse(node[0]) if type(node[1]) in [IdentNode, NumLitNode, StrLitNode]: self.out("push " + str(node[1].val)) self.out("\n") else: traverse(node[1]) self.out(bin_opmap[node.op]+"\n") self.out("\n");
def BinOpNode(self, node, traverse): #self.tlevel += 1 print("eek!", node.op) if node.op == ".": if type(node[0]) == IdentNode: self.out("gv " + node[0].val + "\n") else: traverse(node[0]) if type(node[1]) == IdentNode: self.out("push " + node[1].val) self.out("\n") else: traverse(node[1]) self.out("get\n") else: if type(node[0]) in [IdentNode, NumLitNode, StrLitNode]: self.out("push " + str(node[0].val)) self.out("\n") else: traverse(node[0]) if type(node[1]) in [IdentNode, NumLitNode, StrLitNode]: self.out("push " + str(node[1].val)) self.out("\n") else: traverse(node[1]) self.out(bin_opmap[node.op] + "\n") self.out("\n")
def process_generators(result, tspace): global typespace typespace = tspace traverse(result, YieldNode, visit_yields) traverse(result, FunctionNode, visit_generators) del_attrs = [] def cleanup_generator_garbage(n): for a in del_attrs: if hasattr(n, a): delattr(n, a) for c in n.children: cleanup_generator_garbage(c) cleanup_generator_garbage(result)
def FunctionNode(self, node, traverse): self.push_label(node.name) for c in node[1:]: traverse(c) if not self.buf.strip().endswith("ret"): self.out("ret\n") self.pop_label() #XXX #self.out("endfunction\n\n"); #XXX need to deal with continuations, nested functions, etc. #for now though. . . self.out(";add function to scope\n") self.out("cf %s, %d\n" % (node.name, len(node[0]))) self.out("sv " + node.name + "\n") self.out("drop\n\n")
def VarDeclNode(self, node, traverse): for c in node[1:]: traverse(c) self.out(";declare " + node.val + "\n") self.tlevel += 1 if type(node[0]) in [IdentNode, NumLitNode, StrLitNode]: self.out("push ") self.traverse(node[0]) self.out("\n") else: traverse(node[0]) self.out("setvar " + node.val + "\n") self.out("drop\n") self.tlevel -= 1 self.scope[node.val] = node
def VarDeclNode(self, node, traverse): for c in node[1:]: traverse(c) self.out(";declare " + node.val + "\n") self.tlevel += 1 if type(node[0]) in [IdentNode, NumLitNode, StrLitNode]: self.out("push ") self.traverse(node[0]) self.out("\n"); else: traverse(node[0]) self.out("setvar " + node.val + "\n") self.out("drop\n"); self.tlevel -= 1 self.scope[node.val] = node;
def FunctionNode(self, node, traverse): self.push_label(node.name) for c in node[1:]: traverse(c) if not self.buf.strip().endswith("ret"): self.out("ret\n"); self.pop_label() #XXX #self.out("endfunction\n\n"); #XXX need to deal with continuations, nested functions, etc. #for now though. . . self.out(";add function to scope\n"); self.out("cf %s, %d\n" % (node.name, len(node[0]))); self.out("sv " + node.name + "\n"); self.out("drop\n\n");
def create_elseif_nodes(result, typespace): def visit(node): n = node[0] while type(n) == StatementList: if len(n) > 1: return n = n[0] if type(n) == IfNode: node.parent.replace(node, ElseIfNode(n)) traverse(result, ElseNode, visit) def visit2(node): if type(node) in [ElseIfNode, ElseNode]: ni = node.parent.index(node) if ni == 0: typespace.error("invalid else 1", node) prev = node.parent[ni-1] print("PREV", type(prev)) if type(prev) != IfNode: typespace.error("invalid else 2", node) node.parent.remove(node) prev.add(node) else: i = 0 while i < len(node): i_inc = 1 if type(node[i]) not in [ElseIfNode, ElseNode] else 0 visit2(node[i]) i += i_inc #collapse if nodes visit2(result) print("\n\n", result, "\n\n")
def create_elseif_nodes(result, typespace): def visit(node): n = node[0] while type(n) == StatementList: if len(n) > 1: return n = n[0] if type(n) == IfNode: node.parent.replace(node, ElseIfNode(n)) traverse(result, ElseNode, visit) def visit2(node): if type(node) in [ElseIfNode, ElseNode]: ni = node.parent.index(node) if ni == 0: typespace.error("invalid else 1", node) prev = node.parent[ni - 1] print("PREV", type(prev)) if type(prev) != IfNode: typespace.error("invalid else 2", node) node.parent.remove(node) prev.add(node) else: i = 0 while i < len(node): i_inc = 1 if type(node[i]) not in [ElseIfNode, ElseNode] else 0 visit2(node[i]) i += i_inc #collapse if nodes visit2(result) print("\n\n", result, "\n\n")
def expand_of_loops(result, typespace): def expand_mozilla_forloops_new(node, scope): use_in_iter = False if (node.of_keyword == "in"): use_in_iter = True typespace.warning("Temporary warning: detected for-in usage", node) if not inside_generator(node): return func = node.parent while not null_node(func) and type(func) != FunctionNode: func = func.parent if not null_node(func): if func.name in forloop_expansion_exclude: return def prop_ident_change(node, oldid, newid): if type(node) in [IdentNode, VarDeclNode] and node.val == oldid: if type(node.parent) == BinOpNode and node.parent.op == ".": if node != node.parent[1]: node.val = newid else: node.val = newid for c in node.children: if type(c) == FunctionNode: continue prop_ident_change(c, oldid, newid) #for-in-loops don't seem to behave like for-C-loops, #the iteration variable is in it's own scope, and #doesn't seem to affect the parent scope. val = node[0].val di = 0 while node[0].val in scope: node[0].val = "%s_%d" % (val, di) di += 1 #print(node[0].val) if node[0].val != val: scope[node[0].val] = node[0] prop_ident_change(node.parent, val, node[0].val) slist = node.parent.children[1] if type(slist) != StatementList: s = StatementList() s.add(slist) slist = s getiter = "__get_in_iter" if use_in_iter else "__get_iter" itername = node[0].val objname = node[1].gen_js(0) if glob.g_log_forloops: n2 = js_parse( """ var __iter_$s1 = __get_iter($s2, $s3, $s4, $s5); var $s1; while (1) { var __ival_$s1 = __iter_$s1.next(); if (__ival_$s1.done) { break; } $s1 = __ival_$s1.value; } """.replace("__get_iter", getiter), (itername, GETITER, objname, "'" + node[0].file + "'", node[0].line, "'" + node.of_keyword + "'")) else: n2 = js_parse( """ var __iter_$s1 = __get_iter($s2); var $s1; while (1) { var __ival_$s1 = __iter_$s1.next(); if (__ival_$s1.done) { break; } $s1 = __ival_$s1.value; } """.replace("__get_iter", getiter), (itername, objname)) def set_line(n, slist, line, lexpos): n.line = line n.lexpos = lexpos for c in n.children: set_line(c, slist, line, lexpos) #preserving line info is a bit tricky. #slist goes through a js->gen_js->js cycle, #so make sure we still have it (and its #line/lexpos information). set_line(n2, slist, node.line, node.lexpos) for c in slist: n2[2][1].add(c) node.parent.parent.replace(node.parent, n2) #expand_of_loops lexical scope here traverse(result, ForInNode, expand_mozilla_forloops_new, use_scope=True)
def process_generators_old(result, typespace): def visit_yields(node): p = node while not null_node(p) and type(p) != FunctionNode: p = p.parent if null_node(p): typespace.error("yield keyword only valid within functions") p.is_generator = True traverse(result, YieldNode, visit_yields) def node_has_yield(node): if type(node) == YieldNode: return True for c in node.children: if type(c) == FunctionNode: continue ret = node_has_yield(c) if ret: return True return False def visit_generators(node): def print_frames(frames, tlevel=0): tstr = tab(tlevel) tstr2 = tab(tlevel + 1) s = "" for f in frames: if type(f) == Frame: if f.node != None: nstr = "%s %d " % (f.node.get_line_str(), f.label) else: nstr = str(f.label) + " " s += tstr + nstr + "{\n" + print_frames(f, tlevel + 1) s += tstr + "}\n" else: s += tstr + f.get_line_str() + "\n" if tlevel == 0: print(s) return s if 0: file = open("generator_test.html", "w") file.write(""" <html><head><title>Generator Test</title></head> <script> FrameContinue = {1:1}; FrameBreak = {2:2}; """) file.write(node2.gen_js(3).replace("yield", "return")) file.write(""" j = 0; for (var tst in new range(2, 8)) { console.log(tst); if (j > 10) break; j++; } </script> </html> """) file.close() #print(node2.gen_js(1)) #print_frames(frames2) traverse(result, FunctionNode, visit_generators) del_attrs = ["_cur", "_startcur", "frame", "return_frame", "pop_trystack"] def cleanup_generator_garbage(n): for a in del_attrs: if hasattr(n, a): delattr(n, a) for c in n.children: cleanup_generator_garbage(c) cleanup_generator_garbage(result)
def bleh(): for frames in flatframes: fname = f_name(frames) n = js_parse(""" function $s1(scope) { if (_do_frame_debug) console.log("in $s1"); }""", (fname), start_node=FunctionNode) if type(n[1]) != StatementList: n.replace(n[1], StatementList()) n = n[1] func = n while type(func) != FunctionNode: func = func.parent excl = (type(frames.node) == StatementList and type(frames.parent.node) == FunctionNode) if frames.node != None and not excl and type(frames.node) != FunctionNode: f = frames sl = StatementList() f.node[f.node._startcur] = sl frames.funcnode = func frames.subnode = frames.funcnode local_frames = "[" totframes = 0 for i, f in enumerate(frames): if type(f) != Frame: frames.subnode.add(f) frames.leaf = True else: frames.leaf = False if len(local_frames) > 1: local_frames += ", " local_frames += f_ref(f) #.replace("this.", "") totframes += 1 if f.node != None and type(f.node) != FunctionNode: if len(f.node.children) > f.node._startcur + 1: do_conv(f.node, f) if frames.leaf: f2 = f_next(frames) f2 = f2.label if f2 != -1 else -1 frames.subnode.add(js_parse("return [$i, undefined];", [f2], start_node=ReturnNode)); local_frames = "%s_frames = "%f_ref(frames) + local_frames + "];" frames.frames = js_parse(local_frames) frames.totframes = totframes def build_next(f, parent=None): if type(f) != Frame: return subnode = f.subnode if f.label >= 0: # and f.label < 3: n2 = js_parse("this.$s1 = 0;", [f_name(f)], start_node=AssignNode) n2.replace(n2[1], f.funcnode) f.funcnode.name = "(anonymous)" f.funcnode.is_anonymous = True node2.add(n2) #f.funcnode) if f.totframes > 0: if f.node != None and type(f.node) == WhileNode: f2 = f_next(f) f2 = f2.label if f2 != -1 else -1 n = js_parse(""" if (!"placeholder") { return [$i1, undefined]; } """, [f2]) if n == None: typespace.error("internal error", subnode); n2 = find_node(n, StrLitNode); n2.parent.replace(n2, f.node[0]) subnode.add(n) f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) elif f.node != None and type(f.node) == TryNode: n = StatementList() if n == None: typespace.error("internal error", subnode); f3 = f_raw_next(f) while f3 != -1 and type(f3.node) != CatchNode: f3 = f_raw_next(f3); if f3 == -1: typespace.error("Orphaned try block", f.node) f3name = "_nfothing" if len(f3.node) > 0: f3name = f3.node[0].gen_js(0).replace("scope.", "") n.add(js_parse(""" this.trystack.push([$i, "$s"]); """, [f3.label, f3name])) f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == IfNode: f2 = f_first(f) f1 = f_raw_next(f) while type(f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse(""" if (!("placeholder")) { return [$i1, undefined]; } else { return [$i2, undefined]; } """, [f1.label, f2.label]); n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0].copy()) if n == None: typespace.error("internal error", subnode); f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == ElseNode: f2 = f_first(f) f1 = f_raw_next(f) while type(f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse(""" return [$i1, undefined]; """, [f2.label]); if n == None: typespace.error("internal error", subnode); f2 = f_first(f); subnode.add(n) elif f.node != None and type(f.node) == CatchNode: f2 = f_first(f) n = js_parse(""" return [$i1, undefined]; """, [f2.label]); if n == None: typespace.error("internal error", subnode); subnode.add(n) elif f.node != None and type(f.node) == ForLoopNode: f2 = f_first(f); f3 = f_next(f) f3 = f3.label if f2 != -1 else -1 f2 = f2.label if f2 != -1 else -1 n = js_parse(""" if ($n) { return [$i, undefined]; } else { return [$i, undefined]; } """, [f.node[0][1], f2, f3]) if n == None: typespace.error("internal error", subnode); subnode.add(n) node2.insert(1, js_parse(""" this[Symbol.iterator] = function() { return this; } """)[0]) for f in flatframes: build_next(f, f.parent) #process returns from within try nodes for f in flatframes: if f.parent != None and type(f.parent.node) == TryNode: def visit_rets1(n2): target = n2[0][0][0].val isyield = n2[0][0][1].val ni = n2.parent.index(n2) if target >= f_next(f.parent).label: n3 = js_parse("this.trystack.pop();")[0] n2.parent.insert(ni, n3) traverse(f.subnode, ReturnNode, visit_rets1, copy_children=True); #process yields for f in flatframes: f2 = f.parent set_yield = None def visit_rets2(n2): if set_yield != None: #print(n2) n2[0][0].replace(n2[0][0][1], set_yield); set_yield = find_node(f.subnode, YieldNode); if set_yield != None: set_yield.parent.remove(set_yield); set_yield = ArrayLitNode(ExprListNode([set_yield[0]])) traverse(f.subnode, ReturnNode, visit_rets2, copy_children=True); def find_parent_frame(f, ntypes, include_first=True): p = f if not include_first: p = p.parent while p != None: if type(p.node) in ntypes: return p p = p.parent return None #process breaks for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: if type(p) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p != f.subnode and not null_node(p): return #break corresponds to a loop internal to this frame p = find_parent_frame(f, [WhileNode, DoWhileNode, ForLoopNode], True) if p == None: typespace.error("Invalid break statement (switches within generators aren't supported yet)", n2) f2 = f_next(p) n3 = js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode); n2.parent.replace(n2, n3) traverse(f.subnode, BreakNode, visit_rets3, copy_children=True); #process continues for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: p = p.parent if p != f.subnode and not null_node(p): return #continue corresponds to a loop internal to this frame p = f.parent while p != None: if type(p.node) in [WhileNode, DoWhileNode, ForLoopNode]: break; p = p.parent if p == None: typespace.error("Invalid continue statement") n3 = js_parse("return [$i, undefined];", [p.label], start_node=ReturnNode); n2.parent.replace(n2, n3) traverse(f.subnode, ContinueNode, visit_rets3, copy_children=True); firstnode = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode2 = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode.replace(firstnode[1], StatementList()) firstnode2.replace(firstnode2[1], StatementList()) flatframes[0].subnode.add(firstnode); node2.insert(1, firstnode2[1]); firstnode = firstnode[1] firstnode2 = firstnode2[1] args = list(node.children[0]) for i3 in range(len(args)): argn = args[i3] while type(argn) not in [IdentNode, VarDeclNode]: argn = argn[0] args[i3] = argn.val scope = {} for f in flatframes: scope.update(f.scope) s = "{" j2 = 0 for j, v in enumerate(scope.keys()): if j2 > 0: s += ", " j2 += 1 if v in args: s += "%s:%s" % ("%s_%s"%(v, scope[v]), v) else: s += "%s:undefined" % ("%s_%s"%(v, scope[v])) s += "}" s = "this.scope = %s;\n" % s firstnode2.add(js_parse(s)[0]) #ensure all frames have returns for f in flatframes: if not find_node(f.subnode, ReturnNode): f.subnode.add(js_parse("return [$i, undefined];", [f_next(f).label], start_node=ReturnNode)); framelist = "[" for i, f in enumerate(flatframes): if i > 0: framelist += ", " framelist += "this.frame_%i" % f.label framelist = "this.frames = %s];"%framelist node2.add(js_parse(framelist)); node2.add(js_parse(""" this.cur = 1; this.trystack = new Array(); this.next = function() { var ret; while (this.cur < this.frames.length) { try { ret = this.frames[this.cur].call(this, this.scope); } catch (_generator_error) { if (this.trystack.length > 0) { var ts1 = this.trystack.pop(); this.scope[ts1[1]] = _generator_error; ret = [ts1[0], undefined]; } else { throw _generator_error; } } if (ret[0] == this.frames.length) { return {done : true, value : undefined}; break; } if (ret[0] == this.cur) { console.trace(); console.log("YEEK!") return {done : true, value : undefined}; } this.cur = ret[0]; if (ret[1] != undefined) { return {value : ret[1][0], done : false}; } else { return {value : undefined, done : false}; } } } """, [])) node.parent.replace(node, node2)
def parse_intern(data, create_logger=False, expand_loops=True, expand_generators=True): glob.g_lines = data.split("\n") if glob.g_preprocess_code: data = preprocess_text(data, glob.g_file) if glob.g_print_tokens: plexer.input(data) tok = plexer.token() while tok != None: print(tok) tok = plexer.token() plexer.input(data) glob.g_lexer = plexer result = parser.parse(data, lexer=plexer) if result == None: if glob.g_error_pre != None: glob.g_error = True result = StatementList() if glob.g_error: print_err(glob.g_error_pre) typespace = JSTypeSpace() #handle some directives for c in result: if type(c) != StrLitNode: break if c.val[1:-1] == "use strict": glob.g_force_global_strict = True elif c.val[1:-1] == "not_a_module": glob.g_es6_modules = False if glob.g_profile_coverage: from js_process_ast import coverage_profile coverage_profile(result, typespace) if glob.g_compile_statics_only: process_static_vars(result, typespace) return result.gen_js(0), result if glob.g_enable_let: process_let(result, typespace) if glob.g_force_global_strict: kill_bad_globals(result, typespace) #handle .? operator transform_exisential_operators(result, typespace) if glob.g_write_manifest and glob.g_outfile != "": buf = gen_manifest_file(result, typespace) file = open(glob.g_outfile + ".manifest", "w") file.write(buf) file.close() if glob.g_es6_modules: module_transform(result, typespace) if glob.g_require_js: expand_requirejs_classes(result, typespace) elif glob.g_expand_classes: expand_harmony_classes(result, typespace) expand_typed_classes(result, typespace) else: create_class_list(result, typespace) if glob.g_clear_slashr: print("\n") if result != None and len(result) == 0: result = None return "", None #sys.stdout.write("Error: empty compilation\n"); #raise JSError("Empty compilation"); global f_id f_id = [0] flatten_statementlists(result, typespace) has_generators = [False] def has_generator(n): if not glob.g_expand_generators: return if type(n) == YieldNode: has_generators[0] = True for c in n: has_generator(c) has_generator(result) if expand_loops or has_generators[0]: expand_of_loops(result, typespace) #combine_try_nodes may have nested statementlists again, so better reflatten flatten_statementlists(result, typespace) if create_logger: traverse(result, FunctionNode, create_type_logger) #don't need to do this anymore, yay! #process_arrow_function_this(result, typespace) if expand_generators: flatten_statementlists(result, typespace) process_generators(result, typespace) flatten_statementlists(result, typespace) if glob.g_add_opt_initializers: add_func_opt_code(result, typespace) debug_forloop_expansion = False if debug_forloop_expansion: reset = 0 if reset or not os.path.exists("cur_d.txt"): f = open("cur_d.txt", "w") f.write("-1") f.close() f = open("cur_d.txt", "r") d = int(f.read()) print("\n\nd: %d\n" % d) traverse_i(result, ForInNode, expand_mozilla_forloops, d, use_scope=True) f.close() f = open("cur_d.txt", "w") f.write(str(d + 1)) f.close() if glob.g_combine_ifelse_nodes: combine_if_else_nodes(result) if glob.g_print_nodes: print("nodes: ", result) pass if glob.g_replace_instanceof and not glob.g_require_js: replace_instanceof(result, typespace) if glob.g_enable_static_vars: process_static_vars(result, typespace) if glob.g_do_docstrings: process_docstrings(result, typespace) if glob.g_gen_source_map: smap = SourceMap() def set_smap(node, smap): node.smap = smap for n in node: set_smap(n, smap) set_smap(result, smap) if not glob.g_minify: buf = result.gen_js(0) map = gen_source_map(data, buf, smap) else: buf, smap = js_minify(result) map = gen_source_map(data, buf, smap) if glob.g_add_srcmap_ref: spath = glob.g_outfile if os.path.sep in spath: spath = os.path.split(spath)[1] buf += "\n//# sourceMappingURL=/content/%s\n" % (spath + ".map") if glob.g_gen_smap_orig: f = open(glob.g_outfile + ".origsrc", "w") f.write(data) f.close() else: if not glob.g_minify: buf = result.gen_js(0) else: buf, smap = js_minify(result) if glob.g_outfile == "": print(buf) if 0: file = open("generator_test.html", "w") file.write(""" <html><head><title>Generator Test</title></head> <script> function arr_iter(keys) { this.keys = keys; this.cur = 0; this.next = function() { if (this.cur >= this.keys.length) { throw StopIteration; } return this.keys[this.cur++]; } } __use_Iterator = true; function __get_iter(obj) { if (obj.__proto__.hasOwnProperty("__iterator__") || obj.hasOwnProperty("__iterator__")) { return obj.__iterator__(); } else { if (__use_Iterator) { return Iterator(obj); } else { keys = [] for (var k in obj) { keys.push(k) } return new arr_iter(keys); } } } function __get_iter2(obj) { if (obj.__proto__.hasOwnProperty("__iterator__") || obj.hasOwnProperty("__iterator__")) { return obj.__iterator__(); } else { keys = [] for (var k in obj) { keys.push([k, obj[k]]) } return new arr_iter(keys); } } try { _tst = Iterator({}); } catch (Error) { __use_Iterator = false; Iterator = __get_iter2; } FrameContinue = {1:1}; FrameBreak = {2:2}; """) file.write(buf.replace("yield", "return")) file.write(""" j = 0; for (var tst in new range2(2, 8)) { if (_do_frame_debug) console.log(tst); if (j > 10) break; j++; } </script> </html> """) file.close() pass return buf, result
def value_traverse(self, node, traverse): if type(node) in [IdentNode, StrLitNode, NumLitNode]: self.out("push ") traverse(node) self.out("\n")
def expand_of_loops(result, typespace): def expand_mozilla_forloops_new(node, scope): if node.of_keyword == "in": typespace.warning("Temporary warning: detected for-in usage", node) return func = node.parent while not null_node(func) and type(func) != FunctionNode: func = func.parent if not null_node(func): if func.name in forloop_expansion_exclude: return def prop_ident_change(node, oldid, newid): if type(node) in [IdentNode, VarDeclNode] and node.val == oldid: if type(node.parent) == BinOpNode and node.parent.op == ".": if node != node.parent[1]: node.val = newid else: node.val = newid for c in node.children: if type(c) == FunctionNode: continue prop_ident_change(c, oldid, newid) # for-in-loops don't seem to behave like for-C-loops, # the iteration variable is in it's own scope, and # doesn't seem to affect the parent scope. val = node[0].val di = 0 while node[0].val in scope: node[0].val = "%s_%d" % (val, di) di += 1 # print(node[0].val) if node[0].val != val: scope[node[0].val] = node[0] prop_ident_change(node.parent, val, node[0].val) slist = node.parent.children[1] if type(slist) != StatementList: s = StatementList() s.add(slist) slist = s itername = node[0].val objname = node[1].gen_js(0) if glob.g_log_forloops: n2 = js_parse( """ var __iter_$s1 = __get_iter($s2, $s3, $s4, $s5); var $s1; while (1) { var __ival_$s1 = __iter_$s1.next(); if (__ival_$s1.done) { break; } $s1 = __ival_$s1.value; } """, (itername, objname, "'" + node[0].file + "'", node[0].line, "'" + node.of_keyword + "'"), ) else: n2 = js_parse( """ var __iter_$s1 = __get_iter($s2); var $s1; while (1) { var __ival_$s1 = __iter_$s1.next(); if (__ival_$s1.done) { break; } $s1 = __ival_$s1.value; } """, (itername, objname), ) def set_line(n, slist, line, lexpos): n.line = line n.lexpos = lexpos for c in n.children: set_line(c, slist, line, lexpos) # preserving line info is a bit tricky. # slist goes through a js->gen_js->js cycle, # so make sure we still have it (and its # line/lexpos information). set_line(n2, slist, node.line, node.lexpos) for c in slist: n2[2][1].add(c) node.parent.parent.replace(node.parent, n2) # expand_of_loops lexical scope here traverse(result, ForInNode, expand_mozilla_forloops_new, use_scope=True)
def process_generators_old(result, typespace): def visit_yields(node): p = node while not null_node(p) and type(p) != FunctionNode: p = p.parent if null_node(p): typespace.error("yield keyword only valid within functions") p.is_generator = True traverse(result, YieldNode, visit_yields) def node_has_yield(node): if type(node) == YieldNode: return True for c in node.children: if type(c) == FunctionNode: continue ret = node_has_yield(c) if ret: return True return False def visit_generators(node): def print_frames(frames, tlevel=0): tstr = tab(tlevel) tstr2 = tab(tlevel+1) s = "" for f in frames: if type(f) == Frame: if f.node != None: nstr = "%s %d " % (f.node.get_line_str(), f.label) else: nstr = str(f.label) + " " s += tstr + nstr + "{\n" + print_frames(f, tlevel+1) s += tstr + "}\n"; else: s += tstr + f.get_line_str() + "\n" if tlevel == 0: print(s) return s if 0: file = open("generator_test.html", "w") file.write(""" <html><head><title>Generator Test</title></head> <script> FrameContinue = {1:1}; FrameBreak = {2:2}; """) file.write(node2.gen_js(3).replace("yield", "return")) file.write(""" j = 0; for (var tst in new range(2, 8)) { console.log(tst); if (j > 10) break; j++; } </script> </html> """) file.close() #print(node2.gen_js(1)) #print_frames(frames2) traverse(result, FunctionNode, visit_generators) del_attrs = ["_cur", "_startcur", "frame", "return_frame", "pop_trystack"] def cleanup_generator_garbage(n): for a in del_attrs: if hasattr(n, a): delattr(n, a) for c in n.children: cleanup_generator_garbage(c) cleanup_generator_garbage(result)
def parse_intern(data, create_logger=False, expand_loops=True, expand_generators=True): glob.g_lines = data.split("\n") if glob.g_preprocess_code: data = preprocess_text(data, glob.g_file) if glob.g_print_tokens: plexer.input(data) tok = plexer.token() while tok != None: print(tok) tok = plexer.token() plexer.input(data) glob.g_lexer = plexer result = parser.parse(data, lexer=plexer) if result == None: if glob.g_error_pre != None: glob.g_error = True result = StatementList() if glob.g_error: print_err(glob.g_error_pre) typespace = JSTypeSpace() if result != None: if len(result) > 0 and type(result[0]) == StrLitNode and result[0].val == '"use strict"': glob.g_force_global_strict = True elif len(result) > 0 and type(result[0]) == StrLitNode and result[0].val == '"not_a_module"': glob.g_es6_modules = False if glob.g_compile_statics_only: process_static_vars(result, typespace) return result.gen_js(0), result if glob.g_enable_let: process_let(result, typespace) if glob.g_force_global_strict: kill_bad_globals(result, typespace) #handle .? operator transform_exisential_operators(result, typespace) if glob.g_write_manifest and glob.g_outfile != "": buf = gen_manifest_file(result, typespace); file = open(glob.g_outfile+".manifest", "w") file.write(buf) file.close() if glob.g_es6_modules: module_transform(result, typespace) if glob.g_require_js: expand_requirejs_classes(result, typespace); else: expand_harmony_classes(result, typespace); expand_typed_classes(result, typespace) if glob.g_clear_slashr: print("\n") if result != None and len(result) == 0: result = None return "", None #sys.stdout.write("Error: empty compilation\n"); #raise JSError("Empty compilation"); global f_id f_id = [0] flatten_statementlists(result, typespace) has_generators = [False] def has_generator(n): if type(n) == YieldNode: has_generators[0] = True for c in n: has_generator(c) has_generator(result) if expand_loops or has_generators[0]: expand_of_loops(result, typespace) #combine_try_nodes may have nested statementlists again, so better reflatten flatten_statementlists(result, typespace) if create_logger: traverse(result, FunctionNode, create_type_logger) process_arrow_function_this(result, typespace) if expand_generators: flatten_statementlists(result, typespace) process_generators(result, typespace); flatten_statementlists(result, typespace) if glob.g_add_opt_initializers: add_func_opt_code(result, typespace) if glob.g_combine_ifelse_nodes: combine_if_else_nodes(result) if glob.g_print_nodes: print("nodes: ", result) pass if glob.g_replace_instanceof and not glob.g_require_js: replace_instanceof(result, typespace) if glob.g_enable_static_vars: process_static_vars(result, typespace) if glob.g_do_docstrings: process_docstrings(result, typespace) if glob.g_gen_v7_bytecode: from js_opcode_emit2 import v7_emit_bytecode buf = v7_emit_bytecode(result, typespace) elif not glob.g_minify: buf = result.gen_js(0) else: buf, smap = js_minify(result) if glob.g_outfile == "": print(buf) return buf, result
def visit_generators(node): if not node.is_generator: return def _remove_this(n): if n.val != "this": return if type(n.parent) != BinOpNode or n.parent.op != ".": #typespace.error("Can only reference members of 'this' in generators"); n.val = "__gen_this2" else: n.val = "__gen_this2" #n.parent.parent.replace(n.parent, n.parent[1]) def set_cur(n): if type(n) in [IfNode, WhileNode, DoWhileNode, ForLoopNode, CatchNode]: n._cur = 1; n._startcur = 1; else: n._cur = 0 n._startcur = 0 n._start = True n._has_yield = node_has_yield(n) for c in n: set_cur(c) def prior_if(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n)-1 while 1: while i >= 0: if type(sl[i]) == IfNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl); sl = sl.parent; if i < 0: typespace.error("Orphaned else node", n) sys.exit(-1) return sl[i] def prior_try(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n)-1 while 1: while i >= 0: if type(sl[i]) == TryNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl); sl = sl.parent; if i < 0: typespace.error("Orphaned catch node", n) sys.exit(-1) return sl[i] def is_stype(n): ret = type(n) in stypes # and (n._has_yield or n.parent._has_yield) return ret if type(n) == CatchNode: ret |= prior_try(n)._has_yield if type(n) == ElseNode: ret |= prior_if(n)._has_yield if type(n) in [IfNode, ElseNode]: p5 = n.parent while not null_node(p5): if hasattr(p5, "_has_yield") and p5._has_yield: ret = True; break p5 = p5.parent return ret combine_if_else_nodes(node) traverse(node, ForCNode, unpack_for_c_loops, exclude=[FunctionNode], copy_children=True); traverse(node, IdentNode, _remove_this) traverse(node, VarDeclNode, _remove_this) frames = frame = Frame(node=node) stack = [c for c in node.children[1:]] stack.reverse() stypes = set([ForLoopNode, WhileNode, DoWhileNode, IfNode, ElseNode, TryNode, CatchNode]) for c in stack: set_cur(c) while len(stack) > 0: n = stack.pop(-1) if is_stype(n) or type(n) == StatementList: if n._start: if type(n) != StatementList: f = Frame(node=n) frame.append(f) frame = f n._start = False if n._cur < len(n.children): stack.append(n) stack.append(n[n._cur]) n._cur += 1 elif type(n) != StatementList: frame = frame.parent else: frame.append(n) def compact_frames(frames): i = 0 frm = None while i < len(frames): f1 = frames[i] if type(f1) == YieldNode: frm = None if type(f1) != Frame: if frm == None: frm = Frame() frames.insert(i, frm) frm.parent = frames i += 1 frames.remove(f1) i -= 1 frm.append(f1) else: compact_frames(f1) frm = None if type(f1) == YieldNode: frm = None i += 1 def label_frames(frames, cur=None): if cur == None: cur = [0] frames.label = cur[0] cur[0] += 1 for f in frames: if type(f) == Frame: if f.node != None: f.node.frame = f label_frames(f, cur) else: f.frame = f def prop_frame_refs(node, f): if hasattr(node, "frame"): f = node.frame else: node.frame = f for c in node.children: prop_frame_refs(c, f) def apply_frame_scope(n, scope, frames): if type(n) == IdentNode: if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) else: p = n.parent n2 = n #check for implicit declarations within catch and loop nodes while not null_node(p): if type(p) in [CatchNode, WhileNode, ForLoopNode]: break n2 = p p = p.parent if not null_node(p) and n2 == p[0]: scope[n.val] = frames.label n.val = "scope.%s_%d" % (n.val, scope[n.val]) elif type(n) == VarDeclNode: n.local = False; if "local" in n.modifiers: n.modifiers.remove("local") if hasattr(n.parent, "_c_loop_node"): frames = n.parent._c_loop_node.frame #print("yay", n.parent._c_loop_node.frame.label) if n.val not in scope: scope[n.val] = frames.label if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) for c in n.children: #ignore expr functions, but not nested functions? if type(c) == FunctionNode and type(c.parent) == AssignNode: continue if type(n) == BinOpNode and n.op == "." and c == n[1] and type(c) == IdentNode: continue if type(n) == FuncCallNode and type(c) == IdentNode and c == n[0]: continue apply_frame_scope(c, scope, frames) def frame_scope(frames, scope, depth=0): frames.scope = scope for f in frames: ss = "-" fstr = "" if type(f) == Frame: if f.node != None: fstr = f.node.get_line_str() else: if type(f[0]) == Frame: fstr = f[0].node.get_line_str() else: fstr = f[0].get_line_str() if f.node != None: ss = "+" scope2 = dict(scope) for i in range(f.node._startcur): apply_frame_scope(f.node[i], scope2, f) frame_scope(f, scope2, depth+1) else: frame_scope(f, scope, depth) else: fstr = f.get_line_str() apply_frame_scope(f, scope, frames) scope = {} for a in node.children[0]: scope[a.val] = 0 compact_frames(frames) label_frames(frames) prop_frame_refs(node, frames) frame_scope(frames, scope) #print_frames(frames) def frames_validate(frames): def gen_frame_validate(frames, tlevel=0): s = "" tstr = tab(tlevel+1) tstr2 = tab(tlevel+2) for f in frames: if type(f) == Frame: if f.node != None: cs = f.node.children f.node.children = f.node.children[:node._startcur] f.node.add(ExprNode([])) c = f.node.gen_js(tlevel+1).split("\n")[0].replace("{", "").replace("\n", "").replace("}", "").strip() if c.endswith(";"): c = c[:-1] s += tstr + c + " {\n" f.node.children = cs s += gen_frame_validate(f, tlevel+1) if f.node != None: s += tstr + "}\n" else: c = tstr + f.gen_js(tlevel+2) s += c if c.strip().endswith("}") == 0 and c.strip().endswith(";") == 0: s += ";" s += "\n" if tlevel == 0: c = node.gen_js(0).split("\n")[0] + "\n" s = c + s + "}\n" return s #print(node.gen_js(0)) #print(scope) #print_frames(frames) s = gen_frame_validate(frames) s2 = js_parse(s).gen_js(0).strip() s = node.gen_js(0).strip() s = js_parse(s, print_stack=False).gen_js(0).strip() print(s==s2) if s != s2: import difflib print(dir(difflib)) d = difflib.ndiff(s.split("\n"), s2.split("\n")) ds = "" for l in d: ds += l + "\n" #print(ds) line_print(s) line_print(s2) #frames_validate(frames) flatframes = [] def flatten_frames(frames): flatframes.append(frames) for f in frames: if type(f) == Frame: flatten_frames(f) flatten_frames(frames) #print([f.label for f in flatframes]) def frames_transform(frames, node2): scope = frames.scope node2 = FunctionNode(node.name, node.lineno) node2.add(ExprListNode([])) for c in node.children[0]: node2[0].add(IdentNode(c.val)) frames2 = frames for j, frames in enumerate(flatframes[1:]): p = frames.parent f = frames frames.return_frame = 0 frames.return_frame_parent = 0 i = p.index(f) while i >= len(p)-1 and p.parent != None: f = p p = p.parent i = p.index(f) if p.parent == None: frames.return_frame = 0 frames.return_frame_parent = p.label else: frames.return_frame = p[i+1].label frames.return_frame_parent = p.label def f_name(f): return "frame_%d" % f.label def f_ref(f): return "this.frame_%d" % f.label def f_raw_next(f): if f.parent == None: f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f)+1 while i < len(f.parent): if type(f.parent[i]) == Frame: return f.parent[i] i += 1 f = f.parent f = Frame() f.label = len(flatframes) return f def f_next(f, ignore_loops=False): if f.parent == None: if debug_gen: print("no f.parent! make frame"); f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f)+1 while i < len(f.parent): if type(f.parent[i]) == Frame: if type(f.parent[i].node) not in [CatchNode, ElseNode]: return f.parent[i] i += 1 if not ignore_loops and f.parent != None and \ type(f.parent.node) in \ [WhileNode, DoWhileNode, ForLoopNode]: if debug_gen: print("looper!", f.label, f.parent.label) return f.parent f = f.parent if debug_gen: print("made frame!", len(flatframes)) f = Frame() f.label = len(flatframes) return f def f_first(f): for f2 in f: if type(f2) == Frame: return f2 #return f def f_last(f): return f[-1] def has_parent(f, p): while f != p and f != None: f = f.parent return f == p def find_exit_points(f, p=None, vset=None): stack = [] if p == None: p = f if vset == None: vset = set() lst = [] """ lst = [] for f2 in f: if type(f2) == Frame: for f3 in f2.paths: if type(f3) == Frame: if not has_parent(f3, p) and f3.label not in vset: lst.append(f3) vset.add(f3.label) lst += find_exit_points(f3, p, vset) else: continue """ for f2 in f.paths: if not has_parent(f2, p) and f2.label not in vset: lst.append(f) vset.add(f.label) else: lst += find_exit_points(f2, p, vset) for f in lst: print(f.label) #sys.exit() return lst tot = len(node)-1 for i in range(tot): node.pop(1) def param_var(k): for c in node[0]: val = c.gen_js(0) val = val.strip() k = k.strip() if k == val: return True return False #build generator state data scopestr = "{" for k in scope: if scopestr != "{": scopestr += ", "; if param_var(k): scopestr += "%s_%i : %s" % (k, scope[k], k); else: scopestr += "%s_%i : %s" % (k, scope[k], "undefined"); scopestr += "}" node.add(js_parse("this.scope = $s;", [scopestr], start_node=AssignNode)) node.add(js_parse("this.ret = {done : false, value : undefined};", start_node=AssignNode)) node.add(js_parse("this.state = 1;", start_node=AssignNode)) node.add(js_parse("this.trystack = [];", start_node=AssignNode)) node.add(js_parse(""" this.next = function() { var ret; var stack = this.trystack; try { ret = this._next(); } catch (err) { if (stack.length > 0) { var item = stack.pop(stack.length-1); this.state = item[0]; this.scope[item[1]] = err; return this.next(); } else { throw err; } } return ret; }""", start_node=AssignNode)) node.add(js_parse(""" this.push_trystack = function(catchstate, catchvar) { this.trystack.push([catchstate, catchvar]); }""", start_node=AssignNode)) node.add(js_parse(""" this.pop_trystack = function() { this.trystack.pop(this.trystack.length-1); }""", start_node=AssignNode)) #build next function keynode = IdentNode("$__state"); sn = SwitchNode(keynode); slist = js_parse("var $__ret = undefined; var $__state = this.state; var scope = this.scope;"); slist2 = StatementList() slist2.add(sn) wn = WhileNode(BinOpNode(IdentNode("$__state"), NumLitNode(len(flatframes)), "<")) wn.add(slist2) wn[1].add(js_parse(""" if ($__ret != undefined) { break; } """, start_node=IfNode)); slist.add(wn); slist.add(js_parse(""" if ($__ret != undefined) { this.ret.value = $__ret.value; } else { this.ret.done = true; this.ret.value = undefined; } this.state = $__state; return this.ret; """)); next = js_parse("this._next = function() { };", start_node=AssignNode) next[1].add(slist) node.add(next) sn.line = slist.line = node.line sn.lexpos = slist.lexpos = node.lexpos #find leaves for f in flatframes: if len(f) > 0: f.leaf = True for c in f: if type(c) == Frame: f.leaf = False break #move control frame of dowhile statements to #after their statement body frames. visit = set() for i in range(len(flatframes)): if i in visit: continue f = flatframes[i] if f.leaf or type(f.node) != DoWhileNode: continue f2 = f_first(f) if f2 == None: continue last = f2.label while (f2 != f_next(f) and f2 != f): last = f2.label f2 = f_next(f2) last = ((last-1) if last > i else last) + 1 flatframes.pop(i); flatframes.insert(last, f); visit.add(last) for i, f in enumerate(flatframes): f.label = i #set up case statements for f in flatframes: n2 = CaseNode(NumLitNode(f.label)) sl = StatementList() if debug_gen: sl.add(js_parse("console.log(\"in frame $s\");", [f.label])); #set line/lexpos data if f.node != None: n2.line = f.node.line n2.lexpos = f.node.lexpos sl.line = f.node.line sl.lexpos = f.node.lexpos f.case_sl = sl n2.add(sl) #add to switch statement sn.add(n2) def set_linepos(n, line, lexpos): n.line = line n.lexpos = lexpos for c in n: set_linepos(c, line, lexpos) for f in flatframes: if f.leaf: for c in f: c.frame = f else: f.node.frame = f #handle loop breaks/continues visit = set() def visit_breaks(n): wn = n if n in visit: return visit.add(n) while type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]: if type(wn) == SwitchNode: typespace.error("Switches in generators not supported yet.", wn); wn = wn.parent if not wn: typespace.error("Invalid break statement.", n); if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f_next(f).label)) if "frame" in n.__dict__: n.frame.insert(n.frame.index(n), n2) else: n.parent.insert(i, n2) def visit_continues(n): if n in visit: return visit.add(n) wn = n while wn != None and (type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]): wn = wn.parent if wn == None: typespace.error("Invalid continue statement.", n); if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f.label)); n3 = BreakNode(); visit.add(n3) n.parent.remove(n) n.frame.replace(n, n2) n.frame.insert(n.frame.index(n2)+1, n3) def handle_yields(node): slist = js_parse("""$__ret = this.ret;"""); is_empty = type(node[0]) == ExprNode and len(node[0]) == 0 if is_empty: slist.add(js_parse("""$s.value = undefined;""", ["$__ret"], start_node=AssignNode)); else: slist.add(js_parse("""$s.value = $n;""", ["$__ret", node[0]], start_node=AssignNode)) slen = len(slist) #print(slist) if node in node.parent: i = node.parent.index(node) node.parent.remove(node) for j in range(slen): node.parent.insert(i, slist[slen-j-1]) i = node.frame.index(node) node.frame.remove(node) for j in range(slen): node.frame.insert(i, slist[slen-j-1]) #handle loop breaks for f in flatframes: if not f.leaf: continue; for c in f: traverse(c, BreakNode, visit_breaks, exclude=FunctionNode) pass #handle loop continues for f in flatframes: if not f.leaf: continue; for c in f: traverse(c, ContinueNode, visit_continues, exclude=FunctionNode) pass #handle yields for f in flatframes: if not f.leaf: continue for c in f: traverse(c, YieldNode, handle_yields, exclude=FunctionNode); def has_common_parent(n1, n2, p): while n1 != p and n1 != None: n1 = n1.parent while n2 != p and n2 != None: n2 = n2.parent if n1 == n2 and n1 == p: return True else: return False #build control code for f in flatframes: if f.leaf: continue n = f.node sl = f.case_sl if type(n) == IfNode: f2 = f_first(f) if f2 == None: #empty if node f2 = Frame() f2.label = len(flatframes) if len(n) > 2: f3 = n[2].frame else: f3 = f_next(f) f.paths += [f2, f3] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ["$__state", n[0], f2.label, f3.label]); set_linepos(n2, n.line, n.lexpos); sl.add(n2) elif type(n) == ElseNode: f2 = f_first(f) if f2 == None: #empty else node f2 = Frame() f2.label = len(flatframes) f.paths += [f2] n2 = js_parse(";$s = $s;", ("$__state", str(f2.label))) set_linepos(n2, n.line, n.lexpos); sl.add(n2) elif type(n) == WhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label)); set_linepos(n2, n.line, n.lexpos); sl.add(n2) elif type(n) == ForLoopNode: #okay, why did I say to ignore loops here? f.paths += [f_first(f), f_next(f, False)] if type(n[0]) == ForCNode: n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0][1], f_first(f).label, f_next(f, False).label)); set_linepos(n2, n.line, n.lexpos); sl.add(n2) else: typespace.error("process_generators expects unpacked iterator for loops") elif type(n) == DoWhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == TryNode: f.paths += [f_first(f)] cn = f_raw_next(f).node if type(cn) != CatchNode: typespace.error("Missing catch block", f.node) ident = cn[0].gen_js(0).replace("scope.", "") n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) n3 = js_parse("this.push_trystack($s, \"$s\");", [f_raw_next(f).label, ident]) set_linepos(n2, n.line, n.lexpos) set_linepos(n3, n.line, n.lexpos) sl.add(n2) sl.add(n3) elif type(n) == CatchNode: f.paths += [f_first(f)] n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) #build leaf code for f in flatframes: if not f.leaf: continue sl = f.case_sl for n in f: sl.add(n) f2 = f_next(f) sl.add(js_parse(";$s=$s;", ("$__state", str(f2.label)))) f.paths += [f2] #add in pop_trystack calls for f in flatframes: if type(f.node) != TryNode: continue f2 = f_last(f) ps = find_exit_points(f) for f2 in ps: f2.case_sl.add(js_parse("this.pop_trystack();")) #add case breaks for f in flatframes: bn = BreakNode() bn.line = f.case_sl.line bn.lexpos = f.case_sl.lexpos f.case_sl.add(bn); #add final state case cn = CaseNode(NumLitNode(len(flatframes))) sl2 = StatementList() sl2.add(BreakNode()) cn.add(sl2) sn.add(cn) #default case df = DefaultCaseNode() df.add(js_parse("console.log(\"Generator state error\"); console.trace();")) df[0].add(BreakNode()) sn.add(df) outernode = js_parse(""" function() { var __gen_this2 = this; function _generator_iter() { } return new _generator_iter(); } """, start_node=FunctionNode); #add a self-referencing [Symbol.iterator] method n = js_parse(""" this[Symbol.iterator] = function() { return this; } """); for c in n: node.add(c); #and, a es5.1-style forEach method n = js_parse(""" this.forEach = function(callback, thisvar) { if (thisvar == undefined) thisvar = self; var _i = 0; while (1) { var ret = this.next(); if (ret == undefined || ret.done || (ret._ret != undefined && ret._ret.done)) break; callback.call(thisvar, ret.value); if (_i++ > 100) { console.log("inf loop", ret); break; } } } """); for c in n: node.add(c); outernode.name = node.name; if node.is_anonymous: outernode.is_anonymous = True outernode.replace(outernode[0], node[0]) node.parent.replace(node, outernode); node2 = outernode[2] cs = node[:] for c in cs[1:]: node2.add(c)
def visit_generators(node): if not node.is_generator: return def _remove_this(n): if n.val != "this": return if type(n.parent) != BinOpNode or n.parent.op != ".": #typespace.error("Can only reference members of 'this' in generators"); n.val = "__gen_this2" else: n.val = "__gen_this2" #n.parent.parent.replace(n.parent, n.parent[1]) def set_cur(n): if type(n) in [IfNode, WhileNode, DoWhileNode, ForLoopNode, CatchNode]: n._cur = 1 n._startcur = 1 else: n._cur = 0 n._startcur = 0 n._start = True n._has_yield = node_has_yield(n) for c in n: set_cur(c) def prior_if(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n) - 1 while 1: while i >= 0: if type(sl[i]) == IfNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: typespace.error("Orphaned else node", n) sys.exit(-1) return sl[i] def prior_try(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n) - 1 while 1: while i >= 0: if type(sl[i]) == TryNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: typespace.error("Orphaned catch node", n) sys.exit(-1) return sl[i] def is_stype(n): ret = type(n) in stypes # and (n._has_yield or n.parent._has_yield) return ret if type(n) == CatchNode: ret |= prior_try(n)._has_yield if type(n) == ElseNode: ret |= prior_if(n)._has_yield if type(n) in [IfNode, ElseNode]: p5 = n.parent while not null_node(p5): if hasattr(p5, "_has_yield") and p5._has_yield: ret = True break p5 = p5.parent return ret combine_if_else_nodes(node) traverse(node, ForCNode, unpack_for_c_loops, exclude=[FunctionNode], copy_children=True) traverse(node, IdentNode, _remove_this) traverse(node, VarDeclNode, _remove_this) frames = frame = Frame(node=node) stack = [c for c in node.children[1:]] stack.reverse() stypes = set([ ForLoopNode, WhileNode, DoWhileNode, IfNode, ElseNode, TryNode, CatchNode ]) for c in stack: set_cur(c) while len(stack) > 0: n = stack.pop(-1) if is_stype(n) or type(n) == StatementList: if n._start: if type(n) != StatementList: f = Frame(node=n) frame.append(f) frame = f n._start = False if n._cur < len(n.children): stack.append(n) stack.append(n[n._cur]) n._cur += 1 elif type(n) != StatementList: frame = frame.parent else: frame.append(n) def compact_frames(frames): i = 0 frm = None while i < len(frames): f1 = frames[i] if type(f1) == YieldNode: frm = None if type(f1) != Frame: if frm == None: frm = Frame() frames.insert(i, frm) frm.parent = frames i += 1 frames.remove(f1) i -= 1 frm.append(f1) else: compact_frames(f1) frm = None if type(f1) == YieldNode: frm = None i += 1 def label_frames(frames, cur=None): if cur == None: cur = [0] frames.label = cur[0] cur[0] += 1 for f in frames: if type(f) == Frame: if f.node != None: f.node.frame = f label_frames(f, cur) else: f.frame = f def prop_frame_refs(node, f): if hasattr(node, "frame"): f = node.frame else: node.frame = f for c in node.children: prop_frame_refs(c, f) def apply_frame_scope(n, scope, frames): if type(n) == IdentNode: if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) else: p = n.parent n2 = n #check for implicit declarations within catch and loop nodes while not null_node(p): if type(p) in [CatchNode, WhileNode, ForLoopNode]: break n2 = p p = p.parent if not null_node(p) and n2 == p[0]: scope[n.val] = frames.label n.val = "scope.%s_%d" % (n.val, scope[n.val]) elif type(n) == VarDeclNode: n.local = False if "local" in n.modifiers: n.modifiers.remove("local") if hasattr(n.parent, "_c_loop_node"): frames = n.parent._c_loop_node.frame #print("yay", n.parent._c_loop_node.frame.label) if n.val not in scope: scope[n.val] = frames.label if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) for c in n.children: #ignore expr functions, but not nested functions? if type(c) == FunctionNode and type(c.parent) == AssignNode: continue if type(n) == BinOpNode and n.op == "." and c == n[1] and type( c) == IdentNode: continue if type(n) == FuncCallNode and type(c) == IdentNode and c == n[0]: continue apply_frame_scope(c, scope, frames) def frame_scope(frames, scope, depth=0): frames.scope = scope for f in frames: ss = "-" fstr = "" if type(f) == Frame: if f.node != None: fstr = f.node.get_line_str() else: if type(f[0]) == Frame: fstr = f[0].node.get_line_str() else: fstr = f[0].get_line_str() if f.node != None: ss = "+" scope2 = dict(scope) for i in range(f.node._startcur): apply_frame_scope(f.node[i], scope2, f) frame_scope(f, scope2, depth + 1) else: frame_scope(f, scope, depth) else: fstr = f.get_line_str() apply_frame_scope(f, scope, frames) scope = {} for a in node.children[0]: scope[a.val] = 0 compact_frames(frames) label_frames(frames) prop_frame_refs(node, frames) frame_scope(frames, scope) #print_frames(frames) def frames_validate(frames): def gen_frame_validate(frames, tlevel=0): s = "" tstr = tab(tlevel + 1) tstr2 = tab(tlevel + 2) for f in frames: if type(f) == Frame: if f.node != None: cs = f.node.children f.node.children = f.node.children[:node._startcur] f.node.add(ExprNode([])) c = f.node.gen_js(tlevel + 1).split("\n")[0].replace( "{", "").replace("\n", "").replace("}", "").strip() if c.endswith(";"): c = c[:-1] s += tstr + c + " {\n" f.node.children = cs s += gen_frame_validate(f, tlevel + 1) if f.node != None: s += tstr + "}\n" else: c = tstr + f.gen_js(tlevel + 2) s += c if c.strip().endswith("}") == 0 and c.strip().endswith( ";") == 0: s += ";" s += "\n" if tlevel == 0: c = node.gen_js(0).split("\n")[0] + "\n" s = c + s + "}\n" return s #print(node.gen_js(0)) #print(scope) #print_frames(frames) s = gen_frame_validate(frames) s2 = js_parse(s).gen_js(0).strip() s = node.gen_js(0).strip() s = js_parse(s, print_stack=False).gen_js(0).strip() print(s == s2) if s != s2: import difflib print(dir(difflib)) d = difflib.ndiff(s.split("\n"), s2.split("\n")) ds = "" for l in d: ds += l + "\n" #print(ds) line_print(s) line_print(s2) #frames_validate(frames) flatframes = [] def flatten_frames(frames): flatframes.append(frames) for f in frames: if type(f) == Frame: flatten_frames(f) flatten_frames(frames) #print([f.label for f in flatframes]) def frames_transform(frames, node2): scope = frames.scope node2 = FunctionNode(node.name, node.lineno) node2.add(ExprListNode([])) for c in node.children[0]: node2[0].add(IdentNode(c.val)) frames2 = frames for j, frames in enumerate(flatframes[1:]): p = frames.parent f = frames frames.return_frame = 0 frames.return_frame_parent = 0 i = p.index(f) while i >= len(p) - 1 and p.parent != None: f = p p = p.parent i = p.index(f) if p.parent == None: frames.return_frame = 0 frames.return_frame_parent = p.label else: frames.return_frame = p[i + 1].label frames.return_frame_parent = p.label def f_name(f): return "frame_%d" % f.label def f_ref(f): return "this.frame_%d" % f.label def f_raw_next(f): if f.parent == None: f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f) + 1 while i < len(f.parent): if type(f.parent[i]) == Frame: return f.parent[i] i += 1 f = f.parent f = Frame() f.label = len(flatframes) return f def f_next(f, ignore_loops=False): if f.parent == None: if debug_gen: print("no f.parent! make frame") f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f) + 1 while i < len(f.parent): if type(f.parent[i]) == Frame: if type(f.parent[i].node) not in [CatchNode, ElseNode]: return f.parent[i] i += 1 if not ignore_loops and f.parent != None and \ type(f.parent.node) in \ [WhileNode, DoWhileNode, ForLoopNode]: if debug_gen: print("looper!", f.label, f.parent.label) return f.parent f = f.parent if debug_gen: print("made frame!", len(flatframes)) f = Frame() f.label = len(flatframes) return f def f_first(f): for f2 in f: if type(f2) == Frame: return f2 #return f def f_last(f): return f[-1] def has_parent(f, p): while f != p and f != None: f = f.parent return f == p def find_exit_points(f, p=None, vset=None): stack = [] if p == None: p = f if vset == None: vset = set() lst = [] """ lst = [] for f2 in f: if type(f2) == Frame: for f3 in f2.paths: if type(f3) == Frame: if not has_parent(f3, p) and f3.label not in vset: lst.append(f3) vset.add(f3.label) lst += find_exit_points(f3, p, vset) else: continue """ for f2 in f.paths: if not has_parent(f2, p) and f2.label not in vset: lst.append(f) vset.add(f.label) else: lst += find_exit_points(f2, p, vset) for f in lst: print(f.label) #sys.exit() return lst tot = len(node) - 1 for i in range(tot): node.pop(1) def param_var(k): for c in node[0]: val = c.gen_js(0) val = val.strip() k = k.strip() if k == val: return True return False #build generator state data scopestr = "{" for k in scope: if scopestr != "{": scopestr += ", " if param_var(k): scopestr += "%s_%i : %s" % (k, scope[k], k) else: scopestr += "%s_%i : %s" % (k, scope[k], "undefined") scopestr += "}" node.add(js_parse("this.scope = $s;", [scopestr], start_node=AssignNode)) node.add( js_parse("this.ret = {done : false, value : undefined};", start_node=AssignNode)) node.add(js_parse("this.state = 1;", start_node=AssignNode)) node.add(js_parse("this.trystack = [];", start_node=AssignNode)) node.add( js_parse(""" this.next = function() { var ret; var stack = this.trystack; try { ret = this._next(); } catch (err) { if (stack.length > 0) { var item = stack.pop(stack.length-1); this.state = item[0]; this.scope[item[1]] = err; return this.next(); } else { throw err; } } return ret; }""", start_node=AssignNode)) node.add( js_parse(""" this.push_trystack = function(catchstate, catchvar) { this.trystack.push([catchstate, catchvar]); }""", start_node=AssignNode)) node.add( js_parse(""" this.pop_trystack = function() { this.trystack.pop(this.trystack.length-1); }""", start_node=AssignNode)) #build next function keynode = IdentNode("$__state") sn = SwitchNode(keynode) slist = js_parse( "var $__ret = undefined; var $__state = this.state; var scope = this.scope;" ) slist2 = StatementList() slist2.add(sn) wn = WhileNode( BinOpNode(IdentNode("$__state"), NumLitNode(len(flatframes)), "<")) wn.add(slist2) wn[1].add( js_parse(""" if ($__ret != undefined) { break; } """, start_node=IfNode)) slist.add(wn) slist.add( js_parse(""" if ($__ret != undefined) { this.ret.value = $__ret.value; } else { this.ret.done = true; this.ret.value = undefined; } this.state = $__state; return this.ret; """)) next = js_parse("this._next = function() { };", start_node=AssignNode) next[1].add(slist) node.add(next) sn.line = slist.line = node.line sn.lexpos = slist.lexpos = node.lexpos #find leaves for f in flatframes: if len(f) > 0: f.leaf = True for c in f: if type(c) == Frame: f.leaf = False break #move control frame of dowhile statements to #after their statement body frames. visit = set() for i in range(len(flatframes)): if i in visit: continue f = flatframes[i] if f.leaf or type(f.node) != DoWhileNode: continue f2 = f_first(f) if f2 == None: continue last = f2.label while (f2 != f_next(f) and f2 != f): last = f2.label f2 = f_next(f2) last = ((last - 1) if last > i else last) + 1 flatframes.pop(i) flatframes.insert(last, f) visit.add(last) for i, f in enumerate(flatframes): f.label = i #set up case statements for f in flatframes: n2 = CaseNode(NumLitNode(f.label)) sl = StatementList() if debug_gen: sl.add(js_parse("console.log(\"in frame $s\");", [f.label])) #set line/lexpos data if f.node != None: n2.line = f.node.line n2.lexpos = f.node.lexpos sl.line = f.node.line sl.lexpos = f.node.lexpos f.case_sl = sl n2.add(sl) #add to switch statement sn.add(n2) def set_linepos(n, line, lexpos): n.line = line n.lexpos = lexpos for c in n: set_linepos(c, line, lexpos) for f in flatframes: if f.leaf: for c in f: c.frame = f else: f.node.frame = f #handle loop breaks/continues visit = set() def visit_breaks(n): wn = n if n in visit: return visit.add(n) while type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]: if type(wn) == SwitchNode: typespace.error("Switches in generators not supported yet.", wn) wn = wn.parent if not wn: typespace.error("Invalid break statement.", n) if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f_next(f).label)) if "frame" in n.__dict__: n.frame.insert(n.frame.index(n), n2) else: n.parent.insert(i, n2) def visit_continues(n): if n in visit: return visit.add(n) wn = n while wn != None and (type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]): wn = wn.parent if wn == None: typespace.error("Invalid continue statement.", n) if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f.label)) n3 = BreakNode() visit.add(n3) n.parent.remove(n) n.frame.replace(n, n2) n.frame.insert(n.frame.index(n2) + 1, n3) def handle_yields(node): slist = js_parse("""$__ret = this.ret;""") is_empty = type(node[0]) == ExprNode and len(node[0]) == 0 if is_empty: slist.add( js_parse("""$s.value = undefined;""", ["$__ret"], start_node=AssignNode)) else: slist.add( js_parse("""$s.value = $n;""", ["$__ret", node[0]], start_node=AssignNode)) slen = len(slist) #print(slist) if node in node.parent: i = node.parent.index(node) node.parent.remove(node) for j in range(slen): node.parent.insert(i, slist[slen - j - 1]) i = node.frame.index(node) node.frame.remove(node) for j in range(slen): node.frame.insert(i, slist[slen - j - 1]) #handle loop breaks for f in flatframes: if not f.leaf: continue for c in f: traverse(c, BreakNode, visit_breaks, exclude=FunctionNode) pass #handle loop continues for f in flatframes: if not f.leaf: continue for c in f: traverse(c, ContinueNode, visit_continues, exclude=FunctionNode) pass #handle yields for f in flatframes: if not f.leaf: continue for c in f: traverse(c, YieldNode, handle_yields, exclude=FunctionNode) def has_common_parent(n1, n2, p): while n1 != p and n1 != None: n1 = n1.parent while n2 != p and n2 != None: n2 = n2.parent if n1 == n2 and n1 == p: return True else: return False #build control code for f in flatframes: if f.leaf: continue n = f.node sl = f.case_sl if type(n) == IfNode: f2 = f_first(f) if f2 == None: #empty if node f2 = Frame() f2.label = len(flatframes) if len(n) > 2: f3 = n[2].frame else: f3 = f_next(f) f.paths += [f2, f3] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ["$__state", n[0], f2.label, f3.label]) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == ElseNode: f2 = f_first(f) if f2 == None: #empty else node f2 = Frame() f2.label = len(flatframes) f.paths += [f2] n2 = js_parse(";$s = $s;", ("$__state", str(f2.label))) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == WhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label)) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == ForLoopNode: #okay, why did I say to ignore loops here? f.paths += [f_first(f), f_next(f, False)] if type(n[0]) == ForCNode: n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0][1], f_first(f).label, f_next(f, False).label)) set_linepos(n2, n.line, n.lexpos) sl.add(n2) else: typespace.error( "process_generators expects unpacked iterator for loops", n) elif type(n) == DoWhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == TryNode: f.paths += [f_first(f)] cn = f_raw_next(f).node if type(cn) != CatchNode: typespace.error("Missing catch block", f.node) ident = cn[0].gen_js(0).replace("scope.", "") n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) n3 = js_parse("this.push_trystack($s, \"$s\");", [f_raw_next(f).label, ident]) set_linepos(n2, n.line, n.lexpos) set_linepos(n3, n.line, n.lexpos) sl.add(n2) sl.add(n3) elif type(n) == CatchNode: f.paths += [f_first(f)] n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) #build leaf code for f in flatframes: if not f.leaf: continue sl = f.case_sl for n in f: sl.add(n) f2 = f_next(f) sl.add(js_parse(";$s=$s;", ("$__state", str(f2.label)))) f.paths += [f2] #add in pop_trystack calls for f in flatframes: if type(f.node) != TryNode: continue f2 = f_last(f) ps = find_exit_points(f) for f2 in ps: f2.case_sl.add(js_parse("this.pop_trystack();")) #add case breaks for f in flatframes: bn = BreakNode() bn.line = f.case_sl.line bn.lexpos = f.case_sl.lexpos f.case_sl.add(bn) #add final state case cn = CaseNode(NumLitNode(len(flatframes))) sl2 = StatementList() sl2.add(BreakNode()) cn.add(sl2) sn.add(cn) #default case df = DefaultCaseNode() df.add( js_parse("console.log(\"Generator state error\"); console.trace();")) df[0].add(BreakNode()) sn.add(df) outernode = js_parse(""" function() { var __gen_this2 = this; function _generator_iter() { } return new _generator_iter(); } """, start_node=FunctionNode) #add a self-referencing [Symbol.iterator] method n = js_parse(""" this[Symbol.iterator] = function() { return this; } """) for c in n: node.add(c) #and, a es5.1-style forEach method n = js_parse(""" this.forEach = function(callback, thisvar) { if (thisvar == undefined) thisvar = self; var _i = 0; while (1) { var ret = this.next(); if (ret == undefined || ret.done || (ret._ret != undefined && ret._ret.done)) break; callback.call(thisvar, ret.value); if (_i++ > 100) { console.log("inf loop", ret); break; } } } """) for c in n: node.add(c) outernode.name = node.name if node.is_anonymous: outernode.is_anonymous = True outernode.replace(outernode[0], node[0]) node.parent.replace(node, outernode) node2 = outernode[2] cs = node[:] for c in cs[1:]: node2.add(c)
def parse_intern(data, create_logger=False, expand_loops=True, expand_generators=True): glob.g_lines = data.split("\n") if glob.g_preprocess_code: data = preprocess_text(data, glob.g_file) if glob.g_print_tokens: plexer.input(data) tok = plexer.token() while tok != None: print(tok) tok = plexer.token() plexer.input(data) glob.g_lexer = plexer result = parser.parse(data, lexer=plexer) if result == None: if glob.g_error_pre != None: glob.g_error = True result = StatementList() if glob.g_error: print_err(glob.g_error_pre) typespace = JSTypeSpace() if result != None: if len(result) > 0 and type( result[0]) == StrLitNode and result[0].val == '"use strict"': glob.g_force_global_strict = True elif len(result) > 0 and type( result[0]) == StrLitNode and result[0].val == '"not_a_module"': glob.g_es6_modules = False if glob.g_compile_statics_only: process_static_vars(result, typespace) return result.gen_js(0), result if glob.g_enable_let: process_let(result, typespace) if glob.g_force_global_strict: kill_bad_globals(result, typespace) #handle .? operator transform_exisential_operators(result, typespace) if glob.g_write_manifest and glob.g_outfile != "": buf = gen_manifest_file(result, typespace) file = open(glob.g_outfile + ".manifest", "w") file.write(buf) file.close() if glob.g_es6_modules: module_transform(result, typespace) if glob.g_require_js: expand_requirejs_classes(result, typespace) else: expand_harmony_classes(result, typespace) expand_typed_classes(result, typespace) if glob.g_clear_slashr: print("\n") if result != None and len(result) == 0: result = None return "", None #sys.stdout.write("Error: empty compilation\n"); #raise JSError("Empty compilation"); global f_id f_id = [0] flatten_statementlists(result, typespace) has_generators = [False] def has_generator(n): if type(n) == YieldNode: has_generators[0] = True for c in n: has_generator(c) has_generator(result) if expand_loops or has_generators[0]: expand_of_loops(result, typespace) #combine_try_nodes may have nested statementlists again, so better reflatten flatten_statementlists(result, typespace) if create_logger: traverse(result, FunctionNode, create_type_logger) process_arrow_function_this(result, typespace) if expand_generators: flatten_statementlists(result, typespace) process_generators(result, typespace) flatten_statementlists(result, typespace) if glob.g_add_opt_initializers: add_func_opt_code(result, typespace) if glob.g_combine_ifelse_nodes: combine_if_else_nodes(result) if glob.g_print_nodes: print("nodes: ", result) pass if glob.g_replace_instanceof and not glob.g_require_js: replace_instanceof(result, typespace) if glob.g_enable_static_vars: process_static_vars(result, typespace) if glob.g_do_docstrings: process_docstrings(result, typespace) if glob.g_gen_v7_bytecode: from js_opcode_emit2 import v7_emit_bytecode buf = v7_emit_bytecode(result, typespace) elif not glob.g_minify: buf = result.gen_js(0) else: buf, smap = js_minify(result) if glob.g_outfile == "": print(buf) return buf, result
def combine_try_nodes(node): return def error(msg, srcnode): if glob.g_print_stack: pass #traceback.print_stack() lines = glob.g_lexdata.split("\n") s = max(srcnode.line - 30, 0) e = min(srcnode.line + 3, len(lines) - 1) ls = "" for i in range(s, e): ls += str(i + 1) + ": " + lines[i] + "\n" sys.stderr.write("\n%s\n%s:(%s): error: %s\n" % (ls, srcnode.file, srcnode.line + 1, msg)) sys.exit(-1) def visit(n): if type(n.parent) == TryNode: return sl = n.parent i = sl.children.index(n) i -= 1 #we remove n here, since we may have to ascend through #several layers of StatementList nodes #sl.children.remove(n) p = n.parent lastp = n while p and type(p) == StatementList: print(p.get_line_str()) if lastp is not None and type(p) == StatementList: i = max(p.index(lastp) - 1, 0) if type(p[i]) == TryNode: p = p[i] break elif type(p[i]) != StatementList: n = p error( "%s:(%d): error: orphaned catch block 1\n" % (n.file, n.line), n) sys.exit(-1) lastp = p p = p.parent if type(p) != TryNode or len(p) >= 2: n = p error( "%s:(%d): error: orphaned catch block 2\n" % (n.file, n.line), n) sys.exit(-1) n.parent.remove(n) p.add(n) return traverse(node, CatchNode, visit, copy_children=True)
def bleh(): for frames in flatframes: fname = f_name(frames) n = js_parse(""" function $s1(scope) { if (_do_frame_debug) console.log("in $s1"); }""", (fname), start_node=FunctionNode) if type(n[1]) != StatementList: n.replace(n[1], StatementList()) n = n[1] func = n while type(func) != FunctionNode: func = func.parent excl = (type(frames.node) == StatementList and type(frames.parent.node) == FunctionNode) if frames.node != None and not excl and type( frames.node) != FunctionNode: f = frames sl = StatementList() f.node[f.node._startcur] = sl frames.funcnode = func frames.subnode = frames.funcnode local_frames = "[" totframes = 0 for i, f in enumerate(frames): if type(f) != Frame: frames.subnode.add(f) frames.leaf = True else: frames.leaf = False if len(local_frames) > 1: local_frames += ", " local_frames += f_ref(f) #.replace("this.", "") totframes += 1 if f.node != None and type(f.node) != FunctionNode: if len(f.node.children) > f.node._startcur + 1: do_conv(f.node, f) if frames.leaf: f2 = f_next(frames) f2 = f2.label if f2 != -1 else -1 frames.subnode.add( js_parse("return [$i, undefined];", [f2], start_node=ReturnNode)) local_frames = "%s_frames = " % f_ref(frames) + local_frames + "];" frames.frames = js_parse(local_frames) frames.totframes = totframes def build_next(f, parent=None): if type(f) != Frame: return subnode = f.subnode if f.label >= 0: # and f.label < 3: n2 = js_parse("this.$s1 = 0;", [f_name(f)], start_node=AssignNode) n2.replace(n2[1], f.funcnode) f.funcnode.name = "(anonymous)" f.funcnode.is_anonymous = True node2.add(n2) #f.funcnode) if f.totframes > 0: if f.node != None and type(f.node) == WhileNode: f2 = f_next(f) f2 = f2.label if f2 != -1 else -1 n = js_parse( """ if (!"placeholder") { return [$i1, undefined]; } """, [f2]) if n == None: typespace.error("internal error", subnode) n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0]) subnode.add(n) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) elif f.node != None and type(f.node) == TryNode: n = StatementList() if n == None: typespace.error("internal error", subnode) f3 = f_raw_next(f) while f3 != -1 and type(f3.node) != CatchNode: f3 = f_raw_next(f3) if f3 == -1: typespace.error("Orphaned try block", f.node) f3name = "_nfothing" if len(f3.node) > 0: f3name = f3.node[0].gen_js(0).replace("scope.", "") n.add( js_parse( """ this.trystack.push([$i, "$s"]); """, [f3.label, f3name])) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == IfNode: f2 = f_first(f) f1 = f_raw_next(f) while type( f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse( """ if (!("placeholder")) { return [$i1, undefined]; } else { return [$i2, undefined]; } """, [f1.label, f2.label]) n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0].copy()) if n == None: typespace.error("internal error", subnode) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == ElseNode: f2 = f_first(f) f1 = f_raw_next(f) while type( f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse( """ return [$i1, undefined]; """, [f2.label]) if n == None: typespace.error("internal error", subnode) f2 = f_first(f) subnode.add(n) elif f.node != None and type(f.node) == CatchNode: f2 = f_first(f) n = js_parse( """ return [$i1, undefined]; """, [f2.label]) if n == None: typespace.error("internal error", subnode) subnode.add(n) elif f.node != None and type(f.node) == ForLoopNode: f2 = f_first(f) f3 = f_next(f) f3 = f3.label if f2 != -1 else -1 f2 = f2.label if f2 != -1 else -1 n = js_parse( """ if ($n) { return [$i, undefined]; } else { return [$i, undefined]; } """, [f.node[0][1], f2, f3]) if n == None: typespace.error("internal error", subnode) subnode.add(n) node2.insert( 1, js_parse(""" this[Symbol.iterator] = function() { return this; } """)[0]) for f in flatframes: build_next(f, f.parent) #process returns from within try nodes for f in flatframes: if f.parent != None and type(f.parent.node) == TryNode: def visit_rets1(n2): target = n2[0][0][0].val isyield = n2[0][0][1].val ni = n2.parent.index(n2) if target >= f_next(f.parent).label: n3 = js_parse("this.trystack.pop();")[0] n2.parent.insert(ni, n3) traverse(f.subnode, ReturnNode, visit_rets1, copy_children=True) #process yields for f in flatframes: f2 = f.parent set_yield = None def visit_rets2(n2): if set_yield != None: #print(n2) n2[0][0].replace(n2[0][0][1], set_yield) set_yield = find_node(f.subnode, YieldNode) if set_yield != None: set_yield.parent.remove(set_yield) set_yield = ArrayLitNode(ExprListNode([set_yield[0]])) traverse(f.subnode, ReturnNode, visit_rets2, copy_children=True) def find_parent_frame(f, ntypes, include_first=True): p = f if not include_first: p = p.parent while p != None: if type(p.node) in ntypes: return p p = p.parent return None #process breaks for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: if type(p) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p != f.subnode and not null_node(p): return #break corresponds to a loop internal to this frame p = find_parent_frame(f, [WhileNode, DoWhileNode, ForLoopNode], True) if p == None: typespace.error( "Invalid break statement (switches within generators aren't supported yet)", n2) f2 = f_next(p) n3 = js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode) n2.parent.replace(n2, n3) traverse(f.subnode, BreakNode, visit_rets3, copy_children=True) #process continues for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: p = p.parent if p != f.subnode and not null_node(p): return #continue corresponds to a loop internal to this frame p = f.parent while p != None: if type(p.node) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p == None: typespace.error("Invalid continue statement") n3 = js_parse("return [$i, undefined];", [p.label], start_node=ReturnNode) n2.parent.replace(n2, n3) traverse(f.subnode, ContinueNode, visit_rets3, copy_children=True) firstnode = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode2 = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode.replace(firstnode[1], StatementList()) firstnode2.replace(firstnode2[1], StatementList()) flatframes[0].subnode.add(firstnode) node2.insert(1, firstnode2[1]) firstnode = firstnode[1] firstnode2 = firstnode2[1] args = list(node.children[0]) for i3 in range(len(args)): argn = args[i3] while type(argn) not in [IdentNode, VarDeclNode]: argn = argn[0] args[i3] = argn.val scope = {} for f in flatframes: scope.update(f.scope) s = "{" j2 = 0 for j, v in enumerate(scope.keys()): if j2 > 0: s += ", " j2 += 1 if v in args: s += "%s:%s" % ("%s_%s" % (v, scope[v]), v) else: s += "%s:undefined" % ("%s_%s" % (v, scope[v])) s += "}" s = "this.scope = %s;\n" % s firstnode2.add(js_parse(s)[0]) #ensure all frames have returns for f in flatframes: if not find_node(f.subnode, ReturnNode): f.subnode.add( js_parse("return [$i, undefined];", [f_next(f).label], start_node=ReturnNode)) framelist = "[" for i, f in enumerate(flatframes): if i > 0: framelist += ", " framelist += "this.frame_%i" % f.label framelist = "this.frames = %s];" % framelist node2.add(js_parse(framelist)) node2.add( js_parse( """ this.cur = 1; this.trystack = new Array(); this.next = function() { var ret; while (this.cur < this.frames.length) { try { ret = this.frames[this.cur].call(this, this.scope); } catch (_generator_error) { if (this.trystack.length > 0) { var ts1 = this.trystack.pop(); this.scope[ts1[1]] = _generator_error; ret = [ts1[0], undefined]; } else { throw _generator_error; } } if (ret[0] == this.frames.length) { return {done : true, value : undefined}; break; } if (ret[0] == this.cur) { console.trace(); console.log("YEEK!") return {done : true, value : undefined}; } this.cur = ret[0]; if (ret[1] != undefined) { return {value : ret[1][0], done : false}; } else { return {value : undefined, done : false}; } } } """, [])) node.parent.replace(node, node2)
def parse_intern(data, create_logger=False, expand_loops=True, expand_generators=True): glob.g_lines = data.split("\n") if glob.g_preprocess_code: data = preprocess_text(data, glob.g_file) if glob.g_print_tokens: plexer.input(data) tok = plexer.token() while tok != None: print(tok) tok = plexer.token() plexer.input(data) glob.g_lexer = plexer result = parser.parse(data, lexer=plexer) if result == None: if glob.g_error_pre != None: glob.g_error = True result = StatementList() if glob.g_error: print_err(glob.g_error_pre) typespace = JSTypeSpace() if result != None: if len(result) > 0 and type(result[0]) == StrLitNode and result[0].val == '"use strict"': glob.g_force_global_strict = True elif len(result) > 0 and type(result[0]) == StrLitNode and result[0].val == '"not_a_module"': glob.g_es6_modules = False if glob.g_force_global_strict: kill_bad_globals(result, typespace) # handle .? operator transform_exisential_operators(result, typespace) if glob.g_write_manifest and glob.g_outfile != "": buf = gen_manifest_file(result, typespace) file = open(glob.g_outfile + ".manifest", "w") file.write(buf) file.close() if glob.g_es6_modules: module_transform(result, typespace) if glob.g_require_js: expand_requirejs_classes(result, typespace) else: expand_harmony_classes(result, typespace) expand_typed_classes(result, typespace) if glob.g_clear_slashr: print("\n") if result != None and len(result) == 0: result = None return "", None # sys.stdout.write("Error: empty compilation\n"); # raise JSError("Empty compilation"); global f_id f_id = [0] flatten_statementlists(result, typespace) if expand_loops: expand_of_loops(result, typespace) # combine_try_nodes may have nested statementlists again, so better reflatten flatten_statementlists(result, typespace) if create_logger: traverse(result, FunctionNode, create_type_logger) if expand_generators: flatten_statementlists(result, typespace) process_generators(result, typespace) flatten_statementlists(result, typespace) if glob.g_add_opt_initializers: add_func_opt_code(result, typespace) debug_forloop_expansion = False if debug_forloop_expansion: reset = 0 if reset or not os.path.exists("cur_d.txt"): f = open("cur_d.txt", "w") f.write("-1") f.close() f = open("cur_d.txt", "r") d = int(f.read()) print("\n\nd: %d\n" % d) traverse_i(result, ForInNode, expand_mozilla_forloops, d, use_scope=True) f.close() f = open("cur_d.txt", "w") f.write(str(d + 1)) f.close() if glob.g_combine_ifelse_nodes: combine_if_else_nodes(result) if glob.g_print_nodes: print("nodes: ", result) pass if glob.g_replace_instanceof and not glob.g_require_js: replace_instanceof(result, typespace) if glob.g_enable_static_vars: process_static_vars(result, typespace) if glob.g_do_docstrings: process_docstrings(result, typespace) if glob.g_gen_source_map: smap = SourceMap() def set_smap(node, smap): node.smap = smap for n in node: set_smap(n, smap) set_smap(result, smap) if not glob.g_minify: buf = result.gen_js(0) map = gen_source_map(data, buf, smap) else: buf, smap = js_minify(result) map = gen_source_map(data, buf, smap) if glob.g_add_srcmap_ref: spath = glob.g_outfile if os.path.sep in spath: spath = os.path.split(spath)[1] buf += "\n//# sourceMappingURL=/content/%s\n" % (spath + ".map") if glob.g_gen_smap_orig: f = open(glob.g_outfile + ".origsrc", "w") f.write(data) f.close() else: if not glob.g_minify: buf = result.gen_js(0) else: buf, smap = js_minify(result) if glob.g_outfile == "": print(buf) if 0: file = open("generator_test.html", "w") file.write( """ <html><head><title>Generator Test</title></head> <script> function arr_iter(keys) { this.keys = keys; this.cur = 0; this.next = function() { if (this.cur >= this.keys.length) { throw StopIteration; } return this.keys[this.cur++]; } } __use_Iterator = true; function __get_iter(obj) { if (obj.__proto__.hasOwnProperty("__iterator__") || obj.hasOwnProperty("__iterator__")) { return obj.__iterator__(); } else { if (__use_Iterator) { return Iterator(obj); } else { keys = [] for (var k in obj) { keys.push(k) } return new arr_iter(keys); } } } function __get_iter2(obj) { if (obj.__proto__.hasOwnProperty("__iterator__") || obj.hasOwnProperty("__iterator__")) { return obj.__iterator__(); } else { keys = [] for (var k in obj) { keys.push([k, obj[k]]) } return new arr_iter(keys); } } try { _tst = Iterator({}); } catch (Error) { __use_Iterator = false; Iterator = __get_iter2; } FrameContinue = {1:1}; FrameBreak = {2:2}; """ ) file.write(buf.replace("yield", "return")) file.write( """ j = 0; for (var tst in new range2(2, 8)) { if (_do_frame_debug) console.log(tst); if (j > 10) break; j++; } </script> </html> """ ) file.close() pass return buf, result
def expand_of_loops(result, typespace): onIterEnd = """ if (__re_t_.done && typeof __itervar_["return"] === "function") { __itervar_["return"](); } """ def addIterEnds(node): def visit(n): sn = StatementList() sn.force_block = True n.parent.replace(n, sn) sn.add(js_parse(onIterEnd)) sn.add(n) traverse(node, ReturnNode, visit) def expand_expand_loop(node, scope): sn = StatementList() sn.force_block = True n3 = None vdecl = None if node[0].etype == "array": namenode = BinOpNode(IdentNode("__re_t_"), IdentNode("value"), ".") for i, c in enumerate(node[0]): n4 = VarDeclNode(ArrayRefNode(namenode, NumLitNode(i)), name=c.val) if len(n4) == 1: n4.add(UnknownTypeNode()) if n3 is None: n3 = n4 else: n3.add(n4) if vdecl is None: vdecl = n3 n3 = n4 else: namenode = BinOpNode(IdentNode("__re_t_"), IdentNode("value"), ".") for i, c in enumerate(node[0]): n4 = VarDeclNode(BinOpNode(namenode, c, "."), name=c.val) if len(n4) == 1: n4.add(UnknownTypeNode()) if n3 is None: n3 = n4 else: n3.add(n4) if vdecl is None: vdecl = n3 n3 = n4 vdecl.modifiers = node[0].modifiers label = node.parent.label label = "" if label is None else label innersn = StatementList() for c in node.parent[1:]: innersn.add(c) n2 = js_parse( """ let __itervar_ = __get_iter($n1); let __re_t_ = __itervar_.next(); $s4 for (; !__re_t_.done; __re_t_ = __itervar_.next()) { $n3; $n2; } $s5 """, [ node[1], innersn, vdecl, label + ": " if len(label) > 0 else "", onIterEnd ]) sn.add(n2) sn.force_block = True node.parent.parent.replace(node.parent, sn) addIterEnds(sn) Found = [0] def expand_mozilla_forloops_new(node, scope): if node.of_keyword != "of": return Found[0] = 1 if type(node[0]) == ExpandNode: expand_expand_loop(node, scope) return vdecl = node[0] label = node.parent.label label = "" if label is None else label + ":" name = vdecl.val if isinstance(name, Node): name = name.gen_js(0) innersn = StatementList() for c in node.parent[1:]: innersn.add(c) flatten_statementlists(innersn, typespace) #print(innersn) n2 = js_parse( """ let __itervar_ = __get_iter($n1); let __re_t_ = __itervar_.next(); $n3; $s5 for (; !__re_t_.done; __re_t_ = __itervar_.next()) { $s4 = __re_t_.value; $n2 } $s6 """, [node[1], innersn, vdecl, name, label, onIterEnd]) sn = StatementList() sn.force_block = True sn.add(n2) #print("===============================>", sn.gen_js(0), "<==========================") node.parent.parent.replace(node.parent, sn) addIterEnds(sn) def old_expand_mozilla_forloops_new(node, scope): if type(node[0]) == ExpandNode: expand_expand_loop(node, scope) return use_in_iter = False if (node.of_keyword == "in"): use_in_iter = True if glob.g_warn_for_in: typespace.warning("Detected for-in usage", node) if not inside_generator(node): return func = node.parent while not null_node(func) and type(func) != FunctionNode: func = func.parent if not null_node(func): if func.name in forloop_expansion_exclude: return def prop_ident_change(node, oldid, newid): if type(node) in [IdentNode, VarDeclNode] and node.val == oldid: if type(node.parent) == BinOpNode and node.parent.op == ".": if node != node.parent[1]: node.val = newid else: node.val = newid for c in node.children: if type(c) == FunctionNode: continue prop_ident_change(c, oldid, newid) #for-in-loops don't seem to behave like for-C-loops, #the iteration variable is in it's own scope, and #doesn't seem to affect the parent scope. val = node[0].val di = 0 while node[0].val in scope: node[0].val = "%s_%d" % (val, di) di += 1 #print(node[0].val) if node[0].val != val: scope[node[0].val] = node[0] prop_ident_change(node.parent, val, node[0].val) slist = node.parent.children[1] if type(slist) != StatementList: s = StatementList() s.add(slist) slist = s getiter = "__get_in_iter" if use_in_iter else "__get_iter" label = node.parent.label label = label + ": " if label is not None else "" itername = node[0].val objname = node[1].gen_js(0) if glob.g_log_forloops: n2 = js_parse( """ var __iter_$s1 = __get_iter($s2, $s3, $s4, $s5); var $s1; LABEL while (1) { var __ival_$s1 = __iter_$s1.next(); if (__ival_$s1.done) { break; } $s1 = __ival_$s1.value; } """.replace("LABEL", label).replace("__get_iter", getiter), (itername, GETITER, objname, "'" + node[0].file + "'", node[0].line, "'" + node.of_keyword + "'")) else: n2 = js_parse( """ var __iter_$s1 = __get_iter($s2); var $s1; LABEL while (1) { var __ival_$s1 = __iter_$s1.next(); if (__ival_$s1.done) { break; } $s1 = __ival_$s1.value; } """.replace("LABEL", label).replace("__get_iter", getiter), (itername, objname)) def set_line(n, slist, line, lexpos): n.line = line n.lexpos = lexpos for c in n.children: set_line(c, slist, line, lexpos) #preserving line info is a bit tricky. #slist goes through a js->gen_js->js cycle, #so make sure we still have it (and its #line/lexpos information). set_line(n2, slist, node.line, node.lexpos) for c in slist: n2[2][1].add(c) node.parent.parent.replace(node.parent, n2) #expand_of_loops lexical scope here for i in range(1000): Found[0] = 0 traverse(result, ForInNode, expand_mozilla_forloops_new, use_scope=True) if not Found[0]: break