Add support for .hhas files in systemlib

Will be needed for array_filter/array_map etc

This sets things up so that if we define a builtin in systemlib, we rename
the corresponding c++ builtin with the prefix __builtin_, so its still available
(in case the php builtin wants to delegate some edge cases, and to make
it easy to run comparisons between the php and c++ implementations).

Also did a little reorganization to get rid of Func::isPHPBuiltin,
and use an Attr to identify functions as builtins. C++ builtins can
still be identified by checking the Func::info() method. This is needed
to allow builtin methods defined in php (such as array_map) to lookup their
arguments in the correct context.
Esse commit está contido em:
mwilliams
2013-05-24 08:57:40 -07:00
commit de sgolemon
commit 71859e5566
28 arquivos alterados com 250 adições e 214 exclusões
+32 -77
Ver Arquivo
@@ -5238,6 +5238,7 @@ void EmitterVisitor::emitPostponedMeths() {
PostponedMeth& p = m_postponedMeths.front();
FunctionScopePtr funcScope = p.m_meth->getFunctionScope();
FuncEmitter* fe = p.m_fe;
bool allowOverride = false;
if (!fe) {
assert(p.m_top);
const StringData* methName =
@@ -5253,6 +5254,10 @@ void EmitterVisitor::emitPostponedMeths() {
funcScope->userAttributes();
for (FunctionScope::UserAttributeMap::const_iterator it = userAttrs.begin();
it != userAttrs.end(); ++it) {
if (it->first == "__Overridable") {
allowOverride = true;
continue;
}
const StringData* uaName = StringData::GetStaticString(it->first);
ExpressionPtr uaValue = it->second;
assert(uaValue);
@@ -5394,6 +5399,8 @@ void EmitterVisitor::emitPostponedMeths() {
ModifierExpressionPtr mod(p.m_meth->getModifiers());
Attr attrs = buildAttrs(mod, p.m_meth->isRef());
if (allowOverride) attrs = attrs | AttrAllowOverride;
if (funcScope->mayUseVV()) {
attrs = attrs | AttrMayUseVV;
}
@@ -5926,6 +5933,10 @@ bool EmitterVisitor::canEmitBuiltinCall(FunctionCallPtr fn,
if (func->allowOverride() && !Option::WholeProgram) {
return false;
}
if (Func* f = Unit::lookupFunc(StringData::GetStaticString(name))) {
if (!f->info()) return false;
}
return true;
}
return false;
@@ -6974,20 +6985,7 @@ static Unit* emitHHBCNativeFuncUnit(const HhbcExtFuncInfo* builtinFuncs,
MD5 md5("11111111111111111111111111111111");
UnitEmitter* ue = new UnitEmitter(md5);
ue->setFilepath(StringData::GetStaticString(""));
ue->initMain(0, 0);
FuncEmitter* mfe = ue->getMain();
ue->emitOp(OpInt);
ue->emitInt64(1);
ue->emitOp(OpRetC);
mfe->setMaxStackCells(1);
mfe->finish(ue->bcPos(), false);
ue->recordFunction(mfe);
TypedValue mainReturn;
mainReturn.m_data.num = 1;
mainReturn.m_type = KindOfInt64;
ue->setMainReturn(&mainReturn);
ue->setMergeOnly(true);
ue->addTrivialPseudoMain();
/*
Special function used by FPushCuf* when its argument
@@ -7011,6 +7009,11 @@ static Unit* emitHHBCNativeFuncUnit(const HhbcExtFuncInfo* builtinFuncs,
const ClassInfo::MethodInfo* mi = ClassInfo::FindFunction(name);
assert(mi &&
"MethodInfo not found; may be a problem with the .idl.json files");
if (Unit::lookupFunc(name)) {
// already provided by systemlib, rename to allow the php
// version to delegate if necessary
name = StringData::GetStaticString("__builtin_" + name->toCPPString());
}
FuncEmitter* fe = ue->newFuncEmitter(name, /*top*/ true);
Offset base = ue->bcPos();
fe->setBuiltinFunc(mi, bif, nif, base);
@@ -7097,21 +7100,7 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
MD5 md5("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
UnitEmitter* ue = new UnitEmitter(md5);
ue->setFilepath(StringData::GetStaticString(""));
ue->initMain(0, 0);
FuncEmitter* mfe = ue->getMain();
ue->emitOp(OpInt);
ue->emitInt64(1);
ue->emitOp(OpRetC);
Offset past = ue->bcPos();
mfe->setMaxStackCells(1);
mfe->finish(past, false);
ue->recordFunction(mfe);
TypedValue mainReturn;
mainReturn.m_data.num = 1;
mainReturn.m_type = KindOfInt64;
ue->setMainReturn(&mainReturn);
ue->setMergeOnly(true);
ue->addTrivialPseudoMain();
MetaInfoBuilder metaInfo;
@@ -7400,41 +7389,6 @@ static void batchCommit(std::vector<UnitEmitter*>& ues) {
ues.clear();
}
static void emitSystemLib() {
if (!Option::WholeProgram) return;
string slib = get_systemlib();
if (slib.empty()) return;
Option::WholeProgram = false;
SystemLib::s_inited = false;
SCOPE_EXIT {
SystemLib::s_inited = true;
Option::WholeProgram = true;
};
AnalysisResultPtr ar(new AnalysisResult());
Scanner scanner(slib.c_str(), slib.size(),
RuntimeOption::GetScannerType(), "/:systemlib.php");
Parser parser(scanner, "/:systemlib.php", ar, slib.size());
parser.parse();
FileScopePtr fsp = parser.getFileScope();
fsp->setOuterScope(ar);
ar->loadBuiltins();
ar->setPhase(AnalysisResult::AnalyzeAll);
fsp->analyzeProgram(ar);
int md5len;
char* md5str = string_md5(slib.c_str(), slib.size(), false, md5len);
MD5 md5(md5str);
free(md5str);
UnitEmitter* ue = emitHHBCUnitEmitter(ar, fsp, md5);
Repo::get().commitUnit(ue, UnitOriginFile);
}
/**
* This is the entry point for offline bytecode generation.
*/
@@ -7489,8 +7443,6 @@ void emitAllHHBC(AnalysisResultPtr ar) {
break;
}
}
emitSystemLib();
} else {
dispatcher.waitEmpty();
}
@@ -7537,6 +7489,7 @@ Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
}
SCOPE_EXIT { SymbolTable::Purge(); };
UnitEmitter* ue = nullptr;
// Check if this file contains raw hip hop bytecode instead of php.
// For now this is just dictated by file extension, and doesn't ever
// commit to the repo.
@@ -7544,23 +7497,25 @@ Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
if (const char* dot = strrchr(filename, '.')) {
const char hhbc_ext[] = "hhas";
if (!strcmp(dot + 1, hhbc_ext)) {
return assemble_file(filename, md5);
ue = assemble_string(code, codeLen, filename, md5);
}
}
}
AnalysisResultPtr ar(new AnalysisResult());
Scanner scanner(code, codeLen, RuntimeOption::GetScannerType(), filename);
Parser parser(scanner, filename, ar, codeLen);
parser.parse();
FileScopePtr fsp = parser.getFileScope();
fsp->setOuterScope(ar);
if (!ue) {
AnalysisResultPtr ar(new AnalysisResult());
Scanner scanner(code, codeLen, RuntimeOption::GetScannerType(), filename);
Parser parser(scanner, filename, ar, codeLen);
parser.parse();
FileScopePtr fsp = parser.getFileScope();
fsp->setOuterScope(ar);
ar->loadBuiltins();
ar->setPhase(AnalysisResult::AnalyzeAll);
fsp->analyzeProgram(ar);
ar->loadBuiltins();
ar->setPhase(AnalysisResult::AnalyzeAll);
fsp->analyzeProgram(ar);
UnitEmitter* ue = emitHHBCUnitEmitter(ar, fsp, md5);
ue = emitHHBCUnitEmitter(ar, fsp, md5);
}
Repo::get().commitUnit(ue, unitOrigin);
Unit* unit = ue->create();
delete ue;
+1 -1
Ver Arquivo
@@ -80,7 +80,7 @@ FunctionScope::FunctionScope(AnalysisResultConstPtr ar, bool method,
// Support for systemlib functions implemented in PHP
if (!m_method &&
m_userAttributes.find("__Overridable") != m_userAttributes.end()) {
setAllowOverride();
setAllowOverride();
}
}
+4 -1
Ver Arquivo
@@ -837,6 +837,9 @@ void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar) {
RuntimeOption::RepoJournal = "memory";
RuntimeOption::EnableHipHopSyntax = Option::EnableHipHopSyntax;
RuntimeOption::EvalJitEnableRenameFunction = Option::JitEnableRenameFunction;
// Turn off commits, because we don't want systemlib to get included
RuntimeOption::RepoCommit = false;
}
int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar,
@@ -868,7 +871,7 @@ int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar,
/* without this, emitClass allows classes with interfaces to be
hoistable */
SystemLib::s_inited = true;
RuntimeOption::RepoCommit = true;
Option::AutoInline = -1;
if (po.optimizeLevel > 0) {
+22 -28
Ver Arquivo
@@ -105,47 +105,39 @@ void ProcessInit() {
int db = RuntimeOption::EvalDumpBytecode;
bool rp = RuntimeOption::AlwaysUseRelativePath;
bool sf = RuntimeOption::SafeFileAccess;
bool ah = RuntimeOption::EvalAllowHhas;
RuntimeOption::EvalDumpBytecode &= ~1;
RuntimeOption::AlwaysUseRelativePath = false;
RuntimeOption::SafeFileAccess = false;
RuntimeOption::EvalAllowHhas = true;
Transl::TargetCache::requestInit();
Unit* nativeFuncUnit = build_native_func_unit(hhbc_ext_funcs,
hhbc_ext_funcs_count);
SystemLib::s_nativeFuncUnit = nativeFuncUnit;
String currentDir = g_vmContext->getCwd();
if (RuntimeOption::RepoAuthoritative) {
auto file = g_vmContext->lookupPhpFile(String("/:systemlib.php").get(),
currentDir.data(), nullptr);
if (!file) {
// Die a horrible death.
Logger::Error("Unable to find/load systemlib.php");
_exit(1);
}
file->incRef();
SystemLib::s_unit = file->unit();
} else {
string slib = get_systemlib();
string hhas;
string slib = get_systemlib(&hhas);
if (slib.empty()) {
// Die a horrible death.
Logger::Error("Unable to find/load systemlib.php");
_exit(1);
}
SystemLib::s_unit = compile_string(slib.c_str(), slib.size());
if (slib.empty()) {
// Die a horrible death.
Logger::Error("Unable to find/load systemlib.php");
_exit(1);
}
SystemLib::s_unit = compile_string(slib.c_str(), slib.size(),
"systemlib.php");
if (!hhas.empty()) {
SystemLib::s_hhas_unit = compile_string(hhas.c_str(), hhas.size(),
"systemlib.hhas");
}
// Restore most settings before merging anything,
// because of optimizations that depend on them
RuntimeOption::AlwaysUseRelativePath = rp;
RuntimeOption::SafeFileAccess = sf;
// Load the systemlib unit to build the Class objects
SystemLib::s_unit->merge();
if (SystemLib::s_hhas_unit) {
SystemLib::s_hhas_unit->merge();
}
// load builtins
SystemLib::s_nativeFuncUnit = build_native_func_unit(hhbc_ext_funcs,
hhbc_ext_funcs_count);
SystemLib::s_nativeFuncUnit->merge();
SystemLib::s_nullFunc =
Unit::lookupFunc(StringData::GetStaticString("86null"));
@@ -188,8 +180,10 @@ void ProcessInit() {
Stack::ValidateStackSize();
SystemLib::s_inited = true;
// Restore last to avoid dumping system things
RuntimeOption::AlwaysUseRelativePath = rp;
RuntimeOption::SafeFileAccess = sf;
RuntimeOption::EvalDumpBytecode = db;
RuntimeOption::EvalAllowHhas = ah;
}
///////////////////////////////////////////////////////////////////////////////
+14 -5
Ver Arquivo
@@ -1151,16 +1151,26 @@ String canonicalize_path(CStrRef p, const char* root, int rootLen) {
return path;
}
static string systemlib_split(string slib, string* hhas) {
auto pos = slib.find("\n<?hhas\n");
if (pos != string::npos) {
if (hhas) *hhas = slib.substr(pos + 8);
return slib.substr(0, pos);
}
return slib;
}
// Search for systemlib.php in the following places:
// 1) ${HHVM_SYSTEMLIB}
// 2) section "systemlib" in the current executable
// and return its contents
string get_systemlib() {
string get_systemlib(string* hhas) {
if (char *file = getenv("HHVM_SYSTEMLIB")) {
std::ifstream ifs(file);
if (ifs.good()) {
return std::string(std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>());
return systemlib_split(std::string(
std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>()), hhas);
}
}
@@ -1172,8 +1182,7 @@ string get_systemlib() {
ifs.seekg(desc.m_start, std::ios::beg);
std::unique_ptr<char[]> data(new char[desc.m_len]);
ifs.read(data.get(), desc.m_len);
string result(data.get(), desc.m_len);
return result;
return systemlib_split(string(data.get(), desc.m_len), hhas);
}
///////////////////////////////////////////////////////////////////////////////
+1 -1
Ver Arquivo
@@ -83,7 +83,7 @@ void hphp_thread_exit();
void hphp_session_exit();
void hphp_process_exit() ATTRIBUTE_COLD;
bool hphp_is_warmup_enabled();
std::string get_systemlib();
std::string get_systemlib(std::string* hhas = nullptr);
// Generated by hphp/util/generate_buildinfo.sh.
extern const char* const kCompilerId;
+3 -3
Ver Arquivo
@@ -276,7 +276,7 @@ String f_create_function(CStrRef args, CStrRef code) {
Variant f_func_get_arg(int arg_num) {
CallerFrame cf;
ActRec* ar = cf();
ActRec* ar = cf.actRecForArgs();
if (ar == NULL) {
return false;
@@ -367,7 +367,7 @@ Array hhvm_get_frame_args(const ActRec* ar) {
Variant f_func_get_args() {
EagerCallerFrame cf;
ActRec* ar = cf();
ActRec* ar = cf.actRecForArgs();
if (ar && ar->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) {
raise_warning(
"func_get_args(): Called from the global scope - no function context"
@@ -399,7 +399,7 @@ Array func_get_args(int num_args, CArrRef params, CArrRef args) {
int64_t f_func_num_args() {
EagerCallerFrame cf;
ActRec* ar = cf();
ActRec* ar = cf.actRecForArgs();
if (ar == NULL) {
return -1;
}
+2 -1
Ver Arquivo
@@ -330,7 +330,8 @@ static void set_function_info(Array &ret, const Func* func) {
}
if (func->isBuiltin()) {
ret.set(s_internal, true_varNR);
if (func->info()->attribute & ClassInfo::HipHopSpecific) {
if (func->info() &&
func->info()->attribute & ClassInfo::HipHopSpecific) {
ret.set(s_hphp, true_varNR);
}
}
+38 -13
Ver Arquivo
@@ -75,6 +75,8 @@
* @author Jorden DeLong <delong.j@fb.com>
*/
#include "hphp/runtime/vm/as.h"
#include <cstdio>
#include <iostream>
#include <algorithm>
@@ -90,6 +92,7 @@
#include "hphp/runtime/vm/unit.h"
#include "hphp/runtime/vm/hhbc.h"
#include "hphp/runtime/base/builtin_functions.h"
#include "hphp/system/lib/systemlib.h"
TRACE_SET_MOD(hhas);
@@ -944,9 +947,11 @@ OpcodeParserMap opcode_parsers;
#define IMM_FOUR(t1, t2, t3, t4) IMM_##t1; IMM_##t2; IMM_##t3; IMM_##t4
// FCall and NewTuple need to know the the first imm do POP_*MANY.
#define IMM_IVA \
immIVA = read_opcode_arg<int64_t>(as); \
as.ue->emitIVA(immIVA); \
#define IMM_IVA do { \
int imm = read_opcode_arg<int64_t>(as); \
as.ue->emitIVA(imm); \
if (immIVA < 0) immIVA = imm; \
} while (0)
#define IMM_SA as.ue->emitInt32(as.ue->mergeLitstr(read_litstr(as)))
#define IMM_I64A as.ue->emitInt64(read_opcode_arg<int64_t>(as))
@@ -1294,10 +1299,15 @@ enum AttrContext {
};
Attr parse_attribute_list(AsmState& as, AttrContext ctx) {
as.in.skipWhitespace();
if (as.in.peek() != '[') return AttrNone;
int ret = AttrNone;
if (ctx == ClassAttributes || ctx == FuncAttributes) {
if (!SystemLib::s_inited) {
ret |= AttrUnique | AttrPersistent;
}
}
if (as.in.peek() != '[') return Attr(ret);
as.in.getc();
int ret = AttrNone;
std::string word;
for (;;) {
as.in.skipWhitespace();
@@ -1752,6 +1762,13 @@ void parse_class(AsmState& as) {
* ;
*/
void parse_main(AsmState& as) {
if (as.emittedPseudoMain) {
if (!SystemLib::s_inited) {
as.error(".main found in systemlib");
} else {
as.error("Multiple .main directives found");
}
}
as.in.expectWs('{');
as.ue->initMain(as.in.getLineNumber(),
@@ -1801,6 +1818,17 @@ void parse_adata(AsmState& as) {
void parse(AsmState& as) {
as.in.skipWhitespace();
std::string directive;
if (!SystemLib::s_inited) {
/*
* The SystemLib::s_hhas_unit is required to be merge-only,
* and we create the source by concatenating separate .hhas files
* Rather than choosing one to have the .main directive, we just
* generate a trivial pseudoMain automatically.
*/
as.ue->addTrivialPseudoMain();
as.emittedPseudoMain = true;
}
while (as.in.readword(directive)) {
if (directive == ".main") { parse_main(as); continue; }
if (directive == ".function") { parse_function(as); continue; }
@@ -1819,17 +1847,14 @@ void parse(AsmState& as) {
//////////////////////////////////////////////////////////////////////
Unit* assemble_file(const char* filename, const MD5& md5) {
boost::scoped_ptr<UnitEmitter> ue(new UnitEmitter(md5));
UnitEmitter* assemble_string(const char*code, int codeLen,
const char* filename, const MD5& md5) {
std::unique_ptr<UnitEmitter> ue(new UnitEmitter(md5));
StringData* sd = StringData::GetStaticString(filename);
ue->setFilepath(sd);
try {
std::ifstream instr(filename);
if (!instr.is_open()) {
throw std::runtime_error(std::string("couldn't open file ") +
filename + ": " + strerror(errno));
}
std::istringstream instr(string(code, codeLen));
AsmState as(instr);
as.ue = ue.get();
parse(as);
@@ -1847,7 +1872,7 @@ Unit* assemble_file(const char* filename, const MD5& md5) {
ue->recordFunction(fe);
}
return ue->create();
return ue.release();
}
//////////////////////////////////////////////////////////////////////
+4 -3
Ver Arquivo
@@ -21,16 +21,17 @@
namespace HPHP {
class Unit;
class UnitEmitter;
//////////////////////////////////////////////////////////////////////
/*
* Assemble the contents of `filename' and return a Unit.
* Assemble the contents of `filename' and return a UnitEmitter.
*
* Minimal documentation is available in as.cpp.
*/
Unit* assemble_file(const char* filename, const MD5&);
UnitEmitter* assemble_string(const char* code, int codeLen,
const char* filename, const MD5&);
//////////////////////////////////////////////////////////////////////
+11 -9
Ver Arquivo
@@ -114,7 +114,7 @@ ActRec* ActRec::arGetSfp() const {
bool
ActRec::skipFrame() const {
return m_func && m_func->isBuiltin();
return m_func && m_func->skipFrame();
}
template <>
@@ -879,7 +879,7 @@ void Stack::toStringFrame(std::ostream& os, const ActRec* fp,
os << ">";
}
assert(!func->isBuiltin() || func->numIterators() == 0);
assert(!func->info() || func->numIterators() == 0);
if (func->numIterators() > 0) {
os << "|";
Iter* it = &((Iter*)&tv[1])[-1];
@@ -1788,7 +1788,7 @@ bool VMExecutionContext::prepareFuncEntry(ActRec *ar, PC& pc) {
// cppext functions/methods have their own logic for raising
// warnings for missing arguments, so we only need to do this work
// for non-cppext functions/methods
if (raiseMissingArgumentWarnings && !func->isBuiltin()) {
if (raiseMissingArgumentWarnings && !func->info()) {
// need to sync m_pc to pc for backtraces/re-entry
SYNC();
const Func::ParamInfoVec& paramInfo = func->params();
@@ -2282,7 +2282,7 @@ void VMExecutionContext::invokeUnit(TypedValue* retval, Unit* unit) {
void VMExecutionContext::unwindBuiltinFrame() {
// Unwind the frame for a builtin. Currently only used for
// hphpd_break and fb_enable_code_coverage
assert(m_fp->m_func->isBuiltin());
assert(m_fp->m_func->info());
assert(m_fp->m_func->name()->isame(s_hphpd_break.get()) ||
m_fp->m_func->name()->isame(s_fb_enable_code_coverage.get()));
// Free any values that may be on the eval stack
@@ -2839,7 +2839,7 @@ bool VMExecutionContext::evalUnit(Unit* unit, bool local,
ar->setThis(nullptr);
}
Func* func = unit->getMain(cls);
assert(!func->isBuiltin());
assert(!func->info());
assert(!func->isGenerator());
ar->m_func = func;
ar->initNumArgs(0);
@@ -5974,7 +5974,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFPassCW(PC& pc) {
TRACE(1, "FPassCW: function %s(%d) param %d is by reference, "
"raising a strict warning (attr:0x%x)\n",
func->name()->data(), func->numParams(), paramId,
func->isBuiltin() ? func->info()->attribute : 0);
func->info() ? func->info()->attribute : 0);
raise_strict_warning("Only variables should be passed by reference");
}
}
@@ -5985,7 +5985,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFPassCE(PC& pc) {
TRACE(1, "FPassCE: function %s(%d) param %d is by reference, "
"throwing a fatal error (attr:0x%x)\n",
func->name()->data(), func->numParams(), paramId,
func->isBuiltin() ? func->info()->attribute : 0);
func->info() ? func->info()->attribute : 0);
raise_error("Cannot pass parameter %d by reference", paramId+1);
}
}
@@ -6200,8 +6200,8 @@ static int makeNativeRefCall(const Func* f, Ret* ret,
inline void OPTBLD_INLINE VMExecutionContext::iopFCallBuiltin(PC& pc) {
NEXT();
DECODE_IA(numArgs);
DECODE_IA(numNonDefault);
DECODE_IVA(numArgs);
DECODE_IVA(numNonDefault);
DECODE(Id, id);
const NamedEntity* ne = m_fp->m_func->unit()->lookupNamedEntityId(id);
Func* func = Unit::lookupFunc(ne);
@@ -7591,12 +7591,14 @@ void VMExecutionContext::requestInit() {
if (UNLIKELY(RuntimeOption::EvalJitEnableRenameFunction)) {
SystemLib::s_unit->merge();
if (SystemLib::s_hhas_unit) SystemLib::s_hhas_unit->merge();
SystemLib::s_nativeFuncUnit->merge();
SystemLib::s_nativeClassUnit->merge();
} else {
// System units are always merge only, and
// everything is persistent.
assert(SystemLib::s_unit->isEmpty());
assert(!SystemLib::s_hhas_unit || SystemLib::s_hhas_unit->isEmpty());
assert(SystemLib::s_nativeFuncUnit->isEmpty());
assert(SystemLib::s_nativeClassUnit->isEmpty());
}
+2 -1
Ver Arquivo
@@ -1282,7 +1282,8 @@ void Class::setSpecial() {
// Use 86ctor(), since no program-supplied constructor exists
m_ctor = findSpecialMethod(this, sd86ctor);
assert(m_ctor && "class had no user-defined constructor or 86ctor");
assert(m_ctor->attrs() == (AttrPublic|AttrNoInjection|AttrPhpLeafFn));
assert((m_ctor->attrs() & ~AttrBuiltin) ==
(AttrPublic|AttrNoInjection|AttrPhpLeafFn));
}
void Class::applyTraitPrecRule(const PreClass::TraitPrecRule& rule) {
+12
Ver Arquivo
@@ -97,6 +97,15 @@ const Slot kInvalidSlot = Slot(-1);
* to other php functions. It may still call other user-level
* functions via re-entry (e.g. for destructors or autoload), and it
* may make calls to builtins using FCallBuiltin.
*
* AttrBuiltin is set on builtin functions - whether c++ or php
*
* AttrAllowOverride is set on builtin functions that can be replaced
* by user implementations
*
* AttrSkipFrame is set to indicate that the frame should be ignored
* when searching for the context (eg array_map evaluates its
* callback in the context of its caller).
*/
enum Attr {
AttrNone = 0, // class property method //
@@ -121,6 +130,9 @@ enum Attr {
AttrPersistent= (1 << 17), // X X //
AttrDeepInit = (1 << 18), // X //
AttrHot = (1 << 19), // X //
AttrBuiltin = (1 << 20), // X //
AttrAllowOverride = (1 << 21), // X //
AttrSkipFrame = (1 << 22), // X //
};
static inline Attr operator|(Attr a, Attr b) { return Attr((int)a | (int)b); }
+1 -1
Ver Arquivo
@@ -93,7 +93,7 @@ void EventHook::RunUserProfiler(const ActRec* ar, int mode) {
if (fault.m_faultType == Fault::UserException) {
frameinfo.set(s_exception, fault.m_userException);
}
} else if (!ar->m_func->isBuiltin() &&
} else if (!ar->m_func->info() &&
!ar->m_func->isGenerator()) {
// TODO (#1131400) This is wrong for builtins
frameinfo.set(s_return, tvAsCVarRef(g_vmContext->m_stack.topTV()));
+19 -29
Ver Arquivo
@@ -39,10 +39,6 @@ static const Trace::Module TRACEMOD = Trace::bcinterp;
const StringData* Func::s___call = StringData::GetStaticString("__call");
const StringData* Func::s___callStatic =
StringData::GetStaticString("__callStatic");
static const StringData* sd___overridable =
StringData::GetStaticString("__Overridable");
static const StringData* sd___PHPBuiltin =
StringData::GetStaticString("__PHPBuiltin");
//=============================================================================
// Func.
@@ -394,7 +390,7 @@ bool Func::isNameBindingImmutable(const Unit* fromUnit) const {
bool Func::byRef(int32_t arg) const {
// Super special case. A handful of builtins are varargs functions where the
// (not formally declared) varargs are pass-by-reference. psychedelic-kitten
if (arg >= m_numParams && isBuiltin() &&
if (arg >= m_numParams && info() &&
(info()->attribute & (ClassInfo::RefVariableArguments |
ClassInfo::MixedVariableArguments))) {
return true;
@@ -408,7 +404,7 @@ bool Func::byRef(int32_t arg) const {
bool Func::mustBeRef(int32_t arg) const {
// return true if the argument is required to be a reference
// (and thus should be an lvalue)
if (arg >= m_numParams && isBuiltin() &&
if (arg >= m_numParams && info() &&
((info()->attribute & (ClassInfo::RefVariableArguments |
ClassInfo::MixedVariableArguments)) ==
ClassInfo::RefVariableArguments)) {
@@ -514,11 +510,6 @@ void Func::prettyPrint(std::ostream& out) const {
}
}
bool Func::isPHPBuiltin() const {
return shared()->m_userAttributes.find(sd___PHPBuiltin) !=
shared()->m_userAttributes.end();
}
HphpArray* Func::getStaticLocals() const {
return g_vmContext->getFuncStaticCtx(this);
}
@@ -667,13 +658,6 @@ void Func::setCached() {
setCachedFunc(this, isDebuggerAttached());
}
bool Func::isAllowOverride() const {
return (shared()->m_info &&
(shared()->m_info->attribute & ClassInfo::AllowOverride)) ||
(shared()->m_userAttributes.find(sd___overridable) !=
shared()->m_userAttributes.end());
}
const Func* Func::getGeneratorBody(const StringData* name) const {
if (isNonClosureMethod()) {
return cls()->lookupMethod(name);
@@ -744,6 +728,10 @@ void FuncEmitter::init(int line1, int line2, Offset base, Attr attrs, bool top,
m_attrs = attrs;
m_top = top;
m_docComment = docComment;
if (!SystemLib::s_inited) {
m_attrs = m_attrs | AttrBuiltin;
if (!pce()) m_attrs = m_attrs | AttrSkipFrame;
}
}
void FuncEmitter::finish(Offset past, bool load) {
@@ -989,37 +977,39 @@ void FuncEmitter::setBuiltinFunc(const ClassInfo::MethodInfo* info,
m_docComment = StringData::GetStaticString(info->docComment);
m_line1 = 0;
m_line2 = 0;
m_attrs = AttrNone;
m_attrs = AttrBuiltin | AttrSkipFrame;
// TODO: Task #1137917: See if we can avoid marking most builtins with
// "MayUseVV" and still make things work
m_attrs = (Attr)(m_attrs | AttrMayUseVV);
m_attrs = m_attrs | AttrMayUseVV;
if (info->attribute & (ClassInfo::RefVariableArguments |
ClassInfo::MixedVariableArguments)) {
m_attrs = Attr(m_attrs | AttrVariadicByRef);
m_attrs = m_attrs | AttrVariadicByRef;
}
if (info->attribute & ClassInfo::IsReference) {
m_attrs = (Attr)(m_attrs | AttrReference);
m_attrs = m_attrs | AttrReference;
}
if (info->attribute & ClassInfo::NoInjection) {
m_attrs = (Attr)(m_attrs | AttrNoInjection);
m_attrs = m_attrs | AttrNoInjection;
}
if (pce()) {
if (info->attribute & ClassInfo::IsStatic) {
m_attrs = (Attr)(m_attrs | AttrStatic);
m_attrs = m_attrs | AttrStatic;
}
if (info->attribute & ClassInfo::IsFinal) {
m_attrs = (Attr)(m_attrs | AttrFinal);
m_attrs = m_attrs | AttrFinal;
}
if (info->attribute & ClassInfo::IsAbstract) {
m_attrs = (Attr)(m_attrs | AttrAbstract);
m_attrs = m_attrs | AttrAbstract;
}
if (info->attribute & ClassInfo::IsPrivate) {
m_attrs = (Attr)(m_attrs | AttrPrivate);
m_attrs = m_attrs | AttrPrivate;
} else if (info->attribute & ClassInfo::IsProtected) {
m_attrs = (Attr)(m_attrs | AttrProtected);
m_attrs = m_attrs | AttrProtected;
} else {
m_attrs = (Attr)(m_attrs | AttrPublic);
m_attrs = m_attrs | AttrPublic;
}
} else if (info->attribute & ClassInfo::AllowOverride) {
m_attrs = m_attrs | AttrAllowOverride;
}
m_returnType = info->returnType;
+4 -3
Ver Arquivo
@@ -204,8 +204,8 @@ struct Func {
void prettyPrint(std::ostream& out) const;
bool isPseudoMain() const { return m_name->empty(); }
bool isBuiltin() const { return (bool)info(); }
bool isPHPBuiltin() const;
bool isBuiltin() const { return m_attrs & AttrBuiltin; }
bool skipFrame() const { return m_attrs & AttrSkipFrame; }
bool isMethod() const {
return !isPseudoMain() && (bool)cls();
}
@@ -352,7 +352,8 @@ struct Func {
bool hasStaticLocals() const { return !shared()->m_staticVars.empty(); }
int numStaticLocals() const { return shared()->m_staticVars.size(); }
const ClassInfo::MethodInfo* info() const { return shared()->m_info; }
bool isAllowOverride() const;
bool isAllowOverride() const { return m_attrs & AttrAllowOverride; }
const BuiltinFunction& nativeFuncPtr() const {
return shared()->m_nativeFuncPtr;
}
+1 -1
Ver Arquivo
@@ -516,7 +516,7 @@ enum SetOpOp {
O(FPassM, TWO(IVA,MA), LMANY(), ONE(FV), FF) \
O(FCall, ONE(IVA), FMANY, ONE(RV), CF_FF) \
O(FCallArray, NA, ONE(FV), ONE(RV), CF_FF) \
O(FCallBuiltin, THREE(IA,IA,SA), FMANY, ONE(RV), CF) \
O(FCallBuiltin, THREE(IVA,IVA,SA),FMANY, ONE(RV), CF) \
O(CufSafeArray, NA, THREE(RV,CV,CV), ONE(CV), NF) \
O(CufSafeReturn, NA, THREE(RV,CV,CV), ONE(RV), NF) \
O(IterInit, THREE(IA,BA,HA), ONE(CV), NOV, CF) \
+2 -2
Ver Arquivo
@@ -328,7 +328,7 @@ Unit* build_native_class_unit(const HhbcExtClassInfo* builtinClasses,
return g_hphp_build_native_class_unit(builtinClasses, numBuiltinClasses);
}
Unit* compile_string(const char* s, size_t sz) {
Unit* compile_string(const char* s, size_t sz, const char* fname) {
MD5 md5;
int out_len;
md5 = MD5(string_md5(s, sz, false, out_len));
@@ -337,7 +337,7 @@ Unit* compile_string(const char* s, size_t sz) {
if (u != nullptr) {
return u;
}
return g_hphp_compiler_parse(s, sz, md5, nullptr);
return g_hphp_compiler_parse(s, sz, md5, fname);
}
// Returned array has refcount zero! Caller must refcount.
+1 -1
Ver Arquivo
@@ -174,7 +174,7 @@ frame_free_args(TypedValue* args, int count) {
Unit*
compile_file(const char* s, size_t sz, const MD5& md5, const char* fname);
Unit* compile_string(const char* s, size_t sz);
Unit* compile_string(const char* s, size_t sz, const char* fname = nullptr);
Unit* build_native_func_unit(const HhbcExtFuncInfo* builtinFuncs,
ssize_t numBuiltinFuncs);
Unit* build_native_class_unit(const HhbcExtClassInfo* builtinClasses,
+18 -8
Ver Arquivo
@@ -129,26 +129,35 @@ struct EagerVMRegAnchor {
}
};
static inline ActRec* regAnchorFP() {
inline ActRec* regAnchorFP() {
// In builtins, m_fp points to the caller's frame if called
// through FCallBuiltin, else it points to the builtin's frame,
// in which case, getPrevVMState() gets the caller's frame.
// In addition, we need to skip over php-defined builtin functions
// in order to find the true context.
VMExecutionContext* context = g_vmContext;
ActRec* cur = context->getFP();
if (!cur) return nullptr;
if (cur->skipFrame()) {
ActRec* prev = context->getPrevVMState(cur);
if (prev == cur) return nullptr;
return prev;
} else {
return cur;
while (cur && cur->skipFrame()) {
cur = context->getPrevVMState(cur);
}
return cur;
}
inline ActRec* regAnchorFPForArgs() {
// Like regAnchorFP, but only account for FCallBuiltin
VMExecutionContext* context = g_vmContext;
ActRec* cur = context->getFP();
if (cur && cur->m_func->info()) {
cur = context->getPrevVMState(cur);
}
return cur;
}
struct EagerCallerFrame : public EagerVMRegAnchor {
ActRec* operator()() {
return regAnchorFP();
}
ActRec* actRecForArgs() { return regAnchorFPForArgs(); }
};
@@ -158,6 +167,7 @@ struct CallerFrame : public VMRegAnchor {
ActRec* operator()() {
return regAnchorFP();
}
ActRec* actRecForArgs() { return regAnchorFPForArgs(); }
};
#define SYNC_VM_REGS_SCOPED() \
+11 -9
Ver Arquivo
@@ -2284,7 +2284,7 @@ TranslatorX64::emitPrologue(Func* func, int nPassed) {
Fixup fixup(funcBody.m_offset - func->base(), frameCells);
// Emit warnings for any missing arguments
if (!func->isBuiltin()) {
if (!func->info()) {
for (int i = nPassed; i < numParams; ++i) {
if (paramInfo[i].funcletOff() == InvalidAbsoluteOffset) {
emitImmReg(a, (intptr_t)func->name()->data(), argNumToRegName[0]);
@@ -2315,7 +2315,7 @@ TranslatorX64::emitPrologue(Func* func, int nPassed) {
static bool
isNativeImplCall(const Func* funcd, int numArgs) {
return funcd && funcd->isBuiltin() && numArgs == funcd->numParams();
return funcd && funcd->info() && numArgs == funcd->numParams();
}
int32_t // returns the amount by which rVmSp should be adjusted
@@ -6626,17 +6626,19 @@ bool TranslatorX64::eagerRecord(const Func* func) {
"func_num_args",
"array_filter",
"array_map",
"WaitHandle::join",
};
assert(func->info());
const StringData* name = func->isMethod() ?
func->fullName() : func->info()->name.get();
for (int i = 0; i < sizeof(list)/sizeof(list[0]); i++) {
if (!strcmp(func->name()->data(), list[i])) {
if (!strcmp(name->data(), list[i])) {
return true;
}
}
if (func->cls() && !strcmp(func->cls()->name()->data(), "WaitHandle")
&& !strcmp(func->name()->data(), "join")) {
return true;
}
return false;
}
@@ -6680,7 +6682,7 @@ int32_t TranslatorX64::emitNativeImpl(const Func* func,
* contains only a single opcode (NativeImpl), and there are no
* non-argument locals.
*/
assert(func->numIterators() == 0 && func->isBuiltin());
assert(func->numIterators() == 0 && func->info());
assert(func->numLocals() == func->numParams());
assert(*func->getEntry() == OpNativeImpl);
assert(instrLen(func->getEntry()) == func->past() - func->base());
@@ -9681,7 +9683,7 @@ void TranslatorX64::analyzeFCallBuiltin(Tracelet& t,
void TranslatorX64::translateFCallBuiltin(const Tracelet& t,
const NormalizedInstruction& ni) {
int numArgs = ni.imm[0].u_IVA;
int numNonDefault = ni.imm[1].u_IA;
int numNonDefault = ni.imm[1].u_IVA;
Id funcId = ni.imm[2].u_SA;
const NamedEntity* ne = curUnit()->lookupNamedEntityId(funcId);
const Func* func = Unit::lookupFunc(ne);
+2 -1
Ver Arquivo
@@ -3125,7 +3125,7 @@ static bool shouldAnalyzeCallee(const NormalizedInstruction* fcall) {
FTRACE(1, "analyzeCallee: target func not known\n");
return false;
}
if (target->isBuiltin()) {
if (target->info()) {
FTRACE(1, "analyzeCallee: target func is a builtin\n");
return false;
}
@@ -3946,6 +3946,7 @@ ActRecState::getCurrentState() {
const Func* lookupImmutableMethod(const Class* cls, const StringData* name,
bool& magicCall, bool staticLookup) {
if (!cls || RuntimeOption::EvalJitEnableRenameFunction) return nullptr;
if (cls->attrs() & AttrInterface) return nullptr;
bool privateOnly = false;
if (!RuntimeOption::RepoAuthoritative ||
!(cls->preClass()->attrs() & AttrUnique)) {
+19 -1
Ver Arquivo
@@ -181,7 +181,7 @@ Array Unit::getUserFunctions() {
for (NamedEntityMap::const_iterator it = s_namedDataMap->begin();
it != s_namedDataMap->end(); ++it) {
Func* func_ = it->second.getCachedFunc();
if (!func_ || func_->isBuiltin() || func_->isPHPBuiltin() ||
if (!func_ || func_->isBuiltin() ||
isdigit(func_->name()->data()[0])) {
continue;
}
@@ -2236,6 +2236,23 @@ UnitEmitter::~UnitEmitter() {
}
}
void UnitEmitter::addTrivialPseudoMain() {
initMain(0, 0);
FuncEmitter* mfe = getMain();
emitOp(OpInt);
emitInt64(1);
emitOp(OpRetC);
mfe->setMaxStackCells(1);
mfe->finish(bcPos(), false);
recordFunction(mfe);
TypedValue mainReturn;
mainReturn.m_data.num = 1;
mainReturn.m_type = KindOfInt64;
setMainReturn(&mainReturn);
setMergeOnly(true);
}
void UnitEmitter::setBc(const uchar* bc, size_t bclen) {
m_bc = (uchar*)malloc(bclen);
m_bcmax = bclen;
@@ -2278,6 +2295,7 @@ Id UnitEmitter::addPreConst(const StringData* name, const TypedValue& value) {
return id;
}
Id UnitEmitter::mergeLitstr(const StringData* litstr) {
LitstrMap::const_iterator it = m_litstr2id.find(litstr);
if (it == m_litstr2id.end()) {
+1
Ver Arquivo
@@ -717,6 +717,7 @@ class UnitEmitter {
explicit UnitEmitter(const MD5& md5);
~UnitEmitter();
void addTrivialPseudoMain();
int repoId() const { return m_repoId; }
void setRepoId(int repoId) { m_repoId = repoId; }
int64_t sn() const { return m_sn; }
+22 -10
Ver Arquivo
@@ -10,12 +10,15 @@ $outputPath = $argv[1];
function processPhpFile($phpfile, $systemlib_php) {
$firstchar = true;
$contents = file_get_contents($phpfile);
$i = 0;
$k = strpos($contents, "\n") + 1;
$header = trim(substr($contents, 0, $k));
if ($header !== "<?php") {
echo "ERROR: Unexpected header in file $phpfile\n";
throw new Exception("Unexpected header in file $phpfile");
if (preg_match('/\.hhas$/', $phpfile)) {
$k = 0;
} else {
$k = strpos($contents, "\n") + 1;
$header = trim(substr($contents, 0, $k));
if ($header !== "<?php") {
echo "ERROR: Unexpected header in file $phpfile\n";
throw new Exception("Unexpected header in file $phpfile");
}
}
fwrite($systemlib_php, substr($contents, $k));
}
@@ -24,7 +27,7 @@ function populatePhpFiles($input_files) {
$php_files = array();
foreach ($input_files as $file) {
$key = strtolower(basename($file));
if (!preg_match('/\.php$/', $file)) {
if (!preg_match('/\.(php|hhas)$/', $file)) {
$errMsg = "ERROR: Encountered non-php file ($file)";
echo $errMsg . "\n";
throw new Exception($errMsg);
@@ -66,11 +69,20 @@ function genSystemlib($input_files) {
unset($phpfiles[$initialFile]);
}
}
foreach ($phpfiles as $phpfile) {
processPhpFile($phpfile, $systemlib_php);
foreach ($phpfiles as $key => $phpfile) {
if (preg_match('/\.php$/', $phpfile)) {
processPhpFile($phpfile, $systemlib_php);
unset($phpfiles[$key]);
}
}
fwrite($systemlib_php, "\n");
if (count($phpfiles)) {
fwrite($systemlib_php, "\n\n<?hhas\n\n");
foreach ($phpfiles as $key => $phpfile) {
processPhpFile($phpfile, $systemlib_php);
unset($phpfiles[$key]);
}
}
fclose($systemlib_php);
$systemlib_php = null;
chmod($systemlib_php_tempnam, 0644);
+1
Ver Arquivo
@@ -34,6 +34,7 @@ namespace HPHP {
bool SystemLib::s_inited = false;
HPHP::Unit* SystemLib::s_unit = nullptr;
HPHP::Unit* SystemLib::s_hhas_unit = nullptr;
HPHP::Unit* SystemLib::s_nativeFuncUnit = nullptr;
HPHP::Unit* SystemLib::s_nativeClassUnit = nullptr;
HPHP::Func* SystemLib::s_nullFunc = nullptr;
+1
Ver Arquivo
@@ -73,6 +73,7 @@ class SystemLib {
public:
static bool s_inited;
static HPHP::Unit* s_unit;
static HPHP::Unit* s_hhas_unit;
static HPHP::Unit* s_nativeFuncUnit;
static HPHP::Unit* s_nativeClassUnit;
+1 -5
Ver Arquivo
@@ -198,11 +198,7 @@ array(3) {
[0]=>
array(4) {
[0]=>
array(6) {
["file"]=>
string(%d) "%S"
["line"]=>
int(42)
array(4) {
["function"]=>
string(9) "initTrace"
["class"]=>