static void gc_traverse(pobject env) { pobject object; while (is_cons(env)) { gc_flag_set(env, GC_FLAG_ON); object = cons_car(env); if (object && (gc_flag_get(object) == 0)) { /* printf("%p\n", object); */ gc_flag_set(object, GC_FLAG_ON); /* XXX: dotted list support??? */ if (is_cons(object)) { gc_traverse(object); } else if (is_closure(object)) { gc_traverse(object->data.closure.env); gc_traverse(object->data.closure.code); } else if (is_macro(object)) { gc_traverse(object->data.macro.env); gc_traverse(object->data.macro.code); } } env = cons_cdr(env); } }
bool is_topologically_sorted(const std::set<int>& nodes, const std::map<int,std::set<int>>& edges) { if (!is_closure(nodes, edges)) { std::stringstream err; err << __FILE__ << " " << __LINE__ << " :: " << "graph is not a closure"; throw lbann_exception(err.str()); } for (const auto& node : nodes) { const auto& neighbors = get_neighbors(node, edges); if (neighbors.size() > 0 && *neighbors.begin() <= node) { return false; } } return true; }
std::map<int,std::set<int>> transpose(const std::set<int>& nodes, const std::map<int,std::set<int>>& edges) { if (!is_closure(nodes, edges)) { std::stringstream err; err << __FILE__ << " " << __LINE__ << " :: " << "graph is not a closure"; throw lbann_exception(err.str()); } std::map<int,std::set<int>> transpose_edges; for (const auto& node : nodes) { for (const auto& neighbor : get_neighbors(node, edges)) { transpose_edges[neighbor].insert(node); } } return transpose_edges; }
std::vector<int> topological_sort(const std::set<int>& nodes, const std::map<int,std::set<int>>& edges) { // Check that graph is valid if (!is_closure(nodes, edges)) { std::stringstream err; err << __FILE__ << " " << __LINE__ << " :: " << "graph is not a closure"; throw lbann_exception(err.str()); } if (is_cyclic(nodes, edges)) { std::stringstream err; err << __FILE__ << " " << __LINE__ << " :: " << "graph is cyclic"; throw lbann_exception(err.str()); } // Return original order if already sorted if (is_topologically_sorted(nodes, edges)) { return std::vector<int>(nodes.begin(), nodes.end()); } // Perform depth-first searches on nodes std::stack<int> sorted_stack; std::unordered_map<int,bool> is_sorted; for (const auto& root : nodes) { if (!is_sorted[root]) { const auto& dfs = depth_first_search(root, edges); for (const auto& node : dfs) { if (!is_sorted[node]) { is_sorted[node] = true; sorted_stack.push(node); } } } } // Reverse DFS post-order is topologically sorted std::vector<int> sorted_nodes; while (!sorted_stack.empty()) { sorted_nodes.push_back(sorted_stack.top()); sorted_stack.pop(); } return sorted_nodes; }
bool is_cyclic(const std::set<int>& nodes, const std::map<int,std::set<int>>& edges) { // Check that graph is valid if (!is_closure(nodes, edges)) { std::stringstream err; err << __FILE__ << " " << __LINE__ << " :: " << "graph is not a closure"; throw lbann_exception(err.str()); } // Topologically sorted graphs are not cyclic if (is_topologically_sorted(nodes, edges)) { return false; } // Perform depth-first searches to detect cycles std::unordered_map<int,bool> is_visited, is_sorted; std::stack<int> search_stack; for (auto&& it = nodes.rbegin(); it != nodes.rend(); ++it) { search_stack.push(*it); } while (!search_stack.empty()) { const auto& node = search_stack.top(); search_stack.pop(); if (!is_sorted[node]) { if (is_visited[node]) { is_sorted[node] = true; } else { is_visited[node] = true; search_stack.push(node); for (const auto& neighbor : get_neighbors(node, edges)) { if (is_visited[neighbor] && !is_sorted[neighbor]) { return true; } search_stack.push(neighbor); } } } } return false; }
bool check(const php::Class& c) { assert(checkName(c.name)); for (DEBUG_ONLY auto& m : c.methods) assert(check(*m)); // Some invariants about Closure classes. auto const isClo = is_closure(c); if (c.closureContextCls) { assert(c.closureContextCls->unit == c.unit); assert(isClo); } if (isClo) { assert(c.methods.size() == 1); assert(c.methods[0]->name->isame(s_invoke.get())); assert(c.methods[0]->isClosureBody); } else { assert(!c.closureContextCls); } return true; }
std::string TypeSpec::string () const { std::string str; if (is_closure() || is_closure_array()) { str += "closure color"; if (is_unsized_array()) str += "[]"; else if (arraylength() > 0) str += Strutil::format ("[%d]", arraylength()); } else if (structure() > 0) { str += Strutil::format ("struct %d", structure()); if (is_unsized_array()) str += "[]"; else if (arraylength() > 0) str += Strutil::format ("[%d]", arraylength()); } else { str += simpletype().c_str(); } return str; }
void emit_class(EmitUnitState& state, UnitEmitter& ue, const php::Class& cls) { FTRACE(2, " class: {}\n", cls.name->data()); auto const pce = ue.newPreClassEmitter( cls.name, cls.hoistability ); pce->init( std::get<0>(cls.srcInfo.loc), std::get<1>(cls.srcInfo.loc), ue.bcPos(), cls.attrs, cls.parentName ? cls.parentName : s_empty.get(), cls.srcInfo.docComment ); pce->setUserAttributes(cls.userAttributes); for (auto& x : cls.interfaceNames) pce->addInterface(x); for (auto& x : cls.usedTraitNames) pce->addUsedTrait(x); for (auto& x : cls.requirements) pce->addClassRequirement(x); for (auto& x : cls.traitPrecRules) pce->addTraitPrecRule(x); for (auto& x : cls.traitAliasRules) pce->addTraitAliasRule(x); pce->setNumDeclMethods(cls.numDeclMethods); pce->setIfaceVtableSlot(state.index.lookup_iface_vtable_slot(&cls)); for (auto& m : cls.methods) { FTRACE(2, " method: {}\n", m->name->data()); auto const fe = ue.newMethodEmitter(m->name, pce); emit_init_func(*fe, *m); pce->addMethod(fe); auto const info = emit_bytecode(state, ue, *m); emit_finish_func(*m, *fe, info); } auto const privateProps = state.index.lookup_private_props(&cls); auto const privateStatics = state.index.lookup_private_statics(&cls); for (auto& prop : cls.properties) { auto const makeRat = [&] (const Type& ty) -> RepoAuthType { if (ty.couldBe(TCls)) { return RepoAuthType{}; } auto const rat = make_repo_type(*state.index.array_table_builder(), ty); merge_repo_auth_type(ue, rat); return rat; }; auto const privPropTy = [&] (const PropState& ps) -> Type { /* * Skip closures, because the types of their used vars can be * communicated via assert opcodes right now. At the time of this * writing there was nothing to gain by including RAT's for the * properties, since closure properties are only used internally by the * runtime, not directly via opcodes like CGetM. */ if (is_closure(cls)) return Type{}; auto it = ps.find(prop.name); if (it == end(ps)) return Type{}; return it->second; }; Type propTy; auto const attrs = prop.attrs; if (attrs & AttrPrivate) { propTy = privPropTy((attrs & AttrStatic) ? privateStatics : privateProps); } else if ((attrs & AttrPublic) && (attrs & AttrStatic)) { propTy = state.index.lookup_public_static(&cls, prop.name); } pce->addProperty( prop.name, prop.attrs, prop.typeConstraint, prop.docComment, &prop.val, makeRat(propTy) ); } for (auto& cconst : cls.constants) { if (!cconst.val.hasValue()) { pce->addAbstractConstant( cconst.name, cconst.typeConstraint, cconst.isTypeconst ); } else { pce->addConstant( cconst.name, cconst.typeConstraint, &cconst.val.value(), cconst.phpCode, cconst.isTypeconst ); } } pce->setEnumBaseTy(cls.enumBaseTy); }
void emit_class(EmitUnitState& state, UnitEmitter& ue, const php::Class& cls) { FTRACE(2, " class: {}\n", cls.name->data()); auto const pce = ue.newPreClassEmitter( cls.name, cls.hoistability ); pce->init( std::get<0>(cls.srcInfo.loc), std::get<1>(cls.srcInfo.loc), ue.bcPos(), cls.attrs, cls.parentName ? cls.parentName : s_empty.get(), cls.srcInfo.docComment ); pce->setUserAttributes(cls.userAttributes); for (auto& x : cls.interfaceNames) pce->addInterface(x); for (auto& x : cls.usedTraitNames) pce->addUsedTrait(x); for (auto& x : cls.requirements) pce->addClassRequirement(x); for (auto& x : cls.traitPrecRules) pce->addTraitPrecRule(x); for (auto& x : cls.traitAliasRules) pce->addTraitAliasRule(x); pce->setNumDeclMethods(cls.numDeclMethods); pce->setIfaceVtableSlot(state.index.lookup_iface_vtable_slot(&cls)); for (auto& m : cls.methods) { FTRACE(2, " method: {}\n", m->name->data()); auto const fe = ue.newMethodEmitter(m->name, pce); emit_init_func(*fe, *m); pce->addMethod(fe); auto const info = emit_bytecode(state, ue, *m); emit_finish_func(*m, *fe, info); } std::vector<Type> useVars; if (is_closure(cls)) { auto f = find_method(&cls, s_invoke.get()); useVars = state.index.lookup_closure_use_vars(f); } auto uvIt = useVars.begin(); auto const privateProps = state.index.lookup_private_props(&cls); auto const privateStatics = state.index.lookup_private_statics(&cls); for (auto& prop : cls.properties) { auto const makeRat = [&] (const Type& ty) -> RepoAuthType { if (ty.couldBe(TCls)) { return RepoAuthType{}; } auto const rat = make_repo_type(*state.index.array_table_builder(), ty); merge_repo_auth_type(ue, rat); return rat; }; auto const privPropTy = [&] (const PropState& ps) -> Type { if (is_closure(cls)) { // For closures use variables will be the first properties of the // closure object, in declaration order if (uvIt != useVars.end()) return *uvIt++; return Type{}; } auto it = ps.find(prop.name); if (it == end(ps)) return Type{}; return it->second; }; Type propTy; auto const attrs = prop.attrs; if (attrs & AttrPrivate) { propTy = privPropTy((attrs & AttrStatic) ? privateStatics : privateProps); } else if ((attrs & AttrPublic) && (attrs & AttrStatic)) { propTy = state.index.lookup_public_static(&cls, prop.name); } pce->addProperty( prop.name, prop.attrs, prop.typeConstraint, prop.docComment, &prop.val, makeRat(propTy) ); } assert(uvIt == useVars.end()); for (auto& cconst : cls.constants) { if (!cconst.val.hasValue()) { pce->addAbstractConstant( cconst.name, cconst.typeConstraint, cconst.isTypeconst ); } else { pce->addConstant( cconst.name, cconst.typeConstraint, &cconst.val.value(), cconst.phpCode, cconst.isTypeconst ); } } pce->setEnumBaseTy(cls.enumBaseTy); }
void emit_class(EmitUnitState& state, UnitEmitter& ue, const php::Class& cls) { FTRACE(2, " class: {}\n", cls.name->data()); auto const pce = ue.newPreClassEmitter( cls.name, cls.hoistability ); pce->init( std::get<0>(cls.srcInfo.loc), std::get<1>(cls.srcInfo.loc), ue.bcPos(), cls.attrs, cls.parentName ? cls.parentName : s_empty.get(), cls.srcInfo.docComment ); pce->setUserAttributes(cls.userAttributes); for (auto& x : cls.interfaceNames) pce->addInterface(x); for (auto& x : cls.usedTraitNames) pce->addUsedTrait(x); for (auto& x : cls.requirements) pce->addClassRequirement(x); for (auto& x : cls.traitPrecRules) pce->addTraitPrecRule(x); for (auto& x : cls.traitAliasRules) pce->addTraitAliasRule(x); for (auto& m : cls.methods) { FTRACE(2, " method: {}\n", m->name->data()); auto const fe = ue.newMethodEmitter(m->name, pce); emit_init_func(*fe, *m); pce->addMethod(fe); auto const info = emit_bytecode(state, ue, *m); emit_finish_func(*m, *fe, info); } auto const privateProps = state.index.lookup_private_props(&cls); auto const privateStatics = state.index.lookup_private_statics(&cls); for (auto& prop : cls.properties) { auto const repoTy = [&] (const PropState& ps) -> RepoAuthType { // TODO(#3599292): we don't currently infer closure use var types. if (is_closure(cls)) return RepoAuthType{}; auto it = ps.find(prop.name); if (it == end(ps)) return RepoAuthType{}; auto const rat = make_repo_type(*state.index.array_table_builder(), it->second); merge_repo_auth_type(ue, rat); return rat; }; pce->addProperty( prop.name, prop.attrs, prop.typeConstraint, prop.docComment, &prop.val, (prop.attrs & AttrStatic) ? repoTy(privateStatics) : repoTy(privateProps) ); } for (auto& cconst : cls.constants) { pce->addConstant( cconst.name, cconst.typeConstraint, &cconst.val, cconst.phpCode ); } }