diff --git a/hphp/compiler/analysis/emitter.cpp b/hphp/compiler/analysis/emitter.cpp index 752c04c78..46788889c 100644 --- a/hphp/compiler/analysis/emitter.cpp +++ b/hphp/compiler/analysis/emitter.cpp @@ -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& 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; diff --git a/hphp/compiler/analysis/function_scope.cpp b/hphp/compiler/analysis/function_scope.cpp index 661bba8a3..b66602ba0 100644 --- a/hphp/compiler/analysis/function_scope.cpp +++ b/hphp/compiler/analysis/function_scope.cpp @@ -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(); } } diff --git a/hphp/compiler/compiler.cpp b/hphp/compiler/compiler.cpp index d65abe385..bda7a904c 100644 --- a/hphp/compiler/compiler.cpp +++ b/hphp/compiler/compiler.cpp @@ -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) { diff --git a/hphp/hhvm/process_init.cpp b/hphp/hhvm/process_init.cpp index 0bb349c64..ac0cc9421 100644 --- a/hphp/hhvm/process_init.cpp +++ b/hphp/hhvm/process_init.cpp @@ -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; } /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/base/program_functions.cpp b/hphp/runtime/base/program_functions.cpp index 0caae61d2..4619cbc59 100644 --- a/hphp/runtime/base/program_functions.cpp +++ b/hphp/runtime/base/program_functions.cpp @@ -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(ifs), - std::istreambuf_iterator()); + return systemlib_split(std::string( + std::istreambuf_iterator(ifs), + std::istreambuf_iterator()), hhas); } } @@ -1172,8 +1182,7 @@ string get_systemlib() { ifs.seekg(desc.m_start, std::ios::beg); std::unique_ptr 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); } /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/base/program_functions.h b/hphp/runtime/base/program_functions.h index 9ca2a94ec..e80d90cf8 100644 --- a/hphp/runtime/base/program_functions.h +++ b/hphp/runtime/base/program_functions.h @@ -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; diff --git a/hphp/runtime/ext/ext_function.cpp b/hphp/runtime/ext/ext_function.cpp index b3ac018a0..2bf90c52d 100644 --- a/hphp/runtime/ext/ext_function.cpp +++ b/hphp/runtime/ext/ext_function.cpp @@ -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; } diff --git a/hphp/runtime/ext/ext_reflection.cpp b/hphp/runtime/ext/ext_reflection.cpp index 62ba100ac..67afab5e8 100644 --- a/hphp/runtime/ext/ext_reflection.cpp +++ b/hphp/runtime/ext/ext_reflection.cpp @@ -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); } } diff --git a/hphp/runtime/vm/as.cpp b/hphp/runtime/vm/as.cpp index 89feda7a1..a2c751de0 100644 --- a/hphp/runtime/vm/as.cpp +++ b/hphp/runtime/vm/as.cpp @@ -75,6 +75,8 @@ * @author Jorden DeLong */ +#include "hphp/runtime/vm/as.h" + #include #include #include @@ -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(as); \ - as.ue->emitIVA(immIVA); \ +#define IMM_IVA do { \ + int imm = read_opcode_arg(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(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 ue(new UnitEmitter(md5)); +UnitEmitter* assemble_string(const char*code, int codeLen, + const char* filename, const MD5& md5) { + std::unique_ptr 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(); } ////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/vm/as.h b/hphp/runtime/vm/as.h index e10d538ee..590f22d5b 100644 --- a/hphp/runtime/vm/as.h +++ b/hphp/runtime/vm/as.h @@ -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&); ////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index ed7a47462..18d4baf62 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -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()); } diff --git a/hphp/runtime/vm/class.cpp b/hphp/runtime/vm/class.cpp index eb2a01a8b..db5634c0e 100644 --- a/hphp/runtime/vm/class.cpp +++ b/hphp/runtime/vm/class.cpp @@ -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) { diff --git a/hphp/runtime/vm/core_types.h b/hphp/runtime/vm/core_types.h index 5cc79e0ce..033435868 100644 --- a/hphp/runtime/vm/core_types.h +++ b/hphp/runtime/vm/core_types.h @@ -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); } diff --git a/hphp/runtime/vm/event_hook.cpp b/hphp/runtime/vm/event_hook.cpp index 2dfbfcc09..65976a680 100644 --- a/hphp/runtime/vm/event_hook.cpp +++ b/hphp/runtime/vm/event_hook.cpp @@ -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())); diff --git a/hphp/runtime/vm/func.cpp b/hphp/runtime/vm/func.cpp index 0e92b9dac..c8dced764 100644 --- a/hphp/runtime/vm/func.cpp +++ b/hphp/runtime/vm/func.cpp @@ -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; diff --git a/hphp/runtime/vm/func.h b/hphp/runtime/vm/func.h index c4e453b3d..c343a43dc 100644 --- a/hphp/runtime/vm/func.h +++ b/hphp/runtime/vm/func.h @@ -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; } diff --git a/hphp/runtime/vm/hhbc.h b/hphp/runtime/vm/hhbc.h index 7483a0267..9cf2b1c5d 100644 --- a/hphp/runtime/vm/hhbc.h +++ b/hphp/runtime/vm/hhbc.h @@ -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) \ diff --git a/hphp/runtime/vm/runtime.cpp b/hphp/runtime/vm/runtime.cpp index 760711df3..3891fa53b 100644 --- a/hphp/runtime/vm/runtime.cpp +++ b/hphp/runtime/vm/runtime.cpp @@ -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. diff --git a/hphp/runtime/vm/runtime.h b/hphp/runtime/vm/runtime.h index 5e567fd7f..bdd749d8b 100644 --- a/hphp/runtime/vm/runtime.h +++ b/hphp/runtime/vm/runtime.h @@ -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, diff --git a/hphp/runtime/vm/translator/translator-inline.h b/hphp/runtime/vm/translator/translator-inline.h index 2745d9866..f025145f0 100644 --- a/hphp/runtime/vm/translator/translator-inline.h +++ b/hphp/runtime/vm/translator/translator-inline.h @@ -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() \ diff --git a/hphp/runtime/vm/translator/translator-x64.cpp b/hphp/runtime/vm/translator/translator-x64.cpp index cc2eb4064..79e69534c 100644 --- a/hphp/runtime/vm/translator/translator-x64.cpp +++ b/hphp/runtime/vm/translator/translator-x64.cpp @@ -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); diff --git a/hphp/runtime/vm/translator/translator.cpp b/hphp/runtime/vm/translator/translator.cpp index 07bd1c812..3ece7afbd 100644 --- a/hphp/runtime/vm/translator/translator.cpp +++ b/hphp/runtime/vm/translator/translator.cpp @@ -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)) { diff --git a/hphp/runtime/vm/unit.cpp b/hphp/runtime/vm/unit.cpp index f3cc77785..db672c9ad 100644 --- a/hphp/runtime/vm/unit.cpp +++ b/hphp/runtime/vm/unit.cpp @@ -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()) { diff --git a/hphp/runtime/vm/unit.h b/hphp/runtime/vm/unit.h index 6e15293f4..d53b8e0bf 100644 --- a/hphp/runtime/vm/unit.h +++ b/hphp/runtime/vm/unit.h @@ -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; } diff --git a/hphp/system/lib/gen_systemlib.php b/hphp/system/lib/gen_systemlib.php index fb5f2195c..5779b9f9d 100644 --- a/hphp/system/lib/gen_systemlib.php +++ b/hphp/system/lib/gen_systemlib.php @@ -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 !== " $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 $phpfile) { + processPhpFile($phpfile, $systemlib_php); + unset($phpfiles[$key]); + } + } fclose($systemlib_php); $systemlib_php = null; chmod($systemlib_php_tempnam, 0644); diff --git a/hphp/system/lib/systemlib.cpp b/hphp/system/lib/systemlib.cpp index 5e9ab13b3..56980bafa 100644 --- a/hphp/system/lib/systemlib.cpp +++ b/hphp/system/lib/systemlib.cpp @@ -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; diff --git a/hphp/system/lib/systemlib.h b/hphp/system/lib/systemlib.h index 925736348..4eb48f618 100644 --- a/hphp/system/lib/systemlib.h +++ b/hphp/system/lib/systemlib.h @@ -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; diff --git a/hphp/test/quick/Setprofile.php.expectf b/hphp/test/quick/Setprofile.php.expectf index c914b27f7..ca54ce84c 100644 --- a/hphp/test/quick/Setprofile.php.expectf +++ b/hphp/test/quick/Setprofile.php.expectf @@ -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"]=>