49 Commits

Autor SHA1 Mensagem Data
Sara Golemon a3803ffa8a HPHP-2.0.2 2013-04-23 10:29:42 -07:00
Jordan DeLong a85369c7ce Fix some mismatched include guards 2013-04-22 23:34:10 -07:00
Edwin Smith 1cea03f3f7 hphp/src is now hphp/hphp
That's it
2013-04-22 23:32:31 -07:00
Mark Williams 52db7956bb Fix reflection for default values that were optimized
If a default value is optimized from a class constant,
to the value of the class constant, then reflection needs to
be able to get the text of the original class constant.
Fortunately hphp tags that onto the replacement expression for
just this reason. Lets use it in the emitter.
2013-04-22 23:31:09 -07:00
Mark Williams f6f1655ca5 Fix a crash if an exception is thrown in a constructor's surprise check
The unwinder assumed that if the actrec's constructor flag
was set, then there must be a $this. But the $this is cleared during
the return sequence.
2013-04-22 23:30:52 -07:00
Mark Williams 3fa077dd6f Free the VarEnv the correct way
detach, rather than destroy.
2013-04-22 23:30:12 -07:00
mwilliams 4caa51283a Fix order of evaluation for unused binary operators
eg

  (new X) == (new X);

Was converted to something like:

  (new X);
  (new X);

Which calls the destructor for the first X before constructing the second.

I tried a fix where we used an ExpressionList with ListKindLeft, which
would preserve both expressions until after both objects are created;
but that ends up calling the second object's destructor first. Thats
much better; its not clear to me that there's any guarantee about which
object's destructor is called first when they both go out of scope at
the same point; but currently hhvm and zend seem to aggree, so Im
going with a solution that preserves the left-to-right order.
2013-04-22 23:29:21 -07:00
aravind 4f70af91f7 Fix bug in shuffleArgs
This diff fixes a bug in shuffleArgs when there
are three register arguments in a cycle, and one
of them needs a zero extend.

Assume the shuffle that needs to be performed is
rdi -> rsi, rsi -> rdx, rdx -> rdi. doRegMoves()
determines the sequence of moves to be:

  xchg rdx, rsi
  xchg rsi, rdi

Assume also that the second dest reg (rsi) needs a zero extend.
The current implementatin will spit out the code
sequence:

  xchg   %rdx, %rsi
  movzbl %sil, %esi
  xchg   %rsi, %rdi
  movzbl %sil, $esi

Basically, the problem is that if a move sequence uses
two xchg's for a cycle of three registers, we should not perform
the zero extending (and address-lea) till both the exchanges are done.
2013-04-22 23:29:10 -07:00
mwilliams 8a0dd4fae4 Don't fold invalid string operations at compile time
It turns out the String::rvalAt and String::set don't raise
warnings on invalid offsets, so check explicitly before using them.
2013-04-22 23:10:28 -07:00
mwilliams afd5f72964 Fix crash in linting
Redeclared functions in non-whole-program analyze mode caused
an assertion failure.

Test Plan:
  % cat bug.php
  <?php
  if (isset($g)) {
    function f($x, $y) {}
  } else {
    function f($x) {}
  }
  f($x);
  % hphp -vWholeProgram=false -t analyze bug.php
2013-04-22 23:10:28 -07:00
mikemag ec6617f6bd Fix bug in heredoc processing
My recent change to now/heredoc processing resulted in variables in heredocs mistakenly being tacked onto the end of the previous line in the output. This tends not to be a problem when emitting things that get parsed by something else, like emitting PHP. But it's noticable when emitting raw text.
2013-04-22 23:10:28 -07:00
jdelong 964de35b10 Fix an exception safety issue for undefined properties in vectortranslator
I think the only case where we currently don't spillstack as
part of a vector translation is CGetM of a defined property.  However,
this can still raise exceptions in the unlikely case of an unset
property.  For now, just spill before any vector translation---a
follow up diff relaxes this to not do it in cases where we know the
property can't be undefined.  Presumably later we'll push the
spillstacks into the unlikely paths for these translations (or put
them in unwind handlers)---currently we can't handle control flow
where the join point has a different stack.
2013-04-22 23:10:27 -07:00
mwilliams 819058d081 Fix UnsetM with global base
If the global was not defined, it would try to return
a pointer into the MInstrCtx, which was not passed in.

We dont actually need it though, because in that case we can
just return a pointer to init_null_variant.
2013-04-22 23:10:27 -07:00
ptarjan e6542d070a stop segfaulting in propery_exists 2013-04-22 22:25:54 -07:00
mwilliams 83bd8ff445 Fix cgJcc for bool comparisons with constants
The bool value is in the bottom byte of the register,
with the rest of the register undefined, but we were doing
a 32 bit compare.
2013-04-22 21:55:22 -07:00
jdelong cf10678119 SpillStack of an ActRec is a use of the old fp
A bug like this was bound to happen.  Thanks to whoever added
the assert for saving me from real debugging (@smith?).
2013-04-22 18:19:58 -07:00
mwilliams 5226f80d05 Fix FPushClsMethodD in hhir
Non-persistent classes can't be burnt in (or at least,
you have to check the target cache before using it).
2013-04-22 18:19:45 -07:00
jdelong a2ffdae2cf Fix an assertion in traceRet, relating to generator frames
This was firing in my sandbox.  I didn't find anything that
looked like it changed it recently, so I'm not sure why it wasn't
firing earlier.
2013-04-22 18:18:59 -07:00
mikemag 785cf27e00 Fix heredoc/nowdoc bugs with large docs, docs crossing buffer boundaries
Fixed a few issues in the lexer for heredocs and nowdocs. The source file is read in 64k chunks, and any time a doc was split across a buffer boundary the lexer would fail to consume the doc properly. Modified the lexer to refill the file buffer when it is used, or when more data is needed in a variety of cases. Also fixed a number of other corner cases where we'd fail to recognize the doc end label or other special characters. The old code was also a bit over- and under-flowy.
2013-04-22 18:17:18 -07:00
bsimmers b32b2ec879 Fix baseValChanged check for empty base promotion in VectorEffects
I thought I'd have to teach memelim about the PtrToBoxed*
srcs for vector instructions but it turns out the refactoring I did in
the UnsetElem diff was enough. It already clears out the local map for
instructions that may modify refs.
2013-04-22 17:28:03 -07:00
mwilliams 1f638e631c Fix a crash in exif processing
Given a zero length string, the pointer was left unset, but
later code checked if the value of the pointer was non-null,
and ignored the length.
2013-04-22 17:27:47 -07:00
mwilliams 1ab14f87af Fix closure dispatch when there's more than one Func*
We need to make sure that we execute the code
corresponding to the actual Func*. This re-uses the
prolog array to hold the entry points for the cloned
Func*.
2013-04-22 17:27:32 -07:00
mwilliams fefd56e9d7 Fix refcounting issues with string SetM
The code was assuming that the result of the assignment
would be the value assigned, but its actually a new string
containing the first character of the value assigned. The result
was that the SetM had already decRef'd the rhs, and then the
jitted code decRef'd it again. Since that last decRef was a decRefNZ,
we would often get away with simply leaking both the original rhs and
the value of the SetM. But the new testcase crashes in a DEBUG build
without the fix.
2013-04-22 17:27:16 -07:00
mwilliams 17049b3a42 Fix assert in CodeGenerator::emitTypeCheck
The vector translator produced a type that was
boxed-array-or-string, which we couldnt guard on.

Since applying a SetM to a string will almost always produce
a string, change it to report string instead (which will still
be guarded on).
2013-04-22 17:25:04 -07:00
Sara Golemon be4c7698eb Switch to reentrant safe calls in posix
posix_getpwuid()
posix_getpwnam()
posix_getgrgid()
posix_getgrnam()
posix_ttyname()

were using non-threadsafe posix calls.
Most using AttachLiteral for shared space as well.
2013-03-28 12:47:16 -07:00
Sara Golemon 0dcbc83f74 Cast result of unpack to uint64_t when requesting unsigned type 'I'
ZendPack::unpack() always returns a signed int32_t,
regardless of actual storage type being unpacked.  For 32bit
unsigned types ('L', 'N', 'V', and 'I'(on I32 systems)) this
means overflowing in the helper and we have to explicitly
recast it to a uint64_t to get the data back out.

For LNV, this was already handled, it was missed for I.
2013-03-27 18:04:52 -07:00
Sara Golemon d660972153 HPHP 2.0.1 2013-03-22 19:14:35 -07:00
ptarjan ca437add64 fix this callsite incase someone ever does the TODO for setting valueClass() 2013-03-25 13:59:21 -07:00
mwilliams 3611bf3d58 Fix getContextClassName and getParentContextClassName
They both returned the late static bound class, not the context
class. This meant that eg "constant('self::FOO')" was actually
returning what "constant('static::FOO')" should have done.

In addition, we often want the Class*, not its name, so
change them to return Class*. The remaining places that then
read the name from the Class* should be fixed to use the Class*
directly (in a later diff).

Finally, noticed that while "defined()" was recently fixed to
support "static::", "constant()" was not. Pulled out a common
function to find the correct Class*.
2013-03-25 13:59:02 -07:00
jan 565c8477f8 Fix ref generator parameters
Alias manager does not know whether generator parameters are passed by
reference. This didn't matter, because every generator had at least one
function call (hphp_continuation_done()) that pretty much disabled unused
variable elimination.

This diff fixes that, lets us get rid of artificial function calls in
generators and will allow later improvements in alias manager.
2013-03-22 13:57:25 -07:00
mwilliams 8f3512f6d3 Filter strict_warnings like notices
There is a runtime option to filter out notices and warnings,
but strict_warnings were left out. Bundle them with notices.

We raise a lot of strict_warnings; and when we fix hphpiCompat
(to match zend better) we will raise a lot more, so this could
matter.
2013-03-22 13:57:18 -07:00
ottoni 7241c247dd Disable spilling into MMX registers 2013-03-22 13:57:06 -07:00
jan f06575d4e2 Fix local propagation of generator parameters
Alias manager does not know that generator parameters are populated and
assumes they are uninit. The current code works because control flow
algorithm gives up while trying to deal with the continuation switch
statement full of gotos.

This diff fixes it by setting isGeneratorParameter flag in symbols
representing parameters of enclosing generator wrapper and use variables
of enclosing closure.
2013-03-21 20:41:02 -07:00
Owen Yamauchi cc897749f8 Fix the RIP_REGISTER macro
Pretty simple. This makes me slightly nervous because I've only
confirmed it works in my stupid OpenEmbedded ARM SDK; a different Linux
might call this something else. But we'll cross that bridge when we get
to it, and this works for now.

I'm also sneaking in a change to remove x29 from the list of
callee-saved regs; I put it in there by accident last time.
2013-03-21 20:41:02 -07:00
ptarjan b597e3a753 fix static closures
I added the check for this in the interpreter but ##f_array_map## re-enteres the VM via a different path than FCall. Here is the equivilent check for the VM.
2013-03-21 20:41:02 -07:00
mwilliams 8ec621676d Fix args for embedded repo
When we generate a binary with an embedded repo,
we add various args (-vRepo.Authoritative etc) to the end
of the command line. But the argument "--" is taken to mean
"pass the rest of the args to the script". So if you use
"--" on such a binary, it gets rather badly broken.

