Move io_tests to folly/io/async/test
[hiphop-php.git] / hphp / runtime / vm / func-emitter.cpp
blobe439b1fd79e73465009226f046e7c8e72591e058
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/func-emitter.h"
19 #include "hphp/runtime/base/array-iterator.h"
20 #include "hphp/runtime/base/coeffects-config.h"
21 #include "hphp/runtime/base/unit-cache.h"
22 #include "hphp/runtime/base/record-replay.h"
23 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/ext/extension.h"
27 #include "hphp/runtime/vm/bytecode.h"
28 #include "hphp/runtime/vm/native.h"
29 #include "hphp/runtime/vm/preclass-emitter.h"
30 #include "hphp/runtime/vm/reified-generics.h"
31 #include "hphp/runtime/vm/repo-autoload-map-builder.h"
32 #include "hphp/runtime/vm/repo-file.h"
33 #include "hphp/runtime/vm/runtime.h"
35 #include "hphp/runtime/vm/jit/types.h"
37 #include "hphp/runtime/vm/verifier/cfg.h"
39 #include "hphp/system/systemlib.h"
41 #include "hphp/util/atomic-vector.h"
42 #include "hphp/util/blob-encoder.h"
43 #include "hphp/util/file.h"
44 #include "hphp/util/trace.h"
45 #include <iterator>
47 namespace HPHP {
49 ///////////////////////////////////////////////////////////////////////////////
50 // FuncEmitter.
52 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, Id id, const StringData* n)
53 : m_ue(ue)
54 , m_pce(nullptr)
55 , m_sn(sn)
56 , m_id(id)
57 , m_bc()
58 , m_bclen(0)
59 , m_bcmax(0)
60 , name(n)
61 , maxStackCells(0)
62 , retUserType(nullptr)
63 , docComment(nullptr)
64 , originalFilename(nullptr)
65 , originalModuleName(nullptr)
66 , memoizePropName(nullptr)
67 , memoizeGuardPropName(nullptr)
68 , memoizeSharedPropIndex(0)
69 , m_numLocals(0)
70 , m_numUnnamedLocals(0)
71 , m_numIterators(0)
72 , m_nextFreeIterator(0)
73 , m_ehTabSorted(false)
76 FuncEmitter::FuncEmitter(UnitEmitter& ue, int sn, const StringData* n,
77 PreClassEmitter* pce)
78 : m_ue(ue)
79 , m_pce(pce)
80 , m_sn(sn)
81 , m_id(kInvalidId)
82 , m_bc()
83 , m_bclen(0)
84 , m_bcmax(0)
85 , name(n)
86 , maxStackCells(0)
87 , retUserType(nullptr)
88 , docComment(nullptr)
89 , originalFilename(nullptr)
90 , originalModuleName(nullptr)
91 , memoizePropName(nullptr)
92 , memoizeGuardPropName(nullptr)
93 , memoizeSharedPropIndex(0)
94 , m_numLocals(0)
95 , m_numUnnamedLocals(0)
96 , m_numIterators(0)
97 , m_nextFreeIterator(0)
98 , m_ehTabSorted(false)
101 FuncEmitter::~FuncEmitter() {
102 if (m_bc.isPtr()) {
103 if (auto const p = m_bc.ptr()) free(p);
105 if (m_lineTable.isPtr()) {
106 if (auto const p = m_lineTable.ptr()) delete p;
111 ///////////////////////////////////////////////////////////////////////////////
112 // Source locations.
114 SourceLocTable FuncEmitter::createSourceLocTable() const {
115 assertx(m_sourceLocTab.size() != 0);
116 SourceLocTable locations;
117 for (size_t i = 0; i < m_sourceLocTab.size(); ++i) {
118 Offset endOff = i < m_sourceLocTab.size() - 1
119 ? m_sourceLocTab[i + 1].first
120 : m_bclen;
121 locations.push_back(SourceLocEntry(endOff, m_sourceLocTab[i].second));
123 return locations;
126 void FuncEmitter::setLineTable(LineTable table) {
127 if (m_lineTable.isPtr()) {
128 if (auto p = m_lineTable.ptr()) {
129 *p = std::move(table);
130 return;
133 m_lineTable = Func::LineTablePtr::FromPtr(new LineTable{std::move(table)});
136 namespace {
138 using SrcLoc = std::vector<std::pair<Offset, SourceLoc>>;
141 * Create a LineTable from `srcLoc'.
143 LineTable createLineTable(const SrcLoc& srcLoc, Offset bclen) {
144 LineTable lines;
145 if (srcLoc.empty()) {
146 return lines;
149 auto prev = srcLoc.begin();
150 for (auto it = prev + 1; it != srcLoc.end(); ++it) {
151 if (prev->second.line1 != it->second.line1) {
152 lines.push_back(LineEntry(it->first, prev->second.line1));
153 prev = it;
157 lines.push_back(LineEntry(bclen, prev->second.line1));
158 return lines;
163 void FuncEmitter::recordSourceLocation(const Location::Range& sLoc,
164 Offset start) {
165 // Some byte codes, such as for the implicit "return 0" at the end of a
166 // a source file do not have valid source locations. This check makes
167 // sure we don't record a (dummy) source location in this case.
168 if (start > 0 && sLoc.line0 == -1) return;
169 SourceLoc newLoc(sLoc);
170 if (!m_sourceLocTab.empty()) {
171 if (m_sourceLocTab.back().second == newLoc) {
172 // Combine into the interval already at the back of the vector.
173 assertx(start >= m_sourceLocTab.back().first);
174 return;
176 assertx(m_sourceLocTab.back().first < start &&
177 "source location offsets must be added to UnitEmitter in "
178 "increasing order");
179 } else {
180 // First record added should be for bytecode offset zero or very rarely one
181 // when the source starts with a label and a Nop is inserted.
182 assertx(start == 0 || start == 1);
184 m_sourceLocTab.push_back(std::make_pair(start, newLoc));
187 ///////////////////////////////////////////////////////////////////////////////
188 // Initialization and execution.
190 void FuncEmitter::init(int l1, int l2, Attr attrs_,
191 const StringData* docComment_) {
192 line1 = l1;
193 line2 = l2;
194 docComment = docComment_;
195 attrs = attrs_;
197 assertx(!ue().isASystemLib() || attrs & AttrBuiltin);
200 void FuncEmitter::finish() {
201 sortEHTab();
204 const StaticString
205 s_construct("__construct"),
206 s_DynamicallyCallable("__DynamicallyCallable"),
207 s_Memoize("__Memoize"),
208 s_MemoizeLSB("__MemoizeLSB"),
209 s_KeyedByIC("KeyedByIC"),
210 s_MakeICInaccessible("MakeICInaccessible"),
211 s_SoftMakeICInaccessible("SoftMakeICInaccessible"),
212 s_Uncategorized("Uncategorized"),
213 s_SoftInternal("__SoftInternal");
215 namespace {
216 bool is_interceptable(const PreClass* preClass, const StringData* name) {
217 if (RO::RepoAuthoritative) return false;
218 if (Cfg::Eval::NonInterceptableFunctions.empty()) return true;
219 auto fullname = [&]() {
220 auto n = name->toCppString();
221 if (!preClass) {
222 return n;
224 return preClass->name()->toCppString() +"::"+ n;
225 }();
226 return Cfg::Eval::NonInterceptableFunctions.count(fullname) == 0;
230 Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
231 auto attrs = this->attrs;
233 auto persistent = RO::RepoAuthoritative || (ue().isASystemLib() && (!RO::funcIsRenamable(name) || preClass));
234 assertx(IMPLIES(attrs & AttrPersistent, persistent));
235 attrSetter(attrs, persistent, AttrPersistent);
237 auto interceptable = is_interceptable(preClass, name);
238 attrSetter(attrs, interceptable, AttrInterceptable);
240 if (!(attrs & AttrPersistent) && attrs & AttrBuiltin) {
241 assertx(!preClass);
242 SystemLib::s_anyNonPersistentBuiltins = true;
245 if (isAsync && !isGenerator) {
246 // Async functions can return results directly.
247 attrs |= AttrSupportsAsyncEagerReturn;
249 if (!coeffectRules.empty()) attrs |= AttrHasCoeffectRules;
251 if (attrs & AttrInternal &&
252 userAttributes.find(s_SoftInternal.get()) != userAttributes.end()) {
253 attrs |= AttrInternalSoft;
256 if (hasVar(s_86productAttributionData.get())) {
257 attrs |= AttrHasAttributionData;
260 auto const dynCallSampleRate = [&] () -> Optional<int64_t> {
261 if (!(attrs & AttrDynamicallyCallable)) return {};
263 auto const uattr = userAttributes.find(s_DynamicallyCallable.get());
264 if (uattr == userAttributes.end()) return {};
266 auto const tv = uattr->second;
267 assertx(isArrayLikeType(type(tv)));
268 auto const rate = val(tv).parr->get(int64_t(0));
269 if (!isIntType(type(rate)) || val(rate).num < 0) return {};
271 attrs = Attr(attrs & ~AttrDynamicallyCallable);
272 return val(rate).num;
273 }();
275 assertx(!m_pce == !preClass);
276 auto f = m_ue.newFunc(this, unit, name, attrs, params.size());
278 f->m_isPreFunc = !!preClass;
280 auto const uait = userAttributes.find(s___Reified.get());
281 auto const hasReifiedGenerics = uait != userAttributes.end();
283 // Returns (static coeffects, escapes)
284 auto const coeffectsInfo = getCoeffectsInfoFromList(
285 staticCoeffects,
286 preClass && name == s_construct.get());
287 f->m_requiredCoeffects = coeffectsInfo.first.toRequired();
289 Func::MemoizeICType icType = Func::MemoizeICType::MakeICInaccessible;
290 if (isMemoizeWrapper || isMemoizeWrapperLSB) {
291 auto const getICType = [&] (TypedValue tv) {
292 assertx(tvIsVec(tv));
293 IterateV(tv.m_data.parr, [&](TypedValue elem) {
294 if (tvIsString(elem)) {
296 * TODO: simplify this into a 2 state if else once Soft states are
297 * entirely removed
299 if (elem.m_data.pstr->same(s_KeyedByIC.get())) {
300 assertx(tv.m_data.parr->size() == 1);
301 icType = Func::MemoizeICType::KeyedByIC;
302 } else if (elem.m_data.pstr->same(s_MakeICInaccessible.get())) {
303 assertx(tv.m_data.parr->size() == 1);
304 icType = Func::MemoizeICType::MakeICInaccessible;
305 } else if (elem.m_data.pstr->same(s_SoftMakeICInaccessible.get())) {
306 assertx(tv.m_data.parr->size() <= 2);
307 icType = Func::MemoizeICType::MakeICInaccessible;
308 } else if (elem.m_data.pstr->same(s_Uncategorized.get())) {
309 icType = Func::MemoizeICType::MakeICInaccessible;
310 } else {
311 assertx(false && "invalid string");
313 } else if (tvIsInt(elem)) {
315 * This would be the sample rate, we don't use it
316 * this branch will be removed once the SoftMakeICInaccessible
317 * is removed from www
318 * for now assert that the SoftMakeICInaccessible was mapped to non-soft
320 assertx(icType == Func::MemoizeICType::MakeICInaccessible);
321 } else {
322 assertx(false && "invalid input");
326 auto const attrName = isMemoizeWrapperLSB ? s_MemoizeLSB : s_Memoize;
327 auto const it = userAttributes.find(attrName.get());
328 if (it != userAttributes.end()) {
329 getICType(it->second);
330 assertx((icType & 0x3) == icType);
334 bool const needsExtendedSharedData =
335 isNative ||
336 line2 - line1 >= Func::kSmallDeltaLimit ||
337 m_bclen >= Func::kSmallDeltaLimit ||
338 m_sn >= Func::kSmallDeltaLimit ||
339 hasReifiedGenerics ||
340 hasParamsWithMultiUBs ||
341 hasReturnWithMultiUBs ||
342 dynCallSampleRate ||
343 coeffectsInfo.second.value() != 0 ||
344 !coeffectRules.empty() ||
345 (docComment && !docComment->empty()) ||
346 requiresFromOriginalModule;
348 f->m_shared.reset(
349 needsExtendedSharedData
350 ? new Func::ExtendedSharedData(m_bc, m_bclen, preClass, m_sn, line1, line2,
351 !containsCalls)
352 : new Func::SharedData(m_bc, m_bclen, preClass, m_sn, line1, line2,
353 !containsCalls)
356 f->init(params.size());
358 if (auto const ex = f->extShared()) {
359 ex->m_allFlags.m_hasExtendedSharedData = true;
360 ex->m_arFuncPtr = nullptr;
361 ex->m_nativeFuncPtr = nullptr;
362 ex->m_bclen = m_bclen;
363 ex->m_sn = m_sn;
364 ex->m_line2 = line2;
365 ex->m_dynCallSampleRate = dynCallSampleRate.value_or(-1);
366 ex->m_allFlags.m_returnByValue = false;
367 ex->m_allFlags.m_isUntrustedReturnType = false;
368 ex->m_allFlags.m_isMemoizeWrapper = false;
369 ex->m_allFlags.m_isMemoizeWrapperLSB = false;
370 ex->m_allFlags.m_memoizeICType = Func::MemoizeICType::NoIC;
372 if (!coeffectRules.empty()) ex->m_coeffectRules = coeffectRules;
373 ex->m_coeffectEscapes = coeffectsInfo.second;
374 ex->m_docComment = docComment;
375 ex->m_originalModuleName =
376 originalModuleName ? originalModuleName : LowStringPtr(unit.moduleName());
377 assertx(ex->m_originalModuleName);
380 std::vector<Func::ParamInfo> fParams;
381 for (unsigned i = 0; i < params.size(); ++i) {
382 Func::ParamInfo pi = params[i];
383 fParams.push_back(pi);
384 auto const& fromUBs = params[i].upperBounds;
385 if (!fromUBs.isTop()) {
386 auto& ub = f->extShared()->m_paramUBs[i];
387 ub.m_constraints.resize(fromUBs.m_constraints.size());
388 std::copy(fromUBs.m_constraints.begin(), fromUBs.m_constraints.end(), ub.m_constraints.begin());
389 f->shared()->m_allFlags.m_hasParamsWithMultiUBs = true;
393 auto const originalFullName =
394 (!originalFilename ||
395 !RuntimeOption::RepoAuthoritative ||
396 FileUtil::isAbsolutePath(originalFilename->slice())) ?
397 originalFilename :
398 makeStaticString(Cfg::Server::SourceRoot +
399 originalFilename->toCppString());
401 f->shared()->m_localNames.create(m_localNames);
402 f->shared()->m_numLocals = m_numLocals;
403 f->shared()->m_numIterators = m_numIterators;
404 f->m_maxStackCells = maxStackCells;
405 f->shared()->m_ehtab = ehtab;
406 f->shared()->m_allFlags.m_isClosureBody = isClosureBody;
407 f->shared()->m_allFlags.m_isAsync = isAsync;
408 f->shared()->m_allFlags.m_isGenerator = isGenerator;
409 f->shared()->m_allFlags.m_isPairGenerator = isPairGenerator;
410 f->shared()->m_userAttributes = userAttributes;
411 f->shared()->m_retTypeConstraint = retTypeConstraint;
412 f->shared()->m_retUserType = retUserType;
413 if (!retUpperBounds.isTop()) {
414 f->extShared()->m_returnUBs.m_constraints.resize(retUpperBounds.m_constraints.size());
415 std::copy(retUpperBounds.m_constraints.begin(), retUpperBounds.m_constraints.end(),
416 f->extShared()->m_returnUBs.m_constraints.begin());
417 f->shared()->m_allFlags.m_hasReturnWithMultiUBs = true;
419 f->shared()->m_originalFilename = originalFullName;
420 f->shared()->m_allFlags.m_isGenerated = HPHP::is_generated(name);
421 f->shared()->m_repoReturnType = repoReturnType;
422 f->shared()->m_repoAwaitedReturnType = repoAwaitedReturnType;
423 f->shared()->m_allFlags.m_isMemoizeWrapper = isMemoizeWrapper;
424 f->shared()->m_allFlags.m_isMemoizeWrapperLSB = isMemoizeWrapperLSB;
425 f->shared()->m_allFlags.m_memoizeICType = icType;
426 f->shared()->m_allFlags.m_hasReifiedGenerics = hasReifiedGenerics;
428 for (auto const& name : staticCoeffects) {
429 f->shared()->m_staticCoeffectNames.push_back(name);
432 if (hasReifiedGenerics) {
433 auto const tv = uait->second;
434 assertx(tvIsVec(tv));
435 f->extShared()->m_reifiedGenericsInfo =
436 extractSizeAndPosFromReifiedAttribute(tv.m_data.parr);
440 * If we have a m_sourceLocTab, use that to create the line
441 * table. Otherwise use the line table we loaded out of the repo, or
442 * a token to lazy load it.
444 if (m_sourceLocTab.size() != 0) {
445 f->setLineTable(createLineTable(m_sourceLocTab, m_bclen));
446 // If the debugger is enabled, or we plan to dump hhas we will
447 // need the extended line table information in the output, and if
448 // we're not writing the repo, stashing it here is necessary for
449 // it to make it through.
450 if (needs_extended_line_table()) {
451 f->stashExtendedLineTable(createSourceLocTable());
453 } else if (m_lineTable.isPtr()) {
454 f->setLineTable(*m_lineTable.ptr());
455 } else {
456 assertx(RO::RepoAuthoritative);
457 f->setLineTable(m_lineTable.token());
460 if (isNative) {
461 auto const ex = f->extShared();
463 auto const info = getNativeInfo();
465 Attr dummy = AttrNone;
466 auto nativeAttributes = parseNativeAttributes(dummy);
467 Native::getFunctionPointers(
468 info,
469 nativeAttributes,
470 ex->m_arFuncPtr,
471 ex->m_nativeFuncPtr
474 if (ex->m_nativeFuncPtr) {
475 if (UNLIKELY(RO::EvalRecordReplay)) {
476 rr::addNativeFuncAttrs(ex->m_nativeFuncPtr, attrs);
478 if (info.sig.ret == Native::NativeSig::Type::MixedTV ||
479 info.sig.ret == Native::NativeSig::Type::StringNN ||
480 info.sig.ret == Native::NativeSig::Type::ArrayNN ||
481 info.sig.ret == Native::NativeSig::Type::ObjectNN) {
482 // the non null ref or mixed TV should be return by value
483 ex->m_allFlags.m_returnByValue = true;
485 if (info.sig.ret == Native::NativeSig::Type::String ||
486 info.sig.ret == Native::NativeSig::Type::Array ||
487 info.sig.ret == Native::NativeSig::Type::Object ||
488 info.sig.ret == Native::NativeSig::Type::Resource ) {
489 // these types are nullable
490 // set return by value flag
491 ex->m_allFlags.m_isUntrustedReturnType = true;
494 int extra = isMethod() ? 1 : 0;
495 assertx(info.sig.args.size() == params.size() + extra);
496 for (auto i = params.size(); i--; ) {
497 switch (info.sig.args[extra + i]) {
498 case Native::NativeSig::Type::ObjectNN:
499 case Native::NativeSig::Type::StringNN:
500 case Native::NativeSig::Type::ArrayNN:
501 case Native::NativeSig::Type::ResourceArg:
502 fParams[i].setFlag(Func::ParamInfo::Flags::NativeArg);
503 break;
504 case Native::NativeSig::Type::MixedTV:
505 fParams[i].setFlag(Func::ParamInfo::Flags::NativeArg);
506 fParams[i].setFlag(Func::ParamInfo::Flags::AsTypedValue);
507 break;
508 case Native::NativeSig::Type::Mixed:
509 fParams[i].setFlag(Func::ParamInfo::Flags::AsVariant);
510 break;
511 default:
512 break;
518 f->finishedEmittingParams(fParams);
520 if (RuntimeOption::EvalEnableReverseDataMap && !preClass) {
521 f->registerInDataMap();
523 return f;
526 String FuncEmitter::nativeFullname() const {
527 return Native::fullName(name, m_pce ? m_pce->name() : nullptr,
528 (attrs & AttrStatic));
531 Native::NativeFunctionInfo FuncEmitter::getNativeInfo() const {
532 assertx(m_ue.m_extension);
533 return Native::getNativeFunction(
534 m_ue.m_extension->nativeFuncs(),
535 name,
536 m_pce ? m_pce->name() : nullptr,
537 (attrs & AttrStatic)
541 ///////////////////////////////////////////////////////////////////////////////
542 // Locals, iterators, and parameters.
544 void FuncEmitter::allocVarId(const StringData* name, bool slotless) {
545 assertx(name != nullptr);
546 UNUSED Id id;
547 if (m_localNames.find(name) == m_localNames.end()) {
548 // Slotless locals must come after all locals with slots.
549 if (!slotless) {
550 id = (m_numLocals++);
551 assertx(id == (int)m_localNames.size());
553 m_localNames.add(name, name);
557 Id FuncEmitter::allocIterator() {
558 assertx(m_numIterators >= m_nextFreeIterator);
559 Id id = m_nextFreeIterator++;
560 if (m_numIterators < m_nextFreeIterator) {
561 m_numIterators = m_nextFreeIterator;
563 return id;
566 Id FuncEmitter::allocUnnamedLocal() {
567 m_localNames.addUnnamed(nullptr);
568 ++m_numUnnamedLocals;
569 return m_numLocals++;
572 ///////////////////////////////////////////////////////////////////////////////
573 // Unit tables.
575 EHEnt& FuncEmitter::addEHEnt() {
576 assertx(!m_ehTabSorted
577 || "should only mark the ehtab as sorted after adding all of them");
578 ehtab.emplace_back();
579 ehtab.back().m_parentIndex = 7777;
580 return ehtab.back();
583 namespace {
586 * Ordering on EHEnts where e1 < e2 iff
588 * a) e1 and e2 do not overlap, and e1 comes first
589 * b) e1 encloses e2
591 struct EHEntComp {
592 bool operator()(const EHEnt& e1, const EHEnt& e2) const {
593 if (e1.m_base == e2.m_base) {
594 return e1.m_past > e2.m_past;
596 return e1.m_base < e2.m_base;
602 void FuncEmitter::sortEHTab() {
603 if (m_ehTabSorted) return;
605 std::sort(ehtab.begin(), ehtab.end(), EHEntComp());
607 for (unsigned int i = 0; i < ehtab.size(); i++) {
608 ehtab[i].m_parentIndex = -1;
609 for (int j = i - 1; j >= 0; j--) {
610 if (ehtab[j].m_past >= ehtab[i].m_past) {
611 // parent EHEnt better enclose this one.
612 assertx(ehtab[j].m_base <= ehtab[i].m_base);
613 ehtab[i].m_parentIndex = j;
614 break;
619 setEHTabIsSorted();
622 void FuncEmitter::setEHTabIsSorted() {
623 m_ehTabSorted = true;
624 if (!debug) return;
626 Offset curBase = 0;
627 for (size_t i = 0; i < ehtab.size(); ++i) {
628 auto& eh = ehtab[i];
630 // Base offsets must be monotonically increasing.
631 always_assert(curBase <= eh.m_base);
632 curBase = eh.m_base;
634 // Parent should come before, and must enclose this guy.
635 always_assert(eh.m_parentIndex == -1 || eh.m_parentIndex < i);
636 if (eh.m_parentIndex != -1) {
637 auto& parent = ehtab[eh.m_parentIndex];
638 always_assert(parent.m_base <= eh.m_base &&
639 parent.m_past >= eh.m_past);
644 ///////////////////////////////////////////////////////////////////////////////
645 // Bytecode
647 void FuncEmitter::setBc(const unsigned char* bc, size_t bclen) {
648 if (m_bc.isPtr()) {
649 if (auto const p = m_bc.ptr()) free(p);
651 auto const p = (unsigned char*)malloc(bclen);
652 m_bcmax = bclen;
653 if (bclen) memcpy(p, bc, bclen);
654 m_bc = Func::BCPtr::FromPtr(p);
655 m_bclen = bclen;
658 void FuncEmitter::setBcToken(Func::BCPtr::Token token, size_t bclen) {
659 if (m_bc.isPtr()) {
660 if (auto const p = m_bc.ptr()) free(p);
662 m_bcmax = bclen;
663 m_bclen = bclen;
664 m_bc = Func::BCPtr::FromToken(token);
667 Optional<Func::BCPtr::Token> FuncEmitter::loadBc() {
668 if (m_bc.isPtr()) return std::nullopt;
669 assertx(RO::RepoAuthoritative);
670 auto const old = m_bc.token();
671 auto bc = (unsigned char*)malloc(m_bclen);
672 RepoFile::readRawFromUnit(m_ue.m_sn, old, bc, m_bclen);
673 m_bc = Func::BCPtr::FromPtr(bc);
674 return old;
677 ///////////////////////////////////////////////////////////////////////////////
678 // Complex setters.
680 /* <<__Native>> user attribute causes systemlib declarations
681 * to hook internal (C++) implementation of funcs/methods
683 * The Native attribute may have the following sub-options
684 * "NoFCallBuiltin": Prevent FCallBuiltin optimization
685 * Effectively forces functions to generate an ActRec
686 * "NoInjection": Do not include this frame in backtraces
687 * "NoRecording": Do not include calls to this when recording
689 * e.g. <<__Native("NoFCallBuiltin")>> function foo():mixed;
691 static const StaticString
692 s_native("__Native"),
693 s_nofcallbuiltin("NoFCallBuiltin"),
694 s_noinjection("NoInjection"),
695 s_norecording("NoRecording"),
696 s_opcodeimpl("OpCodeImpl");
698 int FuncEmitter::parseNativeAttributes(Attr& attrs_) const {
699 int ret = Native::AttrNone;
701 auto it = userAttributes.find(s_native.get());
702 assertx(it != userAttributes.end());
703 const TypedValue userAttr = it->second;
704 assertx(isArrayLikeType(userAttr.m_type));
705 for (ArrayIter it(userAttr.m_data.parr); it; ++it) {
706 Variant userAttrVal = it.second();
707 if (userAttrVal.isString()) {
708 String userAttrStrVal = userAttrVal.toString();
709 if (userAttrStrVal.get()->tsame(s_nofcallbuiltin.get())) {
710 attrs_ |= AttrNoFCallBuiltin;
711 } else if (userAttrStrVal.get()->tsame(s_noinjection.get())) {
712 attrs_ |= AttrNoInjection;
713 } else if (userAttrStrVal.get()->tsame(s_norecording.get())) {
714 attrs_ |= AttrNoRecording;
715 } else if (userAttrStrVal.get()->tsame(s_opcodeimpl.get())) {
716 ret |= Native::AttrOpCodeImpl;
720 return ret;
723 ///////////////////////////////////////////////////////////////////////////////
724 // Serialization/Deserialization
726 template<class SerDe>
727 void FuncEmitter::serdeMetaData(SerDe& sd) {
728 // NOTE: name and a few other fields currently handled outside of this.
730 sd(line1)
731 (line2)
732 (attrs)
733 (m_bclen)
734 (staticCoeffects)
735 (repoReturnType)
736 (repoAwaitedReturnType)
737 (docComment)
738 (m_numLocals)
739 (m_numIterators)
740 (maxStackCells)
741 (m_repoBoolBitset)
743 (params)
744 (m_localNames, [](auto s) { return s; })
745 .delta(
746 ehtab,
747 [&](const EHEnt& prev, EHEnt cur) -> EHEnt {
748 if constexpr (!SerDe::deserializing) {
749 cur.m_handler -= cur.m_past;
750 cur.m_past -= cur.m_base;
751 cur.m_base -= prev.m_base;
752 } else {
753 cur.m_base += prev.m_base;
754 cur.m_past += cur.m_base;
755 cur.m_handler += cur.m_past;
757 return cur;
760 (userAttributes)
761 (retTypeConstraint)
762 (retUserType)
763 (retUpperBounds)
764 (originalFilename)
765 (originalModuleName)
766 (coeffectRules)
770 template void FuncEmitter::serdeMetaData<>(BlobDecoder&);
771 template void FuncEmitter::serdeMetaData<>(BlobEncoder&);
773 template<class SerDe>
774 void FuncEmitter::serde(SerDe& sd, bool lazy) {
775 assertx(IMPLIES(lazy, RO::RepoAuthoritative));
776 assertx(IMPLIES(!SerDe::deserializing, !lazy));
778 serdeMetaData(sd);
780 // Never lazy load any builtins (this avoids issues with HHBBC
781 // trying to load data after we've shutdown the repo).
782 if (attrs & AttrBuiltin) lazy = false;
784 if constexpr (SerDe::deserializing) {
785 if (lazy) {
786 m_lineTable = Func::LineTablePtr::FromToken(sd.advanced());
787 sd.skipWithSize();
788 } else {
789 LineTable lineTable;
790 deserializeLineTable(sd, lineTable);
791 setLineTable(std::move(lineTable));
793 } else {
794 auto const& lines = m_sourceLocTab.empty()
795 ? *m_lineTable.ptr()
796 : createLineTable(m_sourceLocTab, m_bclen);
797 sd.withSize(
798 [&] {
799 sd.delta(
800 lines,
801 [&] (const LineEntry& prev, const LineEntry& curDelta) {
802 return LineEntry {
803 curDelta.pastOffset() - prev.pastOffset(),
804 curDelta.val() - prev.val()
812 if constexpr (SerDe::deserializing) {
813 sd(m_sourceLocTab);
814 } else {
815 sd(RO::RepoDebugInfo ? m_sourceLocTab : decltype(m_sourceLocTab){});
818 // Bytecode
819 if constexpr (SerDe::deserializing) {
820 assertx(sd.remaining() >= m_bclen);
821 if (lazy) {
822 setBcToken(sd.advanced(), m_bclen);
823 } else {
824 setBc(sd.data(), m_bclen);
826 sd.advance(m_bclen);
827 } else {
828 sd.writeRaw((const char*)m_bc.ptr(), m_bclen);
832 template void FuncEmitter::serde<>(BlobDecoder&, bool);
833 template void FuncEmitter::serde<>(BlobEncoder&, bool);
835 void FuncEmitter::deserializeLineTable(BlobDecoder& decoder,
836 LineTable& lineTable) {
837 decoder.withSize(
838 [&] {
839 decoder.delta(
840 lineTable,
841 [&] (const LineEntry& prev, const LineEntry& curDelta) {
842 return LineEntry {
843 curDelta.pastOffset() + prev.pastOffset(),
844 curDelta.val() + prev.val()
852 LineTable FuncEmitter::loadLineTableFromRepo(int64_t unitSn,
853 RepoFile::Token token) {
854 assertx(RO::RepoAuthoritative);
856 auto const remaining = RepoFile::remainingSizeOfUnit(unitSn, token);
857 always_assert(remaining >= sizeof(uint64_t));
859 size_t actualSize;
861 // We encoded the size of the line table along with the table. So,
862 // peek its size and bail if the decoder doesn't have enough data
863 // remaining.
864 auto const size = std::min<size_t>(remaining, 128);
865 auto const data = std::make_unique<unsigned char[]>(size);
866 RepoFile::readRawFromUnit(unitSn, token, data.get(), size);
867 BlobDecoder decoder{data.get(), size};
868 actualSize = decoder.peekSize();
869 if (actualSize <= decoder.remaining()) {
870 LineTable lineTable;
871 deserializeLineTable(decoder, lineTable);
872 return lineTable;
876 constexpr const size_t kLineTableSizeLimit = 1ull << 32;
877 always_assert(actualSize <= kLineTableSizeLimit);
878 always_assert(actualSize <= remaining);
880 LineTable lineTable;
881 auto const data = std::make_unique<unsigned char[]>(actualSize);
882 RepoFile::readRawFromUnit(unitSn, token, data.get(), actualSize);
883 BlobDecoder decoder{data.get(), actualSize};
884 deserializeLineTable(decoder, lineTable);
885 decoder.assertDone();
886 return lineTable;
889 ///////////////////////////////////////////////////////////////////////////////