def push(stack, node, parent_idx): """ Push the node's index, and its children's types into stack. The stack helps align the frag_info_seq[:] with frag_seq[1:], so that each frag in frag_seq knows its parent's id. When predict, we want not only the frag itself, but its type (the same as frag's type; so never mind) and its parent's type (idx). :param stack: :param node: :param parent_idx: :return: """ node_type = get_node_type(node) for key in reversed(PROP_DICT[node_type]): if key not in node: continue child = node[key] # If it has a single child if (is_single_node(child) and get_node_type(child) not in TERM_TYPE): frag_type = get_node_type(child) frag_info = (parent_idx, frag_type) stack.append(frag_info) # If it has multiple children elif is_node_list(child): for _child in reversed(child): if (_child is not None and get_node_type(_child) not in TERM_TYPE): frag_type = get_node_type(_child) frag_info = (parent_idx, frag_type) stack.append(frag_info)
def build_def_dict(node, def_dict): node_type = get_node_type(node) if node_type in ['ClassExpression', 'FunctionExpression']: return if (node_type in ['FunctionDeclaration', 'ClassDeclaration'] and node['id'] != None and node['id']['type'] == 'Identifier'): func_name = node['id']['name'] def_dict.add(func_name) return elif (node_type == 'VariableDeclarator' and 'type' in node['id'] and node['id']['type'] == 'Identifier'): var_name = node['id']['name'] def_dict.add(var_name) elif (node_type == 'AssignmentExpression' and 'type' in node['left'] and node['left']['type'] == 'Identifier'): var_name = node['left']['name'] def_dict.add(var_name) for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] if (is_single_node(child) and child['type'] not in TERM_TYPE): build_def_dict(child, def_dict) elif is_node_list(child): for _child in child: if _child != None: build_def_dict(_child, def_dict)
def build_ast(self, node, stack, frag): node_type = get_node_type(node) for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] # If it has a single child if is_single_node(child): if not is_pruned(child): frag = self.build_ast(child, stack, frag) # Expand the frag elif frag: self.push(stack, frag) node[key] = frag return None # If it has multiple children elif is_node_list(child): for idx, _child in enumerate(child): if _child == None: continue elif not is_pruned(_child): frag = self.build_ast(child[idx], stack, frag) # Expand the frag elif frag: self.push(stack, frag) child[idx] = frag return None return frag
def rewrite(self, node, parser): node_type = get_node_type(node) if self.is_eval(node): # Parse arguments and retrieve new subtrees args = node['expression']['arguments'] org_args = deepcopy(args) new_args = self.str2code(args, parser) if org_args != new_args: return new_args else: return # Recursive traversal for key in PROP_DICT[ node_type]: # why not iterating over $node's keys? $node can be invalid if key not in node: continue # not every possible prop is in node child = node[key] if is_single_node(child): self.rewrite(child, parser) elif is_node_list(child): child_idx = 0 for _child in child: if _child is not None: subtree = self.rewrite(_child, parser) if subtree is not None: node[key] = self.append(node[key], subtree, child_idx) child_idx += len(subtree) - 1 child_idx += 1
def normalize_id(node, id_dict, parent=None, prop=None): node_type = get_node_type(node) if node_type == 'ObjectPattern': return for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] # Traversal if is_single_node(child): normalize_id(child, id_dict, node, key) elif is_node_list(child): for _child in child: if _child is not None: normalize_id(_child, id_dict, node, key) # Exit if the node is not an ID if node_type != 'Identifier': return # Exit if the node is a property of an object if (parent['type'] == 'MemberExpression' and prop != 'object' and parent['computed'] == False): return # Do not normalize keys (ObjectExpression) if (parent['type'] == 'Property' and prop == 'key'): return # Replace the ID id_name = node['name'] if id_name in id_dict: node['name'] = id_dict[id_name]
def make_frags(node, frag_seq, frag_info_seq, node_types, stack): # Append the node before visiting its children frag = dict() frag_idx = len(frag_seq) # # pre-order depth-first traverse: add root first # cannot postpone -- need to increase frag_idx frag_seq.append(frag) # Push node info into the stack if len(stack) > 0: frag_info = stack.pop() frag_info_seq.append(frag_info) push(stack, node, frag_idx) node_type = get_node_type(node) node_types.add(node_type) for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] # If it has a single child if (is_single_node(child) and get_node_type(child) not in TERM_TYPE): frag[key] = prune(child) make_frags(child, frag_seq, frag_info_seq, node_types, stack) # If it has multiple children elif is_node_list(child): frag[key] = [] for _child in child: if _child is None: frag[key].append(None) elif get_node_type(_child) in TERM_TYPE: frag[key].append(_child) else: pruned_child = prune(_child) frag[key].append(pruned_child) make_frags(_child, frag_seq, frag_info_seq, node_types, stack) # If it is a terminal (attributes without structure) else: # print("terminal: ", key, child) frag[key] = node[key] # Append the fragment -- redundant; can be deleted frag_seq[frag_idx] = frag return frag
def make_edges(node, frag_seq, frag_info_seq, node_types, stack): node_type = get_node_type(node) for key in PROP_DICT[node_type]: if key not in node: continue node_types.add(node_type) # Append the node before visiting its children frag = dict() frag_idx = len(frag_seq) frag_seq.append(frag) # Push node info into the stack if len(stack) > 0: frag_info = stack.pop() frag_info_seq.append(frag_info) push(stack, node, frag_idx) child = node[key] # If it has a single child if (is_single_node(child) and get_node_type(child) not in TERM_TYPE): frag[(node_type, key)] = prune(child) make_edges(child, frag_seq, frag_info_seq, node_types, stack) # If it has multiple children elif is_node_list(child): frag[(node_type, key)] = [] for _child in child: if _child is None: frag[(node_type, key)].append(None) elif get_node_type(_child) in TERM_TYPE: frag[(node_type, key)].append(_child) else: pruned_child = prune(_child) frag[(node_type, key)].append(pruned_child) make_edges(_child, frag_seq, frag_info_seq, node_types, stack) # If it is a terminal else: frag[(node_type, key)] = node[key] # Append the fragment frag_seq[frag_idx] = frag
def push(stack, node, parent_idx): node_type = get_node_type(node) for key in reversed(PROP_DICT[node_type]): if key not in node: continue child = node[key] # If it has a single child if (is_single_node(child) and get_node_type(child) not in TERM_TYPE): frag_type = get_node_type(child) frag_info = (parent_idx, frag_type) stack.append(frag_info) # If it has multiple children elif is_node_list(child): for _child in reversed(child): if (_child is not None and get_node_type(_child) not in TERM_TYPE): frag_type = get_node_type(_child) frag_info = (parent_idx, frag_type) stack.append(frag_info)
def func_hoisting(node, sym_list): if node == None: return node_type = get_node_type(node) for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] if is_single_node(child): if get_node_type(child) == 'FunctionDeclaration': sym_list.append(Symbol(child['id'], child, JSType.js_func)) elif get_node_type(child) == 'BlockStatement': func_hoisting(child, sym_list) elif is_node_list(child): for _child in child: if _child == None: continue if get_node_type(_child) == 'FunctionDeclaration': sym_list.append(Symbol(_child['id'], _child, JSType.js_func)) elif get_node_type(_child) == 'BlockStatement': func_hoisting(_child, sym_list)
def traverse(self, node, frag_seq, stack): node_type = get_node_type(node) if node_type not in TERM_TYPE: parent_idx = self.frag2idx(node) else: return for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] # If it has a single child if is_single_node(child): if is_pruned(child): frag_idx = frag_seq.pop(0) if frag_idx == -1: if stack != None: frag_info = (parent_idx, get_node_type(child)) stack.append(frag_info) continue frag = self.idx2frag(frag_idx) node[key] = frag self.traverse(node[key], frag_seq, stack) # If it has multiple children elif is_node_list(child): for idx, _child in enumerate(child): if _child == None: continue elif is_pruned(_child): frag_idx = frag_seq.pop(0) if frag_idx == -1: if stack != None: frag_info = (parent_idx, get_node_type(_child)) stack.append(frag_info) continue frag = self.idx2frag(frag_idx) child[idx] = frag self.traverse(child[idx], frag_seq, stack)
def var_hoisting(node, parent, sym_list): node_type = get_node_type(node) if node_type in [ 'FunctionDeclaration', 'FunctionExpression', 'ClassDeclaration', 'ClassExpression' ]: return elif (parent != None and get_node_type(parent) == 'VariableDeclaration' and parent['kind'] == 'var' and node_type == 'VariableDeclarator'): symbols = pattern_hoisting(node['id'], node) sym_list += symbols else: for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] if (is_single_node(child) and get_node_type(child) not in TERM_TYPE): var_hoisting(child, node, sym_list) elif is_node_list(child): for _child in child: if _child != None: var_hoisting(_child, node, sym_list)
def collect_id(node, id_dict, id_cnt, parent=None, prop=None): node_type = get_node_type(node) # Tree traversal for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] if is_single_node(child): collect_id(child, id_dict, id_cnt, node, key) elif is_node_list(child): for _child in child: if _child is not None: collect_id(_child, id_dict, id_cnt, node, key) if parent is not None and is_func_decl(parent): id_type = 'f' else: id_type = 'v' if is_declared_id(node, parent, prop): id_name = node['name'] add_id(id_name, id_dict, id_cnt, id_type)
def resolve_id(node, parent, symbols, is_global, is_check=False, cand=[], hlist=[]): if node == None: return symbols node_type = get_node_type(node) if node_type == 'Identifier': return resolve_identifier(node, parent, symbols, is_global, is_check, cand, hlist) elif (node_type == 'MemberExpression' and node['computed'] == False): return resolve_id(node['object'], node, symbols, is_global, is_check, cand, hlist) elif node_type == 'CallExpression': return resolve_FuncCall(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type == 'AssignmentExpression': return resolve_Assign(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type == 'VariableDeclarator': return resolve_VarDecl(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type in ['FunctionDeclaration', 'FunctionExpression']: return symbols elif node_type == 'IfStatement': return resolve_If(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type in ['DoWhileStatement', 'WhileStatement']: return resolve_While(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type == 'ForStatement': return resolve_For(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type in ['ForInStatement', 'ForOfStatement']: return resolve_ForIn(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type == 'WithStatment': return resolve_With(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type == 'TryStatement': return resolve_Try(node, parent, symbols, is_global, is_check, cand, hlist) elif node_type == 'Property': return resolve_id(node['value'], node, symbols, is_global, is_check, cand, hlist) elif node_type in ['ClassDeclaration', 'ClassExpression']: return resolve_ClassDecl(node, parent, symbols, is_global, is_check, cand, hlist) # Switch for key in PROP_DICT[node_type]: if key not in node: continue child = node[key] if (is_single_node(child) and get_node_type(child) not in TERM_TYPE): resolve_id(child, node, symbols, is_global, is_check, cand, hlist) elif is_node_list(child): resolve_list(child, node, symbols, is_global, is_check, cand, hlist) return symbols