Reorder the args so that the inserted ones come first.
2013-03-21 20:41:02 -07:00
mwilliams a134b25671 Fix nemo warnings about undefined $this
The fix for the crash caused us to take a different path
when checking locals.
2013-03-21 20:41:01 -07:00
mwilliams 15fa1ebfde Fix StringBuffer::resize()
capacity doesnt include the terminating null, so len is
allowed to grow to capacity (not capacity - 1).
2013-03-19 14:47:25 -07:00
mwilliams ce75713972 Fix assertion when target==analyze
Option::OutputHHBC should always be true.
2013-03-19 14:47:15 -07:00
Owen Yamauchi adc56c6d8c Fix includes in curl_tls_workarounds.cpp
raise_notice() wasn't declared in this file, so include runtime_error.h.
This caused further problems with ATTRIBUTE_PRINTF not being defined
yet, so include some more stuff.
2013-03-19 14:47:06 -07:00
Sara Golemon 3fb0fe8a6a SSL_OP_NO_TLSv1_2 is not supported by all openssl versions 2013-03-18 18:38:47 -07:00
ottoni 38306b527f SIMPLIFY_COMMUTATIVE only handles Type::Int
So check that the inputs are really Ints.
2013-03-18 18:38:37 -07:00
aravind e4e3bee5d3 Don't simplify Same to Eq to arrays 2013-03-18 18:38:29 -07:00
michalburger1 d25adc550b Fix bzdecompress
Bad memory allocation, the buffer needs to be large enough to fit all
the data we've decompressed so far plus the extra storage we're
incrementally allocationg, not just the incremental part.
2013-03-18 16:20:44 -07:00
Sara Golemon 8b23419a37 HPHP version 2.0.0 2013-03-14 16:00:42 -07:00
ottoni af5623b4fc Properly patch exit traces ending with JmpZero and JmpNZero
The hoistConditionalJumps pass was not handling traces ending with
JmpZero and JmpNZero.  This was resulting in spurious jumps to
'astubs' instead of patching the jcc+jump pair in 'a'.
2013-03-14 15:45:51 -07:00
ptarjan b51003b5aa tell closures about scope clones
In HHBC mode, traits are flattened into their classes.
When that happens, closures need to know about all the
classes that contain them so that when we find a ##$this##
inside the closure, it can tell EVERY containing scope to
please propogate ##$this## down to me.
2013-03-14 15:45:40 -07:00
mwilliams 5f019f5b43 Fix CodeGenerator::cgNInstanceOf
It assumed the result would be in rax, but it isnt always.
Use the correct register.
2013-03-13 10:16:23 -07:00
bertrand 35e347efa5 Fixed ContNext by writing InitNull rather than Uninit.
Apparently, m_received needs to be InitNull, rather than
Uninit.
2013-03-13 10:16:14 -07:00
115 arquivos alterados com 66143 adições e 64895 exclusões
+2 -1
Ver Arquivo
@@ -1503,7 +1503,8 @@ ExpressionPtr AliasManager::canonicalizeNode(
e->is(Expression::KindOfSimpleVariable) &&
!e->isThis()) {
Symbol *s = spc(SimpleVariable, e)->getSymbol();
if (s && !s->isParameter() && !s->isClosureVar()) {
if (s && !s->isParameter() && !s->isGeneratorParameter() &&
!s->isClosureVar()) {
rep = e->makeConstant(m_arp, "null");
Compiler::Error(Compiler::UseUndeclaredVariable, e);
if (m_variables->getAttribute(VariableTable::ContainsCompact)) {
+31
Ver Arquivo
@@ -25,6 +25,7 @@
#include <compiler/analysis/variable_table.h>
#include <compiler/construct.h>
#include <compiler/expression/class_constant_expression.h>
#include <compiler/expression/closure_expression.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/unary_op_expression.h>
@@ -32,6 +33,7 @@
#include <compiler/option.h>
#include <compiler/parser/parser.h>
#include <compiler/statement/interface_statement.h>
#include <compiler/statement/function_statement.h>
#include <compiler/statement/method_statement.h>
#include <compiler/statement/statement_list.h>
#include <runtime/base/builtin_functions.h>
@@ -445,6 +447,7 @@ ClassScope::importTraitMethod(const TraitMethod& traitMethod,
cloneMeth->getModifiers()));
cloneMeth->resetScope(cloneFuncScope, true);
cloneFuncScope->setOuterScope(shared_from_this());
informClosuresAboutScopeClone(cloneMeth, cloneFuncScope, ar);
cloneMeth->addTraitMethodToScope(ar,
dynamic_pointer_cast<ClassScope>(shared_from_this()));
@@ -452,6 +455,34 @@ ClassScope::importTraitMethod(const TraitMethod& traitMethod,
return cloneMeth;
}
void ClassScope::informClosuresAboutScopeClone(
ConstructPtr root,
FunctionScopePtr outerScope,
AnalysisResultPtr ar) {
if (!root) {
return;
}
for (int i = 0; i < root->getKidCount(); i++) {
ConstructPtr cons = root->getNthKid(i);
ClosureExpressionPtr closure =
dynamic_pointer_cast<ClosureExpression>(cons);
if (!closure) {
informClosuresAboutScopeClone(cons, outerScope, ar);
continue;
}
FunctionStatementPtr func = closure->getClosureFunction();
HPHP::FunctionScopePtr funcScope = func->getFunctionScope();
assert(funcScope->isClosure());
funcScope->addClonedTraitOuterScope(outerScope);
// Don't need to recurse
}
}
void ClassScope::addImportTraitMethod(const TraitMethod &traitMethod,
const string &methName) {
m_importMethToTraitMap[methName].push_back(traitMethod);
+3
Ver Arquivo
@@ -437,6 +437,9 @@ private:
void addImportTraitMethod(const TraitMethod &traitMethod,
const std::string &methName);
void informClosuresAboutScopeClone(ConstructPtr root,
FunctionScopePtr outerScope,
AnalysisResultPtr ar);
void setImportTraitMethodModifiers(const std::string &methName,
ClassScopePtr traitCls,
+11 -4
Ver Arquivo
@@ -5200,10 +5200,17 @@ void EmitterVisitor::emitPostponedMeths() {
initScalar(dv, vNode);
pi.setDefaultValue(dv);
// Simple case: it's a scalar value so we just serialize it
VariableSerializer vs(VariableSerializer::PHPOutput);
String result = vs.serialize(tvAsCVarRef(&dv), true);
phpCode = StringData::GetStaticString(result.data());
std::string orig = vNode->getComment();
if (orig.empty()) {
// Simple case: it's a scalar value so we just serialize it
VariableSerializer vs(VariableSerializer::PHPOutput);
String result = vs.serialize(tvAsCVarRef(&dv), true);
phpCode = StringData::GetStaticString(result.get());
} else {
// This was optimized from a Constant, or ClassConstant
// use the original string
phpCode = StringData::GetStaticString(orig);
}
} else {
// Non-scalar, so we have to output PHP from the AST node
std::ostringstream os;
+40
Ver Arquivo
@@ -410,6 +410,46 @@ bool FunctionScope::containsReference() const {
return m_attribute & FileScope::ContainsReference;
}
void FunctionScope::setContainsThis(bool f /* = true */) {
m_containsThis = f;
BlockScopePtr bs(this->getOuterScope());
while (bs && bs->is(BlockScope::FunctionScope)) {
FunctionScopePtr fs = static_pointer_cast<FunctionScope>(bs);
if (!fs->isClosure()) {
break;
}
fs->setContainsThis(f);
bs = bs->getOuterScope();
}
for (auto it = m_clonedTraitOuterScope.begin(); it != m_clonedTraitOuterScope.end(); it++) {
(*it)->setContainsThis(f);
}
}
void FunctionScope::setContainsBareThis(bool f, bool ref /* = false */) {
if (f) {
m_containsBareThis |= ref ? 2 : 1;
} else {
m_containsBareThis = 0;
}
BlockScopePtr bs(this->getOuterScope());
while (bs && bs->is(BlockScope::FunctionScope)) {
FunctionScopePtr fs = static_pointer_cast<FunctionScope>(bs);
if (!fs->isClosure()) {
break;
}
fs->setContainsBareThis(f, ref);
bs = bs->getOuterScope();
}
for (auto it = m_clonedTraitOuterScope.begin(); it != m_clonedTraitOuterScope.end(); it++) {
(*it)->setContainsBareThis(f, ref);
}
}
bool FunctionScope::hasImpl() const {
if (!isUserFunction()) {
return !isAbstract();
+10 -8
Ver Arquivo
@@ -176,6 +176,13 @@ public:
m_volatile = false;
}
/**
* Tell this function about another outer scope that contains it.
*/
void addClonedTraitOuterScope(FunctionScopePtr scope) {
m_clonedTraitOuterScope.push_back(scope);
}
/**
* Get/set original name of the function, without case being lowered.
*/
@@ -237,16 +244,10 @@ public:
* Whether this function contains a usage of $this
*/
bool containsThis() const { return m_containsThis;}
void setContainsThis(bool f=true) { m_containsThis = f;}
void setContainsThis(bool f = true);
bool containsBareThis() const { return m_containsBareThis; }
bool containsRefThis() const { return m_containsBareThis & 2; }
void setContainsBareThis(bool f, bool ref = false) {
if (f) {
m_containsBareThis |= ref ? 2 : 1;
} else {
m_containsBareThis = 0;
}
}
void setContainsBareThis(bool f, bool ref = false);
/**
* How many parameters a caller should provide.
*/
@@ -492,6 +493,7 @@ private:
ExpressionListPtr m_closureValues;
ReadWriteMutex m_inlineMutex;
unsigned m_nextID; // used when cloning generators for traits
std::list<FunctionScopeRawPtr> m_clonedTraitOuterScope;
};
///////////////////////////////////////////////////////////////////////////////
+2 -17
Ver Arquivo
@@ -461,25 +461,11 @@ bool LiveDict::color(TypePtr type) {
if (Type::SameType(type, e->getCPPType())) {
SimpleVariablePtr sv(
static_pointer_cast<SimpleVariable>(e));
bool isGenParam = false;
if (sv->getFunctionScope()->isGenerator()) {
// do not allow coalescing of symbols which are parameters/use vars
// in the generator (sym->isParameter() will be false b/c we are in
// the scope of the generator function)
FunctionScopeRawPtr origScope(sv->getFunctionScope()->getOrigGenFS());
assert(origScope);
Symbol *origSym =
origScope->getVariables()->getSymbol(sv->getName());
if (origSym &&
(origSym->isParameter() || origSym->isClosureVar())) {
isGenParam = true;
}
}
Symbol *sym = sv->getSymbol();
if (sym &&
!sym->isGlobal() &&
!sym->isParameter() &&
!isGenParam &&
!sym->isGeneratorParameter() &&
!sym->isClosureVar() &&
!sym->isStatic() &&
!e->isThis()) {
@@ -623,9 +609,8 @@ public:
always_assert(e && e->is(Expression::KindOfSimpleVariable));
SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(e));
Symbol *sym = sv->getSymbol();
bool inGen = sv->getFunctionScope()->isGenerator();
if (!sym || sym->isGlobal() || sym->isStatic() || sym->isParameter() ||
sym->isClosureVar() || sv->isThis() || inGen) {
sym->isGeneratorParameter() || sym->isClosureVar() || sv->isThis()) {
continue;
}
+11 -1
Ver Arquivo
@@ -115,6 +115,8 @@ public:
bool isIndirectAltered() const { return m_flags.m_indirectAltered; }
bool isReferenced() const { return !m_flags.m_notReferenced; }
bool isHidden() const { return m_flags.m_hidden; }
bool isGeneratorParameter() const { return m_flags.m_generatorParameter; }
bool isRefGeneratorParameter() const { return m_flags.m_refGeneratorParameter; }
bool isClosureVar() const { return m_flags.m_closureVar; }
bool isRefClosureVar() const { return m_flags.m_refClosureVar; }
bool isPassClosureVar() const { return m_flags.m_passClosureVar; }
@@ -140,6 +142,8 @@ public:
void setIndirectAltered() { m_flags.m_indirectAltered = true; }
void setReferenced() { m_flags.m_notReferenced = false; }
void setHidden() { m_flags.m_hidden = true; }
void setGeneratorParameter() { m_flags.m_generatorParameter = true; }
void setRefGeneratorParameter() { m_flags.m_refGeneratorParameter = true; }
void setClosureVar() { m_flags.m_closureVar = true; }
void setRefClosureVar() { m_flags.m_refClosureVar = true; }
void setPassClosureVar() { m_flags.m_passClosureVar = true; }
@@ -185,7 +189,7 @@ private:
std::string m_name;
unsigned int m_hash;
union {
unsigned m_flags_val;
uint64_t m_flags_val;
struct {
/* internal */
unsigned m_declaration_set : 1;
@@ -219,6 +223,8 @@ private:
unsigned m_indirectAltered : 1;
unsigned m_notReferenced : 1;
unsigned m_hidden : 1;
unsigned m_generatorParameter : 1;
unsigned m_refGeneratorParameter : 1;
unsigned m_closureVar : 1;
unsigned m_refClosureVar : 1;
unsigned m_passClosureVar : 1;
@@ -227,6 +233,10 @@ private:
unsigned m_stashedVal : 1;
unsigned m_reseated : 1;
} m_flags;
static_assert(
sizeof(m_flags_val) == sizeof(m_flags),
"m_flags_val must cover all the flags");
};
ConstructPtr m_declaration;
ConstructPtr m_value;
+4
Ver Arquivo
@@ -567,6 +567,10 @@ void VariableTable::clearUsed() {
} else {
sym.second.setReferenced();
}
if (sym.second.isRefGeneratorParameter()) {
sym.second.setReferenced();
}
}
}
+1 -1
Ver Arquivo
@@ -473,8 +473,8 @@ int prepareOptions(CompilerOptions &po, int argc, char **argv) {
(Util::format_pattern(po.excludeStaticPatterns[i], true));
}
Option::OutputHHBC = true;
if (po.target == "hhbc" || po.target == "run") {
Option::OutputHHBC = true;
Option::AnalyzePerfectVirtuals = false;
}
@@ -257,6 +257,14 @@ ExpressionPtr ArrayElementExpression::preOptimize(AnalysisResultConstPtr ar) {
return replaceValue(makeConstant(ar, "null"));
}
if (m_offset->isScalar() && m_offset->getScalarValue(o)) {
if (v.isString()) {
if (!o.isInteger() ||
o.toInt64Val() < 0 ||
o.toInt64Val() >= v.toCStrRef().size()) {
// warnings should be raised...
return ExpressionPtr();
}
}
try {
g_context->setThrowAllErrors(true);
Variant res = v.rvalAt(
@@ -249,6 +249,14 @@ ExpressionPtr AssignmentExpression::preOptimize(AnalysisResultConstPtr ar) {
g_context->setThrowAllErrors(true);
if (aoff) {
if (!aoff->getScalarValue(o)) break;
if (v.isString()) {
if (!o.isInteger() ||
o.toInt64Val() < 0 ||
o.toInt64Val() >= v.toCStrRef().size()) {
// warnings should be raised...
break;
}
}
v.set(o, r);
} else {
v.append(r);
@@ -161,12 +161,16 @@ bool BinaryOpExpression::isLogicalOrOperator() const {
}
ExpressionPtr BinaryOpExpression::unneededHelper() {
if (!isShortCircuitOperator() || !m_exp2->getContainedEffects()) {
bool shortCircuit = isShortCircuitOperator();
if (!m_exp2->getContainedEffects() ||
(!shortCircuit && !m_exp1->getContainedEffects())) {
return Expression::unneededHelper();
}
m_exp2 = m_exp2->unneeded();
m_exp2->setExpectedType(Type::Boolean);
if (shortCircuit) {
m_exp2 = m_exp2->unneeded();
m_exp2->setExpectedType(Type::Boolean);
}
return static_pointer_cast<Expression>(shared_from_this());
}
+1 -1
Ver Arquivo
@@ -61,7 +61,7 @@ public:
void pushConst(const std::string &name);
void popConst();
void setComment(const std::string &comment) { m_comment = comment;}
const std::string getComment() { return m_comment;}
std::string getComment() { return m_comment;}
bool isValid() const { return m_valid; }
bool isDynamic() const { return m_dynamic; }
private:
+1
Ver Arquivo
@@ -171,6 +171,7 @@ public:
}
bool hasSubExpr(ExpressionPtr sub) const;
virtual void setComment(const std::string &) {}
virtual std::string getComment() { return ""; }
/**
* Set this expression's error flags.
*/
+1 -1
Ver Arquivo
@@ -69,7 +69,7 @@ public:
int64_t getHash() const;
void setComment(const std::string &comment) { m_comment = comment;}
const std::string getComment() { return m_comment;}
std::string getComment() { return m_comment;}
bool getString(const std::string *&s) const;
@@ -1138,7 +1138,7 @@ TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
setAttribute(VariableTable::NeedGlobalPointer);
}
if (m_params) {
if (func && func->isRedeclaring()) {
if (Option::WholeProgram && func && func->isRedeclaring()) {
FunctionScope::FunctionInfoPtr info =
FunctionScope::GetFunctionInfo(m_name);
always_assert(info);
+1 -1
Ver Arquivo
@@ -172,7 +172,7 @@ void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) {
}
}
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_sym) {
if (m_sym && !m_this) {
if (!m_sym->isSystem() &&
!(getContext() &
(LValue|RefValue|RefParameter|UnsetContext|ExistContext)) &&
+1 -1
Ver Arquivo
@@ -166,7 +166,7 @@ std::string Option::GetSystemRoot() {
throw Exception("Environment variable HPHP_HOME is not set.");
}
SystemRoot = home;
SystemRoot += "/src";
SystemRoot += "/hphp";
}
return SystemRoot;
}
+6 -3
Ver Arquivo
@@ -1569,10 +1569,13 @@ void Parser::onClosure(Token &out, Token &ret, Token &ref, Token &params,
Token func, name;
onFunction(func, ret, ref, name, params, stmts, 0);
ClosureExpressionPtr closure = NEW_EXP(
ClosureExpression,
dynamic_pointer_cast<FunctionStatement>(func->stmt),
dynamic_pointer_cast<ExpressionList>(cparams->exp));
closure->getClosureFunction()->setContainingClosure(closure);
out.reset();
out->exp = NEW_EXP(ClosureExpression,
dynamic_pointer_cast<FunctionStatement>(func->stmt),
dynamic_pointer_cast<ExpressionList>(cparams->exp));
out->exp = closure;
}
void Parser::onClosureParam(Token &out, Token *params, Token &param,
+35 -5
Ver Arquivo
@@ -33,6 +33,7 @@
#include <compiler/expression/parameter_expression.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/closure_expression.h>
#include <compiler/analysis/ast_walker.h>
#include <compiler/analysis/analysis_result.h>
@@ -372,15 +373,44 @@ void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
funcScope->setParamSpecs(ar);
if (funcScope->isGenerator()) {
MethodStatementRawPtr orig = getOrigGeneratorFunc();
VariableTablePtr variables = funcScope->getVariables();
Symbol *cont = variables->getSymbol(CONTINUATION_OBJECT_NAME);
cont->setHidden();
getOrigGeneratorFunc()->getFunctionScope()->addUse(
funcScope, BlockScope::UseKindClosure);
getOrigGeneratorFunc()->getFunctionScope()->setContainsBareThis(
orig->getFunctionScope()->addUse(funcScope, BlockScope::UseKindClosure);
orig->getFunctionScope()->setContainsBareThis(
funcScope->containsBareThis(), funcScope->containsRefThis());
getOrigGeneratorFunc()->getFunctionScope()->setContainsThis(
funcScope->containsThis());
orig->getFunctionScope()->setContainsThis(funcScope->containsThis());
if (ExpressionListPtr params = orig->getParams()) {
for (int i = 0; i < params->getCount(); ++i) {
auto param = dynamic_pointer_cast<ParameterExpression>((*params)[i]);
Symbol *gp = variables->addDeclaredSymbol(
param->getName(), ConstructPtr());
gp->setGeneratorParameter();
if (param->isRef()) {
gp->setRefGeneratorParameter();
gp->setReferenced();
}
}
}
if (ClosureExpressionRawPtr closure = orig->getContainingClosure()) {
if (ExpressionListPtr cvars = closure->getClosureVariables()) {
for (int i = 0; i < cvars->getCount(); ++i) {
auto param = dynamic_pointer_cast<ParameterExpression>((*cvars)[i]);
Symbol *gp = variables->addDeclaredSymbol(
param->getName(), ConstructPtr());
gp->setGeneratorParameter();
if (param->isRef()) {
gp->setRefGeneratorParameter();
gp->setReferenced();
}
}
}
}
}
if (funcScope->isSepExtension() ||
Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
+9
Ver Arquivo
@@ -22,6 +22,7 @@
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
DECLARE_BOOST_TYPES(ClosureExpression);
DECLARE_BOOST_TYPES(ModifierExpression);
DECLARE_BOOST_TYPES(ExpressionList);
DECLARE_BOOST_TYPES(StatementList);
@@ -104,6 +105,13 @@ public:
return m_generatorFunc;
}
void setContainingClosure(ClosureExpressionRawPtr exp) {
m_containingClosure = exp;
}
ClosureExpressionRawPtr getContainingClosure() const {
return m_containingClosure;
}
void setClassName(const std::string &name) { m_className = name; }
void setOriginalClassName(const std::string &name) {
m_originalClassName = name;
@@ -127,6 +135,7 @@ protected:
std::string m_docComment;
MethodStatementRawPtr m_origGeneratorFunc;
MethodStatementRawPtr m_generatorFunc;
ClosureExpressionRawPtr m_containingClosure;
ExpressionListPtr m_attrList;
void setSpecialMethod(ClassScopePtr classScope);
+4 -1
Ver Arquivo
@@ -31,10 +31,13 @@ int main(int argc, char** argv) {
return HPHP::execute_program(argc, argv);
}
std::vector<char*> args;
args.insert(args.begin(), argv, argv + argc);
args.push_back(argv[0]);
args.push_back("-vRepo.Authoritative=true");
args.push_back("-vRepo.Local.Mode=r-");
repo = "-vRepo.Local.Path=" + repo;
args.push_back(const_cast<char*>(repo.c_str()));
if (argc > 1) {
args.insert(args.end(), argv + 1, argv + argc);
}
return HPHP::execute_program(args.size(), &args[0]);
}
+2 -1
Ver Arquivo
@@ -345,8 +345,9 @@ DefineFunction(
'desc' => "This function checks if the given property exists in the specified class.\n\nAs opposed with isset(), property_exists() returns TRUE even if the property has the value NULL.",
'flags' => HasDocComment,
'return' => array(
'type' => Boolean,
'type' => Variant,
'desc' => "Returns TRUE if the property exists, FALSE if it doesn't exist or NULL in case of an error.",
'predicted_type' => Boolean,
),
'args' => array(
array(
+2 -1
Ver Arquivo
@@ -345,7 +345,8 @@ vm_decode_function(CVarRef function,
this_ = function.asCObjRef().get();
cls = nullptr;
const HPHP::VM::Func *f = this_->getVMClass()->lookupMethod(invokeStr);
if (f != nullptr && (f->attrs() & HPHP::VM::AttrStatic)) {
if (f != nullptr &&
((f->attrs() & HPHP::VM::AttrStatic) && !f->isClosureBody())) {
// If __invoke is static, invoke it as such
cls = this_->getVMClass();
this_ = nullptr;
+3 -3
Ver Arquivo
@@ -594,11 +594,11 @@ bool ClassInfo::HasAccess(CStrRef className, CStrRef methodName,
clsInfo->hasMethod(methodName, defClass);
if (!methodInfo) return false;
if (methodInfo->attribute & ClassInfo::IsPublic) return true;
CStrRef ctxName = g_vmContext->getContextClassName();
if (ctxName->size() == 0) {
VM::Class* ctx = g_vmContext->getContextClass();
if (!ctx) {
return false;
}
const ClassInfo *ctxClass = ClassInfo::FindClass(ctxName);
const ClassInfo *ctxClass = ClassInfo::FindClass(ctx->nameRef());
bool hasObject = hasCallObject || g_vmContext->getThis();
if (ctxClass) {
return ctxClass->checkAccess(defClass, methodInfo, staticCall, hasObject);
+2 -2
Ver Arquivo
@@ -591,8 +591,8 @@ public:
HPHP::VM::ActRec* getStackFrame();
ObjectData* getThis();
CStrRef getContextClassName();
CStrRef getParentContextClassName();
VM::Class* getContextClass();
VM::Class* getParentContextClass();
CStrRef getContainingFileName();
int getLine();
Array getCallerInfo();
+2 -2
Ver Arquivo
@@ -14,8 +14,8 @@
+----------------------------------------------------------------------+
*/
#ifndef __PREG_H__
#define __PREG_H_
#ifndef incl_HPHP_PREG_H_
#define incl_HPHP_PREG_H_
#include <runtime/base/types.h>
#include <runtime/base/complex_types.h>
+12 -4
Ver Arquivo
@@ -72,7 +72,13 @@ void raise_recoverable_error(const char *fmt, ...) {
raise_recoverable_error(msg);
}
static int64_t g_notice_counter = 0;
void raise_strict_warning(const std::string &msg) {
if (RuntimeOption::NoticeFrequency <= 0 ||
(g_notice_counter++) % RuntimeOption::NoticeFrequency != 0) {
return;
}
int errnum = ErrorConstants::STRICT;
if (!g_context->errorNeedsHandling(errnum, true,
ExecutionContext::NeverThrow)) {
@@ -84,6 +90,10 @@ void raise_strict_warning(const std::string &msg) {
}
void raise_strict_warning(const char *fmt, ...) {
if (RuntimeOption::NoticeFrequency <= 0 ||
(g_notice_counter++) % RuntimeOption::NoticeFrequency != 0) {
return;
}
std::string msg;
int errnum = ErrorConstants::STRICT;
if (!g_context->errorNeedsHandling(errnum, true,
@@ -117,11 +127,11 @@ void raise_warning(const std::string &msg) {
}
void raise_warning(const char *fmt, ...) {
std::string msg;
if (RuntimeOption::WarningFrequency <= 0 ||
(g_warning_counter++) % RuntimeOption::WarningFrequency != 0) {
return;
}
std::string msg;
int errnum = ErrorConstants::WARNING;
if (!g_context->errorNeedsHandling(errnum, true,
ExecutionContext::NeverThrow)) {
@@ -151,8 +161,6 @@ void raise_debugging(const char *fmt, ...) {
raise_debugging(msg);
}
static int64_t g_notice_counter = 0;
void raise_notice(const std::string &msg) {
if (RuntimeOption::NoticeFrequency <= 0 ||
(g_notice_counter++) % RuntimeOption::NoticeFrequency != 0) {
@@ -169,11 +177,11 @@ void raise_notice(const std::string &msg) {
}
void raise_notice(const char *fmt, ...) {
std::string msg;
if (RuntimeOption::NoticeFrequency <= 0 ||
(g_notice_counter++) % RuntimeOption::NoticeFrequency != 0) {
return;
}
std::string msg;
int errnum = ErrorConstants::NOTICE;
if (!g_context->errorNeedsHandling(errnum, true,
ExecutionContext::NeverThrow)) {
+3
Ver Arquivo
@@ -17,8 +17,11 @@
#ifndef __HPHP_RUNTIME_ERROR_H__
#define __HPHP_RUNTIME_ERROR_H__
#include <cstdarg>
#include <string>
#include "util/base.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
+1 -1
Ver Arquivo
@@ -423,7 +423,7 @@ public:
F(bool, HHIREnableCalleeSavedOpt, true) \
F(bool, HHIREnablePreColoring, true) \
F(bool, HHIREnableCoalescing, true) \
F(bool, HHIREnableMmx, true) \
F(bool, HHIREnableMmx, false) \
F(bool, HHIREnableRefCountOpt, true) \
F(bool, HHIREnableSinking, true) \
F(bool, HHIRGenerateAsserts, debug) \
+9 -2
Ver Arquivo
@@ -14,19 +14,26 @@
+----------------------------------------------------------------------+
*/
#include <runtime/base/util/curl_tls_workarounds.h>
#include <runtime/base/runtime_option.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <openssl/ssl.h>
#include "runtime/base/util/curl_tls_workarounds.h"
#include "runtime/base/runtime_error.h"
#include "runtime/base/runtime_option.h"
namespace HPHP {
CURLcode curl_tls_workarounds_cb(CURL *curl, void *sslctx, void *parm) {
// Check to see if workarounds are enabled.
if (RuntimeOption::TLSDisableTLS1_2) {
#ifdef SSL_OP_NO_TLSv1_2
SSL_CTX* ctx = (SSL_CTX*)sslctx;
SSL_CTX_set_options(ctx, SSL_CTX_get_options (ctx) | SSL_OP_NO_TLSv1_2);
#else
raise_notice("TLSDisableTLS1_2 enabled, but this version of "
"SSL does not support that option");
#endif
}
return CURLE_OK;
}
+2 -2
Ver Arquivo
@@ -150,8 +150,8 @@ void StringBuffer::release() {
}
void StringBuffer::resize(int size) {
assert(size >= 0 && size < m_cap);
if (size >= 0 && size < m_cap) {
assert(size >= 0 && size <= m_cap);
if (size >= 0 && size <= m_cap) {
m_len = size;
}
}
+6 -1
Ver Arquivo
@@ -697,7 +697,12 @@ Variant ZendPack::unpack(CStrRef fmt, CStrRef data) {
}
v |= unpack(&input[inputpos], sizeof(int), issigned, int_map);
ret.set(String(n, CopyString), v);
if (type == 'i') {
ret.set(String(n, CopyString), v);
} else {
uint64_t u64 = uint32_t(v);
ret.set(String(n, CopyString), u64);
}
break;
}
+2 -2
Ver Arquivo
@@ -623,8 +623,6 @@ static Variant f_hphp_get_iterator(VRefParam iterable, bool isMutable) {
CREATE_VECTOR1(iterable));
}
if (iterable.isObject()) {
CStrRef context = g_vmContext->getContextClassName();
ObjectData *obj = iterable.getObjectData();
Variant iterator;
while (obj->instanceof(SystemLib::s_IteratorAggregateClass)) {
@@ -632,6 +630,8 @@ static Variant f_hphp_get_iterator(VRefParam iterable, bool isMutable) {
if (!iterator.isObject()) break;
obj = iterator.getObjectData();
}
VM::Class*ctx = g_vmContext->getContextClass();
CStrRef context = ctx ? ctx->nameRef() : empty_string;
if (isMutable) {
if (obj->instanceof(SystemLib::s_IteratorClass)) {
throw FatalErrorException("An iterator cannot be used for "
+1 -1
Ver Arquivo
@@ -113,7 +113,7 @@ Variant f_bzdecompress(CStrRef source, int small /* = 0 */) {
/* compression is better then 2:1, need to allocate more memory */
bzs.avail_out = source_len;
size = (bzs.total_out_hi32 * (unsigned int) -1) + bzs.total_out_lo32;
dest = (char *) Util::safe_realloc(dest, bzs.avail_out + 1);
dest = (char *) Util::safe_realloc(dest, size + bzs.avail_out + 1);
bzs.next_out = dest + size;
}
+18 -10
Ver Arquivo
@@ -37,7 +37,8 @@ static String get_classname(CVarRef class_or_object) {
}
static inline CStrRef ctxClassName() {
return g_vmContext->getContextClassName();
VM::Class* ctx = g_vmContext->getContextClass();
return ctx ? ctx->nameRef() : empty_string;
}
static const VM::Class* get_cls(CVarRef class_or_object) {
@@ -260,20 +261,27 @@ bool f_method_exists(CVarRef class_or_object, CStrRef method_name) {
return false;
}
bool f_property_exists(CVarRef class_or_object, CStrRef property) {
Variant f_property_exists(CVarRef class_or_object, CStrRef property) {
if (class_or_object.isObject()) {
CStrRef context = ctxClassName();
// Call o_exists for objects, to include dynamic properties.
return class_or_object.toObject()->o_propExists(property, context);
}
const ClassInfo *classInfo =
ClassInfo::FindClass(get_classname(class_or_object));
while (classInfo) {
if (classInfo->hasProperty(property)) {
return true;
} else {
classInfo = classInfo->getParentClassInfo();
}
if (!class_or_object.isString()) {
raise_warning(
"First parameter must either be an object or the name of an existing class"
);
return Variant(Variant::nullInit);
}
VM::Class* cls = VM::Unit::lookupClass(get_classname(class_or_object).get());
if (!cls) {
return false;
}
bool accessible;
VM::Slot propInd = cls->getDeclPropIndex(cls, property.get(), accessible);
if (propInd != VM::kInvalidSlot) {
return true;
}
return false;
}
+9 -8
Ver Arquivo
@@ -686,22 +686,23 @@ TypedValue* fg_method_exists(HPHP::VM::ActRec *ar) {
/*
bool HPHP::f_property_exists(HPHP::Variant const&, HPHP::String const&)
HPHP::Variant HPHP::f_property_exists(HPHP::Variant const&, HPHP::String const&)
_ZN4HPHP17f_property_existsERKNS_7VariantERKNS_6StringE
(return value) => rax
class_or_object => rdi
property => rsi
_rv => rdi
class_or_object => rsi
property => rdx
*/
bool fh_property_exists(TypedValue* class_or_object, Value* property) asm("_ZN4HPHP17f_property_existsERKNS_7VariantERKNS_6StringE");
TypedValue* fh_property_exists(TypedValue* _rv, TypedValue* class_or_object, Value* property) asm("_ZN4HPHP17f_property_existsERKNS_7VariantERKNS_6StringE");
TypedValue * fg1_property_exists(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) __attribute__((noinline,cold));
TypedValue * fg1_property_exists(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) {
TypedValue* args UNUSED = ((TypedValue*)ar) - 1;
rv->m_type = KindOfBoolean;
tvCastToStringInPlace(args-1);
rv->m_data.num = (fh_property_exists((args-0), (Value*)(args-1))) ? 1LL : 0LL;
fh_property_exists((rv), (args-0), (Value*)(args-1));
if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull;
return rv;
}
@@ -711,8 +712,8 @@ TypedValue* fg_property_exists(HPHP::VM::ActRec *ar) {
TypedValue* args UNUSED = ((TypedValue*)ar) - 1;
if (count == 2LL) {
if (IS_STRING_TYPE((args-1)->m_type)) {
rv.m_type = KindOfBoolean;
rv.m_data.num = (fh_property_exists((args-0), (Value*)(args-1))) ? 1LL : 0LL;
fh_property_exists((&(rv)), (args-0), (Value*)(args-1));
if (rv.m_type == KindOfUninit) rv.m_type = KindOfNull;
frame_free_locals_no_this_inl(ar, 2);
memcpy(&ar->m_r, &rv, sizeof(TypedValue));
return &ar->m_r;
+5 -4
Ver Arquivo
@@ -170,15 +170,16 @@ method_name => rsi
bool fh_method_exists(TypedValue* class_or_object, Value* method_name) asm("_ZN4HPHP15f_method_existsERKNS_7VariantERKNS_6StringE");
/*
bool HPHP::f_property_exists(HPHP::Variant const&, HPHP::String const&)
HPHP::Variant HPHP::f_property_exists(HPHP::Variant const&, HPHP::String const&)
_ZN4HPHP17f_property_existsERKNS_7VariantERKNS_6StringE
(return value) => rax
class_or_object => rdi
property => rsi
_rv => rdi
class_or_object => rsi
property => rdx
*/
bool fh_property_exists(TypedValue* class_or_object, Value* property) asm("_ZN4HPHP17f_property_existsERKNS_7VariantERKNS_6StringE");
TypedValue* fh_property_exists(TypedValue* _rv, TypedValue* class_or_object, Value* property) asm("_ZN4HPHP17f_property_existsERKNS_7VariantERKNS_6StringE");
/*
HPHP::Variant HPHP::f_get_object_vars(HPHP::Variant const&)
+1 -1
Ver Arquivo
@@ -40,7 +40,7 @@ Variant f_get_parent_class(CVarRef object = null_variant);
bool f_is_a(CVarRef class_or_object, CStrRef class_name, bool allow_string = false);
bool f_is_subclass_of(CVarRef class_or_object, CStrRef class_name, bool allow_string = true);
bool f_method_exists(CVarRef class_or_object, CStrRef method_name);
bool f_property_exists(CVarRef class_or_object, CStrRef property);
Variant f_property_exists(CVarRef class_or_object, CStrRef property);
Variant f_get_object_vars(CVarRef object);
///////////////////////////////////////////////////////////////////////////////
+1 -1
Ver Arquivo
@@ -71,7 +71,7 @@ c_Continuation::~c_Continuation() {
contLocal->m_type = KindOfNull;
if (ar->hasVarEnv()) {
VM::VarEnv::destroy(ar->getVarEnv());
ar->getVarEnv()->detach(ar);
} else {
frame_free_locals_inl(ar, m_vmFunc->numLocals());
}
+1
Ver Arquivo
@@ -6016,6 +6016,7 @@ static int exif_process_string_raw(char **result, char *value,
* chars up to byte_count, we also have to add a single NUL character to
* force end of string.
*/
*result = 0;
if (byte_count) {
(*result) = (char*)IM_MALLOC(byte_count + 1);
CHECK_ALLOC_R((*result), byte_count + 1, 0);
+3 -2
Ver Arquivo
@@ -480,7 +480,6 @@ void c_MutableArrayIterator::t___construct(VRefParam array) {
m_valid = mi.advance();
if (!m_valid) mi.~MArrayIter();
} else if (rtv->m_type == KindOfObject) {
CStrRef ctxStr = g_vmContext->getContextClassName();
if (rtv->m_data.pobj->isCollection()) {
raise_error("Collection elements cannot be taken by reference");
}
@@ -489,7 +488,9 @@ void c_MutableArrayIterator::t___construct(VRefParam array) {
if (isIterator) {
raise_error("An iterator cannot be used with foreach by reference");
}
Array iterArray = obj->o_toIterArray(ctxStr, true);
VM::Class* ctx = g_vmContext->getContextClass();
Array iterArray = obj->o_toIterArray(ctx ? ctx->nameRef() : empty_string,
true);
ArrayData* ad = iterArray.detach();
MArrayIter& mi = marr();
(void) new (&mi) MArrayIter(ad);
+41 -72
Ver Arquivo
@@ -54,6 +54,41 @@ int64_t f_connection_timeout() {
return f_connection_status() == k_CONNECTION_TIMEOUT;
}
static VM::Class* getClassByName(const char* name, int len) {
VM::Class* cls = nullptr;
// translate "self" or "parent"
if (len == 4 && !memcmp(name, "self", 4)) {
cls = g_vmContext->getContextClass();
if (!cls) {
throw FatalErrorException("Cannot access self:: "
"when no class scope is active");
}
} else if (len == 6 && !memcmp(name, "parent", 6)) {
cls = g_vmContext->getParentContextClass();
if (!cls) {
throw FatalErrorException("Cannot access parent");
}
} else if (len == 6 && !memcmp(name, "static", 6)) {
CallerFrame cf;
auto ar = cf();
if (ar) {
if (ar->hasThis()) {
cls = ar->getThis()->getVMClass();
} else if (ar->hasClass()) {
cls = ar->getClass();
}
}
if (!cls) {
throw FatalErrorException("Cannot access static:: "
"when no class scope is active");
}
} else {
String className(name, len, CopyString);
cls = VM::Unit::loadClass(className.get());
}
return cls;
}
Variant f_constant(CStrRef name) {
if (!name.get()) return null;
const char *data = name.data();
@@ -63,26 +98,7 @@ Variant f_constant(CStrRef name) {
// class constant
int classNameLen = colon - data;
char *constantName = colon + 2;
String className(data, classNameLen, CopyString);
// translate "self" or "parent"
if (className == "self") {
String this_class = g_vmContext->getContextClassName();
if (this_class.empty()) {
throw FatalErrorException("Cannot access self:: "
"when no class scope is active");
} else {
className = this_class;
}
} else if (className == "parent") {
String parent_class =g_vmContext->getParentContextClassName();
if (parent_class.empty()) {
throw FatalErrorException("Cannot access parent");
} else {
className = parent_class;
}
}
VM::Class* cls = VM::Unit::loadClass(className.get());
VM::Class* cls = getClassByName(data, classNameLen);
if (cls) {
String cnsName(constantName, data + len - constantName, CopyString);
TypedValue* tv = cls->clsCnsGet(cnsName.get());
@@ -123,59 +139,12 @@ bool f_defined(CStrRef name, bool autoload /* = true */) {
// class constant
int classNameLen = colon - data;
char *constantName = colon + 2;
String className(data, classNameLen, CopyString);
// translate "self" or "parent" or "static"
if (className == "self") {
String this_class = g_vmContext->getContextClassName();
if (this_class.empty()) {
throw FatalErrorException("Cannot access self:: "
"when no class scope is active");
} else {
className = this_class;
}
} else if (className == "parent") {
String parent_class = g_vmContext->getParentContextClassName();
if (parent_class.empty()) {
throw FatalErrorException("Cannot access parent");
} else {
className = parent_class;
}
} else if (className == "static") {
CallerFrame cf;
auto ar = cf();
if (ar) {
HPHP::VM::Class* cls;
if (ar->hasThis()) {
cls = ar->getThis()->getVMClass();
} else if (ar->hasClass()) {
cls = ar->getClass();
} else {
cls = NULL;
}
if (cls) {
className = cls->nameRef();
} else {
throw FatalErrorException("Cannot access static:: "
"when no class scope is active");
}
}
}
if (class_exists(className)) { // taking care of volatile class
const ClassInfo *info;
for (String parentClass = className;
!parentClass.empty();
parentClass = info->getParentClass()) {
info = ClassInfo::FindClass(parentClass);
if (!info) {
assert(false);
}
if (info->hasConstant(constantName)) return true;
}
return false;
} else {
return false;
VM::Class* cls = getClassByName(data, classNameLen);
if (cls) {
String cnsName(constantName, data + len - constantName, CopyString);
return cls->clsCnsGet(cnsName.get());
}
return false;
} else {
// system/uniquely defined scalar constant
if (ClassInfo::FindConstant(name)) return true;
+80 -23
Ver Arquivo
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <pwd.h>
#include <memory>
namespace HPHP {
IMPLEMENT_DEFAULT_EXTENSION(posix);
@@ -35,30 +36,55 @@ bool f_posix_access(CStrRef file, int mode /* = 0 */) {
return !access(path.data(), mode);
}
static Variant php_posix_group_to_array(struct group *g) {
if (!g) {
static Variant php_posix_group_to_array(int gid,
CStrRef gname = null_variant) {
// Don't pass a gid *and* a gname to this.
assert((gid < 0) || gname.size() == 0);
if ((gid < 0) && (gname.size() == 0)) {
return false;
}
int grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
if (grbuflen < 1) {
return false;
}
std::unique_ptr<char[]> grbuf(new char[grbuflen]);
struct group gr;
struct group *retgrptr = NULL;
// If we somehow reach this point and both gname and gid were
// passed, then the gid values will override the game values,
// but it will otherwise function just fine.
// The assert() clause above should prevent that, however.
if ((gname.size() > 0) &&
getgrnam_r(gname.data(), &gr, grbuf.get(), grbuflen, &retgrptr)) {
return false;
} else if ((gid >= 0) &&
getgrgid_r(gid, &gr, grbuf.get(), grbuflen, &retgrptr)) {
return false;
}
Array members;
for (int count=0; g->gr_mem[count] != NULL; count++) {
members.append(String(g->gr_mem[count], AttachLiteral));
for (int count=0; gr.gr_mem[count] != NULL; count++) {
members.append(String(gr.gr_mem[count], CopyString));
}
Array ret;
ret.set("name", String(g->gr_name, AttachLiteral));
ret.set("passwd", String(g->gr_passwd, AttachLiteral));
ret.set("name", String(gr.gr_name, CopyString));
ret.set("passwd", String(gr.gr_passwd, CopyString));
ret.set("members", members);
ret.set("gid", (int)g->gr_gid);
ret.set("gid", (int)gr.gr_gid);
return ret;
}
Variant f_posix_getgrgid(int gid) {
return php_posix_group_to_array(getgrgid(gid));
return php_posix_group_to_array(gid);
}
Variant f_posix_getgrnam(CStrRef name) {
return php_posix_group_to_array(getgrnam(name.data()));
return php_posix_group_to_array(-1, name.data());
}
Variant f_posix_getgroups() {
@@ -75,28 +101,53 @@ Variant f_posix_getgroups() {
return ret;
}
static Variant php_posix_passwd_to_array(struct passwd *pw) {
if (!pw) {
static Variant php_posix_passwd_to_array(int uid,
CStrRef name = null_variant) {
// Don't pass a uid *and* a name to this.
assert((uid < 0) || name.size() == 0);
if ((uid < 0) && name.size() == 0) {
return false;
}
int pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
if (pwbuflen < 1) {
return false;
}
std::unique_ptr<char[]> pwbuf(new char[pwbuflen]);
struct passwd pw;
struct passwd *retpwptr = NULL;
// If we somehow reach this point and both name and uid were
// passed, then the uid values will override the name values,
// but it will otherwise function just fine.
// The assert() clauses above should prevent that, however.
if ((name.size() > 0) &&
getpwnam_r(name.data(), &pw, pwbuf.get(), pwbuflen, &retpwptr)) {
return false;
} else if ((uid >= 0) &&
getpwuid_r(uid, &pw, pwbuf.get(), pwbuflen, &retpwptr)) {
return false;
}
Array ret;
ret.set("name", String(pw->pw_name, AttachLiteral));
ret.set("passwd", String(pw->pw_passwd, AttachLiteral));
ret.set("uid", (int)pw->pw_uid);
ret.set("gid", (int)pw->pw_gid);
ret.set("gecos", String(pw->pw_gecos, AttachLiteral));
ret.set("dir", String(pw->pw_dir, AttachLiteral));
ret.set("shell", String(pw->pw_shell, AttachLiteral));
ret.set("name", String(pw.pw_name, CopyString));
ret.set("passwd", String(pw.pw_passwd, CopyString));
ret.set("uid", (int)pw.pw_uid);
ret.set("gid", (int)pw.pw_gid);
ret.set("gecos", String(pw.pw_gecos, CopyString));
ret.set("dir", String(pw.pw_dir, CopyString));
ret.set("shell", String(pw.pw_shell, CopyString));
return ret;
}
Variant f_posix_getpwnam(CStrRef username) {
return php_posix_passwd_to_array(getpwnam(username.data()));
return php_posix_passwd_to_array(-1, username);
}
Variant f_posix_getpwuid(int uid) {
return php_posix_passwd_to_array(getpwuid(uid));
return php_posix_passwd_to_array(uid);
}
static bool posix_addlimit(int limit, const char *name, Array &ret) {
@@ -210,11 +261,17 @@ Variant f_posix_times() {
}
Variant f_posix_ttyname(CVarRef fd) {
char *p = ttyname(php_posix_get_fd(fd));
if (!p) {
int ttyname_maxlen = sysconf(_SC_TTY_NAME_MAX);
if (ttyname_maxlen <= 0) {
return false;
}
return String(p, CopyString);
String ttyname(ttyname_maxlen, ReserveString);
char *p = ttyname.mutableSlice().ptr;
if (ttyname_r(php_posix_get_fd(fd), p, ttyname_maxlen)) {
return false;
}
return ttyname.setSize(strlen(p));
}
Variant f_posix_uname() {
+1 -1
Ver Arquivo
@@ -16,7 +16,7 @@ if (!defined('GLOBAL_SYMBOL_REDECLARED_CLASS')) {define('GLOBAL_SYMBOL_REDECLARE
if (!defined('GLOBAL_SYMBOL_REDECLARED_FUNCTION')) {define('GLOBAL_SYMBOL_REDECLARED_FUNCTION', 5);}
if (!defined('GLOBAL_SYMBOL_STATIC_VARIABLE')) {define('GLOBAL_SYMBOL_STATIC_VARIABLE', 1);}
if (!defined('HPHP_TRIM_CHARLIST')) {define('HPHP_TRIM_CHARLIST', ' \n\r\t\v' . "\0" . '');}
if (!defined('HPHP_VERSION')) {define('HPHP_VERSION', '1.0.0');}
if (!defined('HPHP_VERSION')) {define('HPHP_VERSION', '2.0.2');}
if (!defined('INI_SCANNER_NORMAL')) {define('INI_SCANNER_NORMAL', 0);}
if (!defined('INI_SCANNER_RAW')) {define('INI_SCANNER_RAW', 1);}
if (!defined('PHP_VERSION')) {define('PHP_VERSION', '5.3.3.hiphop');}
+10 -34
Ver Arquivo
@@ -970,8 +970,7 @@ UnwindStatus Stack::unwindFrag(ActRec* fp, int offset,
// count starts over for the caller frame.
fault.m_handledCount = 0;
if (fp->isFromFPushCtor()) {
assert(fp->hasThis());
if (fp->isFromFPushCtor() && fp->hasThis()) {
fp->getThis()->setNoDestruct();
}
@@ -1425,46 +1424,22 @@ ObjectData* VMExecutionContext::getThis() {
return nullptr;
}
CStrRef VMExecutionContext::getContextClassName() {
Class* VMExecutionContext::getContextClass() {
VMRegAnchor _;
ActRec* ar = getFP();
assert(ar != nullptr);
if (ar->skipFrame()) {
ar = getPrevVMState(ar);
if (!ar) return empty_string;
}
if (ar->hasThis()) {
return ar->getThis()->o_getClassName();
} else if (ar->hasClass()) {
return ar->getClass()->nameRef();
} else {
return empty_string;
if (!ar) return nullptr;
}
return ar->m_func->cls();
}
CStrRef VMExecutionContext::getParentContextClassName() {
VMRegAnchor _;
ActRec* ar = getFP();
assert(ar != nullptr);
if (ar->skipFrame()) {
ar = getPrevVMState(ar);
if (!ar) return empty_string;
}
if (ar->hasThis()) {
const Class* cls = ar->getThis()->getVMClass();
if (cls->parent() == nullptr) {
return empty_string;
}
return cls->parent()->nameRef();
} else if (ar->hasClass()) {
const Class* cls = ar->getClass();
if (cls->parent() == nullptr) {
return empty_string;
}
return cls->parent()->nameRef();
} else {
return empty_string;
Class* VMExecutionContext::getParentContextClass() {
if (Class* ctx = getContextClass()) {
return ctx->parent();
}
return nullptr;
}
CStrRef VMExecutionContext::getContainingFileName() {
@@ -2056,7 +2031,8 @@ void VMExecutionContext::invokeFunc(TypedValue* retval,
// If this is a method, either this_ or cls must be non-NULL
assert(!f->preClass() || (this_ || cls));
// If this is a static method, this_ must be NULL
assert(!(f->attrs() & HPHP::VM::AttrStatic) || (!this_));
assert(!(f->attrs() & HPHP::VM::AttrStatic && !f->isClosureBody()) ||
(!this_));
// invName should only be non-NULL if we are calling __call or
// __callStatic
assert(!invName || f->name()->isame(s___call.get()) ||
+6
Ver Arquivo
@@ -308,6 +308,12 @@ const FPIEnt* Func::findPrecedingFPI(Offset o) const {
return fe;
}
bool Func::isClonedClosure() const {
if (!isClosureBody()) return false;
if (!cls()) return true;
return cls()->lookupMethod(name()) != this;
}
bool Func::isNameBindingImmutable(const Unit* fromUnit) const {
if (RuntimeOption::EvalJitEnableRenameFunction ||
m_attrs & AttrDynamicInvoke) {
+3 -2
Ver Arquivo
@@ -144,9 +144,9 @@ struct Func {
#endif
}
FuncId getFuncId() const {
FuncId getFuncId() const {
assert(m_funcId != InvalidId);
return m_funcId;
return m_funcId;
}
void setFuncId(FuncId id);
void setNewFuncId();
@@ -320,6 +320,7 @@ struct Func {
bool top() const { return shared()->m_top; }
const StringData* docComment() const { return shared()->m_docComment; }
bool isClosureBody() const { return shared()->m_isClosureBody; }
bool isClonedClosure() const;
bool isGenerator() const { return shared()->m_isGenerator; }
bool isGeneratorFromClosure() const {
return shared()->m_isGeneratorFromClosure;
+1 -1
Ver Arquivo
@@ -676,7 +676,7 @@ std::string instrToString(const Opcode* it, const Unit* u /* = NULL */) {
#define READOFF() do { \
Offset _value = *(Offset*)it; \
out << " " << _value; \
if (u != nullptr) { \
if (u != nullptr && _value >= 0) { \
out << " (" << u->offsetOf(iStart + _value) << ")"; \
} \
it += sizeof(Offset); \
+52 -71
Ver Arquivo
@@ -222,26 +222,6 @@ pathloop:
}
}
ConditionCode cmpOpToCC(Opcode opc) {
switch (opc) {
case OpGt: return CC_G;
case OpGte: return CC_GE;
case OpLt: return CC_L;
case OpLte: return CC_LE;
case OpEq: return CC_E;
case OpNeq: return CC_NE;
case OpSame: return CC_E;
case OpNSame: return CC_NE;
case InstanceOf: return CC_NZ;
case NInstanceOf: return CC_Z;
case InstanceOfBitmask: return CC_NZ;
case NInstanceOfBitmask: return CC_Z;
case IsType: return CC_NZ;
case IsNType: return CC_Z;
default: always_assert(0);
}
}
const char* getContextName(Class* ctx) {
return ctx ? ctx->name()->data() : ":anonymous:";
}
@@ -250,7 +230,8 @@ const char* getContextName(Class* ctx) {
//////////////////////////////////////////////////////////////////////
ArgDesc::ArgDesc(SSATmp* tmp, bool val) : m_imm(-1), m_zeroExtend(false) {
ArgDesc::ArgDesc(SSATmp* tmp, bool val) : m_imm(-1), m_zeroExtend(false),
m_done(false) {
if (tmp->getType() == Type::None) {
assert(val);
m_kind = None;
@@ -534,7 +515,7 @@ void CodeGenerator::cgJcc(IRInstruction* inst) {
SSATmp* src1 = inst->getSrc(0);
SSATmp* src2 = inst->getSrc(1);
Opcode opc = inst->getOpcode();
ConditionCode cc = cmpOpToCC(queryJmpToQueryOp(opc));
ConditionCode cc = queryJmpToCC(opc);
Type src1Type = src1->getType();
Type src2Type = src2->getType();
@@ -564,17 +545,21 @@ void CodeGenerator::cgJcc(IRInstruction* inst) {
// TODO: use compare with immediate or make sure simplifier
// canonicalizes this so that constant is src2
srcReg1 = rScratch;
m_as.mov_imm64_reg(src1->getValRawInt(), srcReg1);
m_as. mov_imm64_reg(src1->getValRawInt(), srcReg1);
}
if (src2->isConst()) {
m_as.cmp_imm64_reg64(src2->getValRawInt(), srcReg1);
if (src1Type.subtypeOf(Type::Bool)) {
m_as. cmpb (src2->getValRawInt(), Reg8(int(srcReg1)));
} else {
m_as. cmp_imm64_reg64(src2->getValRawInt(), srcReg1);
}
} else {
// Note the reverse syntax in the assembler.
// This cmp will compute srcReg1 - srcReg2
if (src1Type == Type::Bool) {
if (src1Type.subtypeOf(Type::Bool)) {
m_as. cmpb (Reg8(int(srcReg2)), Reg8(int(srcReg1)));
} else {
m_as.cmp_reg64_reg64(srcReg2, srcReg1);
m_as. cmp_reg64_reg64(srcReg2, srcReg1);
}
}
}
@@ -593,7 +578,9 @@ void CodeGenerator::cgJmpNSame(IRInstruction* inst) { cgJcc(inst); }
/**
* Once the arg sources and dests are all assigned; emit moves and exchanges
* to put all the args in desired registers.
* to put all the args in desired registers. In addition to moves and
* exchanges, shuffleArgs also handles adding lea-offsets for dest registers
* (dest = src + lea-offset) and zero extending bools (dest = zeroExtend(src)).
*/
typedef Transl::X64Assembler Asm;
static void shuffleArgs(Asm& a, ArgGroup& args) {
@@ -620,22 +607,10 @@ static void shuffleArgs(Asm& a, ArgGroup& args) {
}
auto dstReg = args[i].getDstReg();
auto srcReg = args[i].getSrcReg();
if (dstReg == srcReg) {
// Ignore register-to-register moves whose src and dst registers
// are the same. But emit the code for load-effective-address
// operations whose src and dst registers are the same as
// doRegMoves won't handle those.
if (kind == ArgDesc::Addr) {
// an lea whose src and dest regs are the same
a. lea (srcReg[args[i].getImm().q()], dstReg);
} else if (args[i].isZeroExtend()) {
// if passing bool as TypedValue.m_data, must zero extend
a. movzbl (rbyte(dstReg), r32(dstReg));
}
continue;
if (dstReg != srcReg) {
moves[int(dstReg)] = int(srcReg);
argDescs[int(dstReg)] = &args[i];
}
moves[int(dstReg)] = int(srcReg);
argDescs[int(dstReg)] = &args[i];
}
std::vector<MoveInfo> howTo;
doRegMoves(moves, int(reg::rScratch), howTo);
@@ -645,44 +620,46 @@ static void shuffleArgs(Asm& a, ArgGroup& args) {
a. movq (howTo[i].m_reg1, howTo[i].m_reg2);
} else {
ArgDesc* argDesc = argDescs[int(howTo[i].m_reg2)];
if (argDesc->getKind() == ArgDesc::Reg ||
argDesc->getKind() == ArgDesc::TypeReg) {
ArgDesc::Kind kind = argDesc->getKind();
if (kind == ArgDesc::Reg || kind == ArgDesc::TypeReg) {
if (argDesc->isZeroExtend()) {
a. movzbl (rbyte(howTo[i].m_reg1), r32(howTo[i].m_reg2));
} else {
a. movq (howTo[i].m_reg1, howTo[i].m_reg2);
}
} else {
assert(argDesc->getKind() == ArgDesc::Addr);
assert(kind == ArgDesc::Addr);
a. lea (howTo[i].m_reg1[argDesc->getImm().q()],
howTo[i].m_reg2);
}
if (kind != ArgDesc::TypeReg) {
argDesc->markDone();
}
}
} else {
a. xchgq (howTo[i].m_reg1, howTo[i].m_reg2);
ArgDesc* argDesc2 = argDescs[int(howTo[i].m_reg2)];
if (argDesc2->getKind() == ArgDesc::Addr) {
a. addq (argDesc2->getImm(), howTo[i].m_reg2);
} else if (argDesc2->isZeroExtend()) {
a. movzbl (rbyte(howTo[i].m_reg2), r32(howTo[i].m_reg2));
}
ArgDesc* argDesc1 = argDescs[int(howTo[i].m_reg1)];
if (argDesc1->getKind() == ArgDesc::Addr) {
a. addq (argDesc1->getImm(), howTo[i].m_reg1);
} else if (argDesc1->isZeroExtend()) {
a. movzbl (rbyte(howTo[i].m_reg1), r32(howTo[i].m_reg1));
}
}
}
// Handle const-to-register moves and type shifting
// Handle const-to-register moves, type shifting,
// load-effective address and zero extending for bools.
// Ignore args that have been handled by the
// move above.
for (size_t i = 0; i < args.size(); ++i) {
if (args[i].getKind() == ArgDesc::Imm) {
emitLoadImm(a, args[i].getImm().q(), args[i].getDstReg());
} else if (args[i].getKind() == ArgDesc::TypeReg) {
a. shlq (kTypeShiftBits, args[i].getDstReg());
} else if (RuntimeOption::EvalHHIRGenerateAsserts &&
args[i].getKind() == ArgDesc::None) {
emitLoadImm(a, 0xbadbadbadbadbad, args[i].getDstReg());
if (!args[i].done()) {
ArgDesc::Kind kind = args[i].getKind();
PhysReg dst = args[i].getDstReg();
if (kind == ArgDesc::Imm) {
emitLoadImm(a, args[i].getImm().q(), dst);
} else if (kind == ArgDesc::TypeReg) {
a. shlq (kTypeShiftBits, dst);
} else if (kind == ArgDesc::Addr) {
a. addq (args[i].getImm(), dst);
} else if (args[i].isZeroExtend()) {
a. movzbl (rbyte(dst), r32(dst));
} else if (RuntimeOption::EvalHHIRGenerateAsserts &&
kind == ArgDesc::None) {
emitLoadImm(a, 0xbadbadbadbadbad, dst);
}
}
}
}
@@ -1467,10 +1444,12 @@ void CodeGenerator::cgInstanceOf(IRInstruction* inst) {
void CodeGenerator::cgNInstanceOf(IRInstruction* inst) {
// TODO(#2058865): having NInstanceOf is no better than InstanceOf
// followed by boolean Not opcode.
emitInstanceCheck(inst, inst->getDst()->getReg());
PhysReg dstReg = inst->getDst()->getReg();
emitInstanceCheck(inst, dstReg);
Reg8 dr((int(dstReg)));
auto& a = m_as;
a. testb (al, al);
a. setz (al);
a. testb (dr, dr);
a. setz (dr);
}
void CodeGenerator::cgJmpInstanceOf(IRInstruction* inst) {
@@ -1903,8 +1882,10 @@ void traceRet(ActRec* fp, Cell* sp, void* rip) {
if (rip == TranslatorX64::Get()->getCallToExit()) {
return;
}
checkFrame(fp, sp, false);
assertTv(sp); // check return value
checkFrame(fp, sp, /*checkLocals*/ false);
assert(sp <= (Cell*)fp || fp->m_func->isGenerator());
// check return value if stack not empty
if (sp < (Cell*)fp) assertTv(sp);
}
void CodeGenerator::emitTraceRet(CodeGenerator::Asm& a) {
@@ -2251,7 +2232,7 @@ void CodeGenerator::cgExitTrace(IRInstruction* inst) {
// Patch the original jcc;jmp, don't emit another
IRInstruction* jcc = toSmash->getInstruction();
Opcode opc = jcc->getOpcode();
ConditionCode cc = cmpOpToCC(queryJmpToQueryOp(opc));
ConditionCode cc = queryJmpToCC(opc);
uint64_t taken = pc->getValInt();
uint64_t notTaken = notTakenPC->getValInt();
+4
Ver Arquivo
@@ -331,6 +331,8 @@ public:
void setDstReg(PhysReg reg) { m_dstReg = reg; }
Immed getImm() const { return m_imm; }
bool isZeroExtend() const {return m_zeroExtend;}
bool done() const { return m_done; }
void markDone() { m_done = true; }
private: // These should be created using ArgGroup.
friend struct ArgGroup;
@@ -341,6 +343,7 @@ private: // These should be created using ArgGroup.
, m_dstReg(reg::noreg)
, m_imm(immVal)
, m_zeroExtend(false)
, m_done(false)
{}
explicit ArgDesc(SSATmp* tmp, bool val = true);
@@ -351,6 +354,7 @@ private:
PhysReg m_dstReg;
Immed m_imm;
bool m_zeroExtend;
bool m_done;
};
/*
@@ -772,14 +772,10 @@ void HhbcTranslator::emitContDone() {
}
void HhbcTranslator::emitContNext() {
emitInterpOneOrPunt(Type::None);
return;
// Task #2140912: Fix and re-enable this
assert(getCurClass());
SSATmp* cont = m_tb->genLdThis(nullptr);
m_tb->genContPreNext(cont, getExitSlowTrace());
m_tb->genSetPropCell(cont, CONTOFF(m_received), m_tb->genDefUninit());
m_tb->genSetPropCell(cont, CONTOFF(m_received), m_tb->genDefInitNull());
}
void HhbcTranslator::emitContSendImpl(bool raise) {
@@ -1452,7 +1448,11 @@ void HhbcTranslator::emitFPushClsMethodD(int32_t numParams,
SSATmp* objOrCls;
if (!mightNotBeStatic) { // definitely static
// static function: store base class into the m_cls/m_this slot
objOrCls = m_tb->genDefConst(baseClass);
if (TargetCache::isPersistentHandle(baseClass->m_cachedOffset)) {
objOrCls = m_tb->genDefConst(baseClass);
} else {
objOrCls = m_tb->gen(LdClsCached, m_tb->genDefConst(className));
}
} else if (m_tb->isThisAvailable()) {
// 'this' pointer is available, so use it.
assert(getCurClass());
@@ -2460,6 +2460,7 @@ std::vector<SSATmp*> HhbcTranslator::getSpillValues() const {
*/
auto* inst = elem->getInstruction();
assert(inst->getNumSrcs() == 5);
ret.push_back(inst->getSrc(0)); // fp
ret.push_back(inst->getSrc(1)); // func
ret.push_back(inst->getSrc(2)); // objOrCls
ret.push_back(inst->getSrc(3)); // numArgs
+48 -2
Ver Arquivo
@@ -629,14 +629,60 @@ inline Opcode queryToJmpOp(Opcode opc) {
}
inline bool isQueryJmpOp(Opcode opc) {
return opc >= JmpGt && opc <= JmpIsNType;
switch (opc) {
case JmpGt:
case JmpGte:
case JmpLt:
case JmpLte:
case JmpEq:
case JmpNeq:
case JmpSame:
case JmpNSame:
case JmpInstanceOf:
case JmpNInstanceOf:
case JmpInstanceOfBitmask:
case JmpNInstanceOfBitmask:
case JmpIsType:
case JmpIsNType:
case JmpZero:
case JmpNZero:
return true;
default:
return false;
}
}
inline Opcode queryJmpToQueryOp(Opcode opc) {
assert(isQueryJmpOp(opc));
assert(opc != JmpZero && opc != JmpNZero);
return Opcode(OpGt + (opc - JmpGt));
}
inline ConditionCode queryJmpToCC(Opcode opc) {
assert(isQueryJmpOp(opc));
switch (opc) {
case JmpGt: return CC_G;
case JmpGte: return CC_GE;
case JmpLt: return CC_L;
case JmpLte: return CC_LE;
case JmpEq: return CC_E;
case JmpNeq: return CC_NE;
case JmpSame: return CC_E;
case JmpNSame: return CC_NE;
case JmpInstanceOf: return CC_NZ;
case JmpNInstanceOf: return CC_Z;
case JmpInstanceOfBitmask: return CC_NZ;
case JmpNInstanceOfBitmask: return CC_Z;
case JmpIsType: return CC_NZ;
case JmpIsNType: return CC_Z;
case JmpZero: return CC_Z;
case JmpNZero: return CC_NZ;
default:
not_reached();
}
}
/*
* Right now branch fusion is too indiscriminate to handle fusing
* with potentially expensive-to-repeat operations. TODO(#2053369)
@@ -2027,7 +2073,7 @@ int32_t spillValueCells(IRInstruction* spillStack);
* When SpillStack takes an ActRec, it has this many extra
* dependencies in the spill vector for the values in the ActRec.
*/
constexpr int kSpillStackActRecExtraArgs = 4;
constexpr int kSpillStackActRecExtraArgs = 5;
inline bool isConvIntOrPtrToBool(IRInstruction* instr) {
if (!(instr->getOpcode() == Conv &&
+1 -4
Ver Arquivo
@@ -23,10 +23,7 @@ namespace HPHP { namespace VM { namespace JIT {
// These are the conditional branches supported for direct branch
// to their target trace at TraceExit, TraceExitType::NormalCc
static bool jccCanBeDirectExit(Opcode opc) {
// JmpGt .. JmpNSame are contiguous and all use cgJcc
return (JmpGt <= opc && opc <= JmpNSame) ||
opc == JmpInstanceOf || opc == JmpNInstanceOf ||
opc == JmpInstanceOfBitmask || opc == JmpNInstanceOfBitmask;
return isQueryJmpOp(opc) && (opc != JmpIsType) && (opc != JmpIsNType);
// TODO(#2053369): JmpIsType, etc
}
+28 -22
Ver Arquivo
@@ -505,28 +505,30 @@ SSATmp* Simplifier::simplifyNot(SSATmp* src) {
} \
} while (0)
#define SIMPLIFY_COMMUTATIVE(OP, NAME) do { \
SIMPLIFY_CONST(OP); \
if (src1->isConst() && !src2->isConst()) { \
return m_tb->gen##NAME(src2, src1); \
} \
IRInstruction* inst1 = src1->getInstruction(); \
IRInstruction* inst2 = src2->getInstruction(); \
if (inst1->getOpcode() == Op##NAME && inst1->getSrc(1)->isConst()) { \
/* (X + C1) + C2 --> X + C3 */ \
if (src2->isConst()) { \
int64_t right = inst1->getSrc(1)->getValInt(); \
right OP##= src2->getValInt(); \
return m_tb->gen##NAME(inst1->getSrc(0), genDefInt(right)); \
} \
/* (X + C1) + (Y + C2) --> X + Y + C3 */ \
if (inst2->getOpcode() == Op##NAME && inst2->getSrc(1)->isConst()) { \
int64_t right = inst1->getSrc(1)->getValInt(); \
right OP##= inst2->getSrc(1)->getValInt(); \
SSATmp* left = m_tb->gen##NAME(inst1->getSrc(0), inst2->getSrc(0)); \
return m_tb->gen##NAME(left, genDefInt(right)); \
} \
} \
#define SIMPLIFY_COMMUTATIVE(OP, NAME) do { \
SIMPLIFY_CONST(OP); \
if (src1->isConst() && !src2->isConst()) { \
return m_tb->gen##NAME(src2, src1); \
} \
if (src1->isA(Type::Int) && src2->isA(Type::Int)) { \
IRInstruction* inst1 = src1->getInstruction(); \
IRInstruction* inst2 = src2->getInstruction(); \
if (inst1->getOpcode() == Op##NAME && inst1->getSrc(1)->isConst()) { \
/* (X + C1) + C2 --> X + C3 */ \
if (src2->isConst()) { \
int64_t right = inst1->getSrc(1)->getValInt(); \
right OP##= src2->getValInt(); \
return m_tb->gen##NAME(inst1->getSrc(0), genDefInt(right)); \
} \
/* (X + C1) + (Y + C2) --> X + Y + C3 */ \
if (inst2->getOpcode() == Op##NAME && inst2->getSrc(1)->isConst()) { \
int64_t right = inst1->getSrc(1)->getValInt(); \
right OP##= inst2->getSrc(1)->getValInt(); \
SSATmp* left = m_tb->gen##NAME(inst1->getSrc(0), inst2->getSrc(0)); \
return m_tb->gen##NAME(left, genDefInt(right)); \
} \
} \
} \
} while (0)
#define SIMPLIFY_DISTRIBUTIVE(OUTOP, INOP, OUTNAME, INNAME) do { \
@@ -766,6 +768,10 @@ SSATmp* Simplifier::simplifyCmp(Opcode opName, SSATmp* src1, SSATmp* src2) {
if (src1->getType() == Type::Obj && src2->getType() == Type::Obj) {
return nullptr;
}
// for arrays, don't simplify Same to Eq
if (src1->getType() == Type::Arr && src2->getType() == Type::Arr) {
return nullptr;
}
// Type is neither a string nor an object - simplify to OpEq/OpNeq
if (opName == OpSame) {
return m_tb->genCmp(OpEq, src1, src2);
@@ -120,8 +120,16 @@ void VectorEffects::init(Opcode op, const Type origBase,
// definitely happen but those cases aren't handled yet. In a perfect world
// we would remove Type::Null from baseType here but that can produce types
// that are tricky to guard against and doesn't buy us much right now.
baseType = baseType.isNull() ? newBase : (baseType | newBase);
baseValChanged = true;
if (!baseBoxed || !baseType.isString()) {
/*
* Uses of boxed types are always guarded, in case the inner
* type was modified. If the base type was String, its extremely
* likely to still be a String, so leave it as such, and we'll
* exit in the rare case that it changed.
*/
baseType = baseType.isNull() ? newBase : (baseType | newBase);
}
baseValChanged = !baseBoxed;
}
if (op == SetElem && baseType.maybe(Type::Arr)) {
// possible COW when modifying an array
@@ -158,6 +166,12 @@ void VectorEffects::init(Opcode op, const Type origBase,
valType = definitelyFail ? Type::InitNull : (valType | Type::InitNull);
}
if (op == SetElem && baseType.maybe(Type::Str)) {
// If the base is a String, the result will be different from the value
// use valTypeChanged (even though the type may not have)
valTypeChanged = true;
}
// The final baseType should be a pointer/box iff the input was
baseType = baseBoxed ? baseType.box() : baseType;
baseType = basePtr ? baseType.ptr() : baseType;
@@ -267,6 +281,14 @@ void HhbcTranslator::VectorTranslator::checkMIState() {
if (simpleProp || simpleArraySet || simpleArrayGet) {
setNoMIState();
}
/*
* In pretty much any case the vector translator will do operations
* that can throw. Currently this means we have to have a spillStack
* so the unwinder can handle it (eventually we'll hook this into an
* unwind codepath). TODO(#2162354)
*/
m_ht.spillStack();
}
void HhbcTranslator::VectorTranslator::emitMPre() {
@@ -341,7 +363,7 @@ SSATmp* HhbcTranslator::VectorTranslator::getInput(unsigned i) {
// as what Transl::Translator came up with.
auto t = Type::fromRuntimeType(dl.rtt);
if (!val->isA(t)) {
FTRACE(0, "{}: hhir stack has a {} where Translator had a {}\n",
FTRACE(1, "{}: hhir stack has a {} where Translator had a {}\n",
__func__, val->getType().toString(), t.toString());
// They'd better not be completely unrelated types...
assert(t.subtypeOf(val->getType()));
@@ -455,8 +477,7 @@ static inline TypedValue* baseGImpl(TypedValue *key,
varEnv->set(name, &tv);
base = varEnv->lookup(name);
} else {
tvWriteNull(&mis->tvScratch);
base = &mis->tvScratch;
return const_cast<TypedValue*>(init_null_variant.asTypedValue());
}
}
decRefStr(name);
+2 -2
Ver Arquivo
@@ -14,8 +14,8 @@
+----------------------------------------------------------------------+
*/
#ifndef _TRANSLATOR_DEPS_H_
#define _TRANSLATOR_DEPS_H
#ifndef incl_HPHP_TRANSLATOR_DEPS_H
#define incl_HPHP_TRANSLATOR_DEPS_H
#include <vector>
#include <tbb/concurrent_hash_map.h>
@@ -86,9 +86,18 @@ void TranslatorX64::reqLitHelper(const ReqLitStaticArgs* args) {
*/
static TCA callAndResume(ActRec *ar) {
VMRegAnchor _(ar, true);
g_vmContext->doFCall<true>(ar, g_vmContext->m_pc);
return Translator::Get()->getResumeHelperRet();
if (!ar->m_func->isClonedClosure()) {
/*
* If the func is a cloned closure, then the original
* closure has already run the prolog, and the prologs
* array is just being used as entry points for the
* dv funclets. Dont run the prolog again.
*/
VMRegAnchor _(ar, true);
g_vmContext->doFCall<true>(ar, g_vmContext->m_pc);
return Translator::Get()->getResumeHelperRet();
}
return Translator::Get()->getResumeHelper();
}
static_assert(rStashedAR == reg::r15,
@@ -99,14 +108,22 @@ asm(
".globl __fcallHelperThunk\n"
"__fcallHelperThunk:\n"
#if defined(__x86_64__)
// fcallHelper is used for prologs, and (in the case of
// closures) for dispatch to the function body. In the first
// case, there's a call, in the second, there's a jmp.
// We can differentiate by comparing r15 and rVmFp
"mov %r15, %rdi\n"
"cmp %r15, %rbp\n"
"jne 1f\n"
"call fcallHelper\n"
"jmp *%rax\n"
// fcallHelper may call doFCall. doFCall changes the return ip
// pointed to by r15 so that it points to TranslatorX64::m_retHelper,
// which does a REQ_POST_INTERP_RET service request. So we need to
// to pop the return address into r15 + m_savedRip before calling
// fcallHelper, and then push it back from r15 + m_savedRip after
// fcallHelper returns in case it has changed it.
"pop 0x8(%r15)\n"
"mov %r15, %rdi\n"
"1: pop 0x8(%r15)\n"
"call fcallHelper\n"
"push 0x8(%r15)\n"
"jmp *%rax\n"
@@ -122,7 +139,7 @@ extern "C"
TCA fcallHelper(ActRec* ar) {
try {
TCA tca =
Translator::Get()->funcPrologue((Func*)ar->m_func, ar->numArgs());
Translator::Get()->funcPrologue((Func*)ar->m_func, ar->numArgs(), ar);
if (tca) {
return tca;
}
+62 -12
Ver Arquivo
@@ -32,7 +32,11 @@
typedef __sighandler_t *sighandler_t;
# define RIP_REGISTER(v) (v).mc_rip
#else
# define RIP_REGISTER(v) (v).gregs[REG_RIP]
# if defined(__x86_64__)
# define RIP_REGISTER(v) (v).gregs[REG_RIP]
# elif defined(__AARCH64EL__)
# define RIP_REGISTER(v) (v).pc
# endif
#endif
#include <boost/bind.hpp>
@@ -2057,8 +2061,16 @@ TranslatorX64::emitPopRetIntoActRec(Asm& a) {
a. pop (rStashedAR[AROFF(m_savedRip)]);
}
static void interp_set_regs(ActRec* ar, Cell* sp, Offset pcOff) {
assert(tl_regState == REGSTATE_DIRTY);
tl_regState = REGSTATE_CLEAN;
vmfp() = (Cell*)ar;
vmsp() = sp;
vmpc() = curUnit()->at(pcOff);
}
TCA
TranslatorX64::funcPrologue(Func* func, int nPassed) {
TranslatorX64::funcPrologue(Func* func, int nPassed, ActRec* ar) {
func->validate();
TRACE(1, "funcPrologue %s(%d)\n", func->fullName()->data(), nPassed);
int numParams = func->numParams();
@@ -2069,6 +2081,27 @@ TranslatorX64::funcPrologue(Func* func, int nPassed) {
// Do a quick test before grabbing the write lease
TCA prologue;
if (checkCachedPrologue(func, paramIndex, prologue)) return prologue;
if (func->isClonedClosure()) {
assert(ar);
const Func::ParamInfoVec& paramInfo = func->params();
Offset entry = func->base();
for (int i = nPassed; i < numParams; ++i) {
const Func::ParamInfo& pi = paramInfo[i];
if (pi.hasDefaultValue()) {
entry = pi.funcletOff();
break;
}
}
interp_set_regs(ar, (Cell*)ar - func->numSlotsInFrame(), entry);
SrcKey funcBody(func, entry);
TCA tca = getTranslation(funcBody, false);
tl_regState = REGSTATE_DIRTY;
if (tca) {
// racy, but ok...
func->setPrologue(paramIndex, tca);
}
return tca;
}
// If the translator is getting replaced out from under us, refuse to
// provide a prologue; we don't know whether this request is running on the
@@ -2078,6 +2111,7 @@ TranslatorX64::funcPrologue(Func* func, int nPassed) {
// Double check the prologue array now that we have the write lease
// in case another thread snuck in and set the prologue already.
if (checkCachedPrologue(func, paramIndex, prologue)) return prologue;
SpaceRecorder sr("_FuncPrologue", a);
// If we're close to a cache line boundary, just burn some space to
// try to keep the func and its body on fewer total lines.
@@ -2365,7 +2399,15 @@ TranslatorX64::emitPrologue(Func* func, int nPassed) {
// code
emitCheckSurpriseFlagsEnter(false, fixup);
emitBindJmp(funcBody);
if (func->isClosureBody() && func->cls()) {
int entry = nPassed <= numParams ? nPassed : numParams + 1;
// Relying on rStashedAR == rVmFp here
a. loadq (rStashedAR[AROFF(m_func)], rax);
a. loadq (rax[Func::prologueTableOff() + sizeof(TCA)*entry], rax);
a. jmp (rax);
} else {
emitBindJmp(funcBody);
}
return funcBody;
}
@@ -3059,6 +3101,22 @@ struct DepthGuard { bool depthOne() const { return false; } };
#endif
/*
* enterTCHelper does not save callee-saved registers except %rbp. This means
* when we call it from C++, we have to tell gcc to clobber all the other
* callee-saved registers.
*/
#if defined(__x86_64__)
# define CALLEE_SAVED_BARRIER() \
asm volatile("" : : : "rbx", "r12", "r13", "r14", "r15")
#elif defined(__AARCH64EL__)
# define CALLEE_SAVED_BARRIER() \
asm volatile("" : : : "x19", "x20", "x21", "x22", "x23", "x24", "x25", \
"x26", "x27", "x28")
#else
# error What are the callee-saved registers on your system?
#endif
/*
* enterTCHelper
*
@@ -3959,14 +4017,6 @@ TranslatorX64::binaryArithLocal(const NormalizedInstruction &i,
}
}
static void interp_set_regs(ActRec* ar, Cell* sp, Offset pcOff) {
assert(tl_regState == REGSTATE_DIRTY);
tl_regState = REGSTATE_CLEAN;
vmfp() = (Cell*)ar;
vmsp() = sp;
vmpc() = curUnit()->at(pcOff);
}
#define O(opcode, imm, pusph, pop, flags) \
/**
* The interpOne methods saves m_pc, m_fp, and m_sp ExecutionContext,
@@ -9067,7 +9117,7 @@ TranslatorX64::translateFPushObjMethodD(const Tracelet &t,
}
if (func) {
if (func->attrs() & AttrStatic) {
if (func->attrs() & AttrStatic && !func->isClosureBody()) {
if (func->attrs() & AttrPrivate) {
emitVStackStoreImm(a, i, uintptr_t(curFunc()->cls()) | 1,
thisOff, sz::qword);
+1 -1
Ver Arquivo
@@ -940,7 +940,7 @@ private:
static int shuffleArgsForMagicCall(ActRec* ar);
static void setArgInActRec(ActRec* ar, int argNum, uint64_t datum,
DataType t);
TCA funcPrologue(Func* func, int nArgs);
TCA funcPrologue(Func* func, int nArgs, ActRec* ar = nullptr);
bool checkCachedPrologue(const Func* func, int param, TCA& plgOut) const;
SrcKey emitPrologue(Func* func, int nArgs);
int32_t emitNativeImpl(const Func*, bool emitSavedRIPReturn);
+1 -1
Ver Arquivo
@@ -888,7 +888,7 @@ public:
virtual void requestInit() = 0;
virtual void requestExit() = 0;
virtual void analyzeInstr(Tracelet& t, NormalizedInstruction& i) = 0;
virtual TCA funcPrologue(Func* f, int nArgs) = 0;
virtual TCA funcPrologue(Func* f, int nArgs, ActRec* ar = nullptr) = 0;
virtual TCA getCallToExit() = 0;
virtual TCA getRetFromInterpretedFrame() = 0;
virtual void defineCns(StringData* name) = 0;
+1 -1
Ver Arquivo
@@ -15,7 +15,7 @@
"is_a", T(Boolean), S(0), "class_or_object", T(Variant), NULL, S(0), NULL, S(0), "class_name", T(String), NULL, S(0), NULL, S(0), "allow_string", T(Boolean), "b:0;", S(4), "false", S(0), NULL, S(16384), "/**\n * ( excerpt from http://php.net/manual/en/function.is-a.php )\n *\n * Checks if the given object is of this class or has this class as one of\n * its parents.\n *\n * @class_or_object\n * mixed The tested object\n * @class_name string The class name\n * @allow_string\n * bool If this parameter set to false (default), string\n * class name as object is not allowed. This also\n * prevents from calling autoloader if the class\n * doesn't exist.\n *\n * @return bool Returns TRUE if the object is of this class or has\n * this class as one of its parents, FALSE otherwise.\n */",
"is_subclass_of", T(Boolean), S(0), "class_or_object", T(Variant), NULL, S(0), NULL, S(0), "class_name", T(String), NULL, S(0), NULL, S(0), "allow_string", T(Boolean), "b:1;", S(4), "true", S(0), NULL, S(16384), "/**\n * ( excerpt from http://php.net/manual/en/function.is-subclass-of.php )\n *\n * Checks if the given object has the class class_name as one of its\n * parents.\n *\n * @class_or_object\n * mixed A class name or an object instance\n * @class_name string The class name\n * @allow_string\n * bool If this parameter set to false, string class name as\n * object is not allowed. This also prevents from\n * calling autoloader if the class doesn't exist.\n *\n * @return bool This function returns TRUE if the object object,\n * belongs to a class which is a subclass of\n * class_name, FALSE otherwise.\n */",
"method_exists", T(Boolean), S(0), "class_or_object", T(Variant), NULL, S(0), NULL, S(0), "method_name", T(String), NULL, S(0), NULL, S(0), NULL, S(16384), "/**\n * ( excerpt from http://php.net/manual/en/function.method-exists.php )\n *\n * Checks if the class method exists in the given object.\n *\n * @class_or_object\n * mixed An object instance or a class name\n * @method_name\n * string The method name\n *\n * @return bool Returns TRUE if the method given by method_name has\n * been defined for the given object, FALSE otherwise.\n */",
"property_exists", T(Boolean), S(0), "class_or_object", T(Variant), NULL, S(0), NULL, S(0), "property", T(String), NULL, S(0), NULL, S(0), NULL, S(16384), "/**\n * ( excerpt from http://php.net/manual/en/function.property-exists.php )\n *\n * This function checks if the given property exists in the specified\n * class.\n *\n * As opposed with isset(), property_exists() returns TRUE even if the\n * property has the value NULL.\n *\n * @class_or_object\n * mixed The class name or an object of the class to test for\n * @property string The name of the property\n *\n * @return bool Returns TRUE if the property exists, FALSE if it\n * doesn't exist or NULL in case of an error.\n */",
"property_exists", T(Variant), S(0), "class_or_object", T(Variant), NULL, S(0), NULL, S(0), "property", T(String), NULL, S(0), NULL, S(0), NULL, S(16384), "/**\n * ( excerpt from http://php.net/manual/en/function.property-exists.php )\n *\n * This function checks if the given property exists in the specified\n * class.\n *\n * As opposed with isset(), property_exists() returns TRUE even if the\n * property has the value NULL.\n *\n * @class_or_object\n * mixed The class name or an object of the class to test for\n * @property string The name of the property\n *\n * @return mixed Returns TRUE if the property exists, FALSE if it\n * doesn't exist or NULL in case of an error.\n */",
"get_object_vars", T(Variant), S(0), "object", T(Variant), NULL, S(0), NULL, S(0), NULL, S(16384), "/**\n * ( excerpt from http://php.net/manual/en/function.get-object-vars.php )\n *\n * Gets the accessible non-static properties of the given object according\n * to scope.\n *\n * @object mixed An object instance.\n *\n * @return mixed Returns an associative array of defined object\n * accessible non-static properties for the specified\n * object in scope. If a property have not been\n * assigned a value, it will be returned with a NULL\n * value.\n */",
"call_user_method_array", T(Variant), S(0), "method_name", T(String), NULL, S(0), NULL, S(0), "obj", T(Variant), NULL, S(0), NULL, S(1), "paramarr", T(Array), NULL, S(0), NULL, S(0), NULL, S(16384), "/**\n * ( excerpt from\n * http://php.net/manual/en/function.call-user-method-array.php )\n *\n *\n * @method_name\n * string The method name being called.\n * @obj mixed The object that method_name is being called on.\n * @paramarr vector An array of parameters.\n *\n * @return mixed\n */",
"call_user_method", T(Variant), S(0), "method_name", T(String), NULL, S(0), NULL, S(0), "obj", T(Variant), NULL, S(0), NULL, S(1), NULL, S(147456), "/**\n * ( excerpt from http://php.net/manual/en/function.call-user-method.php )\n *\n *\n * @method_name\n * string The method name being called.\n * @obj mixed The object that method_name is being called on.\n *\n * @return mixed\n */",
+4 -4
Ver Arquivo
@@ -457,7 +457,7 @@ const int64_t k_GRAPHEME_EXTR_MAXBYTES = 1;
const int64_t k_GRAPHEME_EXTR_MAXCHARS = 2;
const int64_t k_HASH_HMAC = 1;
extern const StaticString k_HPHP_TRIM_CHARLIST("\n\r\t\v\000 ",6);
extern const StaticString k_HPHP_VERSION("1.0.0",5);
extern const StaticString k_HPHP_VERSION("2.0.2",5);
const int64_t k_HTML_ENTITIES = 1;
const int64_t k_HTML_SPECIALCHARS = 0;
extern const StaticString k_ICONV_IMPL("glibc",5);
@@ -12884,8 +12884,8 @@ const char *g_class_map[] = {
NULL,
NULL,
(const char *)0x10006040, "property_exists", "", (const char*)0, (const char*)0,
"/**\n * ( excerpt from http://php.net/manual/en/function.property-exists.php )\n *\n * This function checks if the given property exists in the specified\n * class.\n *\n * As opposed with isset(), property_exists() returns TRUE even if the\n * property has the value NULL.\n *\n * @class_or_object\n * mixed The class name or an object of the class to test for\n * @property string The name of the property\n *\n * @return bool Returns TRUE if the property exists, FALSE if it\n * doesn't exist or NULL in case of an error.\n */",
(const char *)0x9, (const char *)0x2000, "class_or_object", "", (const char *)0xffffffff, "", "", NULL,
"/**\n * ( excerpt from http://php.net/manual/en/function.property-exists.php )\n *\n * This function checks if the given property exists in the specified\n * class.\n *\n * As opposed with isset(), property_exists() returns TRUE even if the\n * property has the value NULL.\n *\n * @class_or_object\n * mixed The class name or an object of the class to test for\n * @property string The name of the property\n *\n * @return mixed Returns TRUE if the property exists, FALSE if it\n * doesn't exist or NULL in case of an error.\n */",
(const char *)0xffffffff, (const char *)0x2000, "class_or_object", "", (const char *)0xffffffff, "", "", NULL,
(const char *)0x2000, "property", "", (const char *)0x14, "", "", NULL,
NULL,
NULL,
@@ -17963,7 +17963,7 @@ const char *g_class_map[] = {
"GRAPHEME_EXTR_MAXCHARS", (const char*)4, "i:2;",
"HASH_HMAC", (const char*)4, "i:1;",
"HPHP_TRIM_CHARLIST", (const char*)13, "s:6:\"\n\r\t\v\000 \";",
"HPHP_VERSION", (const char*)12, "s:5:\"1.0.0\";",
"HPHP_VERSION", (const char*)12, "s:5:\"2.0.2\";",
"HTML_ENTITIES", (const char*)4, "i:1;",
"HTML_SPECIALCHARS", (const char*)4, "i:0;",
"ICONV_IMPL", (const char*)12, "s:5:\"glibc\";",
+1 -1
Ver Arquivo
@@ -445,7 +445,7 @@ define('GRAPHEME_EXTR_MAXBYTES', 1);
define('GRAPHEME_EXTR_MAXCHARS', 2);
define('HASH_HMAC', 1);
define('HPHP_TRIM_CHARLIST', "\n\r\t\v\000 ");
define('HPHP_VERSION', "1.0.0");
define('HPHP_VERSION', "2.0.2");
define('HTML_ENTITIES', 1);
define('HTML_SPECIALCHARS', 0);
define('ICONV_IMPL', "glibc");
+1 -1
Ver Arquivo
@@ -2670,7 +2670,7 @@ DefineConstant(
DefineConstant(
array(
'name' => "HPHP_VERSION",
'value' => "1.0.0",
'value' => "2.0.2",
));
DefineConstant(
+21
Ver Arquivo
@@ -0,0 +1,21 @@
<?php
class X {
function __construct() {
}
}
function handler($kind, $name) {
if ($kind == 'exit' && $name == 'X::__construct') throw new Exception;
}
function test() {
fb_setprofile('handler');
try {
new X;
} catch (Exception $e) {
echo "ok\n";
}
}
test();
@@ -0,0 +1 @@
ok
+265
Ver Arquivo
@@ -9659,6 +9659,236 @@ bool TestCodeRun::TestCollectionClasses() {
"bool(false)\n"
);
{
HipHopSyntax w1(this);
MVCRO("<?php\n"
"function f() {\n"
" $v = Vector {'a', 'b', 'c'};\n"
" $m = new Map($v);\n"
" var_dump($m);\n"
" $sm = new StableMap($m);\n"
" var_dump($sm);\n"
"}\n"
"function g() {\n"
" $m = Map {'a' => 1, 2 => 'b'};\n"
" $v = new Vector($m);\n"
" var_dump($v);\n"
"}\n"
"function h() {\n"
" $arr1 = array(11, 22, 33);\n"
" var_dump(new Vector($arr1));\n"
" var_dump(new Map($arr1));\n"
" $arr2 = array('a' => 1, 2 => 'b');\n"
" var_dump(new Vector($arr2));\n"
" var_dump(new StableMap($arr2));\n"
"}\n"
"function gen() {\n"
" yield 42;\n"
" yield 72;\n"
"}\n"
"function j() {\n"
" $v = new Vector(gen());\n"
" var_dump($v);\n"
"}\n"
"f();\n"
"g();\n"
"h();\n"
"j();\n"
,
"object(Map)#2 (3) {\n"
" [0]=>\n"
" string(1) \"a\"\n"
" [1]=>\n"
" string(1) \"b\"\n"
" [2]=>\n"
" string(1) \"c\"\n"
"}\n"
"object(StableMap)#3 (3) {\n"
" [0]=>\n"
" string(1) \"a\"\n"
" [1]=>\n"
" string(1) \"b\"\n"
" [2]=>\n"
" string(1) \"c\"\n"
"}\n"
"object(Vector)#2 (2) {\n"
" [0]=>\n"
" int(1)\n"
" [1]=>\n"
" string(1) \"b\"\n"
"}\n"
"object(Vector)#1 (3) {\n"
" [0]=>\n"
" int(11)\n"
" [1]=>\n"
" int(22)\n"
" [2]=>\n"
" int(33)\n"
"}\n"
"object(Map)#1 (3) {\n"
" [0]=>\n"
" int(11)\n"
" [1]=>\n"
" int(22)\n"
" [2]=>\n"
" int(33)\n"
"}\n"
"object(Vector)#1 (2) {\n"
" [0]=>\n"
" int(1)\n"
" [1]=>\n"
" string(1) \"b\"\n"
"}\n"
"object(StableMap)#1 (2) {\n"
" [\"a\"]=>\n"
" int(1)\n"
" [2]=>\n"
" string(1) \"b\"\n"
"}\n"
"object(Vector)#1 (2) {\n"
" [0]=>\n"
" int(42)\n"
" [1]=>\n"
" int(72)\n"
"}\n"
);
MVCRO("<?hh\n"
"function g((string,int,string) $t) {}\n"
"class C {\n"
" public $t = Tuple {'foo', 42, '!'};\n"
"}\n"
"function f() {\n"
" $t = Tuple {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,\n"
" 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\n"
" 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};\n"
" var_dump(count($t));\n"
" var_dump($t->count());\n"
" var_dump($t->isEmpty());\n"
" echo \"==========\\n\";\n"
" $c = new C;\n"
" $t = $c->t;\n"
" var_dump(count($t));\n"
" var_dump($t->count());\n"
" var_dump($t->isEmpty());\n"
" g($t);\n"
" echo \"==========\\n\";\n"
" foreach ($t as $k => $v) {\n"
" var_dump($k, $v);\n"
" }\n"
" echo \"==========\\n\";\n"
" var_dump($t[0], $t[1], $t[2]);\n"
" echo \"==========\\n\";\n"
" var_dump($t->at(0), $t->at(1), $t->at(2));\n"
" echo \"==========\\n\";\n"
" var_dump($t->get(0), $t->get(1), $t->get(2), $t->get(3));\n"
" echo \"==========\\n\";\n"
" var_dump((array)$t);\n"
" echo \"==========\\n\";\n"
" var_dump(serialize($t));\n"
" var_dump(unserialize(serialize($t)));\n"
" echo \"==========\\n\";\n"
" var_dump($t->count());\n"
" echo \"==========\\n\";\n"
" var_dump($t->getIterator() instanceof Iterator);\n"
" var_dump($t->getIterator() instanceof KeyedIterator);\n"
" foreach ($t->getIterator() as $k => $v) {\n"
" var_dump($k, $v);\n"
" }\n"
" echo \"==========\\n\";\n"
" var_dump((array)$t);\n"
" var_dump($t->toArray());\n"
" echo \"==========\\n\";\n"
" var_dump(clone $t);\n"
"}\n"
"f();\n"
,
"int(32)\n"
"int(32)\n"
"bool(false)\n"
"==========\n"
"int(3)\n"
"int(3)\n"
"bool(false)\n"
"==========\n"
"int(0)\n"
"string(3) \"foo\"\n"
"int(1)\n"
"int(42)\n"
"int(2)\n"
"string(1) \"!\"\n"
"==========\n"
"string(3) \"foo\"\n"
"int(42)\n"
"string(1) \"!\"\n"
"==========\n"
"string(3) \"foo\"\n"
"int(42)\n"
"string(1) \"!\"\n"
"==========\n"
"string(3) \"foo\"\n"
"int(42)\n"
"string(1) \"!\"\n"
"NULL\n"
"==========\n"
"array(3) {\n"
" [0]=>\n"
" string(3) \"foo\"\n"
" [1]=>\n"
" int(42)\n"
" [2]=>\n"
" string(1) \"!\"\n"
"}\n"
"==========\n"
"string(39) \"V:5:\"Tuple\":3:{s:3:\"foo\";i:42;s:1:\"!\";}\"\n"
"object(Tuple)#6 (3) {\n"
" [0]=>\n"
" string(3) \"foo\"\n"
" [1]=>\n"
" int(42)\n"
" [2]=>\n"
" string(1) \"!\"\n"
"}\n"
"==========\n"
"int(3)\n"
"==========\n"
"bool(true)\n"
"bool(true)\n"
"int(0)\n"
"string(3) \"foo\"\n"
"int(1)\n"
"int(42)\n"
"int(2)\n"
"string(1) \"!\"\n"
"==========\n"
"array(3) {\n"
" [0]=>\n"
" string(3) \"foo\"\n"
" [1]=>\n"
" int(42)\n"
" [2]=>\n"
" string(1) \"!\"\n"
"}\n"
"array(3) {\n"
" [0]=>\n"
" string(3) \"foo\"\n"
" [1]=>\n"
" int(42)\n"
" [2]=>\n"
" string(1) \"!\"\n"
"}\n"
"==========\n"
"object(Tuple)#6 (3) {\n"
" [0]=>\n"
" string(3) \"foo\"\n"
" [1]=>\n"
" int(42)\n"
" [2]=>\n"
" string(1) \"!\"\n"
"}\n"
);
}
return true;
}
@@ -18832,6 +19062,31 @@ bool TestCodeRun::TestClassConstant() {
" var_dump($param->getDefaultValue());"
"}");
MVCR("<?php "
"class W {"
" const FOO = 0;"
"}"
"class X extends W {"
" const FOO = 1;"
" static function foo() {"
" var_dump(constant('self::FOO'));"
" var_dump(constant('parent::FOO'));"
" var_dump(constant('static::FOO'));"
" var_dump(defined('self::FOO'));"
" var_dump(defined('parent::FOO'));"
" var_dump(defined('static::FOO'));"
" var_dump(defined('self::BAR'));"
" var_dump(defined('parent::BAR'));"
" var_dump(defined('static::BAR'));"
" }"
"}"
"class Y extends X {"
" const FOO = 2;"
" const BAR = 1;"
"}"
"X::foo();"
"Y::foo();");
return true;
}
@@ -21190,6 +21445,16 @@ bool TestCodeRun::TestSwitchStatement() {
"}\n"
"f(32);\n");
MVCR("<?php "
"class X {}"
"function test($x) {"
" switch (true) {"
" case $x instanceof X: var_dump('X'); break;"
" default: var_dump('Other'); break;"
" }"
"}"
"test(new X);");
return true;
}
+4
Ver Arquivo
@@ -125,5 +125,9 @@ bool TestExtBzip2::test_bzdecompress() {
ret = f_bzdecompress(ret);
ret = f_bzdecompress(ret);
VS(ret, str);
str = StringUtil::Repeat("x", 1000);
ret = f_bzcompress(str);
ret = f_bzdecompress(ret);
VS(ret, str);
return Count(true);
}
+49 -1
Ver Arquivo
@@ -16,6 +16,7 @@
#include <test/test_ext_misc.h>
#include <runtime/ext/ext_misc.h>
#include <runtime/ext/ext_string.h>
///////////////////////////////////////////////////////////////////////////////
@@ -203,8 +204,55 @@ bool TestExtMisc::test_uniqid() {
return Count(true);
}
#define VUNPACK(fmt, inp, exp) \
{ Array __a = f_unpack(fmt, inp); \
VS(__a.exists(1), true); \
VS(__a[1], (int64_t)exp); }
bool TestExtMisc::test_unpack() {
// covered in TestCodeRun::TestExtMisc
// Also covered in TestCodeRun::TestExtMisc
String iFF = f_str_repeat("\xFF", sizeof(int));
String le32_FF("\xFF\x00\x00\x00", 4, AttachLiteral);
String be32_FF("\x00\x00\x00\xFF", 4, AttachLiteral);
String le16_FF("\xFF\x00", 2, AttachLiteral);
String be16_FF("\x00\xFF", 2, AttachLiteral);
uint32_t endian_check = 1;
bool le = ((char*)&endian_check)[0];
// HPHP, unlike PHP, truncates overflowing ints
if (sizeof(int) == 8) {
VUNPACK("I", iFF, 0x7FFFFFFFFFFFFFFF);
} else if (sizeof(int) == 4) {
VUNPACK("I", iFF, 0xFFFFFFFF);
} else {
// Panic
VS(true, false);
}
VUNPACK("i", iFF, -1);
// LlNV test 32-bit ints specifically
VUNPACK("L", iFF, 0xFFFFFFFF);
VUNPACK("l", iFF, -1);
VUNPACK("N", be32_FF, 0xFF);
VUNPACK("V", le32_FF, 0xFF);
VUNPACK("V", be32_FF, 0xFF000000);
VUNPACK("L", le ? le32_FF : be32_FF, 0xFF);
// Ssnv test 16-bit shorts
VUNPACK("S", iFF, 0xFFFF);
VUNPACK("s", iFF, -1);
VUNPACK("n", be16_FF, 0xFF);
VUNPACK("v", le16_FF, 0xFF);
VUNPACK("v", be16_FF, 0xFF00);
VUNPACK("S", le ? le16_FF : be16_FF, 0xFF);
return Count(true);
}
+11 -1
Ver Arquivo
@@ -104,6 +104,10 @@ bool TestExtPosix::test_posix_getgrgid() {
Variant ret = f_posix_getgrgid(f_posix_getgid());
VERIFY(!same(ret, false));
VERIFY(!ret.toArray().empty());
Variant bynam = f_posix_getgrnam(ret["name"]);
VS(ret, bynam);
return Count(true);
}
@@ -111,6 +115,10 @@ bool TestExtPosix::test_posix_getgrnam() {
Variant ret = f_posix_getgrnam("root");
VERIFY(!same(ret, false));
VERIFY(!ret.toArray().empty());
Variant bygid = f_posix_getgrgid(ret["gid"]);
VS(ret, bygid);
return Count(true);
}
@@ -150,6 +158,7 @@ bool TestExtPosix::test_posix_getpwnam() {
Variant ret = f_posix_getpwnam("root");
VERIFY(!same(ret, false));
VERIFY(!ret.toArray().empty());
VS(f_posix_getpwnam(""), false);
return Count(true);
}
@@ -157,6 +166,7 @@ bool TestExtPosix::test_posix_getpwuid() {
Variant ret = f_posix_getpwuid(0);
VERIFY(!same(ret, false));
VERIFY(!ret.toArray().empty());
VS(f_posix_getpwuid(-1), false);
return Count(true);
}
@@ -247,7 +257,7 @@ bool TestExtPosix::test_posix_times() {
}
bool TestExtPosix::test_posix_ttyname() {
f_posix_ttyname(1);
// Jenkins doesn't have real ttys to test this with
return Count(true);
}
+33
Ver Arquivo
@@ -0,0 +1,33 @@
<?php
trait T {
function f() {
return function ($a) {
if ($a) {
return $this->foo;
}
};
}
}
class X {
private $foo = 1;
use T;
}
class Y {
private $foo = 2;
use T;
}
function test() {
$x = new X;
$c = $x->f();
var_dump($c(true));
$y = new Y;
$c = $y->f();
var_dump($c("foo"));
}
test();
+2
Ver Arquivo
@@ -0,0 +1,2 @@
int(1)
int(2)
+40
Ver Arquivo
@@ -0,0 +1,40 @@
<?php
class A {
private $c = 1;
function b() {
$a = function () {
var_dump($this);
};
$a();
$a = static function () {
var_dump($this);
};
$a();
}
static function c() {
$a = function () {
var_dump($this);
};
$a();
$a = static function () {
var_dump($this);
};
$a();
}
static function d() {
var_dump(array_map(function($a) { return $a; }, array(1,2,3)));
}
}
(new A)->b();
A::b();
(new A)->c();
A::c();
(new A)->d();
A::d();
+34
Ver Arquivo
@@ -0,0 +1,34 @@
object(A)#1 (1) {
["c":"A":private]=>
int(1)
}
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 13
NULL
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 8
NULL
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 13
NULL
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 20
NULL
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 25
NULL
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 20
NULL
HipHop Notice: Undefined variable: this in hphp/test/vm/closure_static.php on line 25
NULL
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
+49
Ver Arquivo
@@ -0,0 +1,49 @@
<?php
/*
* This is a test that checks that CGetM in the IR properly cleans the
* stack before operations that can throw.
*/
class Dtor {
public function __construct() { echo "hi\n"; }
public function __destruct() { echo "ah\n"; }
}
class Something {
public $wat;
public function __construct() {
$this->wat = new Dtor();
}
}
class Unsetter {
private $x;
public function __construct() {
unset($this->x);
}
public function useX($k) {
echo "sup\n";
$z = $k->wat + $this->x;
return $z;
}
}
function thrower() {
throw new Exception("wat");
}
function main() {
set_error_handler('thrower');
$k = new Unsetter;
$k->useX(new Something());
}
try {
main();
} catch (Exception $x) {
echo "out\n";
}
+4
Ver Arquivo
@@ -0,0 +1,4 @@
hi
sup
out
ah
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...6789012345678901
Total length: 65492
Pass
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...7890123456789012
Total length: 65493
Pass
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...8901234567890123
Total length: 65494
Pass
@@ -0,0 +1,13 @@
<?php
$total_size = 0;
$x = <<<"EOD"
\\EOD
{EOD
EOD
;
echo $x;
echo "\nTotal length: ";
echo strlen($x);
echo "\n";
@@ -0,0 +1,3 @@
\EOD
{EOD
Total length: 9
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...8901234567890123
Total length: 262144
Pass
+12
Ver Arquivo
@@ -0,0 +1,12 @@
<?php
$info = "hello";
echo <<<SCRIPT
<?php
$info
throw new Exception(<<<TXT
$info
Fix the above
TXT
);
SCRIPT;
@@ -0,0 +1,8 @@
<?php
hello
throw new Exception(<<<TXT
hello
Fix the above
TXT
);
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...6789012345678901
Total length: 65492
Pass
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...7890123456789012
Total length: 65493
Pass
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...8901234567890123
Total length: 65494
Pass
@@ -0,0 +1,14 @@
<?php
$total_size = 0;
$x = <<<'EOD'
\\EOD
{EOD
$EOD
EOD
;
echo $x;
echo "\nTotal length: ";
echo strlen($x);
echo "\n";
@@ -0,0 +1,4 @@
\\EOD
{EOD
$EOD
Total length: 15
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...8901234567890123
Total length: 262144
Pass

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais