Permalink
Browse files

Represent reified functions/methods with unique names

Summary:
Similar to what we did in D10153546 for classes, we repeat the same unique names for functions and methods in this diff. This is done by reusing the machinery built in D10153546.

The goal of this diff is to enable extraction of reified generics from the name of the function for the next diff on the stack.

Reviewed By: paulbiss

Differential Revision: D10242446

fbshipit-source-id: 1ccc4a60df452c90b103f4cfa61465840fafa614
  • Loading branch information...
oulgen authored and hhvm-bot committed Oct 13, 2018
1 parent 71650de commit a825853c3eb82dde1ff95b39c90a91f7fe10d067
@@ -3284,8 +3284,40 @@ and emit_call_lhs_with_this env instrs = Local.scope @@ fun () ->
and has_inout_args es =
List.exists es ~f:(function _, A.Callconv (A.Pinout, _) -> true | _ -> false)
and emit_call_lhs env outer_pos (pos, expr_ as expr) nargs has_splat inout_arg_positions =
and emit_call_lhs
env outer_pos (pos, expr_ as expr) targs nargs has_splat inout_arg_positions =
let has_inout_args = List.length inout_arg_positions <> 0 in
let reified_targs =
List.filter_map targs ~f:(function (_, false) -> None | (h, true) ->
Some (fst @@ emit_reified_arg env h)) in
let reified_call_body name =
gather [
gather reified_targs;
instr_string name;
instr_reified_name (List.length reified_targs + 1);
] in
let reified_fun_name_call name =
gather [
reified_call_body name;
instr_fpushfunc nargs inout_arg_positions;
] in
let reified_objmethod_call name nullf =
gather [
reified_call_body name;
instr_fpushobjmethod nargs nullf inout_arg_positions;
] in
let reified_clsmethod_call name cid =
gather [
reified_call_body name;
instr_string cid;
instr_clsrefgetc;
instr_fpushclsmethod nargs [];
] in
let reified_clsmethods_call name clsref =
gather [
reified_call_body name;
instr_fpushclsmethods nargs clsref;
] in
match expr_ with
| A.Obj_get (obj, (_, A.Id ((_, str) as id)), null_flavor)
when str.[0] = '$' ->
@@ -3305,9 +3337,12 @@ and emit_call_lhs env outer_pos (pos, expr_ as expr) nargs has_splat inout_arg_p
gather [
emit_object_expr env ~last_pos:outer_pos obj;
emit_pos outer_pos;
instr_fpushobjmethodd nargs name null_flavor;
(if List.is_empty reified_targs then
instr_fpushobjmethodd nargs name null_flavor
else
reified_objmethod_call id null_flavor)
]
| A.Obj_get(obj, method_expr, null_flavor) ->
| A.Obj_get (obj, method_expr, null_flavor) ->
gather [
emit_pos outer_pos;
emit_object_expr env ~last_pos:outer_pos obj;
@@ -3324,6 +3359,7 @@ and emit_call_lhs env outer_pos (pos, expr_ as expr) nargs has_splat inout_arg_p
then Hhbc_id.Method.add_suffix method_id
(Emit_inout_helpers.inout_suffix inout_arg_positions)
else method_id in
let method_id_string = Hhbc_id.Method.to_raw_string method_id in
let cexpr = match cexpr with
| Class_id (_, name) ->
Option.value ~default:cexpr (get_reified_var_cexpr env name)
@@ -3332,24 +3368,36 @@ and emit_call_lhs env outer_pos (pos, expr_ as expr) nargs has_splat inout_arg_p
(* Statically known *)
| Class_id cid ->
let fq_cid, _ = Hhbc_id.Class.elaborate_id (Emit_env.get_namespace env) cid in
Emit_symbol_refs.add_class (Hhbc_id.Class.to_raw_string fq_cid);
instr_fpushclsmethodd nargs method_id fq_cid
let fq_cid_string = Hhbc_id.Class.to_raw_string fq_cid in
Emit_symbol_refs.add_class fq_cid_string;
(if List.is_empty reified_targs then
instr_fpushclsmethodd nargs method_id fq_cid
else
reified_clsmethod_call method_id_string fq_cid_string)
| Class_static ->
instr_fpushclsmethodsd nargs SpecialClsRef.Static method_id
if List.is_empty reified_targs then
instr_fpushclsmethodsd nargs SpecialClsRef.Static method_id
else reified_clsmethods_call method_id_string SpecialClsRef.Static
| Class_self ->
instr_fpushclsmethodsd nargs SpecialClsRef.Self method_id
if List.is_empty reified_targs then
instr_fpushclsmethodsd nargs SpecialClsRef.Self method_id
else reified_clsmethods_call method_id_string SpecialClsRef.Self
| Class_parent ->
instr_fpushclsmethodsd nargs SpecialClsRef.Parent method_id
if List.is_empty reified_targs then
instr_fpushclsmethodsd nargs SpecialClsRef.Parent method_id
else reified_clsmethods_call method_id_string SpecialClsRef.Parent
| Class_expr (_, A.Lvar (_, x)) when x = SN.SpecialIdents.this ->
let method_name = Hhbc_id.Method.to_raw_string method_id in
let name_instrs =
if List.is_empty reified_targs then instr_string method_id_string
else reified_call_body method_id_string in
gather [
emit_call_lhs_with_this env @@ instr_string method_name;
emit_call_lhs_with_this env name_instrs;
instr_fpushclsmethod nargs []
]
| _ ->
let method_name = Hhbc_id.Method.to_raw_string method_id in
(* TODO(T31677864): Implement reification here *)
gather [
of_pair @@ emit_class_expr env cexpr (Pos.none, A.Id (Pos.none, method_name));
of_pair @@ emit_class_expr env cexpr (Pos.none, A.Id (Pos.none, method_id_string));
instr_fpushclsmethod nargs []
]
end
@@ -3403,12 +3451,16 @@ and emit_call_lhs env outer_pos (pos, expr_ as expr) nargs has_splat inout_arg_p
else fq_id in
emit_pos_then outer_pos @@
begin match id_opt with
| _ when not @@ List.is_empty reified_targs ->
reified_fun_name_call (Hhbc_id.Function.to_raw_string fq_id)
| Some id -> instr (ICall (FPushFuncU (nargs, fq_id, id)))
| None -> instr (ICall (FPushFuncD (nargs, fq_id)))
end
| A.String s ->
emit_pos_then outer_pos @@
instr_fpushfuncd nargs (Hhbc_id.Function.from_raw_string s)
(if List.is_empty reified_targs then
instr_fpushfuncd nargs (Hhbc_id.Function.from_raw_string s)
else reified_fun_name_call s)
| _ ->
gather [
emit_expr ~need_ref:false env expr;
@@ -3579,7 +3631,7 @@ and emit_call env pos (_, expr_ as expr) targs args uargs async_eager_label =
gather [
gather @@ List.init num_uninit ~f:(fun _ -> instr_nulluninit);
emit_call_lhs
env pos expr nargs (not (List.is_empty uargs)) inout_arg_positions;
env pos expr targs nargs (not (List.is_empty uargs)) inout_arg_positions;
emit_args_and_call env pos reified_targs args uargs async_eager_label;
], flavor in
@@ -133,6 +133,8 @@ void FuncEmitter::commit(RepoTxn& txn) const {
.insert(*this, txn, usn, m_sn, m_pce ? m_pce->id() : -1, name, top);
}
const StaticString s___Reified("__Reified");
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
bool isGenerated = isdigit(name->data()[0]) || needsStripInOut(name);
@@ -222,6 +224,9 @@ Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
makeStaticString(RuntimeOption::SourceRoot +
originalFilename->toCppString());
auto m_hasReifiedGenerics =
userAttributes.find(s___Reified.get()) != userAttributes.end();
f->shared()->m_localNames.create(m_localNames);
f->shared()->m_numLocals = m_numLocals;
f->shared()->m_numIterators = m_numIterators;
@@ -243,6 +248,7 @@ Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
f->shared()->m_isMemoizeWrapper = isMemoizeWrapper;
f->shared()->m_isMemoizeWrapperLSB = isMemoizeWrapperLSB;
f->shared()->m_numClsRefSlots = m_numClsRefSlots;
f->shared()->m_hasReifiedGenerics = m_hasReifiedGenerics;
if (isNative) {
auto const ex = f->extShared();
@@ -633,6 +633,10 @@ inline bool Func::isPhpLeafFn() const {
return shared()->m_isPhpLeafFn;
}
inline bool Func::hasReifiedGenerics() const {
return shared()->m_hasReifiedGenerics;
}
///////////////////////////////////////////////////////////////////////////////
// Unit table entries.
@@ -770,6 +770,7 @@ Func::SharedData::SharedData(PreClass* preClass, Offset base, Offset past,
, m_isMemoizeWrapperLSB(false)
, m_isPhpLeafFn(isPhpLeafFn)
, m_takesNumArgs(false)
, m_hasReifiedGenerics(false)
, m_numClsRefSlots(0)
, m_originalFilename(nullptr)
{
@@ -1025,6 +1025,11 @@ struct Func final {
*/
bool isPhpLeafFn() const;
/*
* Does this function has reified generics?
*/
bool hasReifiedGenerics() const;
/////////////////////////////////////////////////////////////////////////////
// Unit table entries. [const]
@@ -1266,12 +1271,13 @@ struct Func final {
bool m_isMemoizeWrapperLSB : 1;
bool m_isPhpLeafFn : 1;
bool m_takesNumArgs : 1;
bool m_hasReifiedGenerics : 1;
// Needing more than 2 class ref slots basically doesn't happen, so just use
// two bits normally. If we actually need more than that, we'll store the
// count in ExtendedSharedData.
unsigned int m_numClsRefSlots : 2;
// 19 bits of padding here in LOWPTR builds
// 18 bits of padding here in LOWPTR builds
LowStringPtr m_retUserType;
UserAttributeMap m_userAttributes;
@@ -65,6 +65,9 @@ const Func* lookupMethodCtx(const Class* cls,
} else {
assertx(callType == CallType::ObjMethod || callType == CallType::ClsMethod);
assertx(methodName != nullptr);
if (isReifiedName(methodName)) {
methodName = stripTypeFromReifiedName(methodName);
}
method = cls->lookupMethod(methodName);
while (!method) {
if (UNLIKELY(methodName->isame(s___construct.get()))) {
@@ -136,11 +136,16 @@ inline std::string mangleReifiedName(
const StringData* name,
const std::string& tsName
) {
return folly::sformat("{}$${}", name, tsName);
return folly::sformat("$${}$${}", name, tsName);
}
inline bool isReifiedName(const StringData* name) {
return name->toCppString().find("$$<") != std::string::npos;
// Length larger than $$name$$<type>
return name->size() > 7 &&
name->data()[0] == '$' &&
name->data()[1] == '$' &&
folly::qfind(name->slice(), folly::StringPiece("$$<"))
!= std::string::npos;
}
inline std::string stripClsOrFnNameFromReifiedName(const std::string& name) {
@@ -151,8 +156,9 @@ inline std::string stripClsOrFnNameFromReifiedName(const std::string& name) {
inline std::string stripTypeFromReifiedName(const std::string& name) {
auto i = name.find("$$<");
if (i == std::string::npos) raise_error("Not a reified name");
return name.substr(0, i);
if (i == std::string::npos || i < 2) raise_error("Not a reified name");
// Remove the initial $$
return name.substr(2, i - 2);
}
inline StringData* stripTypeFromReifiedName(const StringData* name) {
@@ -712,7 +712,22 @@ Func* Unit::loadFunc(const StringData* name) {
if (normStr) {
name = normStr.get();
}
return loadFunc(ne, name);
auto func_ = loadFunc(ne, name);
if (LIKELY(func_ != nullptr) || !isReifiedName(name)) return func_;
// We are loading a reified function for the first time
name = stripTypeFromReifiedName(name);
auto generic_ne = NamedEntity::get(name, true, &normStr);
if (normStr) {
name = normStr.get();
}
func_ = loadFunc(generic_ne, name);
// If the function still does not exists, return null
if (!func_) return nullptr;
assertx(func_->hasReifiedGenerics());
ne->m_cachedFunc.bind(
func_->isPersistent() ? rds::Mode::Persistent : rds::Mode::Normal);
ne->setCachedFunc(func_);
return func_;
}
void Unit::bindFunc(Func *func) {

0 comments on commit a825853

Please sign in to comment.