347 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
Sara Golemon 6169f73d80 TCP ports should be unsigned 16 bit integers.
Summary:
Specifying Signed 16-bit Int in HDF parsing
prevents use of any port greater than 32767.

Test Plan: Run a server on port 40000

Reviewers: andrewparoski, jdelong, smith, kma, mwilliams

Reviewed By: jdelong

CC: hphp-diffs@lists, ps, ottoni, aalexandre

Differential Revision: https://phabricator.fb.com/D735202
2013-03-12 14:46:38 -07:00
hermanv 96350d1a66 add support for late bound constants to the defined function
The defined function did not have code to deal with "static::const" expressions. Added the necessary logic.
2013-03-12 14:22:14 -07:00
mwilliams f51b3c302b Remove some more dead code 2013-03-12 14:22:14 -07:00
bsimmers 6366959956 Implement VectorTranslator::emitSetNewElem and emit(Empty|Isset)Elem
Three more straightforward vector operations.
2013-03-12 14:22:14 -07:00
mwilliams a7a3c2e8cc Get rid of hphpc's switch helpers
hashForIntSwitch and hashForStringSwitch were used as part of
an efficient switch mechanism in hphpc, but are now dead.
2013-03-12 14:22:14 -07:00
andrewparoski 9df492da6a Update inconsistencies doc
Remove HPHPc-only inconsistencies from the inconsistencies doc. Also,
clarify the wording to make it clear we are comparing HipHop VM with the
Zend PHP engine.
2013-03-12 14:22:14 -07:00
ottoni 8b36e4b067 Enable branch fusion for [N]InstanceOfBitMask
This translates to a single 'testb' x86 instruction, so it's cheap
even if we end up redoing it.
2013-03-12 14:22:14 -07:00
bsimmers 518ae943da Enable VectorTranslator::emitRatchetRefs
The code was mostly already there, it was just waiting for
proper control flow support.
2013-03-12 14:22:14 -07:00
bsimmers f5b168643d Implement support for boxed bases in VectorTranslator
Nothing too fancy here. genLdLocAddr now takes an optional
exit trace to guard the inner type of the local.
2013-03-12 14:22:14 -07:00
bsimmers 4b075be2ee Add stronger assertions when killing IncRefs in dce
This diff changes DceFlags to be an opaque struct, and uses
the new decRefNZed flag to ensure that we don't kill any IncRefs
unless they're consumed by a DecRefNZ in at least one path. This
doesn't catch all cases of malformed IR, but it catches common
mistakes, including one in the vector translator that I just spent 2
days hunting down.

A couple things came out of this:
- dce now removes unreachable code before performing the real dead
  code analysis, since any unreachable IncRefs should be killed
  regardless of what consumes them.
- I found a bug in memelim where it wouldn't properly account for
  Uninit -> InitNull promotion when boxing a local. The fix for this
  was to never emit "Box Uninit".
2013-03-12 14:22:14 -07:00
bsimmers 51f986a0ae Implement VectorTranslator::emitElem
I'm going to pull my big vector translator diff apart into
more manageable chunks, both to keep things simpler and help me
debug. This is part 1.
2013-03-12 14:22:14 -07:00
jdelong e8d372f813 Fix gzinflate for strings larger than 128M
gzinflate tries to do some kind of exponential growth that
hits the max string limit immediately when you try to deflate a string
larger than 128 megs.  The logic is a little convoluted with a few
ways to bail, but rather than try to really fix it I just made it
behave close to the same on small strings, but use a smaller starting
factor on long ones and be willing to try to deflate it into a
max-sized string.
2013-03-12 14:22:13 -07:00
andrewparoski 6ddef58d56 Remove LVariableTable, RVariableTable, and getHphpBinaryType()
This diff removes LVariableTable, RVariableTable, and getHphpBinaryType().
It also consolidates some of the include logic that was spread across
multiple files.
2013-03-12 14:22:13 -07:00
smith 6434422e85 Encapsulate access to m_aux in TypedValueAux subclass.
Access to TypedValue.m_aux must now be via TypedValueAux.  For now,
TypedValueAux is an empty subclass with accessors to m_aux, which is
now private.  Once RefData.m_tv is moved out from under RefData._count,
we can move TypedValue.m_aux to TypedValueAux.

Removed unnecessary initialization of m_aux.u_hash from c_Vector.
2013-03-11 21:19:20 -07:00
Sara Golemon aa3124d64d Set/Unset DEBUG/NDEBUG as appropriate
For a debug build:
  cmake -DCMAKE_BUILD_TYPE=Debug .

For a release build:
  cmake -DCMAKE_BUILD_TYPE=Release .

If you never set CMAKE_BUILD_TYPE, the default will remain "Release"
2013-03-11 21:19:20 -07:00
smith b4085ff17b Fix missing zero-extend when passing bool as TypedValue
If the argument was in the expected register, we failed to do the
zero-extension.  This showed up as a heisenbug when experimenting
with different TypedValue layouts, but could be causing other
stability problems in HHIR as well.
2013-03-11 14:26:34 -07:00
ptarjan 1bcb5de881 assert that func's IDs are valid
I got bit by a really nasty bug where I cloned a ##Func## and didn't set the ID. Then the JIT happily used it and I got really weird segfaults.

Making the ##m_funcId## private turned out to be really hard (~30 callsites) so @andrewparoski said the ##translate()## and ##retranslate()## were the most important places. We just have to remember to do it. If there are other important places I'll just bite the bullet and make it private with an accessor.

This should save days of many future engineers.
2013-03-11 14:26:33 -07:00
smith b5bebbb8b7 Remove ENABLE_HPHP_ARRAY flag (HphpArray is always enabled) 2013-03-11 14:26:33 -07:00
smith 20ac715ec2 Remove dead MutableArrayIter code plus a few cleanups 2013-03-10 13:54:36 -07:00
Sara Golemon a95a84390e Update IDL generators for int64 and extprofiles changes 2013-03-10 13:12:43 -07:00
Sara Golemon e0d098ef70 Add http_response_code(), behavior matching Zend 2013-03-09 20:23:28 -08:00
Daniel Sloof e3c7403906 Support $classname parameter to simplexml_load_string/file
Argument was being ignored.  Allow specifying
custom children of SimpleXMLElement.
2013-03-09 16:41:07 -08:00
bsimmers dd83c93a8a Remove DecRef's source from the cse table
DecRef needs to be the last use of its source, since it might
destruct it. In the new test case, the second cast expression was
reusing the result of the first (through CSE) after it had been
DecReffed and destroyed.
2013-03-09 15:07:50 -08:00
mwilliams 9c470aa8ed Convert remaining instanceof(litStr) to instanceof(litCls) 2013-03-09 15:07:50 -08:00
smith 1569061e8c Rename TypedValue._count to m_aux; Variant extends TypedValue.
This diff removes initializing stores to TypedValue._count, renames
_count to m_aux, and makes m_aux a union with members typed
and named according to their specialized uses.   The few remaining
uses of that field for random tweaks are more obvious and easy to
grep for.

TypedValue no longer extends Value, (allowing m_data to move to a
different offset in the future), and Variant now extends TypedValue,
so we only have to maintain one definition.

HphpArray now explicitly uses TypedValue.m_pad instead of overlapping
TypedValue with an anonymous struct, again so we don't have to maintain
another structure to match TypedValue's layout.

The JIT's were using offsetof(TypedValue, _count) all over the place
for access to String/Array/Object/RefData::_count.  Instead, use
FAST_REFCOUNT_OFFSET.
2013-03-09 15:07:37 -08:00
mwilliams e10f38e7fa Dont burn Func*'s into the tc
If its unit gets edited, the Func* could be freed,
and the memory re-used. The name and cachehandle /are/ fixed,
however. Burn those in instead.
2013-03-09 14:45:16 -08:00
mwilliams ba0cc6be65 Better instanceof checks
o_instanceof was doing class lookups on statically known classes.
Use the statically known Class* instead.
2013-03-09 14:44:00 -08:00
bsimmers 79af6e51fa Eliminate jumps to the next instruction at codegen time
Blocks consisting of just a Jmp_ instruction could result in
emitting jmp instructions to the immediately following
instruction. This was fairly simple to avoid.
2013-03-09 14:44:00 -08:00
jan 5ba5cb3761 IsCppAbstract: mark classes that cannot be subclassed in PHP
Previous deff introduced mechanism to avoid calling constructor of abstract class. In some cases, subclassing an abstract class on the PHP side makes sense.

Let's remove this limitation by introducing IsCppAbstract flag, so that only constructor of classes that are abstract in the C++ sense is not called.
2013-03-09 14:21:19 -08:00
hermanv 089caca5ea Generate code for a call to tvCastToArrayInPlace, rather than punt.
Extend cgConv with support for converting to an array. Except for the trival cases of converting null to an empty array and converting arrays to arrays, which are handled in Simplifier, the conversion is delegated to a helper that calls tvCastToArrayInPlace on its input parameter and then returns the embedded array that results from the call. The helper also increases the ref count of operand to convert since tvCastToArrayInPlace assumes that updating the typed value passed to it will result in one fewer referenced to the argument.
2013-03-09 14:21:19 -08:00
ptarjan 23b681e872 allow closures to be cloned
In HPHPc it didn't allow closures to be cloned but in HHVM it did. When I migrated to a C++ closure then i left the HPHPc code. Zend 5.4 allows them to be cloned so lets go with this.
2013-03-09 14:04:11 -08:00
ptarjan f1a3410eb5 make isMethod check the cls() for closures
For closures, I'm going to clone the ##__invoke## method and set the runtime ##m_cls## to be whatever the containing class was (or nullptr if there isn't one). Instead of finding all the callsites that decRef the ##ActRec##'s ##m_this## based on ##isMethod##, I think it is clenaer to make ##isMethod## reflect the state of the ##Func*##
2013-03-09 13:26:27 -08:00
ptarjan 4a3a5ccc6a add ClosureExpression::hasStaticLocals
closures will need to know at emission time whether there is a

  static $foo;

statement inside them, because if there is, the closure has to stick around to hold the static locals.
2013-03-09 13:26:27 -08:00
bsimmers 2776ebd221 Disable the hhir vector translator unless tx64 is disabled
There are a few code quality issues that affect the IR in
general but are magnified in the vector translator. I'm going to work
on fixing those, but in the meantime we're faster with it disabled
(while punting to tx64).
2013-03-09 13:26:27 -08:00
mwilliams 21dab1f587 Need to call xmlInitParser early
Its required if you're going to be using simple xml
from multiple threads.
2013-03-09 13:26:15 -08:00
bsimmers a06a7e07af Don't spill SSATmps that came from the same stack location they're going to
This is a very simple optimization to avoid some unnecessary
stores. In codegen for SpillStack, if the value we're going to spill
comes from a LdStack instruction corresponding to the spill
destination, don't spill it.
2013-03-09 13:26:15 -08:00
ptarjan c1c68516c1 add zend_closure_020
A good unit test that I think we should have.
2013-03-09 13:26:15 -08:00
ptarjan 477f8330d4 pass 'this' through generators
I think this was just an oversight for generators. They should pass all the attributes inside.
2013-03-09 13:26:15 -08:00
ptarjan 71ee3ea273 get ref correct in closure parser 2013-03-09 13:26:15 -08:00
ptarjan 60c61ee3e1 capitalize Closure
PHP is case insensitive, but this leads to better debugging messages if the case matches the declaration.
2013-03-09 13:26:15 -08:00
ottoni f10ecaf20d Revert "make debug_backtrace go through ignored functions" 2013-03-09 13:26:15 -08:00
ptarjan ea782cbd3e handle create_function with a closure in it
This actually was pretty broken already. If you defined a new function in the ##create_function## string it would return that function but it wouldn't move it to the right unit so it can't execute. A big mess. For example:

    $ cat a.php
    <?php
    $a = create_function('', 'function b() { return 3; }');
    $a();
    var_dump(b());
    $ php a.php
    HipHop Fatal error: Undefined function: b in /data/users/ptarjan/hphp/hphp/a.php on line 4
    $ hhvm a.php
    int(3)
2013-03-09 13:26:15 -08:00
ptarjan 7fe1c94a7c increase readability of 0 -> KindOfUninit
I was perplexed by this. It makes more sense to me this way.
2013-03-09 13:26:15 -08:00
ptarjan 085040d70f fix bytecode spec 2013-03-09 13:25:52 -08:00
ptarjan 3ea0d8b8e0 provide better debugging information if source.size != dest.size 2013-03-09 13:25:52 -08:00
ptarjan fd2f07d1f1 Revert allowing $this in closures
I'm going to do a replacement diff anyways, so all this does is delays it a week.
2013-03-09 12:49:43 -08:00
aravind 3e8cbc356f Reset Xbox threads after each request
Xbox threads can now release memory after each request. Also
gives cleaner semantics as Xbox requests dodn't have to be careful
about not modifying global state.
2013-03-09 12:49:43 -08:00
mwilliams 10f9f6b239 const bool hhvm is dead 2013-03-09 12:49:37 -08:00
aravind a611ac930d Implement Switch in IR 2013-03-09 11:10:13 -08:00
jan 94f8158ca5 Expose reflection thru API
Expose the following data to the PHP:
- asio_get_current_context_idx(): get current context index
- asio_get_running_in_context(): get running wait handle in a given context
- asio_get_running(): get running wait handle in a current context (renamed asio_get_current())
- WaitableWaitHandle::getContextIdx(): get context the wait handle operates in
- WaitableWaitHandle::getCreator(): get continuation wait handle that constructed this wait handle
2013-03-09 10:50:49 -08:00
jan 3c20163337 Keep track of WaitableWaitHandle creators
Keep track of creator that constructed us.
2013-03-09 10:29:30 -08:00
jan a9926b46d2 Move enter/exit context responsibility from PHP to join(), try #2
Move the responsibility of entering/exiting contexts from PHP to the
implementation of $wait_handle->join().

This eliminates possibility of weird situations, like contexts without
any running wait handle. This guarantees that asio_get_current() returns
null only if called completely out of asio framework and simplifies some
logic, such as getCurrentWaitHandleDepth().
2013-03-09 10:29:30 -08:00
aalexandre 26178124a4 Eliminate int32, uint32, int16, uint16, int8, uint8.
This concludes the inttypes replacement.
These replacement have been mostly mechanical
with the use of cxx_replace.
2013-03-09 10:25:16 -08:00
bsimmers c7bb7d8eb6 Fix type mistake in cgOpSub
Sometimes src1 isn't an int. This was causing failed asserts
in debug perflab runs.
2013-03-09 10:25:15 -08:00
jan 05f5808d81 Revert "[ext_asio] Use guard to make sure state is restored correctly" 2013-03-09 10:25:15 -08:00
Sara Golemon 8e30d1c315 Update generated_files.sh to no longer build system/gen
Also split constants.h and class_map.cpp into separate generates.
2013-03-09 10:25:15 -08:00
Sara Golemon 3887bec93f Build hphp/system/class_map.cpp
And remove last of system/gen
2013-03-09 10:06:55 -08:00
bmaurer 2fb7b0e279 Avoid lookup during get_class_constants
Noticed this in perf, maybe I'm missing something obvious
but it seemed like there was a pretty easy way to avoid an extra
hashtable lookup during get_class_constants

This shows up because the Enum class call this method once per enum
the first time isValid is call. Might be worth doing something to
make this even more efficient, but seems like an easy win.
2013-03-08 19:13:06 -08:00
mwilliams fea70b0391 Kill more dead code
Kills
 - ClassPropTable
 - ObjectStaticCallbacks
 - MethodCallPackage
 - StaticStringProxy
 - VariantProxy
 - TheStaticStringSet
 - argvalAt
 - refvalAt

Also removes the more-or-less unused StringData in each
StaticString.
2013-03-08 19:13:05 -08:00
mwilliams a4c6fec8e9 Get rid of lots of non-hhvm code
Kill #ifdef HHVM
Kill lots of unused code
2013-03-08 18:44:01 -08:00
alia f6ac148c09 Removed some redundant code in HhbcTranslator::emitFPushClsMethodD.
Removed code in HhbcTranslator::emitFPushClsMethodD that was
duplicated in IRTranslator::irTranslateFPushClsMethodD.
2013-03-08 17:53:28 -08:00
jan 95fc3dc1c9 Use guard to make sure state is restored correctly
If an internal HPHP exception is thrown in a continuation executed by
ext_asio, m_current pointer was not reset and resources were not cleaned
up. This doesn't matter that much in prod, but when used in debug mode,
an assertion was hit.
2013-03-08 17:53:28 -08:00
jan 2ae5ebf3f5 Ignore exceptions generated by onFailed error handler
It's illegal for onFailed error handler to throw any exceptions. Ignore
them to avoid getting into inconsistent state.
2013-03-08 17:53:28 -08:00
jan c9f6d8aeb9 Remove abstract methods
Take advantage of previous diff that won't try to construct abstract classes.
Abstract methods now don't need to be implemented, so remove their
dummy implementation.
2013-03-08 17:53:28 -08:00
jan 6aa91b97c8 Do not generate new instance helper for abstract extension classes
Abstract classes can't be constructed. Don't generate helper for
constructing new instances of such classes. Set null to m_InstanceCtor
field of HhbcExtClassInfo that eventually gets passed to m_InstanceCtor
of Class. The Instance::newInstance() then ends up raising correct
exception.
2013-03-08 17:52:39 -08:00
ottoni 830c48eb8a Add null check in EmitterVisitor::requiresDeepInit 2013-03-08 17:52:39 -08:00
bsimmers e7a57f89c8 Remove HhbcTranslator::m_unboxPtrs and clean up emitCGetProp a little
m_unboxPtrs looks like a relic from the early days of the
ir. emitCGetProp has been cleaned up to never punt (it checks every
needed condition before deciding to not using the vector translator).
2013-03-08 17:52:39 -08:00
bsimmers e5469c9b19 Don't initialize MInstrState in some more common cases
This was causing the ir to generate much larger code than
tx64 for simple SetMs. tx64 does have a specialized helper for setting
array elements that we may want to replicate in the IR, but that can
come in a separate diff. The code generated by VectorTranslator is now
just a few instructions larger than tx64. Without the SpillStack it
would be 1 instruction smaller.
2013-03-08 17:52:39 -08:00
ottoni 96235149fb Don't insert translation counters by default 2013-03-08 17:52:39 -08:00
mwilliams c59f2ae71f Remove outputCPP support from hphp 2013-03-08 17:52:38 -08:00
mwilliams 4eaf40d7aa Generate g_class_map directly from the idl
This is the last step to being able to get rid of the c++ code
gen in hphp. "make -Chphp/system" is now a no-op.

I'll rip out the actual c++ generating code as a separate diff.
2013-03-08 17:52:31 -08:00
smith 73ebbc2160 Don't use m_mainReturn._count to hold the mergeOnly field in Unit[Emitter].
I found some byte-wide padding in Unit and UnitEmitter that we can
use to store the mergeOnly flag, instead of m_returnValue's unused
_count field.  This is one step towards eliminating TV._count.
2013-03-08 17:52:31 -08:00
alia 181f341d00 Minor code size improvements for HHIR.
Improved the size of the code emitted by HHIR. Checks for
this pointer should use a byte comparison. Added a linearscan
precoloring hint for global address helper. Modified argument
shuffling code to use xor rather than move of an immediate zero. For
StaticMethodCache access, moved the test next to the branch to make it
more x86 friendly.
2013-03-08 17:52:31 -08:00
andrewparoski b9e14c448c Support collection literals in initializers
This diff updates the parser and runtime to support using collection
literals in initializer expressions for instance properties, static
properties, parameters, and static locals.

The runtime as-is was able to correctly handle collection literals in
initializers for static properties, parameters, and static locals. However,
for instance properties I needed to way to make it so that each instance
got a fresh copy of the collection literal.

To achieve this, I added an attribute to indicate that a property requires
'deep' intiialization. When this attribute is set, the Class machinery
will not call setEvalScalar() on the initial value (the value produced by
86pinit), and it will make a deep copy of the initial value when a new
instance of the Class is allocated.
2013-03-08 17:52:31 -08:00
andrewparoski 2fe9a69916 Support == and != for collections
Support == and != operators for collections. Also fix some bugs the <, <=,
>, and >= operators when comparing two objects or when comparing an array
with an object.
2013-03-08 17:52:30 -08:00
andrewparoski 34ba5330d5 Fix bug involving autoload with interfaces and traits
This fixes an old bug with autoloading interfaces and traits where the
runtime erroneously continued to call autoload handlers even though the
requested interface/trait had already been loaded.

This also rips out some dead autoload helpers and removes the 'declared'
parameter from invokeHandler() since it is null for all callsites.
2013-03-08 17:52:30 -08:00
mwilliams 01b29a87df Re-enable static analysis for returns
The control flow issue was fixed a while back.
2013-03-08 17:52:30 -08:00
ottoni cf41a82f42 Fix bug looking up properties in a PreClass
A prior diff added property error-reporting code that was looking up the
property in the PreClass.  However, a PreClass doesn't contain the
inherited properties, so HHVM was crashing when trying to raise an
accessibility error on an inherited property.

This diff changes the code to lookup the property in the actual Class
instead.
2013-03-08 17:52:30 -08:00
smith 65119c99a0 Fix return type in collection fromArray/Vector/Iterable() methods
IDL says Object, but C++ code says Variant.  They always return Object,
but the ext_hhvm adapters don't crash because of the way Object and
Variant happen to be layed out.  When the layout changes, stuff breaks.
2013-03-08 17:52:30 -08:00
bertrand 10c79adeba Add symbols to disassembly
Adds debug symbols on calls into the runtime so we can better
understand/compare the helpers being called when we use DumpIR or
JitCompareHHIR.

I used the GNU backtrace_symbols() facility to do the translation of
address->symbol, since it seemed like the easiest route to this
functionality.
2013-03-08 17:52:30 -08:00
mwilliams 35e456c966 Put the compiler-id into a variable
So that we only have to recompile one file when it changes.
2013-03-08 17:52:20 -08:00
bertrand fd49414b4d Implement BaseG 2013-03-08 13:10:09 -08:00
mwilliams 6850f3fa89 Get rid of dynamic_table_constant.cpp
Moved last few uses into ClassInfo.
2013-03-08 12:23:12 -08:00
mwilliams 4286a730c5 Fix g++ 4.7.1 build
The variable declared in an if condition cant be redeclared in the
(outermost scope of the) else.

g++4.7.1 treats FOO as a literal suffix operator in "x"FOO, even if FOO
is a macro whose expansion wouldnt qualify (not sure if this is a bug
in g++4.7.1)
2013-03-08 08:51:38 -08:00
mwilliams 6c87ecb74e Remove dynamic_table_class.cpp
We only used it to get the values of certain class constants,
and to define the ObjectStaticCallbacks for every class.

We can put the class constants directly into the class_map
(we should have done that before for perf reasons), and then
the only remaining use of ObjectStaticCallbacks is to proxy
the Class* for each builtin class. So just use the Class*
directly.

Once this is in, Im just a small step away from eliminating
make -C hphp/system - so Im leaving a lot of dead code here.
Its going to be easier to delete it en masse, rather than
try to pick and chose now.
2013-03-08 08:50:45 -08:00
alia 7eeb594316 Completed implementing LateBoundCls in HHIR.
Completed implementing LateBoundCls in HHIR by handling the
case where we need to dynamically distinguish between a 'this' pointer
and a class in the ActRec. Introduced new instructions to extract
the context class from the m_cls/m_this field of the ActRec. Re-used
them for FPushClsMethodF.
2013-03-07 22:51:49 -08:00
ottoni ef70ffc4e0 Shrink Ret code sequence
Push return address directly from memory, saving one instruction and
two bytes per Ret* and NativeImpl.
2013-03-07 22:51:49 -08:00
ottoni 1811ef243f Add support for FPassV
However, it triggered a bug in memelim. When chasing the value of a
Ref, if it came from a load other than LdRef (eg LdLoc), the value
tracked was the Ref itself instead of the inner value. This diff
separates RefInfo and LocInfo, and it adds findRefValue() to get the
value of a Ref.
2013-03-07 22:51:49 -08:00
chip bdb5a88268 Expose facebook's libmysqlclient async extensions to hphp
This exposes the core async mysql async APIs we added to our
mysql client.

These APIs are not meant to really be directly used (though they can
be); instead they're the building block upon which higher level APIs can
be implemented.
2013-03-07 22:51:16 -08:00
bsimmers 8be4799db9 Add IR dumps to the output of EvalJitCompareHHIR
This turns the output into three columns: tx64 asm, hhir asm,
then pretty-printed hhir with asm inline. This should help us figure
out what the ir is doing wrong when it makes bigger code. I also
compacted the output of Trace::print a bit and changed it to print
unlikely blocks at the very end.
2013-03-07 22:27:19 -08:00
mwilliams 2f3f5f3c5a Get rid of FrameInjection 2013-03-07 22:04:08 -08:00
mwilliams 3b3f9aa449 Fix TestServer
TestServer finds a free port to use as the server, but it
used the standard admin and rpc ports. If you had a
webserver running, it would own the admin port, causing
TestServer's tests to time out (because it uses the admin
port to stop them). Similarly rpc tests would fail because
they would be talking to the wrong server.

Find free ports for the admin and rpc servers, and use them.
2013-03-07 21:29:34 -08:00
mwilliams 7e321f8d5a Fix uninitialized variable
chars_len holds the capacity of the buffer on input,
and is filled in with the number of chars written.

It was not being set, causing random behavior (including
potential buffer overrun). The testcase actually relied
on it being set to a too-small value.
2013-03-07 21:29:29 -08:00
mwilliams 951d7c0adf Fix various bugs with line numbers and classes
I found 4 separate problems, each of which broke this:
 - A hoistable class didnt generate bytecode, so the line number
   information was associated with whatever line the /next/
   bytecode corresponded to. Often there was no next bytecode,
   so we got the line number of the end of the file.
 - If there /was/ a bytecode for the defClass, the line number
   recorded for it was that of the pseudoMain, not the class
   itself; so again, it would be reported as the end of the file.
 - The parser records two sets of line numbers for a class; its
   start, and end. The end line gets associated with the bytecode.
 - There were optimized paths where we didnt setup an ActRec
   at all, resulting in either reporting the file/line of the
   require (or often a require much higher up the stack), or
   not reporting a file/line at all.

So now, we generate a nop if we would have skipped the defCls.
We override the normal line info, and set both the start and end
lines to the start. And we wrap all errors from defClass in a
temporary frame to ensure the line info is seen.

This changed the output for several of our tests. Spot checking
a couple showed they now report the same line number as zend.
2013-03-07 21:29:29 -08:00
mwilliams 7ff582616f Don't compile systemlib php files
We don't need them in g_class_map, beacuse hhvm processes
the php for itself.
2013-03-07 21:09:46 -08:00
mwilliams c4c4404aa9 Get rid of dynamic_table_func
Its no longer being used.
2013-03-07 21:09:03 -08:00
mwilliams a8e7668189 Get rid of lots of outputCPP code
Very little is actually used anymore
2013-03-07 20:44:34 -08:00
mwilliams 18a45ba0d0 Get rid of invoke_builtin
It was only used to fold SimpleFunctionCall nodes. Ive setup enough
of the runtime that we can call invoke (which goes through hhvm's
normal Func dispatch), and then removed it.
2013-03-07 20:19:00 -08:00
mwilliams a6104b9d6e Get rid of more of system/gen
This basically targetted symbols.php, and Globals, but ended up
killing a lot more. I could keep adding more and more, but
this seems like a good point to stop and continue with
another diff.
2013-03-07 16:19:03 -08:00
Sara Golemon e355d3de17 hphp/test should be using hphp/hhvm externals 2013-03-07 16:19:02 -08:00
jan 99bfd8e9ee Remove inline keywords from class method definitions
Removing redundant inline keywords per @mwilliam's feedback.
2013-03-07 12:12:15 -08:00
alia 30fcc6a0d1 HHIR: Plumbed more instructions from irtranslator to hhbctranslator.
There were a few bytecodes in irtranslator that were
HHIR_UNIMPLEMENTED. These are bytecodes that tx64 handles but the IR
does not. Changed these to use hhbctranslator emit routines and used
interponeOrPunt to implement them for now. This should increase IR's
coverage a little when IRPuntDontInterp is false.
2013-03-07 11:35:07 -08:00
aalexandre 61d3e28392 Add move constructor and move assignment to essential data structures.
This reduces CPU instruction count by about 0.2%, has negligible impact on other metrics. Implementation has been changed a but upon discussion with @mwilliams to account for circular destruction. The question remains open whether this should be in before or after rooting out hphpc, but on the other hand we gotta do what we gotta do to move forward.
2013-03-07 11:34:39 -08:00
smith 21c4fbdbf3 Remove ZendArray
SharedMap was the last dependency on ZendArray.  For its localCache,
use a TypedValue[] array indexed by SharedVariant.getIndex(), and
for escalate(mutableIteration), escalate to an HphpArray instead of
a ZendArray.
2013-03-07 11:34:24 -08:00
Sara Golemon 33e81ef7bb Ignore ext_closure during ext_injection run 2013-03-07 11:33:51 -08:00
Owen Yamauchi e518130c7b Generalize the sys/param.h check
I'm using OpenEmbedded for the ARMv8 build right now, and it seems to be
BSD-like, based on the preprocessor flags that are defined, including
__USE_BSD. OpenEmbedded has the same problem with the "isset" macro, and
this change includes it in the code that undefs isset.

I guess this problem is common to BSD-like systems (if you consider OS X
to be BSD-like for this purpose), but __USE_BSD by itself isn't enough;
OS X doesn't define it.
2013-03-06 15:14:35 -08:00
andrewparoski 5848c633c3 Stop generating C++ for system/classes, remove uses of MethodCallPackage
This diff suppresses the output of C++ for the "pure" classes defined in
system/classes, and it rips out all the uses of MethodCallPackage (except
for the i_* and ifa_* helpers, which we can go after separately).

Also cleans up a bunch of "if (hhvm)" and "#ifdef HHVM" checks in builtin_functions.cpp,
systemlib.cpp, object_data.cpp, and class_info.cpp (and the corresponding .h files).

Note that this does not completely remove the generated C++ files. We
still generate code for the PHP files in "system/globals" and we still
generate the g_class_map (because the VM needs g_class_map at startup
when it creates Class's for the extensions). We also still have the
dynamic_func_table/dynamic_class_table stuff, MethodCallPackage, and
the i_* and ifa_* helpers to support invoke_builtin() (which is still
used by the compiler).
2013-03-06 15:13:40 -08:00
andrewparoski 9be42ffc84 Fix weakRemove() to handle collections 2013-03-06 11:15:50 -08:00
ptarjan e150671d40 Move Closure from php to c++
This is in preparation for saving a Func* on the class to fix the perf.

It turns out Properties were never used on a single class in the IDL so HHVM never implemented them. Now it does.

This diff was a perfect exercise in "change very few lines but finding which lines to change takes hours".
2013-03-06 10:46:47 -08:00
aravind c9ae7340cf Implement BPassV
I forgot to add this with FCallBuiltin. BPassV is ~5% of the
punts on trunk.
2013-03-06 10:06:02 -08:00
bsimmers a7c629366f Update printing of StRaw
The extra offset is gone but we were still trying to print it.
2013-03-06 10:06:02 -08:00
Sara Golemon 45486a934f Nuke gen/ before generating system files so that we don't keep dead files 2013-03-05 23:00:14 -08:00
andrewparoski 92c74b141a Fix some places that use old VM-incompatible machinery
On the back burner I've been working on a diff to pull out of some of
the old VM-incompatible machinery for invoking functions/methods and for
creating object. Along the way I noticed some bugs, so I figured I'd fix
them first in a separate diff to make review easier.
2013-03-05 22:55:46 -08:00
smith 45a794d7dd Linear scan improvements
When allocating registers and inserting Reloads, be less pessimistic
about reusing earlier reloads.  Only "forget" reloads that don't
dominate current block.  e.g. right after an if/then/else block, it
will clear any reloads from if/else arms so they can't be used in
the join block.

Moved numberInstructions() to linearScan.cpp, and don't re-sort the
control flow graph every time; register allocation doesn't change
the CFG shape so its enough to just iterate the existing block list
each time.

Un-pessimize rematerialization by snapshotting state at branches,
merging state when necessary, then restoring state at block starts.
Here, "state" is the previously seen tmps for sp, fp, and each
local variable.

removeUnusedSpills() can just iterate over the spill slots, erasing
spills with no uses; we don't need to visit every instruction.
2013-03-05 22:28:42 -08:00
jan 883a11fbef Fix consistency issue with cross-context cycle exceptions
enterContext() throws an exception when cross-context cycle is found.
The problem is that it modifies state before the exception is thrown,
assuming that the call will succeed.

When an exception is thrown, a dependency is left in invalid state, with
parent being in more specific context. This breaks exitContext()
algorithm and results in either internal invariant violations as seen
in #2091939, or memory corruptions and crashes as seen in #2125762.

Let's fix it by modifying state after returning back from recursive call
instead of before doing such call. This was previously unsafe in case we
tried to import dependency loop. Once D720506 is committed, dependency
loops will not exist anymore.
2013-03-05 22:28:42 -08:00
jan c6475efbec Detect cycles online and fix part of #2125762
Currently, we detect dependency loops by waiting until there is nothing
else to execute. If the wait handle we are waiting for did not finish,
it means it is in a cycle. We find the cycle by simply following the
dependency chain. Once the cycle is found, one edge is eliminated and an
exception is injected.

There are multiple problems with this approach:

1. Unability to exit contet safely

We are unable to exit context safely. When a context is exited, all wait
handles in that context must be kicked out. But we maintain only
references to the SCHEDULED wait handles + BLOCKED wait handles that
recursively depend on them.

If we do not kick out all unfinished wait handles, we end up in
corrupted state.

2. Unability to break edge that caused the cycle

Once the cycle is detected, we don't know which edge caused the cycle to
be formed. We can only use heuristics to eliminate the edge that likely
formed the cycle, we cannot be sure. This may make it very hard to fix
the PHP code that caused the cycle.

Solution:

This diff implements online cycle detection with a naive approach of
visiting the dependency chain from child at a time new edge between
parent and child is being added. If a parent is visited, a cycle is
found. Otherwise we eventually reach non-BLOCKED wait handle as it is
guaranteed the rest of the graph is cycle-free.
2013-03-05 22:28:41 -08:00
jan a1baf2d540 Access context by index
Currently, wait handles store pointer to the context they are in. This
pointer is not protected with reference counting, as it is expected that
whenever a context is exited, references to it are cleaned thru
exitContext() mechanism.

If a bug is present that violates this assumption, it is impossible to
guard against invalid pointer access and a hard to debug memory
corruption occurs.

Since the structure of contexts is a simple stack, let's reference them
by index instead of by pointer.

As a bonus, one pointer worth of memory is saved for every non-trivial wait handle.

The actual bugs will be fixed by the next 2 diffs that do:
1. implement online cycle detection
2. do enterContext() atomically and properly handle failure
2013-03-05 22:28:41 -08:00
smith 0562656c5b Rename some tests to avoid collisions on case-insensitive filesystems.
Avoids headaches if you clone our repo or the OSS repo on a Mac.
2013-03-05 22:28:41 -08:00
Owen Yamauchi c1e71c1917 Conditionally compile assembly helpers
Just treating some more x64-specific assembly. The tx64 helpers are all
written to trap on ARM right now -- we're not going to be running the
jit for a little while, and we don't need real implementations till
then.

Fortunately, the system we're targeting has thread-local storage
support, and it's pretty easy to get at it, so I just did it.
2013-03-05 22:28:41 -08:00
aravind 668a9c1da2 FCallBuiltin for IR 2013-03-05 22:28:41 -08:00
alia f4551719a9 HHIR: Fixed uses of trace exits.
Fixed a few places where we were calling exitTrace when we
should have been calling exitSlowTrace in
hhbctranslator. ExitSlowTrace will exit and not return to the IR,
going to either code generated by tx64 or the interpreter (depending
on the IRPuntDontInterp runtime flag). ExitTrace will simply exit and
retranslate using the IR if possible. In some cases we were using
exitTrace for conditions that the IR could not handle, causing us to
just try the same doomed operation again in the IR.

Used control flow to implement IssetS/G and EmptyS/G instead of
exiting the trace on an undefined static property or global.

Implemented code gen for Unbox and cleaned up its
simplification.

Cleaned up some DefConsts in hhbctranslator.
2013-03-05 22:28:41 -08:00
ottoni 3f0a4f7ece Add support for noSurprise meta-data annotation
This was causing HHIR to emit surprise-flags checks when Tx64 was skipping it.
2013-03-05 22:27:43 -08:00
mwilliams ecdd06cebe Fix array_walk_recursive test
It wasnt testing what it was supposed to
2013-03-05 22:27:43 -08:00
Nick Harper 5744173e90 Fix array_walk_recursive() recursion check
We only have recursion if what we're currently looking at is the same as
one of its parents in the nested arrays. We don't need to keep track of
everything that's been seen, only the elements seen in a path down to that
element.
2013-03-05 22:27:43 -08:00
mwilliams 0f3ba86d84 Dont analyze every tracelet twice
Seems pointless (but maybe Im missing something). Long term its
not important - this only affects things when the ir is off, but
its going to give us a false baseline wrt startup translation
time of the ir.
2013-03-05 22:27:43 -08:00
smith 37d974ef83 Remove VectorArray (dead code)
Also cleaned up some if (hhvm) logic in the files I was touching anyway.
2013-03-05 22:27:42 -08:00
smith 7f43d78955 assert(isNative) in cgCallHelper, fix Native flags, enable MMX spills.
Now we can rely the Native flag to tell whether a call is going to
happen or not.  This should improve precoloring hints, and allows
re-enabling spilling to MMX registers.
2013-03-05 22:27:42 -08:00
smith 1f68fd307f Add DbgCheckTV opcode and use it in a couple places
Instead of custom asm we generate in a few places to generate asserts
that check a TypedValue, use an opcode instead.  This also moves more
of the assert-generation logic to a dedicated pass.

Moved the CSEHash for constants from TraceBuilder to IRFactory, to
simplify generating DefConst instructions later than the TraceBuilder
pass.  IRFactory now owns the constant table.

Cleaned up type names in ir.specification.  We always pretty-print
pointer types as PtrToWhatever, so do it that way in the spec too.
Added spec for Call.
2013-03-05 22:07:58 -08:00
smith 3e12490127 Enhance simplifier pass so it works with control flow.
Snapshot Tracebuilder state at branch points, and merge it at join points.
Remove the no-control-flow restriction from TraceBuilder::optimizeTrace,
since we can deal with control flow now.

In optimizeTrace, When we enter a block that has a snapshot state,
remove entries from the cse table that don't dominate the current block.
2013-03-05 22:07:58 -08:00
ottoni 334a83f7b6 Expand IncDecL support, use inc/dec x86 instructions, avoid useless moves
Add support for boxed ints, and boxed/unboxed doubles, and bools.

Emit inc/dec x86 instructions in some cases.

Got rid of some "mov rX, rX" found along the way.
2013-03-05 22:07:58 -08:00
mwilliams 8c2a7af8d4 Fix integer overflow in fb_serialized_size
We could overflow the size of an int, return a
"legitimate" looking value, and then crash in
fb_serialize_into_buffer when we go past the allocated
size.
2013-03-05 22:07:58 -08:00
mwilliams b40c724281 Fix bogus decRef of Null
If a PopR was marked as having a predicted type, and it was
not in the same tracelet as its producer, we would generate
code that didnt verify the predicted type. Note that normally
we would translate them together, unless we failed to get the
write lease. In that case, we could spend a long time inside
the call, and get the write lease by the time we return.
2013-03-05 22:07:58 -08:00
bertrand 157b59cdac Replace isStaticallyKnown with clearer names
There's widespread dislike of the isStaticallyKnown
predicate, and I think the main problem is the "statically" part in
the name.  Some of the basic types (str, null, array) are actually
unions of a static and counted type, so sometimes we don't precisely
know the type statically, but it doesn't matter.

To remedy the situation, I'm renaming isStaticallyKnown to be simply
"isKnown", which I think is a semantic improvement (@smith suggested
"hasKnownDataType" -- I figured that since the object is a type that
I'd go with something shorter). I'm also providing "needsReg", which
is now just the inverse of "isKnown", but having this available makes
the code clearer in places that care about the type only to figure out
whether to allocate storage for it.

There might be places where more precise tests could be used, but
they're not totally obvious to me.  So, I want to push out the simpler
naming scheme first.
2013-03-05 22:07:57 -08:00
smith 7969b8f2ee Add ifThen[Else] helpers in codegen.cpp; improve guard branching.
Got rid of lots of boilerplate by using ifThen helper.  In nearby
areas, updated asm generation code to use the new style.

Factored two-register-assignment out of cgCallHelper(), use it
in cgIncRef().

Generalized cgIncRefWork slightly (since it was cleaner this way)
so it handles the case of no-static-check but with a type check,
and use shuffle2() instead of more adhoc copy logic.

cgGuardRefs should generate fewer unnecessary jumps now, by crafting
the code we generate based on compile-time-known cases (fixes TODO).
2013-03-05 22:07:57 -08:00
smith addb01eea4 Fix local variable state-tracking for Iter[Next,Init][K]
tracebuilder and rematerialization both rely on tracking the contents
of local variables; these instructions were incomplete.
2013-03-05 22:07:57 -08:00
mwilliams 5eac64e8bd Remove GlobalArrayWrapper 2013-03-05 22:07:57 -08:00
andrewparoski 193ddf4a60 Fix unwinding for generator frames
The logic for unwinding generator frames was assuming that the previous
frame was always a VM frame. This used to be a correct assumption, but now
we have invokeContFunc which allows native code to call generator functions.

This diff fixes the unwinder to correctly computer the bottom of the eval
stack in the case where the previous frame is a native frame. It also
updates invokeContFunc to properly decref the return value.
2013-03-05 22:07:57 -08:00
alia 3f61cabef2 Implemented IterFree in HHIR.
Implemented IterFree in HHIR. This showed up in the list of
top reasons the HHIR punts.
2013-03-05 22:07:57 -08:00
Owen Yamauchi bffeb65b84 Delete NativeXHP flag
This flag isn't being used in any of our deployments. I sure hope not,
anyway, because if you turn it off, things are mega-busted; I can't even
run a sandbox without crashing pretty early on.

This breaks the OSS build's dependency on xhp. We're still depending on
it internally in a Facebook-specific extension (exposing the XHP
preprocessor to PHP code as ##xhp_preprocess_code()##). There might be
some way to replicate this functionality using HPHP's native XHP parser,
but that seems low-pri.
2013-03-05 22:07:57 -08:00
alia 6aa9f21425 Implemented globals in HHIR.
Implemented globals in HHIR (CGetG, SetG, VGetG, BindG,
IssetG, EmptyG). This version doesn't use the targetcache.
2013-03-05 22:07:57 -08:00
Owen Yamauchi de1249af6e Delete hphpc-only parts of Continuation
While I'm looking at this stuff during debugging, get rid of some of the
stuff that's only compiled for the now-nonexistent hphpc build.

I'm aiming for this change to result in identical code after
preprocessing. There is some other stuff in these files that could go
(any method with ##const_assert(!hhvm)##) but getting rid of those is a
bit hairier; I tried, and it resulted in some weird, hard-to-repro
instability, and it doesn't seem worth sinking much time into at the
moment.
2013-03-05 22:07:57 -08:00
mwilliams f5c0cd21ae Fix a maybe-used-uninitialized warning 2013-03-05 22:07:57 -08:00
ptarjan 9f21844eb5 handle reference closures
Handle a closure that returns by reference.
2013-03-05 22:07:56 -08:00
ptarjan 8c6d77deef Put the body of a closure on the class intead of in the __invoke of the closure
Instead of having the body of the closure be in the ##__invoke()## on the ##Closure## class, instead we make an anonymous function on the real class and put the body there. The signature for this function is:

  function methodForClosure$1234($arg1, $arg2, ..., $use1, $use2, ...)

and then ##__invoke## now just takes all the params that were passed to it, puts them as the first args to the anonymous function, then takes all the use variables it had saved up and passed them in as the next params.

I tried to not have an ##__invoke## at all, but I ended up basically doing the same parameter and use var repacking in iopFCall (and would have had to do it in x86 code too). I opted for doing the rejiggering in bytecode. If I did it in raw PHP I think it would have been much slower with many ##func_get_args()## and array operations.
2013-03-05 22:07:56 -08:00
ptarjan e5663bd9ec allow (new Foo)->bar() 2013-03-05 22:07:56 -08:00
ptarjan 7917779c18 make debug_backtrace go through ignored functions
We don't display the stack frame for ##isNoInjection()## functions, but the backtrace does set the previous call lines wrong.

This came up when I made the Closure::__invoke to be hidden, but the line numbers were wrong.
2013-03-05 22:07:56 -08:00
ptarjan a395e9e3dd script to import zend tests
I often want to make sure we are close to parity with zend so
importing their tests seems like a good idea. I used this for an
upcoming diff and it worked out well.

I just tried this on all the closure tests and it needs a bit
of hardening. It worked well though :)
2013-03-05 22:07:50 -08:00
ptarjan 27d247e976 Make hphp.y closer to zend
I'm going through the zend parser file and they are quite similar to ours:

https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y

It seems having our parser file being closer to theirs would help us keep parity. I also prefer less copy and pasting when possible.
2013-03-05 22:07:49 -08:00
smith af3c7ac8df Move assembly address info into AsmInfo
IRInstruction and Block have asmRange fields to record where
their assembly code got generated, but it's only used for
pretty-printing.  Do it with a side structure instead, indexed
by block (for block ranges) or instruction.

Gave each instruction its own range, to simplify pretty-printing.
Modified the pretty-printer to also print code following the last
instruction in a block but which is still in the block's range
(e.g. a jump to the next block)
2013-03-05 22:07:49 -08:00
bertrand 7c1503e778 Always generate Type::Arr instead of Type::StaticArr
Temporarily avoid generating static array to un-break PerfLab
2013-03-05 22:07:49 -08:00
dgomez ab71559f5a Reduce the number of copies of certain data
On the decompression+deserialization path one useless copy will be created in case the data is not compressed, but serialized. Uncompress always returns a platform native string, but if the data was serialized this platform string is not needed, since it will be unserialized into platform variant anyway.
This can be improved by unserializing the data directly from mc_msg_t in case it was not compressed.
2013-03-05 22:07:49 -08:00
hermanv aac3f2bfd5 Add constants asociated with idna functions. Port applicable tests from Zend.
The Zend versions of idn_to_ascii, idn_to_unicode and idn_to_utf8 take parameters whose valid values are defined by predefined constants and whose error codes correspond to
predefined constants. This change adds those predefined constants to hiphop. It also ports two of the applicable unit tests from the Zend code base to hiphop.
2013-03-05 22:07:37 -08:00
srenfro 296b2baede RuntimeOption to disable TLSv1.2 in HPHP runtime, not in OpenSSL
Introduces a new server runtime option TLSDisableTLS1_2,
which is off by default. When on, it disables TLSv1.2
in the two places we curl_init().
2013-03-05 21:17:51 -08:00
alia 2ef2187778 Fixed hphp/test/vm/static_sprop2.php test.
The test hphp/test/vm/static_sprop2.php needed a filter rule
file for a file path in its expected output. This was breaking tests.
2013-03-05 21:17:51 -08:00
alia e30755ddbe HHIR: Fixed potential problems with essential instructions
Fixed a few instructions in the HHIR instruction properties
table related to instructions that should not be moved or eliminated
by dead code elimination. Also made sure codegen can handle essential
instructions whose result are not used (i.e., instructions whose
destination registers are InvalidReg).
2013-03-05 21:17:51 -08:00
lovro bc085bbcf6 Use init_null_variant instead of null_variant
I'm experimenting with ArrayInit::add() and it behaves strange with null_variant
2013-03-05 21:17:40 -08:00
alia 09b97f8336 Fixed 2 HHIR bugs that were causing crashes.
Fixes HHIR bugs that were causing perflab crashes: guards on
class types and tvBox.

We were ignoring stack guards on class, which was causing the IR to
lose track of types for classes on the stack and causing assertion
failures. Fixed by making class stack type guards into stack type
asserts.

tvBox was not returning the right value, causing VGetS to crash in the
IR.

Changed 1 test and added another to cover VGetS in verify_quick_hhir.
2013-03-05 21:17:39 -08:00
bsimmers 991ca4e231 Add a top-level catch to RUN_TEST
A test was hanging on to the result of
std::string::c_str() past the lifetime of the string and then throwing
an uncaught exception.
This diff adds a top-level catch block to RUN_TEST to prevent things like
this from killing the test binary in the future.
2013-03-05 21:17:39 -08:00
smith 449ebdc43f Add a per-block likely/unlikely hint, use it for code placement.
Each Block has a new Likely/Unlikely/Neither hint that can be set
any time.  At codegen time, unlikely blocks are put in astubs.
Codegen takes care of inserting branches to/from them if necessary.

Updated the pretty-printing code to show AStubs code after each
block instead of after each trace.

Refactored the instructions that emit code into both a and astubs
so they work when a == astubs, by using a new unlikelyIfBlock
helper.
2013-03-05 19:52:43 -08:00
smith 6ab2ea2218 Track predecessors of "join" blocks so we can follow use->def chains
In order to find the srcs that reach a given DefLabel destination,
we need to know the Jmps that reach the label.  This diff tracks them
in a singly linked list which stays up-to-date when setTaken()
is called.

Just to exersize the ability, I changed the prettyprint output
to print "phi" pseudio-instructions.  These don't exist in the
actual IR but its helpful to see them in the dump output..
2013-03-05 19:52:43 -08:00
smith 0b27530c65 Make Block the main instruction container in HHIR
Instead of Traces holding a non-intrusive list of instructions,
have Blocks own instructions with an efficient intrusive list
so each instruction can only be in one block at a time.

Traces hold a list of blocks (which hold instructions); the overall
instruction order isn't changed.  However, use the Block api to
insert/remove instructions.

IRInstructions and Blocks point to their "parent" but the accessors
are named specifically (IRInstruction::getBlock and Block::getTrace).

Branches refer to a Block instead of a LabelInstruction.  Fields
of LabelInstruction were moved to Block, and LabelInstruction is
removed.  IRInstruction is now nonvirtual.

Several loops over trace->getInstructionList() were modified to
be nested loops over blocks, then instructions.

Removed the union of SSATmp[*|**] for destinations; instead, use
SSATmp* m_dst, which points to 0 or more destinations.  The
opcode still dictates whether the count must be 0, 1, or N.
Looping over multiple destinations now typically uses SSATmp&.
Renamed typedef SSARange to SrcRange and added DstRange.

Branch-patching state was moved out of LabelInstruction (or Block)
and is private to CodeGen now; each block has an associated patch
list.
2013-03-05 19:52:43 -08:00
Sara Golemon cd7b28ad6a Add folly experimental components 2013-03-05 19:52:43 -08:00
Scott MacVicar 943b945e1b Show message like the regular status messages
Make the CMake output consistent when doing a build.
2013-02-24 23:22:55 -08:00
Scott MacVicar ab6a6508fb Update README.md
Changed paths and added some extra libraries
2013-02-24 23:06:04 -08:00
ptarjan c1b7da6a4c Allow trailing comma in function calls
https://wiki.php.net/rfc/trailing-comma-function-args
2013-02-19 13:41:28 -08:00
bertrand 1d6df52d09 Introduce Counted type union.
This diff creates a Counted type union by splitting Arr into
CountedArr and StaticArr.  This cleans up the *Counted() predicates
and makes needsStaticBitCheck more precise for arrays.  Please check
me (esp on canRunDtor), because I am n00b with respect to PHP
semantics.
2013-02-19 13:41:27 -08:00
ptc 4507c61901 Include Local Var Info in DWARF Debug
Include locals information in gdb via the preexisting dwarf output.

As discussed I have a top level DIE for 'HPHP::TypedValue' with the same name
which gdb takes care of while doing the opaque type resolution.
2013-02-19 13:41:27 -08:00
mwilliams 7f6b1532dc Add smart allocated standard containers
And use them in AsioContext, which was doing a lot of memory
allocation via malloc/free, was itself allocated by malloc, and
needed to be sweepable to deal with the fact that it contained
standard containers.
2013-02-19 13:41:27 -08:00
bertrand 12471034e5 Implement BaseS in the vector translator 2013-02-19 13:41:27 -08:00
smith 28fc7a3a45 Fix result-copying in cgCallHelper
If the return value is a register pair (e.g. TypedValue by value),
and the register assignments required a swap or assigned rdx to
dest-register-0, then we'd clobber rdx before saving it.
2013-02-19 13:41:27 -08:00
bsimmers a9a021e739 Turn on HHIRDisableTx64 for IR automated tests 2013-02-19 13:41:27 -08:00
bsimmers 812070e82d Fix VectorEffects for bad array keys
Setting an array element with a key that's an array or object
raises a warning and evaluates to null. VectorEffects wasn't taking
this into account and was also getting a bunch of other things wrong,
so I rewrote it.
2013-02-19 13:41:27 -08:00
Owen Yamauchi 8d86116117 Conditionally compile cpuid stuff in GetCPUModel
Pretty self-explanatory. I took out the 32-bit compatibility stuff
because we're pretty far away from being able to support 32-bit
platforms (the open source build explicitly checks and fails on 32-bit).

On non-x64 platforms, we'll fall back to calling uname(). As far as I
can tell, ARM doesn't have a "processor name" facility like x86 does,
but if we find one, we can add support for it here.
2013-02-19 13:41:27 -08:00
Owen Yamauchi 520ef90a75 Conditionally compile memcpy-x64.cpp
Don't compile this on other platforms. (Really, this should be something
like HAVE_EMMINTRIN_H but we don't have that kind of infrastructure
right now.)
2013-02-19 13:41:27 -08:00
Owen Yamauchi c3cd188c6f Delete util/config.h
This is dead code. We're not using autotools at all, so let's not have
autotools artifacts sitting around.
2013-02-19 13:41:26 -08:00
Sara Golemon c1a8c937f1 Add support for user-defined streams wrappers 2013-02-19 13:41:26 -08:00
Sara Golemon 35842e9141 Clean up cruft in the CMakeLists 2013-02-19 13:41:26 -08:00
Sara Golemon fc5b95110f Remove support for buildling HPHPc
To get the non-hphpc portions of buliding for HPHPc,
perform an HHVM build and execute it with the --hphp flag

To checkout the commit immediately prior to this:
git checkout use-hphpc
2013-02-19 13:41:26 -08:00
hermanv 9cb2d7af85 Add support for UTS #46, along the lines of Zend, to idn_to_ascii.
Removes the output errorCode parameter (that is not currently used anywhere, except in testing code that gets but ignores it). Adds the options, variant and
idna_info parameters that are present in the Zend version of idn_to_ascii. Implement the new functionality required by these parameters by using the UTS #46 API, if
present.
2013-02-19 13:41:26 -08:00
ptarjan 6a311f2fab make test filters consistent 2013-02-19 13:41:08 -08:00
aalexandre b3b41e08bb Replaced NULL with nullptr 2013-02-19 06:57:54 -08:00
ptarjan 3e24e506f4 OBJMETHOD_BODY -> fPushObjMethodImpl
I was debugging this method and jumping into a macro that is 20 lines is much harder than a method. Shouldn't the compiler inline it if that is the right thing to do anyways?

There also was a hidden variable ##numArgs## which is what was messing me up.
2013-02-19 06:57:06 -08:00
alia ef1d4c9c1e Fixed HHIR bug: LdClsCached should be essential.
LdClsCached should be marked essential so that dead code elim
and code sinking don't touch it. This was causing a sandbox crash.
2013-02-19 06:57:06 -08:00
jan cee31f4412 RescheduleWaitHandle: management of execution priority
Abstract away execution priority management from ContinuationWaitHandle
and create RescheduleWaitHandle.
2013-02-19 06:53:40 -08:00
Owen Yamauchi 65d2487ab7 Increase -ftemplate-depth from 60 to 120 2013-02-14 09:09:34 -08:00
mwilliams 67013476b5 Fix TestExt to work under hhvm
and move it into the hhvm_tests target
2013-02-14 09:09:27 -08:00
bsimmers 2b3ae234fb Fix hphp
Util::s_pageSize wasn't being initialized early enough,
causing us to pass bogus values for stack sizes to pthread_create.
2013-02-14 06:48:37 -08:00
bsimmers 6fed85fbb4 Trace slow exits to tx64 in TRACE=punt:1
They're not dynamically important right now but we might as
well keep the instrumentation code.
2013-02-14 06:48:37 -08:00
andrewparoski bd2e014e8f Make String::operator+=() support StringSlice and MutableSlice
This diff adds support for StringSlice and MutableSlice to the
String::operator+=() method. It also uses operator+= to replace some
manual string manipulation in throwStrOOB inside ext_collection.cpp.
2013-02-14 06:48:37 -08:00
Owen Yamauchi c8cc07a61c Port ffs64 and fls64
These rely on the hilarious BSR and BSF instructions on x64. ARMv8 has
an instruction that does something similar: count leading zeros.
Unfortunately, their semantics differ in a few important ways:

- BSR and BSF, when given a zero input, set the zero flag and have
  undefined output. CLZ outputs 64.

- BSR and BSF return the index (starting from 0 at the LSB) of the most- or
  least-significant 1 bit. CLZ does what it says: count leading zeros.

Bottom line, there has to be some bit trickery somewhere to fit the two
instructions into the same interface. I kept the x64 implementations the
same and put all the nastiness in the ARM implementations to make them
match, since obviously the perf of the ARM implementations doesn't
matter yet.
2013-02-14 06:48:26 -08:00
Owen Yamauchi 26c4d57cfa Replace PAGE_SIZE with sysconf(_SC_PAGESIZE)
PAGE_SIZE isn't portable -- e.g. my ARMv8 sysroot doesn't define it. I'm
replacing it in the way the Linux manpage recommends. This should work
on a wide variety of platforms. For example, it works on OS X.
2013-02-14 06:47:38 -08:00
mwilliams f58323820c More customizable init/fini hooks
Allow extensions to hook into hphp_process_init and hphp_process_exit
in the same way they could already hook into thread_init/thread_fini.

Also fixed a bug where we didnt actually call the fini funcs (nobody
is using it though).
2013-02-14 06:47:38 -08:00
mwilliams 2f9f1a705b Fix QuickTests to work under hhvm 2013-02-13 06:44:30 -08:00
aravind 387a3555e2 Fix cgOpSub
Don't call cgNegateWork() for Type::Dbl operands.
2013-02-13 06:44:30 -08:00
ottoni 8a119496e1 fb_call_user_func_safe_return should try the autoloader
The interpreter apparently does so, but the JIT doesn't.
I suspect this is what is causing the problem reported in the
task. However, I can't reproduce (or properly test) it.

I believe we need a scenario with a least two requests. The first
request has seen the callee function by the time it translates
FPushCufSafe, so that it doesn't interpOne this instruction.  The
second request hasn't loaded the callee by the time it runs this
translation, so it misses in the TargetCache (at which point it should
try the autloader).
2013-02-13 06:44:30 -08:00
alia 5662a67f12 Cleaned up and added functionality to HHIR's handling of static properties and classes.
Broke LdClsPropAddr into 2 instructions by introducing a
ldClsPropAddrCached. LdClsPropAddrCached loads the static property via
the target cache so requires the class and property name to be
compile-time constants. LdClsPropAddr does not use the target cache
and works even if the property and class names are not constant.

Similarly broke LdCls into 2 by introducing LdClsCached. LdClsCached
uses the target cache to handle the case where the class name is a
compile time constant. Added code to detect cases where the class name
can be folded to a Class* constant.

Moved the code that decides between LdCls (LdClsPropAddr) and
LdClsCached (LdClsPropAddrCached) to the simplifier, allowing us to
optimize these instructions as optimization passes discover more
constants.

Changed HhbcTranslator code generation for AGet* and getClsPropAddr to
handle more cases using the above new instructions. Also improved
HhbcTranslator's translations for self and parent bytecodes.

Added a label to LdClsPropAddr to allow control flow when property is
not accessible. This enables implementation of IssetS (which was
incorrect before this diff) and EmptyS.

Implemented BindS, VGetS, EmptyS, and LateBoundCls.

Opportunitistically introduces calls to SSATmp::isA(...) to replace
explicit comparisons against Type::Tag values.

Moved AddElem punt from code generator to hhbctranslator so that we
can interpone.
2013-02-13 06:44:30 -08:00
mwilliams ac63ae02c6 Make TestServer work under hhvm 2013-02-13 06:44:30 -08:00
bsimmers f3a6aaee37 Fix TRACE=punt:1
One of the places that sets m_lastHHIRPunt was lost in
D705347. This will still capture punts that causes partial tracelet
interp because that translation ends up being a full-tracelet punt
when the IR tries to translate it again.
2013-02-13 06:44:20 -08:00
jdelong bbdf9729fb Fix another bug in exception handling
It's not ok for the unwinder to use a reference to elements
living in the m_faults array, since the unwinder can re-enter the VM
when calling destructors (or the FunctionExit hook).  If one of those
re-entries does exception handling, it can modify m_faults.
Additionally, gets rid of VMPrepareThrow and instead just throw Object
and use the same case as we do when exceptions came from an extension.
I had to fix an assembler test catch handler to actually catch to keep
the assertion about m_faults on re-entry correct.
2013-02-13 06:43:40 -08:00
bsimmers 09e01875a0 Table-drive register allocation and codegen for simple native call ops
This diff adds a table containing information about native
helpers called by translated code. It is used by LinearScan and
CodeGenerator to replace the manual and somewhat error-prone
precoloring hints and calls to cgCallHelper. I discovered in the
process that many of these helpers didn't have any precoloring hints,
so register allocation for some things should be improved slightly.
Code generation should be otherwise unchanged.
2013-02-13 06:43:40 -08:00
bsimmers 26a6669575 Implement more of HhbcTranslator::VectorTranslator
- Implement SetProp and SetElem
- SpillStack before each helper call since they can throw
- Stub out the remaining unimplemented vector operations for more
  fine-grained punt counts
- Kill TraceBuilder::gen* wrappers for vector stuff
- Rearrange some of the fake DataTypes to not confuse gdb. Values are
  unaffected.
2013-02-13 06:43:40 -08:00
bsimmers 162b46fb99 Refine some use of types in the IR
- LdLocAddr now returns a properly typed pointer
- Audit and clean up every use of Type::Null
- Add Type::debugString for use in gdb (calling Type::isString often
doesn't work)
- PtrToStr and PtrToNull are statically known
2013-02-13 06:43:40 -08:00
mwilliams f5188b8fe9 Fixes for turning off jemalloc
Some assumptions were made about low memory that broke under
libc malloc.
2013-02-13 06:43:40 -08:00
kma 0f9c93f9f9 Stop interp'ing at FCall.
When interp'ing an N-instruction tracelet, we ask the interpreter
to step through N instructions. The interpreter treats FCall as one of
those instructions, and happily starts interp'ing into the callee. This can
cause us to form weird tracelets in the callee that have no business
existing. Instead, break on any control-flow we encounter.
2013-02-13 06:43:40 -08:00
mwilliams 5ed74f1e03 Use Central repo for RepoAuthoritative repo generation
The central repo is always writable, so use that for writing,
and disable the local repo, so that hphp doesnt try to open
the default central repo, which can cause contention if there
are a lot of processes.
2013-02-13 06:43:40 -08:00
bsimmers d9943ae3c5 Fix bugs in cgGetCtxFwdCall
It was testing the low bit of the destination register before
moving the context to be tested into it, and if the low bit of that
register happened to be set already, nothing was ever put into the
destination reg. In certain situations this was causing fatals from a
null $this in instance methods.
2013-02-13 06:43:39 -08:00
mwilliams f109b02c7d Make stack based StringData safer
If you allocate a StringData on the stack, and it escapes,
you're in trouble. Make the destructor assert by default,
and add a StackStringData which does the appropriate
refCounting and checking.
2013-02-13 06:42:47 -08:00
mwilliams ac0ec5696a Fixup some array helpers
Some StringData* helpers were forwarding to CStrRef
methods - which now forward to StringData* methods. Just
call the StringData* methods directly

Other methods were using stack allocated StringData's.
Thats pointlessly risky for slow-path code. Use a String
instead.
2013-02-13 06:42:47 -08:00
mwilliams 64b6d99ef2 More gcc-4.7.1 compilation issues 2013-02-13 06:42:47 -08:00
mwilliams e300bc8cce Fix nemo warnings
Setup for parameters was skipped by some cleanup of the use of
the WholeProgram flag. Re-enable it.
2013-02-13 06:42:47 -08:00
andrewparoski bc6703d03f Improve the 'key not present' error message for collections 2013-02-13 06:42:47 -08:00
jan aee58ac19e Allow null dependency in tail call
If a continuation yields null in a tail call, we end up failing on
throw_null_pointer_exception. Fix it.
2013-02-13 06:42:47 -08:00
ottoni c375ba70b0 Enable arithmetic involving FP
Although support for FP arithmetic was added to HHIR codegen, it was
not plumbed through.  So it was still punting to tx64 or interpreting
FP Add, Sub, Mul.  This diff enables these instructions, which had to
be changed to have a TypeParam.

This diff also implements some checking for IR instructions' dest
operands in debug mode.

Finally, this diff fixes the 'reduce' script, which got broken when an
HHIR banner generated by DumpIR was changed.
2013-02-13 06:42:47 -08:00
kma 1643cb1d17 Capture the before-punt part of the trace in IR.
Try to scoop up some more instructions in the IR. In a
sequence like:

  BoringInstrA
  BoringInstrB
  PuntInstr
  BoringInstrC

we used to punt the entire tracelet to interp. Now we do:

hhir:
  BoringInstrA
  BoringInstrB

interp:
  PuntInstr
  BoringInstrC

Rescuing BoringInstrC from PuntInstr is future work.
2013-02-13 06:42:43 -08:00
jdelong 51f0a5bd3b Style change to the pretty-print format for IR extra data 2013-02-13 06:42:43 -08:00
bsimmers 8572bdeb7f Put predefined type bit patterns in an enum
This enables gdb to print something more useful than just the
raw integer when inspecting a Type. It also made the union macros
self-documenting and cleaned up the initialization in ir.cpp.
2013-02-13 06:42:43 -08:00
jdelong cf7133b406 Remove ConstInstruction and use a new extra data for DefConst 2013-02-13 06:42:43 -08:00
jdelong a44b190095 Remove LdHome from HHIR; use extra data for local ids
Removes all uses of Type::Home in the IR.  Rather than moving
the local ids into SSATmps via DefConst, beefs up IRExtraData (adding
support for CSE-able extra data and pretty printing them), so we can
use a new LocalId extra data type for all these instructions.  This
should save us a decent number of of ConstInstructions and reduce the
size of the IR, while also making accesses to local ids a little less
untyped.
2013-02-13 06:42:43 -08:00
jdelong e69e05711a Remove clearCountedMapNotIn since it seems impossible
The m_locs map is keyed on SSATmps that are Type::Home.  I
can't see any code path that can insert one of these into m_unescaped,
so remove it.
2013-02-13 06:42:43 -08:00
alia 3cf77e2df9 Implemented is[N]Type and JmpIs[N]Type in HHIR. 2013-02-13 06:42:43 -08:00
bsimmers 7fd841e6c1 Limit number of test jobs to 20 max 2013-02-13 06:42:43 -08:00
smith f3ddbe63f1 Remove the LdCls exception from IRInstruction::isEssential
LdCls is not used as a guard so that term should never be true.
2013-02-11 12:39:50 -08:00
bsimmers 1e4b0ed05b PtrTo* only needs one register 2013-02-11 12:39:50 -08:00
jdelong 2406e11d44 Fix a bug in FPushFuncD 2013-02-11 12:39:50 -08:00
mwilliams 1e54607680 Dont use free for low_malloc'd memory
In the case of a race to create a new class, we did
that, corrupting jemalloc's data structures in strange ways
(it didnt notice, even with a debug build, but bad things
happened later)
2013-02-11 12:04:10 -08:00
jan 28116234fb Use o_instanceof instead of dynamic_cast<>
Avoid dynamic_cast<> on fast paths, use o_instanceof(StaticString)
instead. Saves about ~0.2-0.3% of CPU time.

Once HPHPc is gone, we will convert these calls to HHVM-specific API.

Thanks @bmaurer for discovery and @mwilliams for suggestion how to fix
it.
2013-02-11 12:04:10 -08:00
Sara Golemon 337f15cc8d Sync system generated files 2013-02-11 11:45:25 -08:00
mwilliams a18dd73d06 Better initTrace
The old version read the first element of the array into $top,
checked some values on it, and then either shifted the first
element into $frame, or shifted it off and discarded it.

So its cheaper to just shift it directly into $top, and then
use $top rather than $frame.
2013-02-11 10:10:23 -08:00
jdelong 067e38c23d Fix exception safety for inline returns
If the profiler hook throws (and it used to not be able to),
we can decref a local twice.  The generic return case is safe, but the
inline return case wasn't zeroing the type.
2013-02-11 10:10:23 -08:00
mwilliams 2db86bd721 Remove pthread_setspecific calls
ThreadLocalSingleton had an s_key static property, and called
pthread_setspecific using it. In the USE_GCC_FAST_TLS case. Both
are unneeded, so remove them.
2013-02-11 10:09:31 -08:00
jan 527a155239 Export is{Finished,Succeeded,Failed} methods to PHP
In some cases, it may be useful to be able to check the current status
of wait handle. One particular use case is DynamicYield's get().
2013-02-11 08:55:54 -08:00
mwilliams 1bc06a26cf Fixes for gcc-4.7.1 compilation
g++-4.7.1 treats "FOO"bar as a c++-11 literal operator, even
if bar is a macro with an expansion such as "BAR" - so add a space
after the quote (this seems like a bug, and I fixed a bunch of these
a while ago, but we just added a slew of PRI*64 macros which break
under 4.7.1).

Also, it warned that "explicit by-copy capture of 'this' redundant"
for a lambda declared [=, this] - so I removed the this.

We also needed more than the 60 levels of template expansion that was
allowed by the makefile.
2013-02-11 08:55:54 -08:00
jdelong 76681bfc14 Print arena size after HHIR translations 2013-02-11 08:55:54 -08:00
jan 740868989c Make the individual bytes of o_subclassData easily accessible 2013-02-11 08:55:54 -08:00
jan 26c2654528 Less virtual calls, more predictive layout 2013-02-11 08:55:54 -08:00
jdelong bab632f35a Remove MarkerInstruction and use extra data for it 2013-02-11 08:13:21 -08:00
jdelong 51ffd22f03 Implement SSwitch; add support for instruction "extra data"
Implements the SSwitch opcode.

This involved adding an instruction that needed a whole switch table
of compile-time data, so rather than encode this in SSATmps or adding
a new internal type this adds support for every instruction to have an
associated C++ structure of extra data.  We should be able to move
Marker Label and ConstInstruction to use this, so this extra pointer
will be free since the vtable will be able to go away; we'll also be
able to save down this route by allocating fewer ConstInstructions for
compile-time data.
2013-02-11 08:13:21 -08:00
jan 03c8d47acb Unbox references in CWH::markCurrentAsSucceeded()
Seems that the calling convention allows values to be passed by
reference and it's up to the extension to unbox them.
2013-02-11 08:13:21 -08:00
Sara Golemon a4903eb14e Modify undefined property error to match Zend 2013-02-11 08:12:53 -08:00
Sara Golemon 9efb3c8e25 Expose driver-specific error code in PDOException
Match Zend behavior
2013-02-11 08:12:53 -08:00
mwilliams c21f2c3966 We dont need a boost::shared_ptr for m_bt in ExtendedException
Array is already a SmartPtr, wrapping it in another is pointless.
Except it turns out that there are include dependencies preventing
us from using Array here. Use boost::intrusive_ptr to wrap an
ArrayData instead.
2013-02-11 08:12:53 -08:00
jdelong 3a5087077f Fix release build wrt zend_html.cpp 2013-02-11 07:54:46 -08:00
jdelong dc8cc4656b Make the parser possible to test without compiling libhphp_runtime.a 2013-02-11 06:59:06 -08:00
jdelong 00b2101db0 Move zend_html to util so util/parser doesn't depend on the runtime 2013-02-11 06:59:06 -08:00
jdelong 3248a83461 Make ext_string not depend on zend_html, so it can be moved to util
The parser depends on zend_html, which means it's impossible
to write parser unit tests without compiling all of hphp.  This
separates the pieces that use runtime structures into ext_string, so
the rest can be moved to util/zend.  There was a raise_warning call
indirectly from string_html_decode (in determine_charset), but the
runtime use of string_html_decode already checks that it is a
supported charset and otherwise throws NotImplementedException, so I
removed this.  Another diff will do the actual move to util.
2013-02-11 06:59:06 -08:00
ptarjan 14c9e841c8 capital C in Closure
According to our PHP style guide http://www.phabricator.com/docs/phabricator/article/PHP_Coding_Standards.html classes start with capital letters
2013-02-11 06:59:06 -08:00
ptarjan 839bb3103e break out emitFPass 2013-02-11 06:59:06 -08:00
andrewparoski dec99505a6 Refactor iterator logic
This diff refactors some of the VM's logic for iterators (with a focus on
mutable iteration), delivering several improvements:
  1) MIterCtx was renamed to MArrayIter, and the m_key and m_val fields
     were eliminated.
  2) Eliminated the need for MArrayIter to dynamically allocate a
     MutableArrayIter object, and removed other layers of indirection as
     well.
  3) Reduced the size of HPHP::VM::Iter from 64 bytes down to 32 bytes.
  4) Removed the "if (siPastEnd())" check when adding a new element to an
     HphpArray or a ZendArray.
  5) Moved all of the iterator logic into a single .cpp file.

This diff reworks FullPos's to point to current element instead of pointing
to the next element. It also splits up the IterFree instruction into two
instructions (IterFree and MIterFree). These changes allowed various logic
to be simplified and data structures to be reduced in size. There is
definitely more opportunity for refactoring, but I know the JIT helpers for
iteration have been carefully tuned and so I'll leave further refactoring
for future diffs.

Finally, I spent a little time cleaning up the bytecode spec a bit, mostly
with respect to iteration.
2013-02-11 06:36:51 -08:00
ptarjan f3cf268bb1 create onClosureStart
For parity with onFunctionStart we should have an onClosureStart. It was really confusing to start with onFunctionStart and then end with onClosure.
2013-02-11 06:36:50 -08:00
ptarjan 5455370de0 add space to error message 2013-02-11 06:36:50 -08:00
ottoni 116bb2c662 Mark runtime helpers as hot, and get rid of some of them
This diff marks runtime helpers called from HHIR-generated code as hot.
In addition, it also eliminates some of the intermediate helpers.
2013-02-11 06:36:50 -08:00
jdelong cc5cc6d5cb Print IRInstruction iids instead of m_id in IRInstruction::print 2013-02-11 06:36:50 -08:00
bertrand cc863bf301 Implement AKExists in HHIR
Implements AKExists in HHIR.  I've added a new IR opcode for it which
basically generates a call to helper method (which is very similar to
the tx64 helper).  The major difference is putting the decrefs in the
IR rather than the helper.
2013-02-11 06:36:50 -08:00
bsimmers e09dbb6f89 Add Type::equals 2013-02-11 06:36:30 -08:00
mwilliams 5acac417e2 Fix issues when Exception::__init__() throws
We were calling Exception::__init__() from Instance's constructor.
If an exception is thrown (eg timeout), then we capture a backtrace
including $this pointers, unwind, and then pass the backtrace
to the error handler. But as c++ unwinds through Instance's
constructor, it calls ObjectData's destructor - so we end up
with a lame instance that doesnt work too well.

In addition, we were failing to correctly refCount the object
so we would leak its memory (and properties).
2013-02-11 06:36:29 -08:00
bsimmers b17ec4702a Kill Type::Tag and make Type an opaque class backed by a bitvector
Every non-union type has been assigned a bit between 0 and
47. This includes Boxed*, PtrTo*, and PtrToBoxed* for all PHP-visible
types. There are static const members of Type for all of these, along
with all the union types that used to be in Type::Tag (Cell, Gen,
Uncounted, etc...). Arbitrary union types can now be easily composed
using Type::operator|. Switching on types and comparing using any
operator other than == are no longer allowed. Also gone is
Type::isRefCounted (since the exact needs of code using it can be
unclear), replaced by maybeCounted and notCounted.

Type::Tag still exists as a typedef for Type itself; I'm going to fix
up all of the useage sites in a separate diff to keep this more
manageable. I'm also planning on introducing operator< and operator<=
on Type as synonyms for structSubtypeOf and subtypeOf,
respectively. That will happen after this diff goes in, ensuring that
all old-style uses of those comparisons are dead.
2013-02-11 06:36:22 -08:00
mwilliams f6efec4d7c Enable CodeError generation under hhvm
Just when running as hphp, so we can use the USE_HHVM version
of hphp to generate the reports
2013-02-11 06:32:54 -08:00
mwilliams 75948e48f7 Fix leaks/asserts in ext_curl
When a callback throws an exception, we catch it, and then handle
the exception after the libcurl function that invoked the callback
returns.

But we weren't checking the exception after some libcurl calls that
could invoke callbacks, which leaked the exception object in RELEASE
builds, and asserted in DEBUG builds.
2013-02-11 06:32:54 -08:00
bsimmers 3286e1d7c0 Check RuntimeOption::RepoCommit in Repo::commitUnit instead of callers 2013-02-11 06:32:54 -08:00
Sara Golemon d6be84e026 Sync system files 2013-02-11 06:31:00 -08:00
jan 0763e566f4 Instance::cloneSet: do not initialize dynamic properties if no properties to clone
Instance::cloneSet() unnecessarily creates HPHP array even if cloned
object has no dynamic properties. Don't do this.
2013-02-11 06:07:23 -08:00
smith 8c57d016d5 Move dead code elimination state out of IRInstruction
Introduced a state table for DCE, indexed by IId, instead
of using IRInstruction.id for the transient state of DCE
analysis.
2013-02-11 06:07:23 -08:00
ottoni 44dc3b58f4 Make memelim less conservative about locals
The memelim pass was being overly conservative by assuming that all
boxed locals can be modified by any instruction marked as
MayModifyRefs.  However, we know that this cannot happen for locals
that have not escaped.  This diff uses this fact to be less
conservative for functions other than pseudo-mains.
2013-02-11 06:07:23 -08:00
jdelong b24236e602 Re-enable InstanceOfD translation and fix NInstanceOf 2013-02-11 06:07:23 -08:00
mwilliams dbab820ff4 The fixup for a generator shouldnt include any locals
If a "surprise" happened during the prolog for a continuation,
the fixed-up $rbx was too low by the number of locals. This
resulted in the unwinder trying to unwind through garbage,
with unpredictable results
2013-02-11 06:07:23 -08:00
Owen Yamauchi 441fb578ec Delete pathtrack
This isn't being used right now.
2013-02-11 06:07:22 -08:00
aalexandre 755d38157f Fix serialization test failure introduced by inttypes
It seems serialization is sensitive to the alias name for int64.
2013-02-11 06:07:22 -08:00
andrewparoski 4196354f4f Fix parse issues with function types
Fix the parser to handle function types for functions that have no formal
parameters and for functions that take variable arguments.
2013-02-11 06:07:22 -08:00
andrewparoski f7dd399bd6 Fix handling of "$obj->{__FUNCTION__}" and "$obj->{__CLASS__}"
For "$obj->{__FUNCTION__}" and "$obj->{__CLASS__}", we currently don't
recognize __FUNCTION__ and __CLASS__ as "magic constants" and we just use
the literal strings "__FUNCTION__" and "__CLASS__" for the property names.
This is wrong. Same goes for object method calls (ex. "$obj->{..}(..)").

This diff fixes HipHop to recognize magic constants for properties names in
"$obj->{..}" expressions and method names in "$obj->{..}(..)" expressions.
2013-02-11 06:07:22 -08:00
mwilliams 0b586489c9 Remove a too aggressive assert
There was an assert that the statically inferred type was at least
as good as the runtime type. The new test case shows clearly why
that cannot be the case (and we were tripping over this assert
in production).
2013-02-11 06:07:21 -08:00
jdelong 31222e2c84 Fix another bug in "Fix a bug in exception handling"
When we skip EHEnts for catch blocks that we aren't going to
enter, we still need to count them in the handledCount so that we run
the appropriate fault funclet (see the unit test).  Before this
change, it would fail to account for the catch handler and end up
running the fault funclet twice, leading to an assertion about
IterFree trying to free an already-freed iterator.
2013-02-11 06:07:21 -08:00
smith d87811fd9b Support control flow in HHIR
Add support for in-trace control flow to HHIR.  To support
merging SSATmps, add support for 1+ sources to Jmp_ and
1+ destinations to DefLabel.  (One must use Jmp_ to pass
values to a label).

Every pass is affected by this.  The linearscan passes were
modified to visit blocks in reverse postorder (linear scan
should work even early blocks are not dominators of later
blocks).

Other passes had to be pessimized a bit because the dominator
assumption no longer holds.  Either they need to do full
dataflow analysis, be changed to do preorder traversals
down the dominator tree, changed to work only on a block
at a time, or fully disabled if control flow is detected.
2013-02-11 06:07:21 -08:00
smith 6492f650f5 Disable spilling to MMX registers in HHIR
... Until we have an assert(m_curInst->isNative()) in cgCallHelper
and fix our flags.  Without accurate flags, we can choose to spill
to an MMX register, then do a call anyway.
2013-02-11 06:07:21 -08:00
aalexandre d09fd3e421 inttypes conversion broken down by steps.
Per @mwilliams' suggestion, this is the first stage in a staggered approach to replacing int64 with int64_t. More precisely I inserted "typedef ::int64_t int64;" in util/base.h and dealt with the consequences.
2013-02-11 06:07:07 -08:00
jdelong 5baef2646d Disable InstanceOfD
This is breaking HHIR sandboxes and trunk should work.  I'm
not sure how long it will take me to debug so let's turn it off for
now.
2013-02-11 03:50:50 -08:00
alia 22541f491c Improved HHIR translation for AGetC.
Improved the way HHIR translates AGetC by generating a
defConst<Class> for the case where the loaded class is the same as or
a superclass of the context class. This eliminates an unnecessary
runtime class lookup (LdCls). Documented LdClsPropAddr.
2013-02-11 03:50:50 -08:00
smith a4b2eea173 Spill and Reload take any input type
were marked S(Gen) but can be anything, e.g. Gen*.  Also, Mov needed to be unrestricted.  Added an assert and todo in case Mov is used with types that use both registers.
2013-02-11 03:44:07 -08:00
ottoni e3d0c97679 Implement NullUninit 2013-02-11 03:44:07 -08:00
jdelong 795400d899 Table-drive type checking for IR opcodes
The way type.cpp was handling return types wasn't quite
sufficient for multiple destinations and unfortunately added a step
that's easy to forget when you add new opcodes with the HasDest flag.
Also, assertions about operand types are too distributed through the
code.  Pull everything into a central table, similar to hhbc.h.  This
still leaves several opcodes without type signatures, since figuring
it out got a bit tedious for now.  It did uncover a few documentation
bugs in ir.specification, though.
2013-02-11 03:44:07 -08:00
jdelong 5483751a25 Fix a bug when you print a DefConst of a NamedEntityPtr 2013-02-11 03:44:07 -08:00
jdelong 4797536f9d Fix a bug when throwing from nested FPI regions
Since we now use the original throw location to figure out
which EHEnt applies when propagating from a fault funclet, we need to
check whether we've already unwound the ActRec before doing it again.
Doing it again could cause issues with nested FPI regions (hitting an
assert in sandboxes).
2013-02-11 03:44:06 -08:00
mwilliams c67d35c50a The trouble with magic numbers...
... is that once you put them into the codebase, the odds of them
showing up where you dont want them go up dramatically.

With a gcc 4.7.1 -O0 build I hit a consistent crash in production
where debugBacktrace had stepped too far, thinking that the
first non-vm frame was in fact a vm frame. It did so because
it looked at the word above the frame, and saw that it contained
c_Continuation::kMagic.

It turns out that fixupWork's isVMFrame left kMagic in $rdx if the
frame really was a generator ($rdx was dead, but still). We then
return from the tc via a serviceReq that doesnt need $rdx (so doesnt
set it), and then (with a following wind, so that $rdx hasnt been
smashed yet) enterTCHelper stores $rdx in info.args[1].

By another staggering coincidence, info.args[1] is at exactly the
right address to make the call into enterTCHelper /look/ like a
a continuation (based on kMagic). So if we then have a catch
which re-enters the TC there's a good chance kMagic is still there
and the next debugBacktrace (or uncaught exception) will crash.

This diff rewrites everything in terms of the C++ stack; we
now say that its a VM frame if its not on the C++ stack.
2013-02-11 03:44:06 -08:00
mwilliams 453c9938c9 Dont leave 1.txt lying around 2013-02-11 03:44:06 -08:00
Owen Yamauchi 3e19c90b93 Macros for getting machine registers
This kind of business obviously doesn't work on non-x64 platforms, so
let's abstract it out into platform-specific macros. I've also changed
some of the associated variable names, to try to break people of the
habit of only thinking about x64.

There are a few instances of this trick remaining, referencing rbx and
r15. These are a little more of a pain to abstract out because their
roles are defined by our internal ABI rather than an architecture's ABI,
so their ARM equivalents will have to be tied to our ARM register
allocation convention. These will have to wait a bit.
2013-02-11 03:44:06 -08:00
bertrand bed8c0e6fe Don't allow nulls in regexes passed to preg_replace
Summary: If we allow null characters in a regex, then a user could construct a string that ends with "/e\0" and surreptitiously execute an arbitrary piece of code.  With this patch we fatal if a null is found anywhere before the end of the regex (determined by length()).
2013-02-11 03:44:06 -08:00
aravind 1b83a11ee8 Fix shuffleArgs
shuffleArgs() logic was broken if rScratch was used as part
of the suffle.
2013-02-11 03:44:06 -08:00
ottoni 94081c2cbc Get rid of "Ptr" from the name of internal types
shorter
more consistent with other types (e.g. Obj is a pointer too)
makes some of them more consistent, e.g. FuncClassPtr should
actually be FuncPtrClassPtr in the old scheme
2013-02-11 03:44:06 -08:00
mwilliams c7959bbe6a Fix some warnings at -O1
While tracking down gcc 4.7.1 issues I tried a -O1 build,
and got uninitialized variable warnings. None of them is
real, and at -O3 the compiler can figure out that the variables
are never actually used uninitialized.
2013-02-11 03:44:06 -08:00
alia 94aed72d26 Implemented Issets in HHIR.
Implemented Issets in HHIR. Added 2 new IR opcodes,
IsTypeMem<T> and IsNTypeMem<T>, that test the type of a given Cell* or
Gen*. We should be able to use them for IssetM as well. Fixed a bug in
simplifyIsType: We were not correctly folding the isType query when
the source was a static string. Removed a few unnecessary instructions
that cleared the top bits of a boolean register; booleans are
represented only in the lower byte of a register so its unnecessary to
clear the top bits.
2013-02-11 03:44:06 -08:00
aravind 6ac06a50eb Fix RetCtrl for Eval.HHIRGenerateAsserts=1 2013-02-11 03:44:05 -08:00
aravind 5e7c9d3457 Fix assert for LdRetAddr 2013-02-11 03:44:05 -08:00
aravind 1eeb100510 Delay pushing return address till RetCtrl
LdRetAddr used to modify rsp, which would break
any rsp relative Reload instructions before returning.
2013-02-11 03:44:05 -08:00
ottoni 6a4ec5ae0e Implement FPushClsMethodF in HHIR 2013-02-11 03:44:05 -08:00
mwilliams 52697c84a0 Fix profiling of continuations
In the interpreter, neither ContEnter nor ContExit checked the
surprise flags, resulting in the time being attributed to the
callers. In the jit, ContEnter checked the surprise flags, but
ContExit did not, resulting in what appeared to be highly
recursive profiles.
2013-02-11 03:44:05 -08:00
ottoni c4956fac10 Fix LdClsCns
The loaded value is guaranteed to be a Cell, so no exit label (and
check) is needed.
2013-02-11 03:44:05 -08:00
Sara Golemon ddd8720bf4 Update third_party/folly 2013-02-11 03:44:05 -08:00
vini 84d9aa7236 Update ext_icu_spoof test 2013-02-11 02:16:23 -08:00
Jordan Delong 363d1bb20f Code move src/ -> hphp/
This change is mostly for FB internal organizational reasons.
Building is not effected beyond the fact that the target now
lands in hphp/hhvm/hhvm rather than src/hhvm/hhvm.
2013-02-11 02:10:41 -08:00
9492 arquivos alterados com 322330 adições e 843681 exclusões
+37 -78
Ver Arquivo
@@ -8,102 +8,61 @@
.mkdir
hphp.log
/src/test/test
/src/test/test_fast.inc
/src/test/test_mysql_info.inc
/src/test/test_suite.inc
/src/test/real_mysql_info.inc
/src/test/*.tmp
/src/test/vm/*.out
/src/test/vm/*.diff
/src/test/vm/*.log
/src/test/vm/*.reduce_out
/src/test/vm/*.reduce_diff
/src/test/vm/*.reduce_exp
/src/test/vm/*.perf
/src/test/vm/perf/*.out
/src/test/vm/perf/*.diff
/src/test/vm/perf/*.perf
/src/runtime/ext_hhvm/xconstants.php
/src/runtime/ext_hhvm/ext_hhvm_infotabs.cpp
/src/runtime/ext_hhvm/ext_noinline.cpp
/src/compiler/analysis/code_error.inc
/hphp/test/test
/hphp/test/test_fast.inc
/hphp/test/test_mysql_info.inc
/hphp/test/test_suite.inc
/hphp/test/real_mysql_info.inc
/hphp/test/*.tmp
/hphp/test/vm/*.out
/hphp/test/vm/*.diff
/hphp/test/vm/*.log
/hphp/test/vm/*.reduce_out
/hphp/test/vm/*.reduce_diff
/hphp/test/vm/*.reduce_exp
/hphp/test/vm/*.perf
/hphp/test/vm/perf/*.out
/hphp/test/vm/perf/*.diff
/hphp/test/vm/perf/*.perf
/hphp/runtime/ext_hhvm/ext_noinline.cpp
/hphp/compiler/analysis/code_error.inc
/src/runtime/tmp/Test*
/src/runtime/tmp/run
/src/runtime/tmp/run.sh
/src/runtime/tmp/libtest.so
/src/runtime/vm/repo_schema.h
/src/runtime/base/compiler_id.h
/hphp/runtime/tmp/Test*
/hphp/runtime/tmp/run
/hphp/runtime/tmp/run.sh
/hphp/runtime/tmp/libtest.so
/hphp/runtime/vm/repo_schema.h
/src/hphpi/gen
/src/hphpi/hphpi
/hphp/hphpi/gen
/hphp/hphpi/hphpi
/src/hhvm/gen
/src/hhvm/hhvm
/src/hhvm/hphp
/hphp/hhvm/gen
/hphp/hhvm/hhvm
/src/tools/shmw/shmw
/hphp/tools/shmw/shmw
/src/ffi/java/classes
/src/ffi/java/hphp_ffi_java.h
/hphp/ffi/java/classes
/hphp/ffi/java/hphp_ffi_java.h
# Ignore all makefiles generated for fbcode's third-party repo
/bin/*.mk
!/bin/run.mk
/facebook/arcanist/.phutil_module_cache
/facebook/autoload_files
/facebook/hotcold.hdf
/facebook/benchmark/shootout/output_bench
/facebook/benchmark/shootout/output_bintree_new
/facebook/libcore_files
/facebook/output_*
/facebook/push/install
/facebook/stub_funcs.hdf
/facebook/volatile.hdf
/facebook/www.pid
/facebook/push/apc
/facebook/push/apclog
/facebook/push/autoload_files
/facebook/push/hotcold.hdf
/facebook/push/output_www
/facebook/push/system.tgz
/facebook/push/volatile.hdf
/facebook/push/stub_funcs.hdf
/facebook/push/output
/facebook/push/shared
/facebook/extensions/gen
/facebook/extensions/hphpi
/local/*.mk
CMakeFiles
CMakeCache.txt
cmake_install.cmake
/output_gd/
/src/TAGS
# arcanist working-copy settings
.arc/
# fbcode output directory
/bin/_fbcode_bin/
/hphp/TAGS
# Generated makefiles
/src/third_party/lz4/Makefile
/src/third_party/double-conversion/Makefile
/src/third_party/folly/Makefile
/src/runtime/ext_hhvm/Makefile
/src/test/Makefile
/src/hphp/Makefile
/src/hhvm/Makefile
/src/compiler/Makefile
/src/Makefile
/hphp/runtime/ext_hhvm/Makefile
/hphp/test/Makefile
/hphp/hphp/Makefile
/hphp/hhvm/Makefile
/hphp/compiler/Makefile
/hphp/Makefile
/Makefile
# eclipse files
+13 -18
Ver Arquivo
@@ -19,12 +19,7 @@ include(CheckFunctionExists)
# boost checks
find_package(Boost 1.37.0 COMPONENTS system;program_options;filesystem REQUIRED)
if (BOOST_VERSION EQUAL 104200)
# Boost bug #3942 prevents us using 1.42
message(FATAL_ERROR "Boost 1.42 is not compatible with HipHop")
endif()
find_package(Boost 1.48.0 COMPONENTS system program_options filesystem regex REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
@@ -134,15 +129,15 @@ find_package(EXPAT REQUIRED)
include_directories(${EXPAT_INCLUDE_DIRS})
# SQLite3 + timelib are bundled in HPHP sources
include_directories("${HPHP_HOME}/src/third_party/libsqlite3")
include_directories("${HPHP_HOME}/src/third_party/timelib")
include_directories("${HPHP_HOME}/src/third_party/libafdt/src")
include_directories("${HPHP_HOME}/src/third_party/libmbfl")
include_directories("${HPHP_HOME}/src/third_party/libmbfl/mbfl")
include_directories("${HPHP_HOME}/src/third_party/libmbfl/filter")
include_directories("${HPHP_HOME}/src/third_party/lz4")
include_directories("${HPHP_HOME}/src/third_party/double-conversion/src")
include_directories("${HPHP_HOME}/src/third_party/folly")
include_directories("${HPHP_HOME}/hphp/third_party/libsqlite3")
include_directories("${HPHP_HOME}/hphp/third_party/timelib")
include_directories("${HPHP_HOME}/hphp/third_party/libafdt/src")
include_directories("${HPHP_HOME}/hphp/third_party/libmbfl")
include_directories("${HPHP_HOME}/hphp/third_party/libmbfl/mbfl")
include_directories("${HPHP_HOME}/hphp/third_party/libmbfl/filter")
include_directories("${HPHP_HOME}/hphp/third_party/lz4")
include_directories("${HPHP_HOME}/hphp/third_party/double-conversion/src")
include_directories("${HPHP_HOME}/hphp/third_party/folly")
FIND_LIBRARY(XHP_LIB xhp)
FIND_PATH(XHP_INCLUDE_DIR xhp_preprocess.hpp)
@@ -151,7 +146,7 @@ if (XHP_LIB AND XHP_INCLUDE_DIR)
include_directories(${XHP_INCLUDE_DIR})
set(SKIP_BUNDLED_XHP ON)
else()
include_directories("${HPHP_HOME}/src/third_party/xhp/xhp")
include_directories("${HPHP_HOME}/hphp/third_party/xhp/xhp")
endif()
# ICU
@@ -351,8 +346,8 @@ endif()
# message(FATAL_ERROR "Flex is too old, found ${FLEX_VERSION} and we need 2.5.33")
#endif()
include_directories(${HPHP_HOME}/src)
include_directories(${HPHP_HOME}/src/system/gen)
include_directories(${HPHP_HOME}/hphp)
include_directories(${HPHP_HOME}/hphp/system/gen)
macro(hphp_link target)
if (GOOGLE_HEAP_PROFILER_ENABLED OR GOOGLE_CPU_PROFILER_ENABLED)
+9 -4
Ver Arquivo
@@ -50,6 +50,11 @@ add_definitions(-DHHVM_LIB_PATH_DEFAULT="${HPHP_HOME}/bin")
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
add_definitions(-DRELEASE=1)
add_definitions(-DNDEBUG)
message("Generating Release build")
else()
add_definitions(-DDEBUG)
message("Generating DEBUG build")
endif()
if(INFINITE_LOOP_DETECTION)
@@ -112,10 +117,10 @@ endif()
IF($ENV{CXX} MATCHES "icpc")
set(CMAKE_C_FLAGS "-no-ipo -fp-model precise -wd584 -wd1418 -wd1918 -wd383 -wd869 -wd981 -wd424 -wd1419 -wd444 -wd271 -wd2259 -wd1572 -wd1599 -wd82 -wd177 -wd593 -w")
set(CMAKE_CXX_FLAGS "-no-ipo -fp-model precise -wd584 -wd1418 -wd1918 -wd383 -wd869 -wd981 -wd424 -wd1419 -wd444 -wd271 -wd2259 -wd1572 -wd1599 -wd82 -wd177 -wd593 -fno-omit-frame-pointer -ftemplate-depth-60 -Wall -Woverloaded-virtual -Wno-deprecated -w1 -Wno-strict-aliasing -Wno-write-strings -Wno-invalid-offsetof -fno-operator-names")
set(CMAKE_CXX_FLAGS "-no-ipo -fp-model precise -wd584 -wd1418 -wd1918 -wd383 -wd869 -wd981 -wd424 -wd1419 -wd444 -wd271 -wd2259 -wd1572 -wd1599 -wd82 -wd177 -wd593 -fno-omit-frame-pointer -ftemplate-depth-120 -Wall -Woverloaded-virtual -Wno-deprecated -w1 -Wno-strict-aliasing -Wno-write-strings -Wno-invalid-offsetof -fno-operator-names")
else()
set(CMAKE_C_FLAGS "-w")
set(CMAKE_CXX_FLAGS "-fno-gcse -fno-omit-frame-pointer -ftemplate-depth-60 -Wall -Woverloaded-virtual -Wno-deprecated -Wno-strict-aliasing -Wno-write-strings -Wno-invalid-offsetof -fno-operator-names -Wno-error=array-bounds -Wno-error=switch -std=gnu++0x -Werror=format-security -Wno-unused-result -Wno-sign-compare")
set(CMAKE_CXX_FLAGS "-fno-gcse -fno-omit-frame-pointer -ftemplate-depth-120 -Wall -Woverloaded-virtual -Wno-deprecated -Wno-strict-aliasing -Wno-write-strings -Wno-invalid-offsetof -fno-operator-names -Wno-error=array-bounds -Wno-error=switch -std=gnu++0x -Werror=format-security -Wno-unused-result -Wno-sign-compare")
endif()
IF(CMAKE_COMPILER_IS_GNUCC)
@@ -126,6 +131,6 @@ IF(CMAKE_COMPILER_IS_GNUCXX)
SET (CMAKE_CXX_FLAGS_RELEASE "-O3")
ENDIF()
include_directories(${HPHP_HOME}/src)
include_directories(${HPHP_HOME}/src/lib/system/gen)
include_directories(${HPHP_HOME}/hphp)
include_directories(${HPHP_HOME}/hphp/lib/system/gen)
include_directories(${HPHP_HOME})
+2 -2
Ver Arquivo
@@ -20,7 +20,7 @@ SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/HPHPFunctions.cmake")
include(CheckFunctionExists)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/hphp)
IF(CMAKE_SIZEOF_VOID_P EQUAL 4)
message(FATAL_ERROR "32-bit support is currently unsupported, check back with a later version of HipHop")
@@ -30,5 +30,5 @@ if ("$ENV{USE_HHVM}" STREQUAL "1")
message("Building for HHVM")
endif()
if ("$ENV{USE_HPHPC}" STREQUAL "1")
message("Building for HPHPc")
message(FATAL_ERROR "Building HPHPc is no longer supported")
endif()
+6 -2
Ver Arquivo
@@ -31,13 +31,17 @@ The latest information is available on the [wiki](http://wiki.github.com/faceboo
* libexpat
* libmemcached
* google-glog (http://code.google.com/p/google-glog/)
* libc-client2007
* libdwarf
* libelf
* libunwind
The following packages have had slight modifications added to them. Patches are provided and should be made against the current source copies.
* [libcurl](http://curl.haxx.se/download.html)
* src/third_party/libcurl.fb-changes.diff
* hphp/third_party/libcurl.fb-changes.diff
* [libevent 1.4](http://www.monkey.org/~provos/libevent/)
* src/third_party/libevent-1.4.13.fb-changes.diff OR src/third_party/libevent-1.4.14.fb-changes.diff
* hphp/third_party/libevent-1.4.14.fb-changes.diff
## Installation
+3 -16
Ver Arquivo
@@ -179,16 +179,14 @@ class Exception {
// Remove top stack frames up to and including Exception::__init__,
// set the 'file' and 'line' properties appropriately
while (!empty($this->trace)) {
$top = $this->trace[0];
$top = array_shift($this->trace);
if (isset($top['class']) && isset($top['function']) &&
strcasecmp($top['class'], 'exception') === 0 &&
strcasecmp($top['function'], '__init__') === 0) {
$frame = array_shift($this->trace);
if (isset($frame['file'])) $this->file = $frame['file'];
if (isset($frame['line'])) $this->line = $frame['line'];
if (isset($top['file'])) $this->file = $top['file'];
if (isset($top['line'])) $this->line = $top['line'];
return;
}
array_shift($this->trace);
}
}
@@ -5568,17 +5566,6 @@ class XhprofFrame {
}
}
// Used as the base class for all closures
class Closure {
protected $__static_locals;
// Adding a dummy __sleep() to return an illegal value to make the code
// go through error handling path
public function __sleep() {
return false;
}
}
// Used as a sentinel type in 86pinit().
class __pinitSentinel {
}
+113
Ver Arquivo
@@ -0,0 +1,113 @@
#
# +----------------------------------------------------------------------+
# | HipHop for PHP |
# +----------------------------------------------------------------------+
# | Copyright (c) 2010 Facebook, Inc. (http://www.facebook.com) |
# | Copyright (c) 1997-2010 The PHP Group |
# +----------------------------------------------------------------------+
# | This source file is subject to version 3.01 of the PHP license, |
# | that is bundled with this package in the file LICENSE, and is |
# | available through the world-wide-web at the following url: |
# | http://www.php.net/license/3_01.txt |
# | If you did not receive a copy of the PHP license and are unable to |
# | obtain it through the world-wide-web, please send a note to |
# | license@php.net so we can mail you a copy immediately. |
# +----------------------------------------------------------------------+
#
include(HPHPSetup)
# HHVM Build
SET(USE_HHVM TRUE)
SET(ENV{HHVM} 1)
ADD_DEFINITIONS("-DHHVM -DHHVM_BINARY=1 -DHHVM_PATH=\\\"${HPHP_HOME}/hphp/hhvm/hhvm\\\"")
set(RECURSIVE_SOURCE_SUBDIRS runtime/base runtime/eval runtime/ext runtime/vm system util)
foreach (dir ${RECURSIVE_SOURCE_SUBDIRS})
auto_sources(files "*.cpp" "RECURSE" "${CMAKE_CURRENT_SOURCE_DIR}/${dir}")
list(APPEND CXX_SOURCES ${files})
auto_sources(files "*.c" "RECURSE" "${CMAKE_CURRENT_SOURCE_DIR}/${dir}")
list(APPEND C_SOURCES ${files})
endforeach(dir ${RECURSIVE_SOURCE_SUBDIRS})
# Disable hardware counters off of Linux
if(NOT LINUX)
add_definitions(-DNO_HARDWARE_COUNTERS)
list(REMOVE_ITEM CXX_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/runtime/base/hardware_counter.cpp)
endif()
# remove ext_hhvm, util/tests, and runtime/vm/translator/test
foreach (file ${CXX_SOURCES})
if (${file} MATCHES "ext_hhvm")
list(REMOVE_ITEM CXX_SOURCES ${file})
endif()
if (${file} MATCHES "util/test")
list(REMOVE_ITEM CXX_SOURCES ${file})
endif()
if (${file} MATCHES "runtime/vm/translator/test")
list(REMOVE_ITEM CXX_SOURCES ${file})
endif()
if (${file} MATCHES "runtime/vm/translator/hopt/test")
list(REMOVE_ITEM CXX_SOURCES ${file})
endif()
endforeach(file ${CXX_SOURCES})
# remove ext/sep for hhvm
foreach (file ${CXX_SOURCES})
if (${file} MATCHES "ext/sep")
list(REMOVE_ITEM CXX_SOURCES ${file})
endif()
endforeach(file ${CXX_SOURCES})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")
if (NOT SKIP_BUNDLED_XHP)
add_subdirectory(third_party/xhp/xhp)
endif()
add_subdirectory(third_party/libafdt)
add_subdirectory(third_party/libmbfl)
add_subdirectory(third_party/libsqlite3)
add_subdirectory(third_party/timelib)
add_subdirectory(third_party/lz4)
add_subdirectory(third_party/double-conversion)
add_subdirectory(third_party/folly)
foreach (CXX_FILE ${CXX_SOURCES})
if(${CXX_FILE} MATCHES ".no.cpp$")
SET_SOURCE_FILES_PROPERTIES(
${CXX_FILE}
PROPERTIES
COMPILE_FLAGS -O0
)
endif()
endforeach()
add_custom_command(
OUTPUT runtime/vm/repo_schema.h
COMMAND hphp/tools/generate_repo_schema.sh
DEPENDS ${CXX_SOURCES} ${C_SOURCES}
WORKING_DIRECTORY ${HPHP_HOME}
COMMENT "Generating Repo Schema ID"
VERBATIM)
ADD_LIBRARY(hphp_runtime_static STATIC runtime/vm/repo_schema.h ${CXX_SOURCES} ${C_SOURCES})
SET_TARGET_PROPERTIES(hphp_runtime_static PROPERTIES OUTPUT_NAME "hphp_runtime")
SET_TARGET_PROPERTIES(hphp_runtime_static PROPERTIES PREFIX "lib")
SET_TARGET_PROPERTIES(hphp_runtime_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")
hphp_link(hphp_runtime_static)
add_subdirectory(compiler)
add_subdirectory(runtime/ext_hhvm)
add_subdirectory(hhvm)
if (NOT "$ENV{HPHP_NOTEST}" STREQUAL "1")
add_subdirectory(test)
endif ()
@@ -1438,7 +1438,7 @@ ExpressionPtr AliasManager::canonicalizeNode(
Expression::UnsetContext)) {
ExpressionPtr rep;
int interf =
findInterf(e, true, rep, NULL, doArrayCSE);
findInterf(e, true, rep, nullptr, doArrayCSE);
add(m_accessList, e);
if (interf == SameAccess) {
if (e->getKindOf() == rep->getKindOf()) {
@@ -1498,12 +1498,13 @@ ExpressionPtr AliasManager::canonicalizeNode(
Expression::UnsetContext))) {
ExpressionPtr rep;
int interf =
findInterf(e, true, rep, NULL, doArrayCSE);
findInterf(e, true, rep, nullptr, doArrayCSE);
if (!m_inPseudoMain && interf == DisjointAccess && !m_cleared &&
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)) {
@@ -1562,9 +1563,9 @@ ExpressionPtr AliasManager::canonicalizeNode(
}
cur = next;
}
if ((!m_inCall || (!hhvm && !ae->getValue()->hasEffect())) &&
if (!m_inCall &&
ae->isUnused() && m_accessList.isLast(ae) &&
!(hhvm && Option::OutputHHBC &&
!(Option::OutputHHBC &&
e->hasAnyContext(Expression::AccessContext |
Expression::ObjectContext |
Expression::ExistContext |
@@ -1835,7 +1836,7 @@ ExpressionPtr AliasManager::canonicalizeRecur(ExpressionPtr e) {
break;
case Expression::KindOfSimpleFunctionCall:
if (!hhvm || !Option::OutputHHBC) {
if (!Option::OutputHHBC) {
SimpleFunctionCallPtr f(spc(SimpleFunctionCall, e));
if (!f->getClass()) {
if (f->getClassName().empty()) {
@@ -3324,7 +3325,7 @@ public:
b->setBit(DataFlow::PRefIn, it->second);
b->setBit(DataFlow::PInitIn, it->second);
}
updateParamInfo(m->getParams(), hhvm && Option::HardTypeHints);
updateParamInfo(m->getParams(), Option::HardTypeHints);
updateParamInfo(m->getFunctionScope()->getClosureVars(), false);
}
}
@@ -3669,7 +3670,7 @@ void AliasManager::doFinal(MethodStatementPtr m) {
}
void AliasManager::performReferencedAndNeededAnalysis(MethodStatementPtr m) {
always_assert(m_graph != NULL);
always_assert(m_graph != nullptr);
// bail out for pseudomain context
if (m->getScope()->inPseudoMain()) return;
@@ -3696,7 +3697,7 @@ void AliasManager::performReferencedAndNeededAnalysis(MethodStatementPtr m) {
}
int AliasManager::copyProp(MethodStatementPtr m) {
if (m_graph == NULL) createCFG(m);
if (m_graph == nullptr) createCFG(m);
performReferencedAndNeededAnalysis(m);
@@ -3719,13 +3720,13 @@ int AliasManager::copyProp(MethodStatementPtr m) {
}
void AliasManager::deleteCFG() {
assert(m_graph != NULL);
assert(m_graph != nullptr);
delete m_graph;
m_graph = NULL;
m_graph = nullptr;
}
void AliasManager::createCFG(MethodStatementPtr m) {
assert(m_graph == NULL);
assert(m_graph == nullptr);
m_graph = ControlFlowGraph::buildControlFlow(m);
}
@@ -3735,7 +3736,7 @@ void AliasManager::insertTypeAssertions(AnalysisResultConstPtr ar,
i.walk(m->getStmts());
if (Option::ControlFlow && Option::DumpAst) {
if (m_graph != NULL) deleteCFG();
if (m_graph != nullptr) deleteCFG();
createCFG(m);
printf("-------- BEGIN INSERTED -----------\n");
m_graph->dump(m_arp);
@@ -3750,7 +3751,7 @@ void AliasManager::removeTypeAssertions(AnalysisResultConstPtr ar,
r.walk(m->getStmts());
if (Option::ControlFlow && Option::DumpAst) {
if (m_graph != NULL) deleteCFG();
if (m_graph != nullptr) deleteCFG();
createCFG(m);
printf("-------- BEGIN REMOVED -----------\n");
m_graph->dump(m_arp);
@@ -3778,7 +3779,7 @@ int AliasManager::optimize(AnalysisResultConstPtr ar, MethodStatementPtr m) {
if (copyProp(m)) return 1;
}
if (m_graph != NULL) deleteCFG();
if (m_graph != nullptr) deleteCFG();
m_hasChainRoot = false;
@@ -4146,9 +4147,9 @@ void AliasManager::stringOptsRecur(StatementPtr s) {
case Statement::KindOfBreakStatement:
{
BreakStatementPtr b = spc(BreakStatement, s);
int64 depth = b->getDepth();
int64_t depth = b->getDepth();
if (depth != 1) {
int64 s = m_loopInfo.size() - 1;
int64_t s = m_loopInfo.size() - 1;
if (s >= 0) {
if (!depth || depth > s) depth = s;
while (depth--) {
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+598
Ver Arquivo
@@ -0,0 +1,598 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __ANALYSIS_RESULT_H__
#define __ANALYSIS_RESULT_H__
#include <compiler/code_generator.h>
#include <compiler/analysis/code_error.h>
#include <compiler/option.h>
#include <compiler/analysis/block_scope.h>
#include <compiler/analysis/symbol_table.h>
#include <compiler/analysis/function_container.h>
#include <compiler/package.h>
#include <util/string_bag.h>
#include <util/thread_local.h>
#include <boost/graph/adjacency_list.hpp>
#include <tbb/concurrent_hash_map.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
DECLARE_BOOST_TYPES(ClassScope);
DECLARE_BOOST_TYPES(FileScope);
DECLARE_BOOST_TYPES(FunctionScope);
DECLARE_BOOST_TYPES(Location);
DECLARE_BOOST_TYPES(AnalysisResult);
DECLARE_BOOST_TYPES(ScalarExpression);
class AnalysisResult : public BlockScope, public FunctionContainer {
public:
/**
* There are multiple passes over our syntax trees. This lists all of them.
*/
enum Phase {
// parse
ParseAllFiles,
// analyzeProgram
AnalyzeAll,
AnalyzeFinal,
// pre-optimize
FirstPreOptimize,
// inferTypes
FirstInference,
// post-optimize
PostOptimize,
CodeGen,
};
enum FindClassBy {
ClassName,
MethodName,
PropertyName
};
enum GlobalSymbolType {
KindOfStaticGlobalVariable,
KindOfDynamicGlobalVariable,
KindOfMethodStaticVariable,
KindOfClassStaticVariable,
KindOfDynamicConstant,
KindOfPseudoMain,
KindOfRedeclaredFunction,
KindOfRedeclaredClass,
KindOfRedeclaredClassId,
KindOfVolatileClass,
KindOfLazyStaticInitializer,
GlobalSymbolTypeCount
};
class Locker {
public:
Locker(const AnalysisResult *ar) :
m_ar(const_cast<AnalysisResult*>(ar)),
m_mutex(m_ar->getMutex()) {
m_mutex.lock();
}
Locker(AnalysisResultConstPtr ar) :
m_ar(const_cast<AnalysisResult*>(ar.get())),
m_mutex(m_ar->getMutex()) {
m_mutex.lock();
}
Locker(const Locker &l) : m_ar(l.m_ar), m_mutex(l.m_mutex) {
const_cast<Locker&>(l).m_ar = 0;
}
~Locker() {
if (m_ar) m_mutex.unlock();
}
AnalysisResultPtr get() const {
return m_ar->shared_from_this();
}
AnalysisResult *operator->() const {
return m_ar;
}
private:
AnalysisResult *m_ar;
Mutex &m_mutex;
};
public:
AnalysisResult();
Locker lock() const { return Locker(this); }
void setPackage(Package *package) { m_package = package;}
void setParseOnDemand(bool v) { m_parseOnDemand = v;}
bool isParseOnDemand() const { return m_package && m_parseOnDemand;}
void setParseOnDemandDirs(const std::vector<std::string> &dirs) {
assert(m_package && !m_parseOnDemand);
m_parseOnDemandDirs = dirs;
}
/**
* create_function() generates extra PHP code that defines the lambda.
* Stores the code in a temporary string, so we can parse this as an
* extra file appended to parsed code.
*/
void appendExtraCode(const std::string &key, const std::string &code);
void appendExtraCode(const std::string &key, const std::string &code) const;
void parseExtraCode(const std::string &key);
Phase getPhase() const { return m_phase;}
void setPhase(Phase phase) { m_phase = phase;}
int getFunctionCount() const;
int getClassCount() const;
void countReturnTypes(std::map<std::string, int> &counts);
void addEntryPoint(const std::string &name);
void addEntryPoints(const std::vector<std::string> &names);
void loadBuiltinFunctions();
void loadBuiltins();
void analyzeProgram(bool system = false);
void analyzeIncludes();
void analyzeProgramFinal();
void analyzePerfectVirtuals();
void dump();
void docJson(const std::string &filename);
void visitFiles(void (*cb)(AnalysisResultPtr, StatementPtr, void*),
void *data);
void getScopesSet(BlockScopeRawPtrQueue &v);
void preOptimize();
void inferTypes();
void postOptimize();
/**
* Force all class variables to be variants, since l-val or reference
* of dynamic properties are used.
*/
void forceClassVariants(
ClassScopePtr curScope,
bool doStatic,
bool acquireLocks = false);
/**
* Force specified variable of all classes to be variants.
*/
void forceClassVariants(
const std::string &name,
ClassScopePtr curScope,
bool doStatic,
bool acquireLocks = false);
/**
* Code generation functions.
*/
bool outputAllPHP(CodeGenerator::Output output);
/**
* Parser creates a FileScope upon parsing a new file.
*/
void parseOnDemand(const std::string &name) const;
void parseOnDemandByClass(const std::string &name) const {
parseOnDemandBy(name, Option::AutoloadClassMap);
}
void parseOnDemandByFunction(const std::string &name) const {
parseOnDemandBy(name, Option::AutoloadFuncMap);
}
void parseOnDemandByConstant(const std::string &name) const {
parseOnDemandBy(name, Option::AutoloadConstMap);
}
void parseOnDemandBy(const std::string &name,
const std::map<std::string,std::string>& amap) const;
FileScopePtr findFileScope(const std::string &name) const;
const StringToFileScopePtrMap &getAllFiles() { return m_files;}
const std::vector<FileScopePtr> &getAllFilesVector() {
return m_fileScopes;
}
void addFileScope(FileScopePtr fileScope);
/**
* Declarations
*/
bool declareFunction(FunctionScopePtr funcScope) const;
bool declareClass(ClassScopePtr classScope) const;
void declareUnknownClass(const std::string &name);
bool declareConst(FileScopePtr fs, const std::string &name);
/**
* Dependencies
*/
void link(FileScopePtr user, FileScopePtr provider);
bool addClassDependency(FileScopePtr usingFile,
const std::string &className);
bool addFunctionDependency(FileScopePtr usingFile,
const std::string &functionName);
bool addIncludeDependency(FileScopePtr usingFile,
const std::string &includeFilename);
bool addConstantDependency(FileScopePtr usingFile,
const std::string &constantName);
ClassScopePtr findClass(const std::string &className) const;
ClassScopePtr findClass(const std::string &className,
FindClassBy by);
/**
* Find all the redeclared classes by the name, excluding system classes.
* Note that system classes cannot be redeclared.
*/
const ClassScopePtrVec &findRedeclaredClasses(
const std::string &className) const;
/**
* Find all the classes by the name, including system classes.
*/
ClassScopePtrVec findClasses(const std::string &className) const;
bool classMemberExists(const std::string &name, FindClassBy by) const;
ClassScopePtr findExactClass(ConstructPtr cs, const std::string &name) const;
bool checkClassPresent(ConstructPtr cs, const std::string &name) const;
FunctionScopePtr findFunction(const std::string &funcName) const ;
BlockScopeConstPtr findConstantDeclarer(const std::string &constName) const {
return const_cast<AnalysisResult*>(this)->findConstantDeclarer(constName);
}
BlockScopePtr findConstantDeclarer(const std::string &constName);
bool isConstantDeclared(const std::string &constName) const;
bool isConstantRedeclared(const std::string &constName) const;
bool isSystemConstant(const std::string &constName) const;
/**
* For function declaration parsing.
*/
static std::string prepareFile(const char *root, const std::string &fileName,
bool chop, bool stripPath = true);
void setOutputPath(const std::string &path) {
m_outputPath = path;
}
const std::string &getOutputPath() {
return m_outputPath;
}
/**
* Literal string to String precomputation
*/
std::string getLiteralStringName(int64_t hash, int index, bool iproxy = false);
std::string getLitVarStringName(int64_t hash, int index, bool iproxy = false);
int getLiteralStringId(const std::string &s, int &index);
/**
* Profiling runtime parameter type
*/
std::string getFuncId(ClassScopePtr cls, FunctionScopePtr func);
std::vector<const char *> &getFuncTableBucket(FunctionScopePtr func);
std::set<std::string> m_variableTableFunctions;
std::set<int> m_concatLengths;
int m_arrayLitstrKeyMaxSize;
int m_arrayIntegerKeyMaxSize;
std::string getHashedName(int64_t hash, int index, const char *prefix,
bool longName = false);
void addNamedLiteralVarString(const std::string &s);
void addNamedScalarVarArray(const std::string &s);
StringToClassScopePtrVecMap getExtensionClasses();
void addInteger(int64_t n);
private:
Package *m_package;
bool m_parseOnDemand;
std::vector<std::string> m_parseOnDemandDirs;
Phase m_phase;
StringToFileScopePtrMap m_files;
FileScopePtrVec m_fileScopes;
StringBag m_extraCodeFileNames;
std::map<std::string, std::string> m_extraCodes;
StringToClassScopePtrMap m_systemClasses;
StringToFunctionScopePtrMap m_functionDecs;
StringToFunctionScopePtrVecMap m_functionReDecs;
StringToClassScopePtrVecMap m_classDecs;
StringToClassScopePtrVecMap m_methodToClassDecs;
StringToFileScopePtrMap m_constDecs;
std::set<std::string> m_constRedeclared;
bool m_classForcedVariants[2];
StatementPtrVec m_stmts;
StatementPtr m_stmt;
std::string m_outputPath;
public:
AnalysisResultPtr shared_from_this() {
return boost::static_pointer_cast<AnalysisResult>
(BlockScope::shared_from_this());
}
AnalysisResultConstPtr shared_from_this() const {
return boost::static_pointer_cast<const AnalysisResult>
(BlockScope::shared_from_this());
}
private:
BlockScopePtrVec m_ignoredScopes;
typedef boost::adjacency_list<boost::setS, boost::vecS> Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Graph>::adjacency_iterator adjacency_iterator;
Mutex m_depGraphMutex;
Graph m_depGraph;
typedef std::map<vertex_descriptor, FileScopePtr> VertexToFileScopePtrMap;
VertexToFileScopePtrMap m_fileVertMap;
/**
* Checks whether the file is in one of the on-demand parsing directories.
*/
bool inParseOnDemandDirs(const std::string &filename) const;
void collectFunctionsAndClasses(FileScopePtr fs);
/**
* Making sure symbol orders are not different even with multithreading, so
* to make sure generated code are consistent every time.
*/
void canonicalizeSymbolOrder();
/**
* Checks circular class derivations that can cause stack overflows for
* subsequent analysis. Also checks to make sure no two redundant parents.
*/
void checkClassDerivations();
int getFileSize(FileScopePtr fs);
public:
static DECLARE_THREAD_LOCAL(BlockScopeRawPtr, s_currentScopeThreadLocal);
static DECLARE_THREAD_LOCAL(BlockScopeRawPtrFlagsHashMap,
s_changedScopesMapThreadLocal);
#ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
static int s_NumDoJobCalls;
static ConcurrentBlockScopeRawPtrIntHashMap s_DoJobUniqueScopes;
static int s_NumForceRerunGlobal;
static int s_NumReactivateGlobal;
static int s_NumForceRerunUseKinds;
static int s_NumReactivateUseKinds;
#endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
private:
template <typename Visitor>
void processScopesParallel(const char *id, void *opaque = nullptr);
template <typename Visitor>
void preWaitCallback(bool first,
const BlockScopeRawPtrQueue &scopes,
void *opaque);
template <typename Visitor>
bool postWaitCallback(bool first,
bool again,
const BlockScopeRawPtrQueue &scopes,
void *opaque);
};
///////////////////////////////////////////////////////////////////////////////
// Type Inference
class RescheduleException : public Exception {
public:
RescheduleException(BlockScopeRawPtr scope) :
Exception(), m_scope(scope) {}
BlockScopeRawPtr &getScope() { return m_scope; }
#ifdef HPHP_INSTRUMENT_TYPE_INF
static int s_NumReschedules;
static int s_NumForceRerunSelfCaller;
static int s_NumRetTypesChanged;
#endif /* HPHP_INSTRUMENT_TYPE_INF */
private:
BlockScopeRawPtr m_scope;
};
class SetCurrentScope {
public:
SetCurrentScope(BlockScopeRawPtr scope) {
assert(!((*AnalysisResult::s_currentScopeThreadLocal).get()));
*AnalysisResult::s_currentScopeThreadLocal = scope;
scope->setInVisitScopes(true);
}
~SetCurrentScope() {
(*AnalysisResult::s_currentScopeThreadLocal)->setInVisitScopes(false);
AnalysisResult::s_currentScopeThreadLocal.destroy();
}
};
#define IMPLEMENT_INFER_AND_CHECK_ASSERT(scope) \
do { \
assert(AnalysisResult::s_currentScopeThreadLocal->get()); \
assert(AnalysisResult::s_currentScopeThreadLocal->get() == \
(scope).get()); \
(scope)->getInferTypesMutex().assertOwnedBySelf(); \
} while (0)
#ifdef HPHP_INSTRUMENT_TYPE_INF
typedef std::pair < const char *, int > LEntry;
struct LEntryHasher {
bool equal(const LEntry &l1, const LEntry &l2) const {
assert(l1.first);
assert(l2.first);
return l1.second == l2.second &&
strcmp(l1.first, l2.first) == 0;
}
size_t hash(const LEntry &l) const {
assert(l.first);
return hash_string(l.first) ^ l.second;
}
};
typedef tbb::concurrent_hash_map < LEntry, int, LEntryHasher >
LProfileMap;
#endif /* HPHP_INSTRUMENT_TYPE_INF */
class BaseTryLock {
friend class TryLock;
friend class ConditionalTryLock;
public:
#ifdef HPHP_INSTRUMENT_TYPE_INF
static LProfileMap s_LockProfileMap;
#endif /* HPHP_INSTRUMENT_TYPE_INF */
private:
inline bool acquireImpl(BlockScopeRawPtr scopeToLock) {
// A class scope can NEVER grab a lock on a function scope
BlockScopeRawPtr current ATTRIBUTE_UNUSED =
*(AnalysisResult::s_currentScopeThreadLocal.get());
assert(current);
assert(!current->is(BlockScope::ClassScope) ||
!scopeToLock->is(BlockScope::FunctionScope));
return m_mutex.tryLock();
}
#ifdef HPHP_INSTRUMENT_TYPE_INF
BaseTryLock(BlockScopeRawPtr scopeToLock,
const char * fromFunction,
int fromLine,
bool lockCondition = true,
bool profile = true)
: m_profiler(profile),
m_mutex(scopeToLock->getInferTypesMutex()),
m_acquired(false) {
if (LIKELY(lockCondition)) {
bool success = acquireImpl(scopeToLock);
if (UNLIKELY(!success)) {
// put entry in profiler
LProfileMap::accessor acc;
LEntry key(fromFunction, fromLine);
if (!s_LockProfileMap.insert(acc, key)) {
// pre-existing
acc->second++;
} else {
acc->second = 1;
}
// could not acquire lock, throw reschedule exception
throw RescheduleException(scopeToLock);
}
assert(success);
m_acquired = true;
m_mutex.assertOwnedBySelf();
}
}
#else
BaseTryLock(BlockScopeRawPtr scopeToLock,
bool lockCondition = true,
bool profile = true)
: m_profiler(profile),
m_mutex(scopeToLock->getInferTypesMutex()),
m_acquired(false) {
if (LIKELY(lockCondition)) {
bool success = acquireImpl(scopeToLock);
if (UNLIKELY(!success)) {
// could not acquire lock, throw reschedule exception
throw RescheduleException(scopeToLock);
}
assert(success);
m_acquired = true;
m_mutex.assertOwnedBySelf();
}
}
#endif /* HPHP_INSTRUMENT_TYPE_INF */
~BaseTryLock() {
if (m_acquired) m_mutex.unlock();
}
LockProfiler m_profiler;
InferTypesMutex& m_mutex;
bool m_acquired;
};
class TryLock : public BaseTryLock {
public:
#ifdef HPHP_INSTRUMENT_TYPE_INF
TryLock(BlockScopeRawPtr scopeToLock,
const char * fromFunction,
int fromLine,
bool profile = true) :
BaseTryLock(scopeToLock, fromFunction, fromLine, true, profile) {}
#else
TryLock(BlockScopeRawPtr scopeToLock,
bool profile = true) :
BaseTryLock(scopeToLock, true, profile) {}
#endif /* HPHP_INSTRUMENT_TYPE_INF */
};
class ConditionalTryLock : public BaseTryLock {
public:
#ifdef HPHP_INSTRUMENT_TYPE_INF
ConditionalTryLock(BlockScopeRawPtr scopeToLock,
const char * fromFunction,
int fromLine,
bool condition,
bool profile = true) :
BaseTryLock(scopeToLock, fromFunction, fromLine, condition, profile) {}
#else
ConditionalTryLock(BlockScopeRawPtr scopeToLock,
bool condition,
bool profile = true) :
BaseTryLock(scopeToLock, condition, profile) {}
#endif /* HPHP_INSTRUMENT_TYPE_INF */
};
#define GET_LOCK(scopeToLock) \
SimpleLock _lock((scopeToLock)->getInferTypesMutex())
#define COND_GET_LOCK(scopeToLock, condition) \
SimpleConditionalLock _clock((scopeToLock)->getInferTypesMutex(), \
(condition))
#define GET_LOCK_THIS() \
SimpleLock _lock(this->getInferTypesMutex())
#define COND_GET_LOCK_THIS(condition) \
SimpleConditionalLock _clock(this->getInferTypesMutex(), (condition))
#ifdef HPHP_INSTRUMENT_TYPE_INF
#define TRY_LOCK(scopeToLock) \
TryLock _tl((scopeToLock), __PRETTY_FUNCTION__, __LINE__)
#define COND_TRY_LOCK(scopeToLock, condition) \
ConditionalTryLock _ctl((scopeToLock), __PRETTY_FUNCTION__, \
__LINE__, condition)
#else
#define TRY_LOCK(scopeToLock) \
TryLock _tl((scopeToLock))
#define COND_TRY_LOCK(scopeToLock, condition) \
ConditionalTryLock _ctl((scopeToLock), (condition))
#endif /* HPHP_INSTRUMENT_TYPE_INF */
#define TRY_LOCK_THIS() \
TRY_LOCK(BlockScopeRawPtr(this))
#define COND_TRY_LOCK_THIS(condition) \
COND_TRY_LOCK(BlockScopeRawPtr(this), condition)
///////////////////////////////////////////////////////////////////////////////
}
#endif // __ANALYSIS_RESULT_H__
@@ -116,13 +116,13 @@ public:
static bool SkipRecurse(ConstructRawPtr cp);
static bool SkipRecurse(StatementPtr s) {
return SkipRecurse(s ? s.get() : NULL);
return SkipRecurse(s ? s.get() : nullptr);
}
static bool SkipRecurse(StatementConstPtr s) {
return SkipRecurse(s ? s.get() : NULL);
return SkipRecurse(s ? s.get() : nullptr);
}
static bool SkipRecurse(StatementRawPtr s) {
return SkipRecurse(s ? s.get() : NULL);
return SkipRecurse(s ? s.get() : nullptr);
}
static bool SkipRecurse(const Statement *stmt);
@@ -225,11 +225,6 @@ void BlockScope::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
m_variables->outputPHP(cg, ar);
}
void BlockScope::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) {
m_constants->outputCPP(cg, ar);
m_variables->outputCPP(cg, ar);
}
int BlockScope::ScopeCompare::cmp(const BlockScopeRawPtr &p1,
const BlockScopeRawPtr &p2) const {
int d1 = p1->m_kind - p2->m_kind;
@@ -218,9 +218,7 @@ public:
/**
* Code gen
*/
virtual void outputCPPDef(CodeGenerator &cg) {}
virtual void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar);
virtual void outputCPP(CodeGenerator &cg, AnalysisResultPtr ar);
virtual bool inPseudoMain() const {
return false;
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+492
Ver Arquivo
@@ -0,0 +1,492 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __CLASS_SCOPE_H__
#define __CLASS_SCOPE_H__
#include <compiler/analysis/block_scope.h>
#include <compiler/analysis/function_container.h>
#include <compiler/statement/class_statement.h>
#include <compiler/statement/method_statement.h>
#include <compiler/statement/trait_prec_statement.h>
#include <compiler/statement/trait_alias_statement.h>
#include <compiler/expression/user_attribute.h>
#include <util/json.h>
#include <util/case_insensitive.h>
#include <compiler/option.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
DECLARE_BOOST_TYPES(StatementList);
DECLARE_BOOST_TYPES(FunctionScope);
DECLARE_BOOST_TYPES(ClassScope);
DECLARE_BOOST_TYPES(FileScope);
class Symbol;
/**
* A class scope corresponds to a class declaration. We store all
* inferred types and analyzed results here, so not to pollute syntax trees.
*/
class ClassScope : public BlockScope, public FunctionContainer,
public JSON::CodeError::ISerializable,
public JSON::DocTarget::ISerializable {
public:
enum KindOf {
KindOfObjectClass,
KindOfAbstractClass,
KindOfFinalClass,
KindOfInterface,
KindOfTrait
};
#define DECLARE_MAGIC(prefix, prev) \
prefix ## UnknownPropGetter = prev << 1, /* __get */ \
prefix ## UnknownPropSetter = prev << 2, /* __set */ \
prefix ## UnknownPropTester = prev << 3, /* __isset */ \
prefix ## PropUnsetter = prev << 4, /* __unset */ \
prefix ## UnknownMethodHandler = prev << 5, /* __call */ \
prefix ## UnknownStaticMethodHandler = prev << 6, /* __callStatic */ \
prefix ## InvokeMethod = prev << 7, /* __invoke */ \
prefix ## ArrayAccess = prev << 8 /* Implements ArrayAccess */
enum Attribute {
System = 0x001,
Extension = 0x002,
/**
* set iff there is a __construct method. check ClassNameConstructor if
* you want to know whether there is a class-name constructor.
*/
HasConstructor = 0x0004,
ClassNameConstructor = 0x0008,
HasDestructor = 0x0010,
NotFinal = 0x0020,
UsesUnknownTrait = 0x0040,
DECLARE_MAGIC(Has, UsesUnknownTrait),
DECLARE_MAGIC(MayHave, HasArrayAccess),
DECLARE_MAGIC(Inherits, MayHaveArrayAccess)
};
enum Modifier {
Public = 1,
Protected = 2,
Private = 4,
Static = 8,
Abstract = 16,
Final = 32
};
enum Derivation {
FromNormal = 0,
DirectFromRedeclared,
IndirectFromRedeclared
};
enum JumpTableName {
JumpTableCallInfo
};
public:
ClassScope(KindOf kindOf, const std::string &name,
const std::string &parent,
const std::vector<std::string> &bases,
const std::string &docComment, StatementPtr stmt,
const std::vector<UserAttributePtr> &attrs);
/**
* Special constructor for extension classes.
*/
ClassScope(AnalysisResultPtr ar,
const std::string &name, const std::string &parent,
const std::vector<std::string> &bases,
const FunctionScopePtrVec &methods);
bool classNameCtor() const {
return getAttribute(ClassNameConstructor);
}
const std::string &getOriginalName() const;
std::string getDocName() const;
virtual std::string getId() const;
void checkDerivation(AnalysisResultPtr ar, hphp_string_iset &seen);
const std::string &getOriginalParent() const { return m_parent; }
/**
* Returns topmost parent class that has the method.
*/
ClassScopePtr getRootParent(AnalysisResultConstPtr ar,
const std::string &methodName = "");
void getRootParents(AnalysisResultConstPtr ar, const std::string &methodName,
ClassScopePtrVec &roots, ClassScopePtr curClass);
/**
* Whether this is a user-defined class.
*/
bool isUserClass() const { return !getAttribute(System);}
bool isExtensionClass() const { return getAttribute(Extension); }
bool isDynamic() const { return m_dynamic; }
bool isBaseClass() const { return m_bases.empty(); }
/**
* Whether this class name was declared twice or more.
*/
void setRedeclaring(AnalysisResultConstPtr ar, int redecId);
bool isRedeclaring() const { return m_redeclaring >= 0;}
int getRedeclaringId() { return m_redeclaring; }
void setStaticDynamic(AnalysisResultConstPtr ar);
void setDynamic(AnalysisResultConstPtr ar, const std::string &name);
void addReferer(BlockScopePtr ref, int useKinds);
/* For class_exists */
void setVolatile();
bool isVolatile() const { return m_volatile;}
bool isPersistent() const { return m_persistent; }
void setPersistent(bool p) { m_persistent = p; }
bool needLazyStaticInitializer();
Derivation derivesFromRedeclaring() const {
return m_derivesFromRedeclaring;
}
bool derivedByDynamic() const {
return m_derivedByDynamic;
}
/* Whether this class is brought in by a separable extension */
void setSepExtension() { m_sep = true;}
bool isSepExtension() const { return m_sep;}
/**
* Get/set attributes.
*/
void setSystem();
void setAttribute(Attribute attr) { m_attribute |= attr;}
void clearAttribute(Attribute attr) { m_attribute &= ~attr;}
bool getAttribute(Attribute attr) const {
return m_attribute & attr;
}
bool hasAttribute(Attribute attr, AnalysisResultConstPtr ar) const {
if (getAttribute(attr)) return true;
ClassScopePtr parent = getParentScope(ar);
return parent && !parent->isRedeclaring() && parent->hasAttribute(attr, ar);
}
void setKnownBase(int i) { assert(i < 32); m_knownBases |= 1u << i; }
bool hasUnknownBases() const {
int n = m_bases.size();
if (!n) return false;
if (n >= 32) n = 0;
return m_knownBases != (((1u << n) - 1) & 0xffffffff);
}
bool hasKnownBase(int i) const {
return m_knownBases & (1u << (i < 32 ? i : 31));
}
const FunctionScopePtrVec &getFunctionsVec() const {
return m_functionsVec;
}
/**
* Called by ClassScope to prepare name => method/property map.
*/
void collectMethods(AnalysisResultPtr ar,
StringToFunctionScopePtrMap &func,
bool collectPrivate = true,
bool forInvoke = false);
/**
* Whether or not we can directly call ObjectData::o_invoke() when lookup
* in this class fails. If false, we need to call parent::o_invoke(), which
* may be redeclared or may have private methods that need to check class
* context.
*/
bool needsInvokeParent(AnalysisResultConstPtr ar, bool considerSelf = true);
/*
void collectProperties(AnalysisResultPtr ar,
std::set<std::string> &names,
bool collectPrivate = true) const;
*/
/**
* Testing whether this class derives from another.
*/
bool derivesDirectlyFrom(const std::string &base) const;
bool derivesFrom(AnalysisResultConstPtr ar, const std::string &base,
bool strict, bool def) const;
/**
* Find a common parent of two classes; returns "" if there is no such.
*/
static ClassScopePtr FindCommonParent(AnalysisResultConstPtr ar,
const std::string &cn1,
const std::string &cn2);
/**
* Look up function by name.
*/
FunctionScopePtr findFunction(AnalysisResultConstPtr ar,
const std::string &name,
bool recursive,
bool exclIntfBase = false);
/**
* Look up constructor, both __construct and class-name constructor.
*/
FunctionScopePtr findConstructor(AnalysisResultConstPtr ar,
bool recursive);
Symbol *findProperty(ClassScopePtr &cls, const std::string &name,
AnalysisResultConstPtr ar);
/**
* Caller is assumed to hold a lock on this scope
*/
TypePtr checkProperty(BlockScopeRawPtr context,
Symbol *sym, TypePtr type,
bool coerce, AnalysisResultConstPtr ar);
/**
* Caller is *NOT* assumed to hold any locks. Context is
*/
TypePtr checkConst(BlockScopeRawPtr context,
const std::string &name, TypePtr type,
bool coerce, AnalysisResultConstPtr ar,
ConstructPtr construct,
const std::vector<std::string> &bases,
BlockScope *&defScope);
/**
* Collect parent class names.
*/
void getAllParents(AnalysisResultConstPtr ar,
std::vector<std::string> &names);
void getInterfaces(AnalysisResultConstPtr ar,
std::vector<std::string> &names,
bool recursive = true) const;
std::vector<std::string> &getBases() { return m_bases;}
typedef hphp_hash_map<std::string, ExpressionPtr, string_hashi,
string_eqstri> UserAttributeMap;
UserAttributeMap& userAttributes() { return m_userAttributes;}
ClassScopePtr getParentScope(AnalysisResultConstPtr ar) const;
void addUsedTrait(const std::string &s) {
if (!usesTrait(s)) {
m_usedTraitNames.push_back(s);
}
}
void addUsedTraits(const std::vector<std::string> &names) {
for (unsigned i = 0; i < names.size(); i++) {
if (!usesTrait(names[i])) {
m_usedTraitNames.push_back(names[i]);
}
}
}
const std::vector<std::string> &getUsedTraitNames() const {
return m_usedTraitNames;
}
const std::vector<std::pair<std::string, std::string> > &getTraitAliases()
const {
return m_traitAliases;
}
void addTraitAlias(TraitAliasStatementPtr aliasStmt);
void importUsedTraits(AnalysisResultPtr ar);
/**
* Serialize the iface, not everything.
*/
void serialize(JSON::CodeError::OutputStream &out) const;
void serialize(JSON::DocTarget::OutputStream &out) const;
bool isInterface() const { return m_kindOf == KindOfInterface; }
bool isFinal() const { return m_kindOf == KindOfFinalClass ||
m_kindOf == KindOfTrait; }
bool isAbstract() const { return m_kindOf == KindOfAbstractClass ||
m_kindOf == KindOfTrait; }
bool isTrait() const { return m_kindOf == KindOfTrait; }
bool hasProperty(const std::string &name) const;
bool hasConst(const std::string &name) const;
static bool NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func);
void inheritedMagicMethods(ClassScopePtr super);
void derivedMagicMethods(ClassScopePtr super);
/* true if it might, false if it doesnt */
bool implementsArrayAccess();
/* true if it might, false if it doesnt */
bool implementsAccessor(int prop);
void outputForwardDeclaration(CodeGenerator &cg);
void clearBases() {
m_bases.clear();
m_parent = "";
}
/**
* Override function container
*/
bool addFunction(AnalysisResultConstPtr ar,
FunctionScopePtr funcScope);
void setNeedsCppCtor(bool needsCppCtor) {
m_needsCppCtor = needsCppCtor;
}
bool needsCppCtor() const {
return m_needsCppCtor;
}
void setNeedsInitMethod(bool needsInit) {
m_needsInit = needsInit;
}
bool needsInitMethod() const {
return m_needsInit;
}
bool needsEnableDestructor(AnalysisResultConstPtr ar) const;
bool canSkipCreateMethod(AnalysisResultConstPtr ar) const;
bool checkHasPropTable(AnalysisResultConstPtr ar);
private:
// need to maintain declaration order for ClassInfo map
FunctionScopePtrVec m_functionsVec;
std::string m_parent;
mutable std::vector<std::string> m_bases;
UserAttributeMap m_userAttributes;
std::vector<std::string> m_usedTraitNames;
// m_traitAliases is used to support ReflectionClass::getTraitAliases
std::vector<std::pair<std::string, std::string> > m_traitAliases;
struct TraitMethod {
const ClassScopePtr m_trait;
const MethodStatementPtr m_method;
const std::string m_originalName;
ModifierExpressionPtr m_modifiers;
const StatementPtr m_ruleStmt; // for methods imported via aliasing
TraitMethod(ClassScopePtr trait, MethodStatementPtr method,
ModifierExpressionPtr modifiers, StatementPtr ruleStmt) :
m_trait(trait), m_method(method),
m_originalName(method->getOriginalName()), m_modifiers(modifiers),
m_ruleStmt(ruleStmt) {
}
TraitMethod(ClassScopePtr trait, MethodStatementPtr method,
ModifierExpressionPtr modifiers, StatementPtr ruleStmt,
const std::string &originalName) :
m_trait(trait), m_method(method), m_originalName(originalName),
m_modifiers(modifiers), m_ruleStmt(ruleStmt) {
}
};
typedef std::list<TraitMethod> TraitMethodList;
typedef std::map<std::string, TraitMethodList> MethodToTraitListMap;
typedef std::map<std::string, std::string> GeneratorRenameMap;
MethodToTraitListMap m_importMethToTraitMap;
typedef std::map<std::string, MethodStatementPtr> ImportedMethodMap;
mutable int m_attribute;
int m_redeclaring; // multiple definition of the same class
KindOf m_kindOf;
Derivation m_derivesFromRedeclaring;
enum TraitStatus {
NOT_FLATTENED,
BEING_FLATTENED,
FLATTENED
} m_traitStatus;
unsigned m_dynamic:1;
unsigned m_volatile:1; // for class_exists
unsigned m_persistent:1;
unsigned m_derivedByDynamic:1;
unsigned m_sep:1;
unsigned m_needsCppCtor:1;
unsigned m_needsInit:1;
// m_knownBases has a bit for each base class saying whether
// its known to exist at the point of definition of this class.
// for classes with more than 31 bases, bit 31 is set iff
// bases 32 through n are all known.
unsigned m_knownBases;
mutable unsigned m_needsEnableDestructor:2;
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,
ModifierExpressionPtr modifiers);
MethodStatementPtr importTraitMethod(const TraitMethod& traitMethod,
AnalysisResultPtr ar,
std::string methName,
GeneratorRenameMap& genRenameMap,
const ImportedMethodMap &
importedTraitMethods);
void importTraitProperties(AnalysisResultPtr ar);
void relinkGeneratorMethods(AnalysisResultPtr ar,
ImportedMethodMap& importedMethods);
void findTraitMethodsToImport(AnalysisResultPtr ar, ClassScopePtr trait);
MethodStatementPtr findTraitMethod(AnalysisResultPtr ar,
ClassScopePtr trait,
const std::string &methodName,
std::set<ClassScopePtr> &visitedTraits);
void applyTraitRules(AnalysisResultPtr ar);
void applyTraitPrecRule(TraitPrecStatementPtr stmt);
void applyTraitAliasRule(AnalysisResultPtr ar, TraitAliasStatementPtr stmt);
ClassScopePtr findSingleTraitWithMethod(AnalysisResultPtr ar,
const std::string &methodName) const;
void removeSpareTraitAbstractMethods(AnalysisResultPtr ar);
bool usesTrait(const std::string &traitName) const;
bool hasMethod(const std::string &methodName) const;
const std::string& getNewGeneratorName(FunctionScopePtr genFuncScope,
GeneratorRenameMap& genRenameMap);
void renameCreateContinuationCalls(AnalysisResultPtr ar, ConstructPtr c,
ImportedMethodMap &importedMethods);
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // __CLASS_SCOPE_H__
@@ -178,7 +178,7 @@ void ClearErrors() {
}
void Error(ErrorType error, ConstructPtr construct) {
if (hhvm) return;
if (!Option::RecordErrors) return;
ErrorInfoPtr errorInfo(new ErrorInfo());
errorInfo->m_error = error;
errorInfo->m_construct1 = construct;
@@ -187,7 +187,7 @@ void Error(ErrorType error, ConstructPtr construct) {
}
void Error(ErrorType error, ConstructPtr construct1, ConstructPtr construct2) {
if (hhvm) return;
if (!Option::RecordErrors) return;
ErrorInfoPtr errorInfo(new ErrorInfo());
errorInfo->m_error = error;
errorInfo->m_construct1 = construct1;
@@ -197,7 +197,7 @@ void Error(ErrorType error, ConstructPtr construct1, ConstructPtr construct2) {
}
void Error(ErrorType error, ConstructPtr construct, const std::string &data) {
if (hhvm) return;
if (!Option::RecordErrors) return;
ErrorInfoPtr errorInfo(new ErrorInfo());
errorInfo->m_error = error;
errorInfo->m_construct1 = construct;
+253
Ver Arquivo
@@ -0,0 +1,253 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/type.h>
#include <compiler/code_generator.h>
#include <compiler/expression/expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/option.h>
#include <util/util.h>
#include <util/hash.h>
#include <compiler/analysis/class_scope.h>
#include <runtime/base/complex_types.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
ConstantTable::ConstantTable(BlockScope &blockScope)
: SymbolTable(blockScope, true),
m_hasDynamic(false) {
}
///////////////////////////////////////////////////////////////////////////////
TypePtr ConstantTable::add(const std::string &name, TypePtr type,
ExpressionPtr exp, AnalysisResultConstPtr ar,
ConstructPtr construct) {
if (name == "true" || name == "false") {
return Type::Boolean;
}
Symbol *sym = genSymbol(name, true);
if (!sym->declarationSet()) {
assert(!sym->valueSet());
setType(ar, sym, type, true);
sym->setDeclaration(construct);
sym->setValue(exp);
return type;
}
assert(sym->declarationSet() && sym->valueSet());
if (m_blockScope.isFirstPass()) {
if (construct) {
if (exp != sym->getValue()) {
Compiler::Error(Compiler::DeclaredConstantTwice, construct,
sym->getDeclaration());
if (!sym->isDynamic()) {
sym->setDynamic();
m_hasDynamic = true;
}
type = Type::Variant;
}
} else if (exp) {
sym->setValue(exp);
}
setType(ar, sym, type, true);
}
return type;
}
void ConstantTable::setDynamic(AnalysisResultConstPtr ar,
const std::string &name, bool forceVariant) {
Symbol *sym = genSymbol(name, true);
if (!sym->isDynamic()) {
Lock lock(BlockScope::s_constMutex);
sym->setDynamic();
if (sym->getDeclaration()) {
sym->getDeclaration()->getScope()->
addUpdates(BlockScope::UseKindConstRef);
}
m_hasDynamic = true;
if (forceVariant) {
setType(ar, sym, Type::Variant, true);
}
}
}
void ConstantTable::setValue(AnalysisResultConstPtr ar, const std::string &name,
ExpressionPtr value) {
Symbol *sym = getSymbol(name);
assert(sym && sym->isPresent());
sym->setValue(value);
}
bool ConstantTable::isRecursivelyDeclared(AnalysisResultConstPtr ar,
const std::string &name) const {
if (const Symbol *sym ATTRIBUTE_UNUSED = getSymbol(name)) {
assert(sym->isPresent() && sym->valueSet());
return true;
}
ClassScopePtr parent = findParent(ar, name);
if (parent) {
return parent->getConstants()->isRecursivelyDeclared(ar, name);
}
return false;
}
ConstructPtr ConstantTable::getValueRecur(AnalysisResultConstPtr ar,
const std::string &name,
ClassScopePtr &defClass) const {
if (const Symbol *sym = getSymbol(name)) {
assert(sym->isPresent() && sym->valueSet());
if (sym->getValue()) return sym->getValue();
}
ClassScopePtr parent = findParent(ar, name);
if (parent) {
defClass = parent;
return parent->getConstants()->getValueRecur(ar, name, defClass);
}
return ConstructPtr();
}
ConstructPtr ConstantTable::getDeclarationRecur(AnalysisResultConstPtr ar,
const std::string &name,
ClassScopePtr &defClass)
const {
if (const Symbol *sym = getSymbol(name)) {
assert(sym->isPresent() && sym->valueSet());
if (sym->getDeclaration()) return sym->getDeclaration();
}
ClassScopePtr parent = findParent(ar, name);
if (parent) {
defClass = parent;
return parent->getConstants()->getDeclarationRecur(ar, name, defClass);
}
return ConstructPtr();
}
void ConstantTable::cleanupForError(AnalysisResultConstPtr ar) {
AnalysisResult::Locker lock(ar);
BOOST_FOREACH(Symbol *sym, m_symbolVec) {
if (!sym->isDynamic()) {
sym->setDynamic();
sym->setDeclaration(ConstructPtr());
sym->setValue(ConstructPtr());
}
}
}
TypePtr ConstantTable::check(BlockScopeRawPtr context,
const std::string &name, TypePtr type,
bool coerce, AnalysisResultConstPtr ar,
ConstructPtr construct,
const std::vector<std::string> &bases,
BlockScope *&defScope) {
assert(!m_blockScope.is(BlockScope::FunctionScope));
bool isClassScope = m_blockScope.is(BlockScope::ClassScope);
TypePtr actualType;
defScope = nullptr;
if (name == "true" || name == "false") {
actualType = Type::Boolean;
} else {
Symbol *sym = getSymbol(name);
if (!sym) {
if (ar->getPhase() >= AnalysisResult::AnalyzeAll) {
if (isClassScope) {
ClassScopeRawPtr parent = findBase(ar, name, bases);
if (parent) {
actualType = parent->getConstants()->check(
context, name, type, coerce, ar, construct, bases, defScope);
if (defScope) return actualType;
}
}
if (!isClassScope || !((ClassScope*)&m_blockScope)->isTrait()) {
Compiler::Error(Compiler::UseUndeclaredConstant, construct);
}
actualType = isClassScope || !Option::WholeProgram ?
Type::Variant : Type::String;
}
} else {
assert(sym->isPresent());
assert(sym->getType());
assert(sym->isConstant());
defScope = &m_blockScope;
if (isClassScope) {
// if the current scope is a function scope, grab the lock.
// otherwise if it's a class scope, then *try* to grab the lock.
if (context->is(BlockScope::FunctionScope)) {
GET_LOCK(BlockScopeRawPtr(&m_blockScope));
return setType(ar, sym, type, coerce);
} else {
TRY_LOCK(BlockScopeRawPtr(&m_blockScope));
return setType(ar, sym, type, coerce);
}
} else {
Lock lock(m_blockScope.getMutex());
return setType(ar, sym, type, coerce);
}
}
}
return actualType;
}
ClassScopePtr ConstantTable::findParent(AnalysisResultConstPtr ar,
const std::string &name) const {
for (ClassScopePtr parent = m_blockScope.getParentScope(ar);
parent && !parent->isRedeclaring();
parent = parent->getParentScope(ar)) {
if (parent->hasConst(name)) {
return parent;
}
}
return ClassScopePtr();
}
ClassScopeRawPtr ConstantTable::findBase(
AnalysisResultConstPtr ar, const std::string &name,
const std::vector<std::string> &bases) const {
for (int i = bases.size(); i--; ) {
ClassScopeRawPtr p = ar->findClass(bases[i]);
if (!p || p->isRedeclaring()) continue;
if (p->hasConst(name)) return p;
ConstantTablePtr constants = p->getConstants();
p = constants->findBase(ar, name, p->getBases());
if (p) return p;
}
return ClassScopeRawPtr();
}
///////////////////////////////////////////////////////////////////////////////
void ConstantTable::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (Option::GenerateInferredTypes) {
for (unsigned int i = 0; i < m_symbolVec.size(); i++) {
Symbol *sym = m_symbolVec[i];
if (sym->isSystem()) continue;
cg_printf("// @const %s\t$%s\n",
sym->getFinalType()->toString().c_str(),
sym->getName().c_str());
}
}
}
@@ -81,22 +81,6 @@ public:
* Generate all constant declarations for this symbol table.
*/
void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar);
void outputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
bool newline = true) const;
void getCPPDynamicDecl(CodeGenerator &cg, AnalysisResultPtr ar,
Type2SymbolSetMap &type2names);
void outputCPPDynamicImpl(CodeGenerator &cg, AnalysisResultPtr ar);
bool outputSingleConstant(CodeGenerator &cg, AnalysisResultPtr ar,
const std::string &name) const;
void collectCPPGlobalSymbols(StringPairSet &symbols, CodeGenerator &cg,
AnalysisResultPtr ar);
/**
* Generate all class constants in class info map.
*/
void outputCPPClassMap(CodeGenerator &cg, AnalysisResultPtr ar,
bool last = true);
bool isRecursivelyDeclared(AnalysisResultConstPtr ar,
const std::string &name) const;
@@ -115,10 +99,6 @@ private:
ClassScopeRawPtr findBase(AnalysisResultConstPtr ar,
const std::string &name,
const std::vector<std::string> &bases) const;
bool outputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
const Symbol *sym) const;
void outputCPPConstantSymbol(CodeGenerator &cg, AnalysisResultPtr ar,
Symbol *sym);
};
///////////////////////////////////////////////////////////////////////////////
@@ -128,7 +128,7 @@ private:
ControlFlowInfo *get(ConstructRawPtr cp) {
ConstructCFIMap::iterator it = m_ccfiMap.find(cp);
return it == m_ccfiMap.end() ? NULL : &it->second;
return it == m_ccfiMap.end() ? nullptr : &it->second;
}
ControlFlowInfo &cfi(ConstructRawPtr cp) {
@@ -296,10 +296,10 @@ static int32_t countStackValues(const std::vector<uchar>& immVec) {
#define DEC_MA std::vector<uchar>
#define DEC_BLA std::vector<Label*>&
#define DEC_SLA std::vector<StrOff>&
#define DEC_IVA int32
#define DEC_HA int32
#define DEC_IA int32
#define DEC_I64A int64
#define DEC_IVA int32_t
#define DEC_HA int32_t
#define DEC_IA int32_t
#define DEC_I64A int64_t
#define DEC_DA double
#define DEC_SA const StringData*
#define DEC_AA ArrayData*
@@ -650,7 +650,11 @@ static void checkJmpTargetEvalStack(const SymbolicStack& source,
if (source.size() != dest.size()) {
Logger::Warning("Emitter detected a point in the bytecode where the "
"depth of the stack is not the same for all possible "
"control flow paths");
"control flow paths. source size: %d. dest size: %d",
source.size(),
dest.size());
Logger::Warning("src stack : %s", source.pretty().c_str());
Logger::Warning("dest stack: %s", dest.pretty().c_str());
assert(false);
return;
}
@@ -735,7 +739,7 @@ const StringData* SymbolicStack::getName(int index) const {
if (m_symStack[index].metaType == META_LITSTR) {
return m_symStack[index].metaData.name;
}
return NULL;
return nullptr;
}
const StringData* SymbolicStack::getClsName(int index) const {
@@ -784,7 +788,7 @@ bool SymbolicStack::getNotRef() const {
return se.notRef;
}
void SymbolicStack::setInt(int64 v) {
void SymbolicStack::setInt(int64_t v) {
assert(m_symStack.size());
m_symStack.back().intval = v;
}
@@ -930,7 +934,7 @@ int SymbolicStack::getLoc(int index) const {
return m_symStack[index].intval;
}
int64 SymbolicStack::getInt(int index) const {
int64_t SymbolicStack::getInt(int index) const {
assert(m_symStack.size() > size_t(index));
assert(StackSym::GetSymFlavor(m_symStack[index].sym) == StackSym::I);
return m_symStack[index].intval;
@@ -1092,7 +1096,7 @@ void MetaInfoBuilder::setForUnit(UnitEmitter& target) const {
vector<Offset> index1;
vector<Offset> index2;
vector<uint8> data;
vector<uint8_t> data;
index1.push_back(entries);
size_t sz1 = (2 + entries) * sizeof(Offset);
@@ -1112,8 +1116,8 @@ void MetaInfoBuilder::setForUnit(UnitEmitter& target) const {
data.push_back(mi.m_data << 1);
} else {
union {
uint32 val;
uint8 bytes[4];
uint32_t val;
uint8_t bytes[4];
} u;
u.val = (mi.m_data << 1) | 1;
for (int j = 0; j < 4; j++) {
@@ -1126,7 +1130,7 @@ void MetaInfoBuilder::setForUnit(UnitEmitter& target) const {
index2.push_back(sz1 + sz2 + data.size());
size_t size = sz1 + sz2 + data.size();
uint8* meta = (uint8*)malloc(size);
uint8_t* meta = (uint8_t*)malloc(size);
memcpy(meta, &index1[0], sz1);
memcpy(meta + sz1, &index2[0], sz2);
memcpy(meta + sz1 + sz2, &data[0], data.size());
@@ -1353,7 +1357,7 @@ void EmitterVisitor::prepareEvalStack() {
if (m_evalStackIsUnknown) {
if (!m_evalStack.empty()) {
InvariantViolation("Emitter expected to have an empty evaluation "
"stack because the eval stack was unknown, but"
"stack because the eval stack was unknown, but "
"it was non-empty.");
return;
}
@@ -1400,19 +1404,24 @@ bool EmitterVisitor::isJumpTarget(Offset target) {
}
#define CONTROL_BODY(brk, cnt, brkH, cntH) \
ControlTargetPusher _cop(this, -1, brk, cnt, brkH, cntH)
#define FOREACH_BODY(itId, brk, cnt, brkH, cntH) \
ControlTargetPusher _cop(this, itId, brk, cnt, brkH, cntH)
ControlTargetPusher _cop(this, -1, false, brk, cnt, brkH, cntH)
#define FOREACH_BODY(itId, itRef, brk, cnt, brkH, cntH) \
ControlTargetPusher _cop(this, itId, itRef, brk, cnt, brkH, cntH)
class IterFreeThunklet : public Thunklet {
public:
IterFreeThunklet(Id iterId) : m_id(iterId) {}
IterFreeThunklet(Id iterId, bool itRef) : m_id(iterId), m_itRef(itRef) {}
virtual void emit(Emitter& e) {
e.IterFree(m_id);
if (m_itRef) {
e.MIterFree(m_id);
} else {
e.IterFree(m_id);
}
e.Unwind();
}
private:
Id m_id;
bool m_itRef;
};
/**
@@ -1595,7 +1604,7 @@ void EmitterVisitor::visit(FileScopePtr file) {
StringData::GetStaticString(meth->getOriginalName());
m_methLabels[methName] = new Label();
// Emit afterwards
postponeMeth(meth, NULL, true);
postponeMeth(meth, nullptr, true);
}
}
{
@@ -1718,10 +1727,8 @@ void EmitterVisitor::visit(FileScopePtr file) {
tvWriteUninit(&mainReturn);
tvAsVariant(&mainReturn) = 1;
}
// Use _count as a flag for VMExecutionContext::evalUnit
// since its otherwise unused
mainReturn._count = !notMergeOnly;
m_ue.setMainReturn(&mainReturn);
m_ue.setMergeOnly(!notMergeOnly);
// If the exitHnd label was used, we need to emit some extra code
// to handle stray breaks
Label exit;
@@ -1788,7 +1795,7 @@ static StringData* getClassName(ExpressionPtr e) {
if (cls && !cls->isTrait()) {
return StringData::GetStaticString(cls->getOriginalName());
}
return NULL;
return nullptr;
}
static DataType getPredictedDataType(ExpressionPtr expr) {
@@ -1890,7 +1897,7 @@ bool isTupleInit(ExpressionPtr init_expr, int* cap) {
ExpressionPtr ex = (*el)[i];
if (ex->getKindOf() != Expression::KindOfArrayPairExpression) return false;
ArrayPairExpressionPtr ap = static_pointer_cast<ArrayPairExpression>(ex);
if (ap->getName() != NULL || ap->isRef()) return false;
if (ap->getName() != nullptr || ap->isRef()) return false;
}
*cap = n;
return true;
@@ -1948,7 +1955,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
case Statement::KindOfContinueStatement:
case Statement::KindOfBreakStatement: {
BreakStatementPtr bs(static_pointer_cast<BreakStatement>(s));
int64 n = bs->getDepth();
int64_t n = bs->getDepth();
if (n == 1) {
// Plain old "break;" or "continue;"
if (m_contTargets.empty()) {
@@ -1960,7 +1967,11 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
}
if (bs->is(Statement::KindOfBreakStatement)) {
if (m_contTargets.front().m_itId != -1) {
e.IterFree(m_contTargets.front().m_itId);
if (m_contTargets.front().m_itRef) {
e.MIterFree(m_contTargets.front().m_itId);
} else {
e.IterFree(m_contTargets.front().m_itId);
}
}
e.Jmp(m_contTargets.front().m_brkTarg);
} else {
@@ -1976,7 +1987,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
emitConvertToCell(e);
} else {
// Dynamic break/continue with statically known depth.
if (n > (int64)m_contTargets.size()) {
if (n > (int64_t)m_contTargets.size()) {
std::ostringstream msg;
msg << "Cannot break/continue " << n << " levels";
e.String(StringData::GetStaticString(msg.str()));
@@ -2514,7 +2525,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
return false;
}
postponeMeth(m, NULL, true);
postponeMeth(m, nullptr, true);
} else {
FuncEmitter* fe = m_ue.newFuncEmitter(nName, false);
e.DefFunc(fe->id());
@@ -2834,7 +2845,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
if (op == T_COLLECTION) {
ScalarExpressionPtr cls =
static_pointer_cast<ScalarExpression>(b->getExp1());
const std::string* clsName = NULL;
const std::string* clsName = nullptr;
cls->getString(clsName);
int cType = 0;
if (!strcasecmp(clsName->c_str(), "vector")) {
@@ -2987,7 +2998,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
ExpressionListPtr args(el->getExpressions());
int n = args ? args->getCount() : 0;
int i = 0;
FPIRegionRecorder* fpi = NULL;
FPIRegionRecorder* fpi = nullptr;
if (el->getType() == '`') {
const static StringData* s_shell_exec =
StringData::GetStaticString("shell_exec");
@@ -3398,20 +3409,29 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
ExpressionListPtr params(om->getParams());
int numParams = params ? params->getCount() : 0;
Offset fpiStart;
if (!om->getName().empty()) {
// $obj->name(...)
// ^^^^
// Use getOriginalName, which hasn't been case-normalized, since
// __call() is case-preserving.
StringData* nameLiteral =
StringData::GetStaticString(om->getOriginalName());
fpiStart = m_ue.bcPos();
e.FPushObjMethodD(numParams, nameLiteral);
} else {
Offset fpiStart = 0;
ExpressionPtr methName = om->getNameExp();
bool useDirectForm = false;
if (methName->is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr sval(
static_pointer_cast<ScalarExpression>(methName));
const std::string& methStr = sval->getOriginalLiteralString();
if (!methStr.empty()) {
// $obj->name(...)
// ^^^^
// Use getOriginalLiteralString(), which hasn't been
// case-normalized, since __call() needs to preserve
// the case.
StringData* nameLiteral = StringData::GetStaticString(methStr);
fpiStart = m_ue.bcPos();
e.FPushObjMethodD(numParams, nameLiteral);
useDirectForm = true;
}
}
if (!useDirectForm) {
// $obj->{...}(...)
// ^^^^^
visit(om->getNameExp());
visit(methName);
emitConvertToCell(e);
fpiStart = m_ue.bcPos();
e.FPushObjMethod(numParams);
@@ -3419,7 +3439,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
if (clsName) {
Id id = m_ue.mergeLitstr(clsName);
m_metaInfo.add(fpiStart, Unit::MetaInfo::Class, false,
om->getName().empty() ? 1 : 0, id);
useDirectForm ? 0 : 1, id);
}
{
FPIRegionRecorder fpi(this, m_ue, m_evalStack, fpiStart);
@@ -3625,11 +3645,10 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
TypedValue tvVal;
initScalar(tvVal, val);
if (key != NULL) {
if (key != nullptr) {
// Key.
assert(key->isScalar());
TypedValue tvKey;
tvKey._count = 0;
if (key->is(Expression::KindOfConstantExpression)) {
ConstantExpressionPtr c(
static_pointer_cast<ConstantExpression>(key));
@@ -3653,11 +3672,11 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
ScalarExpressionPtr sval(
static_pointer_cast<ScalarExpression>(key));
const std::string* s;
int64 i;
int64_t i;
double d;
if (sval->getString(s)) {
StringData* sd = StringData::GetStaticString(*s);
int64 n = 0;
int64_t n = 0;
if (sd->isStrictlyInteger(n)) {
tvKey.m_data.num = n;
tvKey.m_type = KindOfInt64;
@@ -3763,12 +3782,12 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
StringData* className = newClosureName();
const static StringData* parentName =
StringData::GetStaticString("closure");
StringData::GetStaticString("Closure");
const Location* sLoc = ce->getLocation().get();
PreClassEmitter* pce = m_ue.newPreClassEmitter(
className, PreClass::AlwaysHoistable);
pce->init(sLoc->line0, sLoc->line1, m_ue.bcPos(),
AttrUnique | AttrPersistent, parentName, NULL);
AttrUnique | AttrPersistent, parentName, nullptr);
// We're still at the closure definition site. Emit code to instantiate
// the new anonymous class, with the use variables as arguments.
@@ -3789,7 +3808,7 @@ bool EmitterVisitor::visitImpl(ConstructPtr node) {
TypedValue uninit;
tvWriteUninit(&uninit);
for (int i = 0; i < useCount; ++i) {
pce->addProperty(useVars[i].first, AttrPrivate, NULL, &uninit);
pce->addProperty(useVars[i].first, AttrPrivate, nullptr, &uninit);
}
// The constructor. This is entirely generated; all it does is stash its
@@ -3985,9 +4004,9 @@ void EmitterVisitor::buildVectorImm(std::vector<uchar>& vectorImm,
encodeIvaToVector(vectorImm, m_evalStack.getLoc(i));
} else if (symFlavor == StackSym::T) {
assert(strid != -1);
encodeToVector<int32>(vectorImm, strid);
encodeToVector<int32_t>(vectorImm, strid);
} else if (symFlavor == StackSym::I) {
encodeToVector<int64>(vectorImm, m_evalStack.getInt(i));
encodeToVector<int64_t>(vectorImm, m_evalStack.getInt(i));
}
++i;
@@ -4135,13 +4154,7 @@ void EmitterVisitor::emitVGet(Emitter& e) {
}
}
namespace PassByRefKind {
static const ssize_t AllowCell = 0;
static const ssize_t WarnOnCell = 1;
static const ssize_t ErrorOnCell = 2;
}
static ssize_t getPassByRefKind(ExpressionPtr exp) {
EmitterVisitor::PassByRefKind EmitterVisitor::getPassByRefKind(ExpressionPtr exp) {
// The PassByRefKind of a list assignment expression is determined
// by the PassByRefKind of the RHS. This loop will repeatedly recurse
// on the RHS until it encounters an expression other than a list
@@ -4265,21 +4278,25 @@ void EmitterVisitor::emitFuncCallArg(Emitter& e,
}
return;
}
emitFPass(e, paramId, getPassByRefKind(exp));
}
void EmitterVisitor::emitFPass(Emitter& e, int paramId, PassByRefKind passByRefKind) {
if (checkIfStackEmpty("FPass*")) return;
LocationGuard locGuard(e, m_tempLoc);
m_tempLoc.reset();
emitClsIfSPropBase(e);
int iLast = m_evalStack.size()-1;
int i = scanStackForLocation(iLast);
int sz = iLast - i;
assert(sz >= 0);
char sym = m_evalStack.get(i);
// This ensures that the FPass instruction will be associated with
// exp's source location.
LocationGuard locGuard(e, m_tempLoc);
m_tempLoc.reset();
if (sz == 0 || (sz == 1 && StackSym::GetMarker(sym) == StackSym::S)) {
switch (sym) {
case StackSym::L: e.FPassL(paramId, m_evalStack.getLoc(i)); break;
case StackSym::C:
switch (getPassByRefKind(exp)) {
switch (passByRefKind) {
case PassByRefKind::AllowCell: e.FPassC(paramId); break;
case PassByRefKind::WarnOnCell: e.FPassCW(paramId); break;
case PassByRefKind::ErrorOnCell: e.FPassCE(paramId); break;
@@ -4575,7 +4592,13 @@ void EmitterVisitor::emitConvertToCell(Emitter& e) {
void EmitterVisitor::emitFreePendingIters(Emitter& e) {
for (unsigned i = 0; i < m_pendingIters.size(); ++i) {
e.IterFree(m_pendingIters[i]);
auto pendingIter = m_pendingIters[i];
if (pendingIter.second == KindOfMIter) {
e.MIterFree(pendingIter.first);
} else {
assert(pendingIter.second == KindOfIter);
e.IterFree(pendingIter.first);
}
}
}
@@ -4739,7 +4762,7 @@ void EmitterVisitor::emitClsIfSPropBase(Emitter& e) {
}
Label* EmitterVisitor::getContinuationGotoLabel(StatementPtr s) {
Label* label = NULL;
Label* label = nullptr;
StatementListPtr stmts(static_pointer_cast<StatementList>(s));
for (int i = 0; i < stmts->getCount(); i++) {
StatementPtr s((*stmts)[i]);
@@ -4749,7 +4772,7 @@ Label* EmitterVisitor::getContinuationGotoLabel(StatementPtr s) {
visit(f);
} else if (s->is(Statement::KindOfGotoStatement)) {
GotoStatementPtr g(static_pointer_cast<GotoStatement>(s));
always_assert(label == NULL);
always_assert(label == nullptr);
label = &m_gotoLabels[StringData::GetStaticString(g->label())];
} else {
not_implemented();
@@ -4817,7 +4840,7 @@ DataType EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw,
} else {
return KindOfInvalid;
}
int64 n;
int64_t n;
bool isNonZero;
if (t == KindOfInt64) {
n = cval.asInt64Val();
@@ -4851,8 +4874,8 @@ DataType EmitterVisitor::analyzeSwitch(SwitchStatementPtr sw,
}
if (t == KindOfInt64) {
int64 base = caseMap.begin()->first;
int64 nTargets = caseMap.rbegin()->first - base + 1;
int64_t base = caseMap.begin()->first;
int64_t nTargets = caseMap.rbegin()->first - base + 1;
// Fail if the cases are too sparse
if ((float)caseMap.size() / nTargets < 0.5) {
return KindOfInvalid;
@@ -4870,8 +4893,8 @@ void EmitterVisitor::emitIntegerSwitch(Emitter& e, SwitchStatementPtr sw,
std::vector<Label>& caseLabels,
Label& done, const SwitchState& state) {
auto& caseMap = state.cases;
int64 base = caseMap.begin()->first;
int64 nTargets = caseMap.rbegin()->first - base + 1;
int64_t base = caseMap.begin()->first;
int64_t nTargets = caseMap.rbegin()->first - base + 1;
// It's on. Map case values to Labels, filling in the blanks as
// appropriate.
@@ -5177,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;
@@ -5292,7 +5322,7 @@ void EmitterVisitor::emitPostponedMeths() {
e.VerifyParamType(i);
}
if (fe->isClosureBody()) {
assert(p.m_closureUseVars != NULL);
assert(p.m_closureUseVars != nullptr);
// Emit code to unpack the instance variables (which store the
// use-variables) into locals. Some of the use-variables may have the
// same name, in which case the last one wins.
@@ -5583,7 +5613,7 @@ void EmitterVisitor::emitPostponedClosureCtors() {
ClosureUseVarVec& useVars = ctor.m_useVars;
FuncEmitter* fe = ctor.m_fe;
const Location* sLoc = ctor.m_expr->getLocation().get();
fe->init(sLoc->line0, sLoc->line1, m_ue.bcPos(), AttrPublic, false, NULL);
fe->init(sLoc->line0, sLoc->line1, m_ue.bcPos(), AttrPublic, false, nullptr);
unsigned n = useVars.size();
Emitter e(ctor.m_expr, m_ue, *this);
@@ -5837,7 +5867,7 @@ void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node) {
ExpressionListPtr params(node->getParams());
int numParams = params ? params->getCount() : 0;
bool isBuiltinCall = false;
StringData* nLiteral = NULL;
StringData* nLiteral = nullptr;
Offset fpiStart;
if (node->getClass() || !node->getClassName().empty()) {
bool isSelfOrParent = node->isSelf() || node->isParent();
@@ -5884,7 +5914,7 @@ void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node) {
e.FPushFuncD(numParams, nLiteral);
} else {
// Special handling for func_get_args and friends inside a generator.
const StringData* specialMethodName = NULL;
const StringData* specialMethodName = nullptr;
static const StringData* contName =
StringData::GetStaticString(CONTINUATION_OBJECT_NAME);
Id contId = m_curFunc->lookupVarId(contName);
@@ -5902,7 +5932,7 @@ void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node) {
specialMethodName = s_get_arg;
}
if (specialMethodName != NULL) {
if (specialMethodName != nullptr) {
emitVirtualLocal(contId);
emitCGet(e);
fpiStart = m_ue.bcPos();
@@ -6059,9 +6089,17 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
PreClassEmitter* pce = m_ue.newPreClassEmitter(className, hoistable);
pce->init(sLoc->line0, sLoc->line1, m_ue.bcPos(), attr, parentName,
classDoc);
LocationPtr loc(new Location(*sLoc));
loc->line1 = loc->line0;
loc->char1 = loc->char0;
e.setTempLocation(loc);
if (hoistable != PreClass::AlwaysHoistable) {
e.DefCls(pce->id());
} else {
// To atatch the line number to for error reporting...
e.Nop();
}
e.setTempLocation(LocationPtr());
for (int i = firstInterface; i < nInterfaces; ++i) {
pce->addInterface(StringData::GetStaticString(bases[i]));
}
@@ -6082,9 +6120,9 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
pce->addUserAttribute(uaName, tv);
}
NonScalarVec* nonScalarPinitVec = NULL;
NonScalarVec* nonScalarSinitVec = NULL;
NonScalarVec* nonScalarConstVec = NULL;
NonScalarVec* nonScalarPinitVec = nullptr;
NonScalarVec* nonScalarSinitVec = nullptr;
NonScalarVec* nonScalarConstVec = nullptr;
if (StatementListPtr stmts = is->getStmts()) {
int i, n = stmts->getCount();
for (i = 0; i < n; i++) {
@@ -6128,12 +6166,15 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
} else {
tvWriteUninit(&tvVal);
if (!(attrs & AttrStatic)) {
if (nonScalarPinitVec == NULL) {
if (requiresDeepInit(vNode)) {
attrs = (Attr)(attrs | AttrDeepInit);
}
if (nonScalarPinitVec == nullptr) {
nonScalarPinitVec = new NonScalarVec();
}
nonScalarPinitVec->push_back(NonScalarPair(propName, vNode));
} else {
if (nonScalarSinitVec == NULL) {
if (nonScalarSinitVec == nullptr) {
nonScalarSinitVec = new NonScalarVec();
}
nonScalarSinitVec->push_back(NonScalarPair(propName, vNode));
@@ -6166,7 +6207,7 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
initScalar(tvVal, vNode);
} else {
tvWriteUninit(&tvVal);
if (nonScalarConstVec == NULL) {
if (nonScalarConstVec == nullptr) {
nonScalarConstVec = new NonScalarVec();
}
nonScalarConstVec->push_back(NonScalarPair(constName, vNode));
@@ -6198,7 +6239,7 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
postponeCtor(is, fe);
}
if (nonScalarPinitVec != NULL) {
if (nonScalarPinitVec != nullptr) {
// Non-scalar property initializers require 86pinit() for run-time
// initialization support.
static const StringData* methName = StringData::GetStaticString("86pinit");
@@ -6207,7 +6248,7 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
postponePinit(is, fe, nonScalarPinitVec);
}
if (nonScalarSinitVec != NULL) {
if (nonScalarSinitVec != nullptr) {
// Non-scalar property initializers require 86sinit() for run-time
// initialization support.
static const StringData* methName = StringData::GetStaticString("86sinit");
@@ -6216,7 +6257,7 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
postponeSinit(is, fe, nonScalarSinitVec);
}
if (nonScalarConstVec != NULL) {
if (nonScalarConstVec != nullptr) {
// Non-scalar constant initializers require 86cinit() for run-time
// initialization support.
static const StringData* methName = StringData::GetStaticString("86cinit");
@@ -6230,15 +6271,25 @@ PreClass::Hoistable EmitterVisitor::emitClass(Emitter& e, ClassScopePtr cNode,
return hoistable;
}
void EmitterVisitor::emitBreakHandler(Emitter& e, Label& brkTarg,
Label& cntTarg, Label& brkHand, Label& cntHand, Id iter /* = -1 */) {
void
EmitterVisitor::emitBreakHandler(Emitter& e,
Label& brkTarg,
Label& cntTarg,
Label& brkHand,
Label& cntHand,
Id iter /* = -1 */,
IterKind itKind /* = KindOfIter */) {
// Handle dynamic break
if (brkHand.isUsed()) {
brkHand.set(e);
// Whatever happens, we have left this loop
if (iter != -1) {
e.IterFree(iter);
if (itKind == KindOfMIter) {
e.MIterFree(iter);
} else {
assert(itKind == KindOfIter);
e.IterFree(iter);
}
}
e.Int(1);
e.Sub();
@@ -6266,7 +6317,12 @@ void EmitterVisitor::emitBreakHandler(Emitter& e, Label& brkTarg,
e.Jmp(cntTarg);
leaving.set(e);
// Leaving this loop
e.IterFree(iter);
if (itKind == KindOfMIter) {
e.MIterFree(iter);
} else {
assert(itKind == KindOfIter);
e.IterFree(iter);
}
e.Jmp(topContHandler());
} else {
e.JmpZ(topContHandler());
@@ -6279,11 +6335,11 @@ void EmitterVisitor::emitBreakHandler(Emitter& e, Label& brkTarg,
class ForeachIterGuard {
EmitterVisitor& m_ev;
public:
ForeachIterGuard(EmitterVisitor& ev, Id iterId) : m_ev(ev) {
m_ev.pushIterId(iterId);
ForeachIterGuard(EmitterVisitor& ev, Id iterId, bool itRef) : m_ev(ev) {
m_ev.pushIterScope(iterId, itRef);
}
~ForeachIterGuard() {
m_ev.popIterId();
m_ev.popIterScope();
}
};
@@ -6306,7 +6362,7 @@ void EmitterVisitor::emitForeach(Emitter& e, ForEachStatementPtr fe) {
Label start;
Offset bIterStart;
Id itId = m_curFunc->allocIterator();
ForeachIterGuard fig(*this, itId);
ForeachIterGuard fig(*this, itId, strong);
bool simpleCase = (!key || key->is(Expression::KindOfSimpleVariable)) &&
val->is(Expression::KindOfSimpleVariable);
@@ -6334,9 +6390,9 @@ void EmitterVisitor::emitForeach(Emitter& e, ForEachStatementPtr fe) {
if (strong) {
emitConvertToVar(e);
if (key) {
e.IterInitMK(itId, exit, valTempLocal, keyTempLocal);
e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
} else {
e.IterInitM(itId, exit, valTempLocal);
e.MIterInit(itId, exit, valTempLocal);
}
} else {
emitConvertToCell(e);
@@ -6366,9 +6422,9 @@ void EmitterVisitor::emitForeach(Emitter& e, ForEachStatementPtr fe) {
if (strong) {
if (key) {
e.IterInitMK(itId, exit, valTempLocal, keyTempLocal);
e.MIterInitK(itId, exit, valTempLocal, keyTempLocal);
} else {
e.IterInitM(itId, exit, valTempLocal);
e.MIterInit(itId, exit, valTempLocal);
}
} else {
if (key) {
@@ -6413,7 +6469,7 @@ void EmitterVisitor::emitForeach(Emitter& e, ForEachStatementPtr fe) {
}
{
FOREACH_BODY(itId, exit, next, brkHand, cntHand);
FOREACH_BODY(itId, strong, exit, next, brkHand, cntHand);
if (body) visit(body);
}
bool needBreakHandler = (brkHand.isUsed() || cntHand.isUsed());
@@ -6432,9 +6488,9 @@ void EmitterVisitor::emitForeach(Emitter& e, ForEachStatementPtr fe) {
m_evalStack.cleanTopMeta();
if (strong) {
if (key) {
e.IterNextMK(itId, start, valTempLocal, keyTempLocal);
e.MIterNextK(itId, start, valTempLocal, keyTempLocal);
} else {
e.IterNextM(itId, start, valTempLocal);
e.MIterNext(itId, start, valTempLocal);
}
} else {
if (key) {
@@ -6443,10 +6499,12 @@ void EmitterVisitor::emitForeach(Emitter& e, ForEachStatementPtr fe) {
e.IterNext(itId, start, valTempLocal);
}
}
newFaultRegion(bIterStart, m_ue.bcPos(), new IterFreeThunklet(itId), itId);
newFaultRegion(bIterStart, m_ue.bcPos(), new IterFreeThunklet(itId, strong),
itId);
if (needBreakHandler) {
e.Jmp(exit);
emitBreakHandler(e, exit, next, brkHand, cntHand, itId);
IterKind itKind = strong ? KindOfMIter : KindOfIter;
emitBreakHandler(e, exit, next, brkHand, cntHand, itId, itKind);
}
if (!simpleCase) {
m_curFunc->freeUnnamedLocal(valTempLocal);
@@ -6604,8 +6662,8 @@ void EmitterVisitor::finishFunc(Emitter& e, FuncEmitter* fe) {
StringData* EmitterVisitor::newClosureName() {
std::ostringstream str;
str << "closure$";
if (m_curFunc->pce() != NULL) {
str << "Closure" << '$';
if (m_curFunc->pce() != nullptr) {
str << m_curFunc->pce()->name()->data();
}
str << '$';
@@ -6629,7 +6687,6 @@ StringData* EmitterVisitor::newClosureName() {
void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val) {
assert(val->isScalar());
tvVal.m_type = KindOfUninit;
tvVal._count = 0;
switch (val->getKindOf()) {
case Expression::KindOfConstantExpression: {
ConstantExpressionPtr ce(static_pointer_cast<ConstantExpression>(val));
@@ -6655,7 +6712,7 @@ void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val) {
tvVal.m_type = KindOfString;
break;
}
int64 i;
int64_t i;
if (sval->getInt(i)) {
tvVal.m_data.num = i;
tvVal.m_type = KindOfInt64;
@@ -6704,6 +6761,41 @@ void EmitterVisitor::initScalar(TypedValue& tvVal, ExpressionPtr val) {
}
}
bool EmitterVisitor::requiresDeepInit(ExpressionPtr initExpr) const {
switch (initExpr->getKindOf()) {
case Expression::KindOfScalarExpression:
case Expression::KindOfConstantExpression:
case Expression::KindOfClassConstantExpression:
return false;
case Expression::KindOfUnaryOpExpression: {
UnaryOpExpressionPtr u(
static_pointer_cast<UnaryOpExpression>(initExpr));
if (u->getOp() == T_ARRAY) {
ExpressionListPtr el =
static_pointer_cast<ExpressionList>(u->getExpression());
if (el) {
int n = el->getCount();
for (int i = 0; i < n; i++) {
ArrayPairExpressionPtr ap =
static_pointer_cast<ArrayPairExpression>((*el)[i]);
ExpressionPtr key = ap->getName();
if (requiresDeepInit(ap->getValue()) ||
(key && requiresDeepInit(key))) {
return true;
}
}
}
return false;
} else if (u->getOp() == '+' || u->getOp() == '-') {
return requiresDeepInit(u->getExpression());
}
// fall through
}
default:
return true;
}
}
Thunklet::~Thunklet() {}
using HPHP::Eval::PhpFile;
@@ -6806,14 +6898,14 @@ static Unit* emitHHBCNativeFuncUnit(const HhbcExtFuncInfo* builtinFuncs,
fe->finish(ue->bcPos(), false);
ue->recordFunction(fe);
for (ssize_t i = 0LL; i < numBuiltinFuncs; ++i) {
for (ssize_t i = 0; i < numBuiltinFuncs; ++i) {
const HhbcExtFuncInfo* info = &builtinFuncs[i];
StringData* name = StringData::GetStaticString(info->m_name);
BuiltinFunction bif = (BuiltinFunction)info->m_builtinFunc;
BuiltinFunction nif = (BuiltinFunction)info->m_nativeFunc;
const ClassInfo::MethodInfo* mi = ClassInfo::FindFunction(name);
assert(mi &&
"MethodInfo not found; probably need to rebuild src/system");
"MethodInfo not found; probably need to rebuild hphp/system");
FuncEmitter* fe = ue->newFuncEmitter(name, /*top*/ true);
Offset base = ue->bcPos();
fe->setBuiltinFunc(mi, bif, nif, base);
@@ -6913,9 +7005,8 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
TypedValue mainReturn;
mainReturn.m_data.num = 1;
mainReturn.m_type = KindOfBoolean;
// _count is the "Unit::isMergeOnly()" flag
mainReturn._count = 1;
ue->setMainReturn(&mainReturn);
ue->setMergeOnly(true);
MetaInfoBuilder metaInfo;
@@ -6929,7 +7020,7 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
// Build up extClassHash, a hashtable that maps class names to structures
// containing C++ function pointers for the class's methods and constructors
assert(Class::s_extClassHash.size() == 0);
for (long long i = 0LL; i < numBuiltinClasses; ++i) {
for (long long i = 0; i < numBuiltinClasses; ++i) {
const HhbcExtClassInfo* info = builtinClasses + i;
StringData *s = StringData::GetStaticString(info->m_name);
Class::s_extClassHash[s] = info;
@@ -6978,13 +7069,13 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
assert(pending.empty());
}
for (unsigned int i = 0LL; i < classEntries.size(); ++i) {
for (unsigned int i = 0; i < classEntries.size(); ++i) {
Entry& e = classEntries[i];
StringData* parentName =
StringData::GetStaticString(e.ci->getParentClass().get());
PreClassEmitter* pce = ue->newPreClassEmitter(e.name,
PreClass::AlwaysHoistable);
pce->init(0, 0, ue->bcPos(), AttrUnique | AttrPersistent, parentName, NULL);
pce->init(0, 0, ue->bcPos(), AttrUnique | AttrPersistent, parentName, nullptr);
pce->setBuiltinClassInfo(e.ci, e.info->m_InstanceCtor, e.info->m_sizeof);
{
ClassInfo::InterfaceVec intfVec = e.ci->getInterfacesVec();
@@ -7013,7 +7104,7 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
const ClassInfo::MethodInfo* mi =
e.ci->getMethodInfo(std::string(methodInfo->m_name));
Offset base = ue->bcPos();
fe->setBuiltinFunc(mi, bcf, NULL, base);
fe->setBuiltinFunc(mi, bcf, nullptr, base);
ue->emitOp(OpNativeImpl);
}
Offset past = ue->bcPos();
@@ -7036,6 +7127,27 @@ static Unit* emitHHBCNativeClassUnit(const HhbcExtClassInfo* builtinClasses,
cnsInfo->name.get(), (TypedValue*)(&val), empty_string.get());
}
}
{
ClassInfo::PropertyVec propVec = e.ci->getPropertiesVec();
for (unsigned i = 0; i < propVec.size(); ++i) {
const ClassInfo::PropertyInfo* propInfo = propVec[i];
assert(propInfo);
int attr = AttrNone;
if (propInfo->attribute & ClassInfo::IsProtected) attr |= AttrProtected;
else if (propInfo->attribute & ClassInfo::IsPrivate) attr |= AttrPrivate;
else attr |= AttrPublic;
if (propInfo->attribute & ClassInfo::IsStatic) attr |= AttrStatic;
TypedValue tvNull;
tvWriteNull(&tvNull);
pce->addProperty(
propInfo->name.get(),
Attr(attr),
propInfo->docComment ? StringData::GetStaticString(propInfo->docComment) : nullptr,
&tvNull
);
}
}
}
Peephole peephole(*ue, metaInfo);
@@ -7062,7 +7174,7 @@ static UnitEmitter* emitHHBCVisitor(AnalysisResultPtr ar, FileScopeRawPtr fsp) {
}
UnitEmitter* ue = emitHHBCUnitEmitter(ar, fsp, md5);
assert(ue != NULL);
assert(ue != nullptr);
if (Option::GenerateTextHHBC) {
std::unique_ptr<Unit> unit(ue->create());
@@ -7075,7 +7187,7 @@ static UnitEmitter* emitHHBCVisitor(AnalysisResultPtr ar, FileScopeRawPtr fsp) {
Logger::Error("Unable to open %s for write", fullPath.c_str());
} else {
CodeGenerator cg(&f, CodeGenerator::TextHHBC);
cg.printf("Hash: %llx%016llx\n", md5.q[0], md5.q[1]);
cg.printf("Hash: %" PRIx64 "%016" PRIx64 "\n", md5.q[0], md5.q[1]);
cg.printRaw(unit->toString().c_str());
f.close();
}
@@ -7087,7 +7199,7 @@ static UnitEmitter* emitHHBCVisitor(AnalysisResultPtr ar, FileScopeRawPtr fsp) {
class UEQ : public Synchronizable {
public:
void push(UnitEmitter* ue) {
assert(ue != NULL);
assert(ue != nullptr);
Lock lock(this);
m_ues.push_back(ue);
notify();
@@ -7097,12 +7209,12 @@ class UEQ : public Synchronizable {
if (m_ues.empty()) {
// Check for empty() after wait(), in case of spurious wakeup.
if (!wait(sec, nsec) || m_ues.empty()) {
return NULL;
return nullptr;
}
}
assert(m_ues.size() > 0);
UnitEmitter* ue = m_ues.front();
assert(ue != NULL);
assert(ue != nullptr);
m_ues.pop_front();
return ue;
}
@@ -7264,7 +7376,7 @@ void emitAllHHBC(AnalysisResultPtr ar) {
// Poll, but with a 100ms timeout so that this thread doesn't spin wildly
// if it gets ahead of the workers.
UnitEmitter* ue = s_ueq.tryPop(0, 100 * 1000 * 1000);
if ((didPop = (ue != NULL))) {
if ((didPop = (ue != nullptr))) {
ues.push_back(ue);
}
if (ues.size() == kBatchSize
@@ -7306,6 +7418,7 @@ Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
for (auto& i : RuntimeOption::DynamicInvokeFunctions) {
Option::DynamicInvokeFunctions.insert(i);
}
Option::RecordErrors = false;
Option::OutputHHBC = true;
Option::ParseTimeOpts = false;
Option::WholeProgram = false;
@@ -7315,7 +7428,7 @@ Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
BuiltinSymbols::Load(ar, true);
BuiltinSymbols::NoSuperGlobals = false;
TypeConstraint tc;
return NULL;
return nullptr;
}
try {
@@ -7350,15 +7463,13 @@ Unit* hphp_compiler_parse(const char* code, int codeLen, const MD5& md5,
fsp->analyzeProgram(ar);
UnitEmitter* ue = emitHHBCUnitEmitter(ar, fsp, md5);
if (RuntimeOption::RepoCommit) {
Repo::get().commitUnit(ue, unitOrigin);
}
Repo::get().commitUnit(ue, unitOrigin);
Unit* unit = ue->create();
delete ue;
return unit;
} catch (const std::exception&) {
// extern "C" function should not be throwing exceptions...
return NULL;
return nullptr;
}
}
@@ -110,10 +110,10 @@ public:
#define MA std::vector<uchar>
#define BLA std::vector<Label*>&
#define SLA std::vector<StrOff>&
#define IVA int32
#define HA int32
#define IA int32
#define I64A int64
#define IVA int32_t
#define HA int32_t
#define IA int32_t
#define I64A int64_t
#define DA double
#define SA const StringData*
#define AA ArrayData*
@@ -200,7 +200,7 @@ private:
DataType dt; // META_DATA_TYPE
} metaData;
const StringData* className;
int64 intval; // used for L and I symbolic flavors
int64_t intval; // used for L and I symbolic flavors
// If intval is an unnamed local temporary, this offset is the start
// of the region we are using it (which we will need to have a
@@ -237,7 +237,7 @@ public:
std::string pretty() const;
void push(char sym);
void setInt(int64 v);
void setInt(int64_t v);
void setString(const StringData* s);
void setKnownCls(const StringData* s, bool nonNull);
void setNotRef();
@@ -274,7 +274,7 @@ public:
ClassBaseType getClsBaseType(int index) const;
int getLoc(int index) const;
int64 getInt(int index) const;
int64_t getInt(int index) const;
Offset getUnnamedLocStart(int index) const;
void pushFDesc();
@@ -379,12 +379,20 @@ public:
EXCEPTION_COMMON_IMPL(IncludeTimeFatalException);
};
void pushIterId(Id id) { m_pendingIters.push_back(id); }
void popIterId() { m_pendingIters.pop_back(); }
enum IterKind {
KindOfIter = 0,
KindOfMIter = 1
};
void pushIterScope(Id id, bool itRef = false) {
IterKind itKind = itRef ? KindOfMIter : KindOfIter;
m_pendingIters.push_back(std::pair<Id,IterKind>(id,itKind));
}
void popIterScope() { m_pendingIters.pop_back(); }
private:
typedef std::pair<StringData*, bool> ClosureUseVar; // (name, byRef)
typedef std::vector<ClosureUseVar> ClosureUseVarVec;
typedef std::vector<Id> IdVec;
typedef std::vector<std::pair<Id,IterKind> > PendingIterVec;
class PostponedMeth {
public:
PostponedMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
@@ -427,10 +435,12 @@ private:
};
class ControlTargets {
public:
ControlTargets(Id itId, Label& brkTarg, Label& cntTarg, Label& brkHand,
Label& cntHand) : m_itId(itId), m_brkTarg(brkTarg), m_cntTarg(cntTarg),
m_brkHand(brkHand), m_cntHand(cntHand) {}
ControlTargets(Id itId, bool itRef, Label& brkTarg, Label& cntTarg,
Label& brkHand, Label& cntHand) :
m_itId(itId), m_itRef(itRef), m_brkTarg(brkTarg), m_cntTarg(cntTarg),
m_brkHand(brkHand), m_cntHand(cntHand) {}
Id m_itId;
bool m_itRef;
Label& m_brkTarg; // Jump here for "break;" (after doing IterFree)
Label& m_cntTarg; // Jump here for "continue;"
Label& m_brkHand; // Push N and jump here for "break N;"
@@ -438,9 +448,9 @@ private:
};
class ControlTargetPusher {
public:
ControlTargetPusher(EmitterVisitor* e, Id itId, Label& brkTarg,
ControlTargetPusher(EmitterVisitor* e, Id itId, bool itRef, Label& brkTarg,
Label& cntTarg, Label& brkHand, Label& cntHand) : m_e(e) {
e->m_contTargets.push_front(ControlTargets(itId, brkTarg, cntTarg,
e->m_contTargets.push_front(ControlTargets(itId, itRef, brkTarg, cntTarg,
brkHand, cntHand));
}
~ControlTargetPusher() {
@@ -484,7 +494,7 @@ private:
typedef std::pair<Id, int> StrCase;
struct SwitchState : private boost::noncopyable {
SwitchState() : nonZeroI(-1), defI(-1) {}
std::map<int64, int> cases; // a map from int (or litstr id) to case index
std::map<int64_t, int> cases; // a map from int (or litstr id) to case index
std::vector<StrCase> caseOrder; // for string switches, a list of the
// <litstr id, case index> in the order
// they appear in the source
@@ -505,7 +515,7 @@ private:
std::deque<PostponedNonScalars> m_postponedSinits;
std::deque<PostponedNonScalars> m_postponedCinits;
std::deque<PostponedClosureCtor> m_postponedClosureCtors;
IdVec m_pendingIters;
PendingIterVec m_pendingIters;
typedef std::map<const StringData*, Label*, string_data_lt> LabelMap;
LabelMap m_methLabels;
SymbolicStack m_evalStack;
@@ -535,6 +545,12 @@ public:
void buildVectorImm(std::vector<uchar>& vectorImm,
int iFirst, int iLast, bool allowW,
Emitter& e);
enum class PassByRefKind {
AllowCell,
WarnOnCell,
ErrorOnCell,
};
PassByRefKind getPassByRefKind(ExpressionPtr exp);
void emitAGet(Emitter& e);
void emitCGetL2(Emitter& e);
void emitCGetL3(Emitter& e);
@@ -561,6 +577,7 @@ public:
void emitConvertToCellOrLoc(Emitter& e);
void emitConvertSecondToCell(Emitter& e);
void emitConvertToVar(Emitter& e);
void emitFPass(Emitter& e, int paramID, PassByRefKind passByRefKind);
void emitVirtualLocal(int localId, DataType dt = KindOfUnknown);
template<class Expr> void emitVirtualClassBase(Emitter&, Expr* node);
void emitResolveClsBase(Emitter& e, int pos);
@@ -587,7 +604,7 @@ public:
void emitAssignment(Emitter& e, ExpressionPtr c, int op, bool bind);
void emitListAssignment(Emitter& e, ListAssignmentPtr lst);
void postponeMeth(MethodStatementPtr m, FuncEmitter* fe, bool top,
ClosureUseVarVec* useVars = NULL);
ClosureUseVarVec* useVars = nullptr);
void postponeCtor(InterfaceStatementPtr m, FuncEmitter* fe);
void postponePinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
void postponeSinit(InterfaceStatementPtr m, FuncEmitter* fe, NonScalarVec* v);
@@ -622,7 +639,8 @@ public:
PreClass::Hoistable emitClass(Emitter& e, ClassScopePtr cNode,
bool topLevel);
void emitBreakHandler(Emitter& e, Label& brkTarg, Label& cntTarg,
Label& brkHand, Label& cntHand, Id iter = -1);
Label& brkHand, Label& cntHand, Id iter = -1,
IterKind itKind = KindOfIter);
void emitForeach(Emitter& e, ForEachStatementPtr fe);
void emitRestoreErrorReporting(Emitter& e, Id oldLevelLoc);
void emitMakeUnitFatal(Emitter& e, const std::string& message);
@@ -638,6 +656,7 @@ public:
StringData* newClosureName();
void initScalar(TypedValue& tvVal, ExpressionPtr val);
bool requiresDeepInit(ExpressionPtr initExpr) const;
void emitClassTraitPrecRule(PreClassEmitter* pce, TraitPrecStatementPtr rule);
void emitClassTraitAliasRule(PreClassEmitter* pce,
+493
Ver Arquivo
@@ -0,0 +1,493 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/statement/statement_list.h>
#include <compiler/statement/exp_statement.h>
#include <compiler/option.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/function_scope.h>
#include <sys/stat.h>
#include <compiler/parser/parser.h>
#include <util/logger.h>
#include <util/util.h>
#include <util/base.h>
#include <compiler/expression/expression_list.h>
#include <compiler/statement/function_statement.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/expression/simple_function_call.h>
#include <compiler/expression/include_expression.h>
#include <compiler/expression/user_attribute.h>
#include <runtime/base/complex_types.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
FileScope::FileScope(const string &fileName, int fileSize, const MD5 &md5)
: BlockScope("", "", StatementPtr(), BlockScope::FileScope),
m_size(fileSize), m_md5(md5), m_module(false), m_privateInclude(false),
m_externInclude(false),
m_includeState(0), m_fileName(fileName), m_redeclaredFunctions(0) {
pushAttribute(); // for global scope
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void FileScope::setFileLevel(StatementListPtr stmtList) {
for (int i = 0; i < stmtList->getCount(); i++) {
StatementPtr stmt = (*stmtList)[i];
stmt->setFileLevel();
if (stmt->is(Statement::KindOfExpStatement)) {
ExpStatementPtr expStmt = dynamic_pointer_cast<ExpStatement>(stmt);
ExpressionPtr exp = expStmt->getExpression();
exp->setFileLevel();
}
if (stmt->is(Statement::KindOfStatementList)) {
setFileLevel(dynamic_pointer_cast<StatementList>(stmt));
}
}
}
FunctionScopePtr FileScope::setTree(AnalysisResultConstPtr ar,
StatementListPtr tree) {
m_tree = tree;
setFileLevel(tree);
return createPseudoMain(ar);
}
void FileScope::cleanupForError(AnalysisResultConstPtr ar,
int line, const string &msg) {
for (StringToClassScopePtrVecMap::const_iterator iter = m_classes.begin();
iter != m_classes.end(); ++iter) {
BOOST_FOREACH(ClassScopePtr cls, iter->second) {
cls->getVariables()->cleanupForError(ar);
}
}
getConstants()->cleanupForError(ar);
StringToFunctionScopePtrMap().swap(m_functions);
delete m_redeclaredFunctions;
m_redeclaredFunctions = 0;
StringToClassScopePtrVecMap().swap(m_classes);
m_pseudoMain.reset();
m_tree.reset();
LocationPtr loc(new Location());
loc->file = m_fileName.c_str();
loc->first(line, 0);
loc->last(line, 0);
BlockScopePtr scope;
ExpressionListPtr args(new ExpressionList(scope, loc));
args->addElement(Expression::MakeScalarExpression(ar, scope, loc, msg));
SimpleFunctionCallPtr e(
new SimpleFunctionCall(scope, loc, "throw_fatal", args, ExpressionPtr()));
e->setThrowFatal();
ExpStatementPtr exp(new ExpStatement(scope, loc, e));
StatementListPtr stmts(new StatementList(scope, loc));
stmts->addElement(exp);
FunctionScopePtr fs = setTree(ar, stmts);
fs->setOuterScope(shared_from_this());
fs->getStmt()->resetScope(fs);
fs->getStmt()->setLocation(loc);
setOuterScope(const_cast<AnalysisResult*>(ar.get())->shared_from_this());
}
bool FileScope::addFunction(AnalysisResultConstPtr ar,
FunctionScopePtr funcScope) {
if (ar->declareFunction(funcScope)) {
FunctionScopePtr &fs = m_functions[funcScope->getName()];
if (fs) {
if (!m_redeclaredFunctions) {
m_redeclaredFunctions = new StringToFunctionScopePtrVecMap;
}
FunctionScopePtrVec &funcVec =
(*m_redeclaredFunctions)[funcScope->getName()];
if (!funcVec.size()) {
fs->setLocalRedeclaring();
funcVec.push_back(fs);
}
funcScope->setLocalRedeclaring();
funcVec.push_back(funcScope);
} else {
fs = funcScope;
}
return true;
}
return false;
}
bool FileScope::addClass(AnalysisResultConstPtr ar, ClassScopePtr classScope) {
if (ar->declareClass(classScope)) {
m_classes[classScope->getName()].push_back(classScope);
return true;
}
return false;
}
ClassScopePtr FileScope::getClass(const char *name) {
StringToClassScopePtrVecMap::const_iterator iter = m_classes.find(name);
if (iter == m_classes.end()) return ClassScopePtr();
return iter->second.back();
}
int FileScope::getFunctionCount() const {
int total = FunctionContainer::getFunctionCount();
for (StringToClassScopePtrVecMap::const_iterator iter = m_classes.begin();
iter != m_classes.end(); ++iter) {
BOOST_FOREACH(ClassScopePtr cls, iter->second) {
total += cls->getFunctionCount();
}
}
return total;
}
void FileScope::countReturnTypes(std::map<std::string, int> &counts) {
FunctionContainer::countReturnTypes(counts, m_redeclaredFunctions);
for (StringToClassScopePtrVecMap::const_iterator iter = m_classes.begin();
iter != m_classes.end(); ++iter) {
BOOST_FOREACH(ClassScopePtr cls, iter->second) {
cls->countReturnTypes(counts, 0);
}
}
}
void FileScope::pushAttribute() {
m_attributes.push_back(0);
}
void FileScope::setAttribute(Attribute attr) {
assert(!m_attributes.empty());
m_attributes.back() |= attr;
}
int FileScope::popAttribute() {
assert(!m_attributes.empty());
int ret = m_attributes.back();
m_attributes.pop_back();
return ret;
}
int FileScope::getGlobalAttribute() const {
assert(m_attributes.size() == 1);
return m_attributes.back();
}
bool FileScope::needPseudoMainVariables() const {
VariableTablePtr variables = m_pseudoMain->getVariables();
return
variables->getAttribute(VariableTable::ContainsDynamicVariable) ||
variables->getSymbols().size() > 0;
}
///////////////////////////////////////////////////////////////////////////////
ExpressionPtr FileScope::getEffectiveImpl(AnalysisResultConstPtr ar) const {
if (m_tree) return m_tree->getEffectiveImpl(ar);
return ExpressionPtr();
}
///////////////////////////////////////////////////////////////////////////////
void FileScope::declareConstant(AnalysisResultPtr ar, const string &name) {
if (!ar->declareConst(shared_from_this(), name)) {
addConstantDependency(ar, name);
}
}
void FileScope::addConstant(const string &name, TypePtr type,
ExpressionPtr value,
AnalysisResultPtr ar, ConstructPtr con) {
BlockScopePtr f = ar->findConstantDeclarer(name);
f->getConstants()->add(name, type, value, ar, con);
}
void FileScope::addIncludeDependency(AnalysisResultPtr ar,
const string &file, bool byInlined) {
ar->addIncludeDependency(shared_from_this(), file);
}
void FileScope::addClassDependency(AnalysisResultPtr ar,
const string &classname) {
ar->addClassDependency(shared_from_this(), classname);
}
void FileScope::addFunctionDependency(AnalysisResultPtr ar,
const string &funcname, bool byInlined) {
ar->addFunctionDependency(shared_from_this(), funcname);
}
void FileScope::addConstantDependency(AnalysisResultPtr ar,
const string &decname) {
ar->addConstantDependency(shared_from_this(), decname);
}
void FileScope::analyzeProgram(AnalysisResultPtr ar) {
if (m_pseudoMain) {
m_pseudoMain->getStmt()->analyzeProgram(ar);
}
}
ClassScopeRawPtr FileScope::resolveClass(ClassScopeRawPtr cls) {
BlockScopeSet::iterator it = m_providedDefs.find(cls);
if (it != m_providedDefs.end()) {
return ClassScopeRawPtr(static_cast<HPHP::ClassScope*>(it->get()));
}
return ClassScopeRawPtr();
}
bool FileScope::checkClass(const string &cls) {
return m_redecBases.find(cls) != m_redecBases.end();
}
FunctionScopeRawPtr FileScope::resolveFunction(FunctionScopeRawPtr func) {
BlockScopeSet::iterator it = m_providedDefs.find(func);
if (it != m_providedDefs.end()) {
return FunctionScopeRawPtr(static_cast<HPHP::FunctionScope*>(it->get()));
}
return FunctionScopeRawPtr();
}
/*
* Insert the class, and its parents (recursively) into m_providedDefs,
* returning true if the class was already known to exist.
* If def is true, this is the actual definition for the class,
* so set the hasBase flags.
*/
bool FileScope::insertClassUtil(AnalysisResultPtr ar,
ClassScopeRawPtr cls, bool def) {
if (!m_providedDefs.insert(cls).second) return true;
const vector<string> &bases = cls->getBases();
bool topBasesKnown = def && bases.size() > 31;
for (unsigned i = 0; i < bases.size(); i++) {
const string &s = bases[i];
ClassScopeRawPtr c = ar->findClass(s);
if (c) {
if (c->isRedeclaring()) {
ClassScopeRawPtr cr = resolveClass(c);
if (!cr) {
if (i >= 31) topBasesKnown = false;
m_redecBases.insert(c->getName());
continue;
}
c = cr;
} else if (!c->isVolatile()) {
if (def && i < 31) cls->setKnownBase(i);
continue;
}
if (insertClassUtil(ar, c, false)) {
if (def && i < 31) cls->setKnownBase(i);
} else {
if (i >= 31) topBasesKnown = false;
}
}
}
if (topBasesKnown) cls->setKnownBase(31);
return false;
}
void FileScope::analyzeIncludesHelper(AnalysisResultPtr ar) {
m_includeState = 1;
if (m_pseudoMain) {
StatementList &stmts = *getStmt();
bool hoistOnly = false;
for (int i = 0, n = stmts.getCount(); i < n; i++) {
StatementPtr s = stmts[i];
if (!s) continue;
if (s->is(Statement::KindOfClassStatement) ||
s->is(Statement::KindOfInterfaceStatement)) {
ClassScopeRawPtr cls(
static_pointer_cast<InterfaceStatement>(s)->getClassScope());
if (hoistOnly) {
const string &parent = cls->getOriginalParent();
if (cls->getBases().size() > (parent.empty() ? 0 : 1)) {
continue;
}
if (!parent.empty()) {
ClassScopeRawPtr c = ar->findClass(parent);
if (!c || (c->isVolatile() &&
!resolveClass(c) && !checkClass(parent))) {
continue;
}
}
}
if (cls->isVolatile()) {
insertClassUtil(ar, cls, true);
}
continue;
}
if (s->is(Statement::KindOfFunctionStatement)) {
FunctionScopeRawPtr func(
static_pointer_cast<FunctionStatement>(s)->getFunctionScope());
if (func->isVolatile()) m_providedDefs.insert(func);
continue;
}
if (!hoistOnly && s->is(Statement::KindOfExpStatement)) {
ExpressionRawPtr exp(
static_pointer_cast<ExpStatement>(s)->getExpression());
if (exp && exp->is(Expression::KindOfIncludeExpression)) {
FileScopeRawPtr fs(
static_pointer_cast<IncludeExpression>(exp)->getIncludedFile(ar));
if (fs && fs->m_includeState != 1) {
if (!fs->m_includeState) {
if (m_module && fs->m_privateInclude) {
BOOST_FOREACH(BlockScopeRawPtr bs, m_providedDefs) {
fs->m_providedDefs.insert(bs);
}
}
fs->analyzeIncludesHelper(ar);
}
BOOST_FOREACH(BlockScopeRawPtr bs, fs->m_providedDefs) {
m_providedDefs.insert(bs);
}
continue;
}
}
}
hoistOnly = true;
}
}
m_includeState = 2;
}
void FileScope::analyzeIncludes(AnalysisResultPtr ar) {
if (!m_privateInclude && !m_includeState) {
analyzeIncludesHelper(ar);
}
}
void FileScope::visit(AnalysisResultPtr ar,
void (*cb)(AnalysisResultPtr, StatementPtr, void*),
void *data)
{
if (m_pseudoMain) {
cb(ar, m_pseudoMain->getStmt(), data);
}
}
const string &FileScope::pseudoMainName() {
if (m_pseudoMainName.empty()) {
m_pseudoMainName = Option::MangleFilename(m_fileName, true);
}
return m_pseudoMainName;
}
FunctionScopePtr FileScope::createPseudoMain(AnalysisResultConstPtr ar) {
StatementListPtr st = m_tree;
FunctionStatementPtr f
(new FunctionStatement(BlockScopePtr(), LocationPtr(),
false, pseudoMainName(),
ExpressionListPtr(), st, 0, "",
ExpressionListPtr()));
f->setFileLevel();
FunctionScopePtr pseudoMain(
new HPHP::FunctionScope(ar, true,
pseudoMainName().c_str(),
f, false, 0, 0,
ModifierExpressionPtr(),
m_attributes[0], "",
shared_from_this(),
vector<UserAttributePtr>(),
true));
f->setBlockScope(pseudoMain);
FunctionScopePtr &fs = m_functions[pseudoMainName()];
always_assert(!fs);
fs = pseudoMain;
m_pseudoMain = pseudoMain;
return pseudoMain;
}
string FileScope::outputFilebase() const {
string file = m_fileName;
string out;
if (file.size() > 4 && file.substr(file.length() - 4) == ".php") {
out = file.substr(0, file.length() - 4);
} else {
out = file + ".nophp";
}
return Option::MangleFilename(out, false);
}
static void getFuncScopesSet(BlockScopeRawPtrQueue &v,
const StringToFunctionScopePtrMap &funcMap) {
for (StringToFunctionScopePtrMap::const_iterator
iter = funcMap.begin(), end = funcMap.end();
iter != end; ++iter) {
FunctionScopePtr f = iter->second;
if (f->isUserFunction()) {
v.push_back(f);
}
}
}
void FileScope::getScopesSet(BlockScopeRawPtrQueue &v) {
const StringToClassScopePtrVecMap &classes = getClasses();
for (StringToClassScopePtrVecMap::const_iterator iter = classes.begin(),
end = classes.end(); iter != end; ++iter) {
for (ClassScopePtrVec::const_iterator it = iter->second.begin(),
e = iter->second.end(); it != e; ++it) {
ClassScopePtr cls = *it;
if (cls->isUserClass()) {
v.push_back(cls);
getFuncScopesSet(v, cls->getFunctions());
}
}
}
getFuncScopesSet(v, getFunctions());
if (const StringToFunctionScopePtrVecMap *redec = m_redeclaredFunctions) {
for (StringToFunctionScopePtrVecMap::const_iterator iter = redec->begin(),
end = redec->end(); iter != end; ++iter) {
FunctionScopePtrVec::const_iterator i = iter->second.begin(),
e = iter->second.end();
v.insert(v.end(), ++i, e);
}
}
}
void FileScope::getClassesFlattened(ClassScopePtrVec &classes) const {
for (StringToClassScopePtrVecMap::const_iterator it = m_classes.begin();
it != m_classes.end(); ++it) {
BOOST_FOREACH(ClassScopePtr cls, it->second) {
classes.push_back(cls);
}
}
}
void FileScope::serialize(JSON::DocTarget::OutputStream &out) const {
JSON::DocTarget::MapStream ms(out);
ms.add("name", getName());
ClassScopePtrVec classes;
getClassesFlattened(classes);
ms.add("classes", classes);
FunctionScopePtrVec funcs;
getFunctionsFlattened(m_redeclaredFunctions, funcs, true);
ms.add("functions", funcs);
// TODO(stephentu): constants
ms.done();
}
+211
Ver Arquivo
@@ -0,0 +1,211 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __FILE_SCOPE_H__
#define __FILE_SCOPE_H__
#include <compiler/analysis/block_scope.h>
#include <compiler/analysis/function_container.h>
#include <compiler/analysis/code_error.h>
#include <compiler/code_generator.h>
#include <boost/graph/adjacency_list.hpp>
#include <util/json.h>
#include <runtime/base/md5.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
class CodeGenerator;
DECLARE_BOOST_TYPES(StatementList);
DECLARE_BOOST_TYPES(ClassScope);
DECLARE_BOOST_TYPES(Location);
DECLARE_BOOST_TYPES(FileScope);
DECLARE_BOOST_TYPES(FunctionScope);
/**
* A FileScope stores what's parsed from one single source file. It's up to
* AnalysisResult objects to grab statements, functions and classes from
* FileScope objects to form execution paths.
*/
class FileScope : public BlockScope,
public FunctionContainer,
public JSON::DocTarget::ISerializable {
public:
enum Attribute {
ContainsDynamicVariable = 0x001,
ContainsLDynamicVariable = 0x002,
VariableArgument = 0x004,
ContainsExtract = 0x008, // contains call to extract()
ContainsCompact = 0x010, // contains call to compact()
ContainsReference = 0x020, // returns ref or has ref parameters
ReferenceVariableArgument = 0x040, // like sscanf or fscanf
ContainsUnset = 0x080, // need special handling
NoEffect = 0x100, // does not side effect
HelperFunction = 0x200, // runtime helper function
ContainsGetDefinedVars = 0x400, // need VariableTable with getDefinedVars
MixedVariableArgument = 0x800, // variable args, may or may not be ref'd
IsFoldable = 0x1000,// function can be constant folded
NeedsActRec = 0x2000,// builtin function needs ActRec
IgnoreRedefinition = 0x4000,// ignore redefinition of builtin function
};
typedef boost::adjacency_list<boost::setS, boost::vecS> Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
public:
FileScope(const std::string &fileName, int fileSize, const MD5 &md5);
~FileScope() { delete m_redeclaredFunctions; }
int getSize() const { return m_size;}
// implementing FunctionContainer
virtual std::string getParentName() const { assert(false); return "";}
const std::string &getName() const { return m_fileName;}
const MD5& getMd5() const { return m_md5; }
StatementListPtr getStmt() const { return m_tree;}
const StringToClassScopePtrVecMap &getClasses() const {
return m_classes;
}
void getClassesFlattened(ClassScopePtrVec &classes) const;
ClassScopePtr getClass(const char *name);
void getScopesSet(BlockScopeRawPtrQueue &v);
int getFunctionCount() const;
void countReturnTypes(std::map<std::string, int> &counts);
int getClassCount() const { return m_classes.size();}
void pushAttribute();
void setAttribute(Attribute attr);
int getGlobalAttribute() const;
int popAttribute();
void serialize(JSON::DocTarget::OutputStream &out) const;
/**
* Whether this file has top level non-declaration statements that
* have CPP implementation.
*/
ExpressionPtr getEffectiveImpl(AnalysisResultConstPtr ar) const;
/**
* Parser functions. Parser only deals with a FileScope object, and these
* are the only functions a parser calls upon analysis results.
*/
FunctionScopePtr setTree(AnalysisResultConstPtr ar, StatementListPtr tree);
void cleanupForError(AnalysisResultConstPtr ar,
int line, const std::string &msg);
bool addFunction(AnalysisResultConstPtr ar, FunctionScopePtr funcScope);
bool addClass(AnalysisResultConstPtr ar, ClassScopePtr classScope);
const StringToFunctionScopePtrVecMap *getRedecFunctions() {
return m_redeclaredFunctions;
}
/**
* For separate compilation
* These add edges between filescopes in the other dep graph and
* save the symbols for our iface.
* This stuff only happens in the filechanged state.
*/
void addConstant(const std::string &name, TypePtr type, ExpressionPtr value,
AnalysisResultPtr ar, ConstructPtr con);
void declareConstant(AnalysisResultPtr ar, const std::string &name);
void getConstantNames(std::vector<std::string> &names);
TypePtr getConstantType(const std::string &name);
void addIncludeDependency(AnalysisResultPtr ar, const std::string &file,
bool byInlined);
void addClassDependency(AnalysisResultPtr ar,
const std::string &classname);
void addFunctionDependency(AnalysisResultPtr ar,
const std::string &funcname, bool byInlined);
void addConstantDependency(AnalysisResultPtr ar,
const std::string &decname);
/**
* Called only by World
*/
vertex_descriptor vertex() { return m_vertex; }
void setVertex(vertex_descriptor vertex) {
m_vertex = vertex;
}
void setModule() { m_module = true; }
void setPrivateInclude() { m_privateInclude = true; }
bool isPrivateInclude() const { return m_privateInclude && !m_externInclude; }
void setExternInclude() { m_externInclude = true; }
void analyzeProgram(AnalysisResultPtr ar);
void analyzeIncludes(AnalysisResultPtr ar);
void analyzeIncludesHelper(AnalysisResultPtr ar);
bool insertClassUtil(AnalysisResultPtr ar, ClassScopeRawPtr cls, bool def);
bool checkClass(const std::string &cls);
ClassScopeRawPtr resolveClass(ClassScopeRawPtr cls);
FunctionScopeRawPtr resolveFunction(FunctionScopeRawPtr func);
void visit(AnalysisResultPtr ar,
void (*cb)(AnalysisResultPtr, StatementPtr, void*),
void *data);
const std::string &pseudoMainName();
void outputFileCPP(AnalysisResultPtr ar, CodeGenerator &cg);
bool load();
bool needPseudoMainVariables() const;
std::string outputFilebase() const;
void addPseudoMainVariable(const std::string &name) {
m_pseudoMainVariables.insert(name);
}
std::set<std::string> &getPseudoMainVariables() {
return m_pseudoMainVariables;
}
FunctionScopeRawPtr getPseudoMain() const {
return m_pseudoMain;
}
FileScopePtr shared_from_this() {
return boost::static_pointer_cast<FileScope>
(BlockScope::shared_from_this());
}
private:
int m_size;
MD5 m_md5;
unsigned m_module : 1;
unsigned m_privateInclude : 1;
unsigned m_externInclude : 1;
unsigned m_includeState : 2;
std::vector<int> m_attributes;
std::string m_fileName;
StatementListPtr m_tree;
StringToFunctionScopePtrVecMap *m_redeclaredFunctions;
StringToClassScopePtrVecMap m_classes; // name => class
FunctionScopeRawPtr m_pseudoMain;
vertex_descriptor m_vertex;
std::string m_pseudoMainName;
std::set<std::string> m_pseudoMainVariables;
BlockScopeSet m_providedDefs;
std::set<std::string> m_redecBases;
FunctionScopePtr createPseudoMain(AnalysisResultConstPtr ar);
void setFileLevel(StatementListPtr stmt);
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // __FILE_SCOPE_H__
@@ -0,0 +1,80 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/analysis/function_container.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/code_error.h>
#include <compiler/statement/statement_list.h>
#include <compiler/option.h>
#include <util/util.h>
#include <util/hash.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
FunctionContainer::FunctionContainer() {
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void FunctionContainer::countReturnTypes(
std::map<std::string, int> &counts,
const StringToFunctionScopePtrVecMap *redec) {
for (StringToFunctionScopePtrMap::const_iterator iter =
m_functions.begin(); iter != m_functions.end(); ++iter) {
FunctionScopePtr f = iter->second;
if (f->isLocalRedeclaring()) {
always_assert(redec);
BOOST_FOREACH(f, redec->find(iter->first)->second) {
TypePtr type = f->getReturnType();
if (type) {
type->count(counts);
}
}
} else {
TypePtr type = f->getReturnType();
if (type) {
type->count(counts);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void FunctionContainer::getFunctionsFlattened(
const StringToFunctionScopePtrVecMap *redec,
FunctionScopePtrVec &funcs,
bool excludePseudoMains /* = false */) const {
for (StringToFunctionScopePtrMap::const_iterator it = m_functions.begin();
it != m_functions.end(); ++it) {
FunctionScopePtr func = it->second;
if (!excludePseudoMains || !func->inPseudoMain()) {
if (func->isLocalRedeclaring()) {
const FunctionScopePtrVec &r = redec->find(it->first)->second;
funcs.insert(funcs.end(), r.begin(), r.end());
} else {
funcs.push_back(func);
}
}
}
}
+59
Ver Arquivo
@@ -0,0 +1,59 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __FUNCTION_CONTAINER_H__
#define __FUNCTION_CONTAINER_H__
#include <compiler/hphp.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
class CodeGenerator;
DECLARE_BOOST_TYPES(AnalysisResult);
DECLARE_BOOST_TYPES(FunctionScope);
DECLARE_BOOST_TYPES(ClassScope);
DECLARE_BOOST_TYPES(FunctionContainer);
/**
* Base class of both FileScope and ClassScope that can contain functions.
*/
class FunctionContainer {
public:
FunctionContainer();
/**
* Functions this container has.
*/
int getFunctionCount() const { return m_functions.size(); }
void countReturnTypes(std::map<std::string, int> &counts,
const StringToFunctionScopePtrVecMap *redec);
const StringToFunctionScopePtrMap &getFunctions() const {
return m_functions;
}
void getFunctionsFlattened(const StringToFunctionScopePtrVecMap *redec,
FunctionScopePtrVec &funcs,
bool excludePseudoMains = false) const;
protected:
// name => functions. Order of declaration
StringToFunctionScopePtrMap m_functions;
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // __FUNCTION_CONTAINER_H__
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -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.
*/
@@ -270,7 +271,6 @@ public:
void fixRetExprs();
bool needsTypeCheckWrapper() const;
const char *getPrefix(AnalysisResultPtr ar, ExpressionListPtr params);
void setOptFunction(FunctionOptPtr fn) { m_optFunction = fn; }
FunctionOptPtr getOptFunction() const { return m_optFunction; }
@@ -352,92 +352,6 @@ public:
* Override BlockScope::outputPHP() to generate return type.
*/
virtual void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar);
/**
* Override to preface with call temps.
*/
virtual void outputCPP(CodeGenerator &cg, AnalysisResultPtr ar);
/**
* Generate parameter declaration.
*/
void outputCPPParamsDecl(CodeGenerator &cg, AnalysisResultPtr ar,
ExpressionListPtr params, bool showDefault);
/**
* This one is a special version that doesn't require a params expression.
* It's for use with extension functions. It only works for the
* implementation since it ignores optional arguments.
*/
void outputCPPParamsImpl(CodeGenerator &cg, AnalysisResultPtr ar);
/**
* If inside this function, we have to make a call to an implementation
* function that has the same signature, how does the parameter list
* look like?
*/
void outputCPPParamsCall(CodeGenerator &cg, AnalysisResultPtr ar,
bool aggregateParams);
/**
* How does a caller prepare parameters.
*/
static void OutputCPPArguments(ExpressionListPtr params,
FunctionScopePtr func,
CodeGenerator &cg, AnalysisResultPtr ar,
int extraArg, bool variableArgument,
int extraArgArrayId = -1,
int extraArgArrayHash = -1,
int extraArgArrayIndex = -1,
bool ignoreFuncParamTypes = false);
/**
* Only generate arguments that have effects. This is for keeping those
* parameters around when generating a error-raising function call, so to
* avoid "unused" variable compiler warnings.
*/
static void OutputCPPEffectiveArguments(ExpressionListPtr params,
CodeGenerator &cg,
AnalysisResultPtr ar);
/**
* Generate invoke proxy.
*/
static void OutputCPPDynamicInvokeCount(CodeGenerator &cg);
void outputCPPDynamicInvoke(CodeGenerator &cg, AnalysisResultPtr ar,
const char *funcPrefix,
const char *name,
bool voidWrapperOff = false,
bool fewArgs = false,
bool ret = true,
const char *extraArg = NULL,
bool constructor = false,
const char *instance = NULL,
const char *class_name = "");
void outputCPPDef(CodeGenerator &cg);
/**
* ...so ClassStatement can call them for classes that don't have
* constructors defined
*/
void outputCPPCreateDecl(CodeGenerator &cg, AnalysisResultPtr ar);
void outputCPPCreateImpl(CodeGenerator &cg, AnalysisResultPtr ar);
/**
* output functions
*/
void outputCPPClassMap(CodeGenerator &cg, AnalysisResultPtr ar);
void outputMethodWrapper(CodeGenerator &cg, AnalysisResultPtr ar,
const char *clsToConstruct);
/**
* Output CallInfo instance for this function.
*/
void outputCPPCallInfo(CodeGenerator &cg, AnalysisResultPtr ar);
void outputCPPPreface(CodeGenerator &cg, AnalysisResultPtr ar);
void outputCPPHelperClassAlloc(CodeGenerator &cg,
AnalysisResultPtr ar);
/**
* Serialize the iface, not everything.
*/
@@ -518,14 +432,6 @@ public:
};
private:
bool outputCPPArrayCreate(CodeGenerator &cg,
AnalysisResultPtr ar,
int m_maxParam);
void outputCPPSubClassParam(CodeGenerator &cg,
AnalysisResultPtr ar,
ParameterExpressionPtr param);
void init(AnalysisResultConstPtr ar);
static StringToFunctionInfoPtrMap s_refParamInfo;
@@ -582,13 +488,12 @@ private:
StatementPtr m_stmtCloned; // cloned method body stmt
int m_inlineIndex;
FunctionOptPtr m_optFunction;
int outputCPPInvokeArgCountCheck(CodeGenerator &cg, AnalysisResultPtr ar,
bool ret, bool constructor, int maxCount);
ExpressionPtrVec m_retExprsToFix;
ExpressionListPtr m_closureVars;
ExpressionListPtr m_closureValues;
ReadWriteMutex m_inlineMutex;
unsigned m_nextID; // used when cloning generators for traits
std::list<FunctionScopeRawPtr> m_clonedTraitOuterScope;
};
///////////////////////////////////////////////////////////////////////////////
@@ -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;
}
@@ -452,7 +452,7 @@ const Symbol *SymbolTable::getSymbolImpl(const std::string &name) const {
if (it != m_symbolMap.end()) {
return &it->second;
}
return NULL;
return nullptr;
}
Symbol *SymbolTable::getSymbol(const std::string &name) {
@@ -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;
+785
Ver Arquivo
@@ -0,0 +1,785 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/analysis/type.h>
#include <compiler/code_generator.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/expression/expression.h>
#include <boost/format.hpp>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// statics
TypePtr Type::Null (new Type(Type::KindOfVoid ));
TypePtr Type::Boolean (new Type(Type::KindOfBoolean ));
TypePtr Type::Int32 (new Type(Type::KindOfInt32 ));
TypePtr Type::Int64 (new Type(Type::KindOfInt64 ));
TypePtr Type::Double (new Type(Type::KindOfDouble ));
TypePtr Type::String (new Type(Type::KindOfString ));
TypePtr Type::Array (new Type(Type::KindOfArray ));
TypePtr Type::Object (new Type(Type::KindOfObject ));
TypePtr Type::Variant (new Type(Type::KindOfVariant ));
TypePtr Type::Numeric (new Type(Type::KindOfNumeric ));
TypePtr Type::PlusOperand (new Type(Type::KindOfPlusOperand ));
TypePtr Type::Primitive (new Type(Type::KindOfPrimitive ));
TypePtr Type::Sequence (new Type(Type::KindOfSequence ));
TypePtr Type::AutoSequence(new Type(Type::KindOfAutoSequence));
TypePtr Type::AutoObject (new Type(Type::KindOfAutoObject ));
TypePtr Type::Any (new Type(Type::KindOfAny ));
TypePtr Type::Some (new Type(Type::KindOfSome ));
Type::TypePtrMap Type::s_TypeHintTypes;
void Type::InitTypeHintMap() {
assert(s_TypeHintTypes.empty());
s_TypeHintTypes["array"] = Type::Array;
if (Option::EnableHipHopSyntax) {
s_TypeHintTypes["bool"] = Type::Boolean;
s_TypeHintTypes["boolean"] = Type::Boolean;
s_TypeHintTypes["int"] = Type::Int64;
s_TypeHintTypes["integer"] = Type::Int64;
s_TypeHintTypes["real"] = Type::Double;
s_TypeHintTypes["double"] = Type::Double;
s_TypeHintTypes["float"] = Type::Double;
s_TypeHintTypes["string"] = Type::String;
}
}
const Type::TypePtrMap &Type::GetTypeHintTypes() {
return s_TypeHintTypes;
}
void Type::ResetTypeHintTypes() {
s_TypeHintTypes.clear();
}
TypePtr Type::CreateObjectType(const std::string &classname) {
return TypePtr(new Type(KindOfObject, classname));
}
TypePtr Type::GetType(KindOf kindOf,
const std::string &clsname /* = "" */) {
assert(kindOf);
if (!clsname.empty()) return TypePtr(new Type(kindOf, clsname));
switch (kindOf) {
case KindOfBoolean: return Type::Boolean;
case KindOfInt32: return Type::Int32;
case KindOfInt64: return Type::Int64;
case KindOfDouble: return Type::Double;
case KindOfString: return Type::String;
case KindOfArray: return Type::Array;
case KindOfVariant: return Type::Variant;
case KindOfObject: return Type::Object;
case KindOfNumeric: return Type::Numeric;
case KindOfPrimitive: return Type::Primitive;
case KindOfPlusOperand: return Type::PlusOperand;
case KindOfSequence: return Type::Sequence;
case KindOfSome: return Type::Some;
case KindOfAny: return Type::Any;
default: return TypePtr(new Type(kindOf));
}
}
TypePtr Type::Intersection(AnalysisResultConstPtr ar,
TypePtr from, TypePtr to) {
// Special case: if we're casting to Some or Any, return the "from" type;
// if we're casting to Variant, return Variant.
if (to->m_kindOf == KindOfSome || to->m_kindOf == KindOfAny) {
return from;
} else if (to->m_kindOf == KindOfVariant) {
return Variant;
}
int resultKind = to->m_kindOf & from->m_kindOf;
std::string resultName = "";
if (resultKind & KindOfObject) {
// if they're the same, or we don't know one's name, then use
// the other
if (to->m_name == from->m_name || from->m_name.empty()) {
resultName = to->m_name;
} else if (to->m_name.empty()) {
resultName = from->m_name;
} else {
// make sure there's a subclass relation
ClassScopePtr cls = ar->findClass(from->m_name);
if (cls) {
if (cls->derivesFrom(ar, to->m_name, true, false)) {
resultName = to->m_name;
} else {
resultKind &= ~KindOfObject;
}
}
}
}
TypePtr res;
// If there is overlap (for instance, they were the same, or we've narrowed
// down something like Sequenece to be more specific), then return the
// intersection of the types.
if (resultKind) {
res = GetType(resultKind, resultName);
} else if (from->mustBe(KindOfObject) && to->m_kindOf == KindOfPrimitive) {
// Special case Object -> Primitive: can we tostring it?
if (!from->m_name.empty()) {
ClassScopePtr cls = ar->findClass(from->m_name);
if (cls && cls->findFunction(ar, "__tostring", true)) {
res = Type::String;
}
}
// Otherwise, return Int32
res = Int32;
} else if (from->m_kindOf == KindOfBoolean
&& to->mustBe(KindOfNumeric | KindOfArray | KindOfString)
&& !IsExactType(to->m_kindOf)) {
res = Int32;
} else {
res = to;
}
if (from->mustBe(KindOfBoolean) && to->m_kindOf == KindOfPrimitive) {
res = Int32;
}
return res;
}
bool Type::IsMappedToVariant(TypePtr t) {
if (!t) return true;
switch (t->m_kindOf) {
case KindOfBoolean:
case KindOfInt32 :
case KindOfInt64 :
case KindOfDouble :
case KindOfString :
case KindOfArray :
case KindOfObject :
return false;
default: break;
}
return true;
}
bool Type::IsCastNeeded(AnalysisResultConstPtr ar, TypePtr from, TypePtr to) {
if (SameType(from, to)) return false;
if (!from->m_kindOf) return true;
if (!to->m_kindOf) return true;
// Special case: all Sequence operations are implemented on both String and
// Array, and vice versa, therefore no need to cast between these types.
if ((from->m_kindOf == KindOfSequence && to->mustBe(KindOfSequence))
|| (to->m_kindOf == KindOfSequence && from->mustBe(KindOfSequence))) {
return false;
}
switch (to->m_kindOf) {
case KindOfVariant:
case KindOfNumeric:
case KindOfPrimitive:
case KindOfPlusOperand:
case KindOfSome:
case KindOfSequence:
case KindOfAny:
// Currently these types are all mapped to Variant in runtime/base, and
// that's why these casting are not needed.
return false;
case KindOfObject:
if (from->m_kindOf == KindOfObject && to->m_name.empty() &&
!from->m_name.empty()) return false;
else return true;
default:
// if we don't have a specific type narrowed down, then
// it will be a Variant at at runtime, so no cast is needed.
return IsExactType(to->m_kindOf);
}
}
bool Type::IsCoercionNeeded(AnalysisResultConstPtr ar, TypePtr t1, TypePtr t2) {
if (t1->m_kindOf == KindOfSome ||
t1->m_kindOf == KindOfAny ||
t2->m_kindOf == KindOfSome ||
t2->m_kindOf == KindOfAny) return true;
// special case: we always coerce to a specific object type so we can
// type checking properties and methods
if (t1->m_kindOf == KindOfObject && !t1->m_name.empty() &&
t2->m_kindOf == KindOfObject && t2->m_name.empty()) {
return true;
}
return !Type::IsLegalCast(ar, t1, t2);
}
TypePtr Type::Coerce(AnalysisResultConstPtr ar, TypePtr type1, TypePtr type2) {
if (SameType(type1, type2)) return type1;
if (type1->m_kindOf == KindOfVariant ||
type2->m_kindOf == KindOfVariant) return Type::Variant;
if (type1->m_kindOf > type2->m_kindOf) {
TypePtr tmp = type1;
type1 = type2;
type2 = tmp;
}
if (type1->m_kindOf == KindOfVoid &&
(type2->m_kindOf == KindOfString ||
type2->m_kindOf == KindOfArray ||
type2->m_kindOf == KindOfObject)) {
return type2;
}
if (type2->m_kindOf == KindOfSome ||
type2->m_kindOf == KindOfAny) return type1;
if (type2->m_kindOf & KindOfAuto) {
if (type1->mustBe(type2->m_kindOf & ~KindOfAuto)) {
if (!(type1->m_kindOf & Type::KindOfString)) {
return type1;
}
if (type2->m_kindOf == KindOfAutoSequence) {
return Type::Sequence;
}
return GetType((KindOf)(type2->m_kindOf & ~KindOfAuto));
}
return Type::Variant;
}
if (type1->mustBe(KindOfInteger)) {
if (type2->mustBe(KindOfInteger)) {
return type2;
} else if (type2->mustBe(KindOfDouble)) {
return Type::Numeric;
}
}
if (type1->mustBe(Type::KindOfObject) &&
type2->mustBe(Type::KindOfObject)) {
if (type1->m_name.empty()) return type1;
if (type2->m_name.empty()) return type2;
ClassScopePtr cls1 = ar->findClass(type1->m_name);
if (cls1 && !cls1->isRedeclaring() &&
cls1->derivesFrom(ar, type2->m_name, true, false)) {
return type2;
}
ClassScopePtr cls2 = ar->findClass(type2->m_name);
if (cls2 && !cls2->isRedeclaring() &&
cls2->derivesFrom(ar, type1->m_name, true, false)) {
return type1;
}
if (cls1 && cls2 &&
!cls1->isRedeclaring() && !cls2->isRedeclaring()) {
ClassScopePtr parent =
ClassScope::FindCommonParent(ar, type1->m_name,
type2->m_name);
if (parent) {
return Type::CreateObjectType(parent->getName());
}
}
return Type::Object;
}
if (type1->mustBe(type2->m_kindOf)) {
return type2;
}
static_assert(Type::KindOfString < Type::KindOfArray,
"Expected Type::KindOfString < Type::KindOfArray");
if (type1->m_kindOf == Type::KindOfString &&
type2->m_kindOf == Type::KindOfArray) {
return Type::Sequence;
}
return Type::Variant;
}
TypePtr Type::Union(AnalysisResultConstPtr ar, TypePtr type1, TypePtr type2) {
if (SameType(type1, type2)) {
return type1;
}
int resultKind = type1->m_kindOf | type2->m_kindOf;
if (resultKind == KindOfObject) {
std::string resultName("");
// if they're the same, or we don't know one's name, then use
// the other
if (type1->m_name == type2->m_name) {
resultName = type1->m_name;
} else if (type1->m_name.empty() || type2->m_name.empty()) {
// resultName was initialized to "", so leave it as such;
// we know it's an object but not what kind.
} else {
// take the superclass
ClassScopePtr res =
ClassScope::FindCommonParent(ar, type1->m_name,
type2->m_name);
if (res) resultName = res->getName();
}
return TypePtr(Type::CreateObjectType(resultName));
}
return GetType((KindOf)resultKind);
}
bool Type::SameType(TypePtr type1, TypePtr type2) {
if (!type1 && !type2) return true;
if (!type1 || !type2) return false;
if (type1->m_kindOf == type2->m_kindOf) {
if ((type1->m_kindOf & KindOfObject) &&
type1->m_name != type2->m_name) return false;
return true;
}
return false;
}
bool Type::SubType(AnalysisResultConstPtr ar, TypePtr type1, TypePtr type2) {
if (!type1 && !type2) return true;
if (!type1 || !type2) return false;
if (type1->m_kindOf != type2->m_kindOf) return false;
if (!(type1->m_kindOf & KindOfObject)) return true;
// both are objects...
if (type1->m_name == type2->m_name) return true;
// ... with different classnames; check subtype relationship.
ClassScopePtr cls1 = ar->findClass(type1->m_name);
return cls1 && !cls1->isRedeclaring() &&
cls1->derivesFrom(ar, type2->m_name, true, false);
}
bool Type::IsExactType(KindOf kindOf) {
// clever trick thanks to mwilliams - this will evaluate
// to true iff exactly one bit is set in kindOf
return kindOf && !(kindOf & (kindOf-1));
}
bool Type::HasFastCastMethod(TypePtr t) {
switch (t->getKindOf()) {
case Type::KindOfBoolean:
case Type::KindOfInt32:
case Type::KindOfInt64:
case Type::KindOfDouble:
case Type::KindOfString:
case Type::KindOfArray:
case Type::KindOfObject:
return true;
default: break;
}
return false;
}
string Type::GetFastCastMethod(
TypePtr dst, bool allowRef, bool forConst) {
const char *prefix0 = allowRef ? "to" : "as";
const char *prefix1 = forConst ? "C" : "";
const char *prefix2 = "Ref";
const char *type ATTRIBUTE_UNUSED;
switch (dst->getKindOf()) {
case Type::KindOfBoolean:
case Type::KindOfInt32:
case Type::KindOfInt64:
case Type::KindOfDouble:
prefix0 = "to";
prefix1 = "";
prefix2 = "Val";
break;
default: break;
}
switch (dst->getKindOf()) {
case Type::KindOfBoolean:
type = "Boolean";
break;
case Type::KindOfInt32:
case Type::KindOfInt64:
type = "Int64";
break;
case Type::KindOfDouble:
type = "Double";
break;
case Type::KindOfString:
type = "Str";
break;
case Type::KindOfArray:
type = "Arr";
break;
case Type::KindOfObject:
type = "Obj";
break;
default:
type = ""; // make the compiler happy
assert(false);
break;
}
return string(prefix0) + string(prefix1) + string(type) + string(prefix2);
}
/* This new IsLegalCast returns true in a few cases where the old version
* (which was basically a hardcoded truth table) returned false; it seems
* like "true" is in fact the right thing to return. The cases that appear
* when compiling www are:
* Sequence -> Array
* PlusOperand -> Array
* String -> PlusOperand
* Boolean -> PlusOperand
*/
bool Type::IsLegalCast(AnalysisResultConstPtr ar, TypePtr from, TypePtr to) {
if (!from->m_kindOf) return true;
// since both 'from' and 'to' represent sets of types, we do
// this by computing the set of types that we could possibly cast 'from'
// to, and then determining whether that overlaps with 'to'.
int canCastTo = KindOfBoolean | from->m_kindOf;
if (from->m_kindOf & KindOfVoid) canCastTo |= KindOfVoid;
// Boolean, Numeric, and String can all be cast among each other
if (from->m_kindOf & (KindOfBoolean | KindOfNumeric | KindOfString)) {
canCastTo |= KindOfNumeric | KindOfString;
}
if (from->m_kindOf & KindOfObject) {
// Objects can only cast to string if they have __tostring
if (from->m_name.empty()) {
canCastTo |= KindOfString; // we don't know which class it is
} else {
ClassScopePtr cls = ar->findClass(from->m_name);
if (!cls || cls->isRedeclaring() ||
cls->findFunction(ar, "__tostring", true)) {
canCastTo |= KindOfString;
}
}
// can only cast between objects if there's a subclass relation
if ((to->m_kindOf & KindOfObject) && !to->m_name.empty() &&
!from->m_name.empty() && to->m_name != from->m_name) {
ClassScopePtr cls = ar->findClass(from->m_name);
if (cls && (cls->isRedeclaring() ||
!cls->derivesFrom(ar, to->m_name, true, true))) {
canCastTo &= ~KindOfObject;
}
}
}
bool overlap = (to->m_kindOf & canCastTo);
return overlap;
}
///////////////////////////////////////////////////////////////////////////////
Type::Type(KindOf kindOf) : m_kindOf(kindOf) {
}
Type::Type(KindOf kindOf, const std::string &name)
: m_kindOf(kindOf), m_name(name) {
// m_name must not be empty only when this type could
// be an object
assert(m_name.empty() || (m_kindOf & KindOfObject));
}
bool Type::isInteger() const {
switch (m_kindOf) {
case KindOfInt32:
case KindOfInt64:
return true;
default:
break;
}
return false;
}
bool Type::isStandardObject() const {
return m_kindOf == KindOfObject && m_name.empty();
}
bool Type::isSpecificObject() const {
return m_kindOf == KindOfObject && !m_name.empty();
}
bool Type::isNonConvertibleType() const {
return m_kindOf == KindOfObject || m_kindOf == KindOfArray;
}
bool Type::isNoObjectInvolved() const {
if (couldBe(KindOfObject)
|| couldBe(KindOfArray))
return false;
else
return true;
}
TypePtr Type::combinedArithmeticType(TypePtr t1, TypePtr t2) {
KindOf kind = KindOfAny;
if ((t1 && t1->is(Type::KindOfArray)) ||
(t2 && t2->is(Type::KindOfArray))) {
return TypePtr();
}
if (t1 && t1->isPrimitive()) {
if (t2 && t2->isPrimitive()) {
if (t2->getKindOf() > t1->getKindOf()) {
kind = t2->getKindOf();
} else {
kind = t1->getKindOf();
}
} else if (t1->is(KindOfDouble)) {
kind = KindOfDouble;
} else {
kind = KindOfNumeric;
}
} else if (t2 && t2->isPrimitive()) {
if (t2->is(KindOfDouble)) {
kind = KindOfDouble;
} else {
kind = KindOfNumeric;
}
} else if ((t1 && t1->mustBe(KindOfNumeric)) ||
(t2 && t2->mustBe(KindOfNumeric))) {
kind = KindOfNumeric;
}
if (kind < KindOfInt64) {
kind = KindOfInt64;
}
if (kind != KindOfAny) {
return GetType(kind);
}
return TypePtr();
}
///////////////////////////////////////////////////////////////////////////////
ClassScopePtr Type::getClass(AnalysisResultConstPtr ar,
BlockScopeRawPtr scope) const {
if (m_name.empty()) return ClassScopePtr();
ClassScopePtr cls = ar->findClass(m_name);
if (cls && cls->isRedeclaring()) {
if (!scope) {
cls.reset();
} else {
cls = scope->findExactClass(cls);
}
}
return cls;
}
DataType Type::getDataType() const {
switch (m_kindOf) {
case KindOfBoolean: return HPHP::KindOfBoolean;
case KindOfInt32:
case KindOfInt64: return HPHP::KindOfInt64;
case KindOfDouble: return HPHP::KindOfDouble;
case KindOfString: return HPHP::KindOfString;
case KindOfArray: return HPHP::KindOfArray;
case KindOfObject: return HPHP::KindOfObject;
case KindOfNumeric:
case KindOfPrimitive:
case KindOfPlusOperand:
case KindOfSequence:
default: return HPHP::KindOfUnknown;
}
}
// This is similar to getDataType() except that it returns
// HPHP::KindOfNull for KindOfVoid;
DataType Type::getHhvmDataType() const {
switch (m_kindOf) {
case KindOfVoid: return HPHP::KindOfNull;
default: return getDataType();
}
}
std::string Type::getPHPName() {
switch (m_kindOf) {
case KindOfArray: return "array";
case KindOfObject: return m_name;
default: break;
}
return "";
}
std::string Type::toString() const {
switch (m_kindOf) {
case KindOfBoolean: return "Boolean";
case KindOfInt32: return "Int32";
case KindOfInt64: return "Int64";
case KindOfDouble: return "Double";
case KindOfString: return "String";
case KindOfArray: return "Array";
case KindOfVariant: return "Variant";
case KindOfSome: return "Some";
case KindOfAny: return "Any";
case KindOfObject: return string("Object - ") + m_name;
case KindOfNumeric: return "Numeric";
case KindOfPrimitive: return "Primitive";
case KindOfPlusOperand: return "PlusOperand";
case KindOfSequence: return "Sequence";
default:
return boost::str(boost::format("[0x%x]") % m_kindOf);
}
return "(unknown)";
}
void Type::Dump(TypePtr type, const char *fmt /* = "%s" */) {
printf(fmt, type ? type->toString().c_str() : "(null)");
}
void Type::Dump(ExpressionPtr exp) {
Dump(exp->getExpectedType(), "Expected: %s\t");
Dump(exp->getActualType(), "Actual: %s\n");
}
void Type::serialize(JSON::CodeError::OutputStream &out) const {
out << toString();
}
void Type::serialize(JSON::DocTarget::OutputStream &out) const {
string s("any");
switch (m_kindOf) {
case KindOfBoolean: s = "boolean"; break;
case KindOfInt32:
case KindOfInt64: s = "integer"; break;
case KindOfDouble: s = "double"; break;
case KindOfString: s = "string"; break;
case KindOfArray: s = "array"; break;
case KindOfVariant:
case KindOfSome:
case KindOfAny: s = "any"; break;
case KindOfObject:
{
if (m_name.empty()) s = "object";
else {
ClassScopePtr c(getClass(out.analysisResult(), BlockScopeRawPtr()));
if (c) {
s = c->getOriginalName();
} else {
s = "object";
}
}
break;
}
case KindOfNumeric: s = "numeric"; break;
case KindOfPrimitive: s = "primitive"; break;
case KindOfPlusOperand: s = "any"; break;
case KindOfSequence: s = "sequence"; break;
}
out << s;
}
void Type::count(std::map<std::string, int> &counts) {
if (is(Type::KindOfObject)) {
if (isSpecificObject()) {
counts["Object - Specific"]++;
} else {
counts["Object"]++;
}
} else {
counts[toString()]++;
}
if (IsExactType(m_kindOf)) {
counts["_strong"]++;
} else {
counts["_weak"]++;
}
counts["_all"]++;
}
TypePtr Type::InferredObject(AnalysisResultConstPtr ar,
TypePtr type1,
TypePtr type2) {
assert(type1->m_kindOf == KindOfObject);
assert(type2->m_kindOf == KindOfObject);
TypePtr resultType = Type::Object;
// if they're the same, or we don't know one's name, then use
// the other
if (type1->m_name == type2->m_name || type1->m_name.empty()) {
resultType = type2;
} else if (type2->m_name.empty()) {
resultType = type1;
} else {
// take the subclass
ClassScopePtr cls1 = ar->findClass(type1->m_name);
ClassScopePtr cls2 = ar->findClass(type2->m_name);
bool c1ok = cls1 && !cls1->isRedeclaring();
bool c2ok = cls2 && !cls2->isRedeclaring();
if (c1ok && cls1->derivesFrom(ar, type2->m_name, true, false)) {
resultType = type1;
} else if (c2ok && cls2->derivesFrom(ar, type1->m_name, true, false)) {
resultType = type2;
} else if (c1ok && c2ok && cls1->derivedByDynamic() &&
cls2->derivesFromRedeclaring()) {
resultType = type2;
} else {
resultType = type1;
}
}
return resultType;
}
/* We have inferred type1 and type2 as the actual types for the same
expression.
Check that the types are compatible (it cant be both a string and
an integer, for example), and return the combined type. If they
are not compatible, return a null pointer.
*/
TypePtr Type::Inferred(AnalysisResultConstPtr ar,
TypePtr type1, TypePtr type2) {
if (!type1) return type2;
if (!type2) return type1;
KindOf k1 = type1->m_kindOf;
KindOf k2 = type2->m_kindOf;
if (k1 == k2) {
return k1 == KindOfObject ?
Type::InferredObject(ar, type1, type2) : type1;
}
// If one set is a subset of the other, return the subset.
if ((k1 & k2) == k1) return type1;
if ((k1 & k2) == k2) return type2;
// If one type must be numeric and the other might be, then assume numeric
if (type1->mustBe(KindOfNumeric) && type2->couldBe(KindOfNumeric)) {
return type1;
}
if (type2->mustBe(KindOfNumeric) && type1->couldBe(KindOfNumeric)) {
return type2;
}
// Otherwise, take the intersection
int resultKind = type1->m_kindOf & type2->m_kindOf;
if (resultKind == KindOfObject) {
return Type::InferredObject(ar, type1, type2);
}
return resultKind ? GetType(resultKind) : TypePtr();
}
@@ -225,35 +225,9 @@ public:
ClassScopePtr getClass(AnalysisResultConstPtr ar,
BlockScopeRawPtr scope) const;
/**
* Generate type specifier in C++.
*/
std::string getCPPDecl(AnalysisResultConstPtr ar,
BlockScopeRawPtr scope,
CodeGenerator *cg = 0);
DataType getDataType() const;
DataType getHhvmDataType() const;
void outputCPPDecl(CodeGenerator &cg, AnalysisResultConstPtr ar,
BlockScopeRawPtr scope);
/**
* Generate type conversion in C++.
*/
void outputCPPFastObjectCast(CodeGenerator &cg,
AnalysisResultConstPtr ar,
BlockScopeRawPtr scope,
bool isConst);
void outputCPPCast(CodeGenerator &cg, AnalysisResultConstPtr ar,
BlockScopeRawPtr scope);
/**
* Generate variable initialization code.
*/
const char *getCPPInitializer();
/**
* Type hint names in PHP.
*/
+781
Ver Arquivo
@@ -0,0 +1,781 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/type.h>
#include <compiler/code_generator.h>
#include <compiler/expression/modifier_expression.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/builtin_symbols.h>
#include <compiler/option.h>
#include <compiler/expression/simple_function_call.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/expression/static_member_expression.h>
#include <runtime/base/class_info.h>
#include <util/util.h>
#include <util/parser/location.h>
#include <util/parser/parser.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// StaticGlobalInfo
string VariableTable::StaticGlobalInfo::GetId
(ClassScopePtr cls, FunctionScopePtr func,
const string &name) {
assert(cls || func);
// format: <class>$$<func>$$name
string id;
if (cls) {
id += cls->getId();
id += Option::IdPrefix;
}
if (func) {
id += func->getId();
id += Option::IdPrefix;
}
id += name;
return id;
}
///////////////////////////////////////////////////////////////////////////////
VariableTable::VariableTable(BlockScope &blockScope)
: SymbolTable(blockScope, false), m_attribute(0), m_nextParam(0),
m_hasGlobal(false), m_hasStatic(false),
m_hasPrivate(false), m_hasNonStaticPrivate(false),
m_forcedVariants(0) {
}
void VariableTable::getLocalVariableNames(vector<string> &syms) const {
FunctionScopeRawPtr fs = getScopePtr()->getContainingFunction();
bool dollarThisIsSpecial = (fs->getContainingClass() ||
fs->inPseudoMain());
for (unsigned int i = 0; i < m_symbolVec.size(); i++) {
const string& name = m_symbolVec[i]->getName();
if (name == "this" && dollarThisIsSpecial) {
// The "this" variable in methods and pseudo-main is special and is
// handled separately below.
continue;
}
syms.push_back(name);
}
if (fs->needsLocalThis()) {
assert(dollarThisIsSpecial);
// We only need a local variable named "this" if the current function
// contains an occurrence of "$this" that is not part of a property
// expression or object method call expression
syms.push_back("this");
}
}
void VariableTable::getNames(std::set<string> &names,
bool collectPrivate /* = true */) const {
for (unsigned int i = 0; i < m_symbolVec.size(); i++) {
if (collectPrivate || !m_symbolVec[i]->isPrivate()) {
names.insert(m_symbolVec[i]->getName());
}
}
}
bool VariableTable::isParameter(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isParameter();
}
bool VariableTable::isPublic(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isPublic();
}
bool VariableTable::isProtected(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isProtected();
}
bool VariableTable::isPrivate(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isPrivate();
}
bool VariableTable::isStatic(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isStatic();
}
bool VariableTable::isGlobal(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isGlobal();
}
bool VariableTable::isRedeclared(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isRedeclared();
}
bool VariableTable::isLocalGlobal(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isLocalGlobal();
}
bool VariableTable::isNestedStatic(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isNestedStatic();
}
bool VariableTable::isLvalParam(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isLvalParam();
}
bool VariableTable::isUsed(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isUsed();
}
bool VariableTable::isNeeded(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isNeeded();
}
bool VariableTable::isSuperGlobal(const string &name) const {
const Symbol *sym = getSymbol(name);
return sym && sym->isSuperGlobal();
}
bool VariableTable::isLocal(const string &name) const {
return isLocal(getSymbol(name));
}
bool VariableTable::isLocal(const Symbol *sym) const {
if (!sym) return false;
if (getScopePtr()->is(BlockScope::FunctionScope)) {
/*
isSuperGlobal is not wanted here. It just means that
$GLOBALS[name] was referenced in this scope.
It doesnt say anything about the variable $name.
*/
return (!sym->isStatic() &&
!sym->isGlobal() &&
!sym->isParameter());
}
return false;
}
bool VariableTable::needLocalCopy(const string &name) const {
return needLocalCopy(getSymbol(name));
}
bool VariableTable::needLocalCopy(const Symbol *sym) const {
return sym &&
(sym->isGlobal() || sym->isStatic()) &&
(sym->isRedeclared() ||
sym->isNestedStatic() ||
sym->isLocalGlobal() ||
getAttribute(ContainsDynamicVariable) ||
getAttribute(ContainsExtract) ||
getAttribute(ContainsUnset));
}
bool VariableTable::needGlobalPointer() const {
return !isPseudoMainTable() &&
(m_hasGlobal ||
m_hasStatic ||
getAttribute(ContainsDynamicVariable) ||
getAttribute(ContainsExtract) ||
getAttribute(ContainsUnset) ||
getAttribute(NeedGlobalPointer));
}
bool VariableTable::isInherited(const string &name) const {
const Symbol *sym = getSymbol(name);
return !sym ||
(!sym->isGlobal() && !sym->isSystem() && !sym->getDeclaration());
}
ConstructPtr VariableTable::getStaticInitVal(string varName) {
if (Symbol *sym = getSymbol(varName)) {
return sym->getStaticInitVal();
}
return ConstructPtr();
}
bool VariableTable::setStaticInitVal(string varName,
ConstructPtr value) {
Symbol *sym = addSymbol(varName);
bool exists = sym->getStaticInitVal();
sym->setStaticInitVal(value);
return exists;
}
ConstructPtr VariableTable::getClassInitVal(string varName) {
if (Symbol *sym = getSymbol(varName)) {
return sym->getClassInitVal();
}
return ConstructPtr();
}
bool VariableTable::setClassInitVal(string varName, ConstructPtr value) {
Symbol *sym = addSymbol(varName);
bool exists = sym->getClassInitVal();
sym->setClassInitVal(value);
return exists;
}
///////////////////////////////////////////////////////////////////////////////
TypePtr VariableTable::addParam(const string &name, TypePtr type,
AnalysisResultConstPtr ar,
ConstructPtr construct) {
Symbol *sym = addDeclaredSymbol(name, construct);
if (!sym->isParameter()) {
sym->setParameterIndex(m_nextParam++);
}
return type ?
add(sym, type, false, ar, construct, ModifierExpressionPtr()) : type;
}
TypePtr VariableTable::addParamLike(const string &name, TypePtr type,
AnalysisResultPtr ar,
ConstructPtr construct, bool firstPass) {
TypePtr ret = type;
if (firstPass) {
ret = add(name, ret, false, ar,
construct, ModifierExpressionPtr());
} else {
ret = checkVariable(name, ret, true, ar, construct);
if (ret->is(Type::KindOfSome)) {
// This is probably too conservative. The problem is that
// a function never called will have parameter types of Any.
// Functions that it calls won't be able to accept variant unless
// it is forced here.
forceVariant(ar, name, VariableTable::AnyVars);
ret = Type::Variant;
}
}
return ret;
}
void VariableTable::addStaticVariable(Symbol *sym,
AnalysisResultPtr ar,
bool member /* = false */) {
if (isGlobalTable(ar) ||
sym->isStatic()) {
return; // a static variable at global scope is the same as non-static
}
sym->setStatic();
m_hasStatic = true;
FunctionScopeRawPtr funcScope = getFunctionScope();
if (funcScope &&
(funcScope->isClosure() || funcScope->isGeneratorFromClosure())) {
// static variables for closures/closure generators are local to the
// function scope
m_staticLocalsVec.push_back(sym);
} else {
VariableTablePtr globalVariables = ar->getVariables();
StaticGlobalInfoPtr sgi(new StaticGlobalInfo());
sgi->sym = sym;
sgi->variables = this;
sgi->cls = getClassScope();
sgi->func = member ? FunctionScopeRawPtr() : getFunctionScope();
globalVariables->m_staticGlobalsVec.push_back(sgi);
}
}
void VariableTable::addStaticVariable(Symbol *sym,
AnalysisResultConstPtr ar,
bool member /* = false */) {
if (isGlobalTable(ar) ||
sym->isStatic()) {
return; // a static variable at global scope is the same as non-static
}
addStaticVariable(sym, ar->lock().get(), member);
}
void VariableTable::cleanupForError(AnalysisResultConstPtr ar) {
if (!m_hasStatic) return;
AnalysisResult::Locker lock(ar);
VariableTablePtr g = lock->getVariables();
ClassScopeRawPtr cls = getClassScope();
for (unsigned i = g->m_staticGlobalsVec.size(); i--; ) {
if (g->m_staticGlobalsVec[i]->cls == cls) {
g->m_staticGlobalsVec.erase(g->m_staticGlobalsVec.begin() + i);
}
}
}
bool VariableTable::markOverride(AnalysisResultPtr ar, const string &name) {
Symbol *sym = getSymbol(name);
assert(sym && sym->isPresent());
bool ret = false;
if (!sym->isStatic() ||
(sym->isPublic() && !sym->getClassInitVal())) {
Symbol *s2;
ClassScopePtr parent = findParent(ar, name, s2);
if (parent) {
assert(s2 && s2->isPresent());
if (!s2->isPrivate()) {
if (!sym->isStatic() || s2->isProtected()) {
if (sym->isPrivate() || sym->isStatic()) {
// don't mark the symbol as overridden
return true;
}
if (sym->isProtected() && s2->isPublic()) {
// still mark the symbol as overridden
ret = true;
}
sym->setOverride();
}
}
}
}
return ret;
}
TypePtr VariableTable::add(const string &name, TypePtr type,
bool implicit, AnalysisResultConstPtr ar,
ConstructPtr construct,
ModifierExpressionPtr modifiers) {
return add(addSymbol(name), type, implicit, ar,
construct, modifiers);
}
TypePtr VariableTable::add(Symbol *sym, TypePtr type,
bool implicit, AnalysisResultConstPtr ar,
ConstructPtr construct,
ModifierExpressionPtr modifiers) {
if (getAttribute(InsideStaticStatement)) {
addStaticVariable(sym, ar);
if (ClassScope::NeedStaticArray(getClassScope(), getFunctionScope())) {
forceVariant(ar, sym->getName(), AnyVars);
}
} else if (getAttribute(InsideGlobalStatement)) {
sym->setGlobal();
m_hasGlobal = true;
AnalysisResult::Locker lock(ar);
if (!isGlobalTable(ar)) {
lock->getVariables()->add(sym->getName(), type, implicit,
ar, construct, modifiers);
}
assert(type->is(Type::KindOfSome) || type->is(Type::KindOfAny));
TypePtr varType = ar->getVariables()->getFinalType(sym->getName());
if (varType) {
type = varType;
} else {
lock->getVariables()->setType(ar, sym->getName(), type, true);
}
} else if (!sym->isHidden() && isPseudoMainTable()) {
// A variable used in a pseudomain
// only need to do this once... should mark the sym.
ar->lock()->getVariables()->add(sym->getName(), type, implicit, ar,
construct, modifiers);
}
if (modifiers) {
if (modifiers->isProtected()) {
sym->setProtected();
} else if (modifiers->isPrivate()) {
sym->setPrivate();
m_hasPrivate = true;
if (!sym->isStatic() && !modifiers->isStatic()) {
m_hasNonStaticPrivate = true;
}
}
if (modifiers->isStatic()) {
addStaticVariable(sym, ar);
}
}
type = setType(ar, sym, type, true);
sym->setDeclaration(construct);
if (!implicit && m_blockScope.isFirstPass()) {
if (!sym->getValue()) {
sym->setValue(construct);
}
}
return type;
}
TypePtr VariableTable::checkVariable(const string &name, TypePtr type,
bool coerce, AnalysisResultConstPtr ar,
ConstructPtr construct) {
return checkVariable(addSymbol(name), type,
coerce, ar, construct);
}
TypePtr VariableTable::checkVariable(Symbol *sym, TypePtr type,
bool coerce, AnalysisResultConstPtr ar,
ConstructPtr construct) {
// Variable used in pseudomain
if (!sym->isHidden() && isPseudoMainTable()) {
// only need to do this once... should mark the sym.
ar->lock()->getVariables()->checkVariable(sym->getName(), type,
coerce, ar, construct);
}
if (!sym->declarationSet()) {
type = setType(ar, sym, type, coerce);
sym->setDeclaration(construct);
return type;
}
return setType(ar, sym, type, coerce);
}
Symbol *VariableTable::findProperty(ClassScopePtr &cls,
const string &name,
AnalysisResultConstPtr ar) {
Symbol *sym = getSymbol(name);
if (sym) {
assert(sym->declarationSet());
if (!sym->isOverride()) {
return sym;
}
assert(!sym->isStatic());
sym = nullptr;
}
if (!sym) {
if (ClassScopePtr parent = findParent(ar, name, sym)) {
sym = parent->findProperty(parent, name, ar);
if (sym) {
cls = parent;
return sym;
}
}
}
return sym;
}
TypePtr VariableTable::checkProperty(BlockScopeRawPtr context,
Symbol *sym, TypePtr type,
bool coerce, AnalysisResultConstPtr ar) {
always_assert(sym->isPresent());
if (sym->isOverride()) {
Symbol *base;
ClassScopePtr parent = findParent(ar, sym->getName(), base);
assert(parent);
assert(parent.get() != &m_blockScope);
assert(base && !base->isPrivate());
if (context->is(BlockScope::FunctionScope)) {
GET_LOCK(parent);
type = parent->getVariables()->setType(ar, base, type, coerce);
} else {
TRY_LOCK(parent);
type = parent->getVariables()->setType(ar, base, type, coerce);
}
}
return setType(ar, sym, type, coerce);
}
bool VariableTable::checkRedeclared(const string &name,
Statement::KindOf kindOf)
{
Symbol *sym = getSymbol(name);
assert(kindOf == Statement::KindOfStaticStatement ||
kindOf == Statement::KindOfGlobalStatement);
if (kindOf == Statement::KindOfStaticStatement && sym->isPresent()) {
if (sym->isStatic()) {
return true;
} else if (!sym->isRedeclared()) {
sym->setRedeclared();
return true;
} else {
return false;
}
} else if (kindOf == Statement::KindOfGlobalStatement &&
sym && !sym->isGlobal() && !sym->isRedeclared()) {
sym->setRedeclared();
return true;
} else {
return false;
}
}
void VariableTable::addLocalGlobal(const string &name) {
addSymbol(name)->setLocalGlobal();
}
void VariableTable::addNestedStatic(const string &name) {
addSymbol(name)->setNestedStatic();
}
void VariableTable::addLvalParam(const string &name) {
addSymbol(name)->setLvalParam();
}
void VariableTable::addUsed(const string &name) {
addSymbol(name)->setUsed();
}
void VariableTable::addNeeded(const string &name) {
addSymbol(name)->setNeeded();
}
bool VariableTable::checkUnused(Symbol *sym) {
if ((!sym || !sym->isHidden()) &&
(isPseudoMainTable() || getAttribute(ContainsDynamicVariable))) {
return false;
}
if (sym) {
return !sym->isUsed() && isLocal(sym);
}
return false;
}
void VariableTable::clearUsed() {
typedef std::pair<const string,Symbol> symPair;
bool ps = isPseudoMainTable();
BOOST_FOREACH(symPair &sym, m_symbolMap) {
if (!ps || sym.second.isHidden()) {
sym.second.clearUsed();
sym.second.clearNeeded();
sym.second.clearReferenced();
sym.second.clearGlobal();
sym.second.clearReseated();
} else {
sym.second.setReferenced();
}
if (sym.second.isRefGeneratorParameter()) {
sym.second.setReferenced();
}
}
}
void VariableTable::forceVariants(AnalysisResultConstPtr ar, int varClass,
bool recur /* = true */) {
int mask = varClass & ~m_forcedVariants;
if (mask) {
if (!m_hasPrivate) mask &= ~AnyPrivateVars;
if (!m_hasStatic) mask &= ~AnyStaticVars;
if (mask) {
for (unsigned int i = 0; i < m_symbolVec.size(); i++) {
Symbol *sym = m_symbolVec[i];
if (!sym->isHidden() && sym->declarationSet() &&
mask & GetVarClassMaskForSym(sym)) {
setType(ar, sym, Type::Variant, true);
sym->setIndirectAltered();
}
}
}
m_forcedVariants |= varClass;
if (recur) {
ClassScopePtr parent = m_blockScope.getParentScope(ar);
if (parent && !parent->isRedeclaring()) {
parent->getVariables()->forceVariants(ar, varClass & ~AnyPrivateVars);
}
}
}
}
void VariableTable::forceVariant(AnalysisResultConstPtr ar,
const string &name, int varClass) {
int mask = varClass & ~m_forcedVariants;
if (!mask) return;
if (!m_hasPrivate) mask &= ~AnyPrivateVars;
if (!m_hasStatic) mask &= ~AnyStaticVars;
if (!mask) return;
if (Symbol *sym = getSymbol(name)) {
if (!sym->isHidden() && sym->declarationSet() &&
mask & GetVarClassMaskForSym(sym)) {
setType(ar, sym, Type::Variant, true);
sym->setIndirectAltered();
}
}
}
TypePtr VariableTable::setType(AnalysisResultConstPtr ar,
const std::string &name,
TypePtr type, bool coerce) {
return setType(ar, addSymbol(name), type, coerce);
}
TypePtr VariableTable::setType(AnalysisResultConstPtr ar, Symbol *sym,
TypePtr type, bool coerce) {
bool force_coerce = coerce;
int mask = GetVarClassMaskForSym(sym);
if (m_forcedVariants & mask && !sym->isHidden()) {
type = Type::Variant;
force_coerce = true;
}
TypePtr ret = SymbolTable::setType(ar, sym, type, force_coerce);
if (!ret) return ret;
if (sym->isGlobal() && !isGlobalTable(ar)) {
ar->lock()->getVariables()->setType(ar, sym->getName(), type, coerce);
}
if (coerce) {
if (sym->isParameter()) {
FunctionScope *func = dynamic_cast<FunctionScope *>(&m_blockScope);
assert(func);
TypePtr paramType = func->setParamType(ar,
sym->getParameterIndex(), type);
if (!Type::SameType(paramType, type)) {
return setType(ar, sym, paramType, true); // recursively
}
}
}
return ret;
}
void VariableTable::dumpStats(std::map<string, int> &typeCounts) {
for (unsigned int i = 0; i < m_symbolVec.size(); i++) {
Symbol *sym = m_symbolVec[i];
if (sym->isGlobal()) continue;
typeCounts[sym->getFinalType()->toString()]++;
}
}
void VariableTable::addSuperGlobal(const string &name) {
addSymbol(name)->setSuperGlobal();
}
bool VariableTable::isConvertibleSuperGlobal(const string &name) const {
return !getAttribute(ContainsDynamicVariable) && isSuperGlobal(name);
}
ClassScopePtr VariableTable::findParent(AnalysisResultConstPtr ar,
const string &name,
const Symbol *&sym) const {
sym = nullptr;
for (ClassScopePtr parent = m_blockScope.getParentScope(ar);
parent && !parent->isRedeclaring();
parent = parent->getParentScope(ar)) {
sym = parent->getVariables()->getSymbol(name);
assert(!sym || sym->isPresent());
if (sym) return parent;
}
return ClassScopePtr();
}
bool VariableTable::isGlobalTable(AnalysisResultConstPtr ar) const {
return ar->getVariables().get() == this;
}
bool VariableTable::isPseudoMainTable() const {
return m_blockScope.inPseudoMain();
}
bool VariableTable::hasPrivate() const {
return m_hasPrivate;
}
bool VariableTable::hasNonStaticPrivate() const {
return m_hasNonStaticPrivate;
}
void VariableTable::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (Option::GenerateInferredTypes) {
for (unsigned int i = 0; i < m_symbolVec.size(); i++) {
Symbol *sym = m_symbolVec[i];
if (isInherited(sym->getName())) continue;
if (sym->isParameter()) {
cg_printf("// @param ");
} else if (sym->isGlobal()) {
cg_printf("// @global ");
} else if (sym->isStatic()) {
cg_printf("// @static ");
} else {
cg_printf("// @local ");
}
cg_printf("%s\t$%s\n", sym->getFinalType()->toString().c_str(),
sym->getName().c_str());
}
}
if (Option::ConvertSuperGlobals && !getAttribute(ContainsDynamicVariable)) {
std::set<string> convertibles;
typedef std::pair<const string,Symbol> symPair;
BOOST_FOREACH(symPair &sym, m_symbolMap) {
if (sym.second.isSuperGlobal() && !sym.second.declarationSet()) {
convertibles.insert(sym.second.getName());
}
}
if (!convertibles.empty()) {
cg_printf("/* converted super globals */ global ");
for (std::set<string>::const_iterator iter = convertibles.begin();
iter != convertibles.end(); ++iter) {
if (iter != convertibles.begin()) cg_printf(",");
cg_printf("$%s", iter->c_str());
}
cg_printf(";\n");
}
}
}
static bool by_location(const VariableTable::StaticGlobalInfoPtr &p1,
const VariableTable::StaticGlobalInfoPtr &p2) {
ConstructRawPtr d1 = p1->sym->getDeclaration();
ConstructRawPtr d2 = p2->sym->getDeclaration();
if (!d1) return d2;
if (!d2) return false;
return d1->getLocation()->compare(d2->getLocation().get()) < 0;
}
void VariableTable::canonicalizeStaticGlobals() {
assert(m_staticGlobals.empty());
sort(m_staticGlobalsVec.begin(), m_staticGlobalsVec.end(), by_location);
for (unsigned int i = 0; i < m_staticGlobalsVec.size(); i++) {
StaticGlobalInfoPtr &sgi = m_staticGlobalsVec[i];
if (!sgi->sym->getDeclaration()) continue;
string id = StaticGlobalInfo::GetId(sgi->cls, sgi->func,
sgi->sym->getName());
assert(m_staticGlobals.find(id) == m_staticGlobals.end());
m_staticGlobals[id] = sgi;
}
}
// Make sure GlobalVariables::getRefByIdx has the correct indices
void VariableTable::checkSystemGVOrder(SymbolSet &variants,
unsigned int max) {
always_assert(variants.size() >= max &&
BuiltinSymbols::NumGlobalNames());
unsigned int i = 0;
for (SymbolSet::const_iterator iterName = variants.begin();
iterName != variants.end(); ++iterName) {
string s = string("gvm_") + BuiltinSymbols::GlobalNames[i];
always_assert(s == iterName->c_str());
i++;
}
}
///////////////////////////////////////////////////////////////////////////////
}
+356
Ver Arquivo
@@ -0,0 +1,356 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __VARIABLE_TABLE_H__
#define __VARIABLE_TABLE_H__
#include <compiler/analysis/symbol_table.h>
#include <compiler/statement/statement.h>
#include <compiler/analysis/class_scope.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
DECLARE_BOOST_TYPES(ModifierExpression);
DECLARE_BOOST_TYPES(CodeError);
DECLARE_BOOST_TYPES(VariableTable);
DECLARE_BOOST_TYPES(Expression);
DECLARE_BOOST_TYPES(ClassScope);
DECLARE_BOOST_TYPES(FunctionScope);
/**
* These are the only places that a new variable can be declared:
*
* variable = expr|variable|new obj(...)
* static_var_list: T_STATIC T_VARIABLE = static_scalar,...
* class_variable_declaration: class { T_VARIABLE = static_scalar,...}
* T_LIST (variable, T_LIST(...), ...) = ...
* try {...} catch (T obj) {...}
* extract(name_value_pair)
*/
class VariableTable : public SymbolTable {
friend class AssignmentExpression;
public:
enum Attribute {
ContainsDynamicVariable = 1,
ContainsLDynamicVariable = ContainsDynamicVariable | 2,
ContainsExtract = 4,
ContainsCompact = 8,
InsideStaticStatement = 16,
InsideGlobalStatement = 32,
ForceGlobal = 64,
ContainsUnset = 128,
NeedGlobalPointer = 256,
ContainsDynamicStatic = 512,
ContainsGetDefinedVars = 1024,
ContainsDynamicFunctionCall = 2048,
};
enum JumpTableType {
JumpReturn,
JumpSet,
JumpInitialized,
JumpInitializedString,
JumpIndex,
JumpReturnString
};
enum JumpTableName {
JumpTableGlobalGetImpl,
JumpTableGlobalExists,
JumpTableGlobalGetIndex,
JumpTableLocalGetImpl,
JumpTableLocalExists,
};
enum AlteredVarClass {
NonPrivateNonStaticVars = 1,
NonPrivateStaticVars = 2,
PrivateNonStaticVars = 4,
PrivateStaticVars = 8,
AnyNonStaticVars = NonPrivateNonStaticVars | PrivateNonStaticVars,
AnyStaticVars = NonPrivateStaticVars | PrivateStaticVars,
AnyNonPrivateVars = NonPrivateNonStaticVars | NonPrivateStaticVars,
AnyPrivateVars = PrivateNonStaticVars | PrivateStaticVars,
AnyVars = AnyStaticVars | AnyNonStaticVars
};
static int GetVarClassMask(bool privates, bool statics) {
return (statics ? 2 : 1) << (privates ? 2 : 0);
}
static int GetVarClassMaskForSym(const Symbol *sym) {
return GetVarClassMask(sym->isPrivate(), sym->isStatic());
}
public:
VariableTable(BlockScope &blockScope);
/**
* Get/set attributes.
*/
void setAttribute(Attribute attr) { m_attribute |= attr;}
void clearAttribute(Attribute attr) { m_attribute &= ~attr;}
bool getAttribute(Attribute attr) const {
return (m_attribute & attr) == attr;
}
bool isParameter(const std::string &name) const;
bool isPublic(const std::string &name) const;
bool isProtected(const std::string &name) const;
bool isPrivate(const std::string &name) const;
bool isStatic(const std::string &name) const;
bool isGlobal(const std::string &name) const;
bool isSuperGlobal(const std::string &name) const;
bool isLocal(const std::string &name) const;
bool isLocal(const Symbol *sym) const;
bool isRedeclared(const std::string &name) const;
bool isLocalGlobal(const std::string &name) const;
bool isNestedStatic(const std::string &name) const;
bool isLvalParam(const std::string &name) const;
bool isUsed(const std::string &name) const;
bool isNeeded(const std::string &name) const;
bool needLocalCopy(const Symbol *sym) const;
bool needLocalCopy(const std::string &name) const;
bool needGlobalPointer() const;
bool isPseudoMainTable() const;
bool hasPrivate() const;
bool hasNonStaticPrivate() const;
bool hasStatic() const { return m_hasStatic; }
virtual bool isInherited(const std::string &name) const;
void getLocalVariableNames(std::vector<std::string> &syms) const;
/**
* Get all variable's names.
*/
void getNames(std::set<std::string> &names,
bool collectPrivate = true) const;
Symbol *addSymbol(const std::string &name) {
return genSymbol(name, false);
}
Symbol *addDeclaredSymbol(const std::string &name, ConstructPtr construct) {
return genSymbol(name, false, construct);
}
/**
* Add a function's parameter to this table.
*/
TypePtr addParam(const std::string &name, TypePtr type,
AnalysisResultConstPtr ar, ConstructPtr construct);
TypePtr addParamLike(const std::string &name, TypePtr type,
AnalysisResultPtr ar, ConstructPtr construct,
bool firstPass);
/**
* Called when a variable is declared or being assigned (l-value).
*/
TypePtr add(const std::string &name, TypePtr type, bool implicit,
AnalysisResultConstPtr ar, ConstructPtr construct,
ModifierExpressionPtr modifiers);
TypePtr add(Symbol *sym, TypePtr type, bool implicit,
AnalysisResultConstPtr ar, ConstructPtr construct,
ModifierExpressionPtr modifiers);
/**
* Called to note whether a class variable overrides
* a definition in a base class. Returns whether or not there
* was an error in marking as override.
*/
bool markOverride(AnalysisResultPtr ar, const std::string &name);
/**
* Called when a variable is used or being evaluated (r-value).
*/
TypePtr checkVariable(const std::string &name, TypePtr type, bool coerce,
AnalysisResultConstPtr ar, ConstructPtr construct);
TypePtr checkVariable(Symbol *sym, TypePtr type, bool coerce,
AnalysisResultConstPtr ar, ConstructPtr construct);
/**
* Find the class which contains the property, and return
* its Symbol
*/
Symbol *findProperty(ClassScopePtr &cls,
const std::string &name,
AnalysisResultConstPtr ar);
/**
* Caller is responsible for grabbing a lock on this class scope,
* This function will be responsible for grabbing (and releasing)
* a lock on the parent scope if necessary.
*/
TypePtr checkProperty(BlockScopeRawPtr context,
Symbol *sym, TypePtr type,
bool coerce, AnalysisResultConstPtr ar);
/**
* Walk up to find first parent that has the specified symbol.
*/
ClassScopePtr findParent(AnalysisResultConstPtr ar,
const std::string &name,
const Symbol *&sym) const;
ClassScopePtr findParent(AnalysisResultConstPtr ar,
const std::string &name,
Symbol *&sym) {
const Symbol *ss;
ClassScopePtr p = findParent(ar, name, ss); // const version
sym = const_cast<Symbol*>(ss);
return p;
}
/**
* Called when analyze global and static statement.
*/
bool checkRedeclared(const std::string &name, Statement::KindOf kindOf);
void addLocalGlobal(const std::string &name);
void addNestedStatic(const std::string &name);
/**
* Helper for static variable default value
*/
ConstructPtr getStaticInitVal(std::string varName);
bool setStaticInitVal(std::string varName, ConstructPtr value);
/**
* Helper for class variable default value
*/
ConstructPtr getClassInitVal(std::string varName);
bool setClassInitVal(std::string varName, ConstructPtr value);
/**
* Called when analyze simple variable
*/
void addLvalParam(const std::string &name);
void addUsed(const std::string &name);
bool checkUnused(Symbol *sym);
void addNeeded(const std::string &name);
void clearUsed();
void addStaticVariable(Symbol *sym, AnalysisResultConstPtr ar,
bool member = false);
void addStaticVariable(Symbol *sym, AnalysisResultPtr ar,
bool member = false);
void cleanupForError(AnalysisResultConstPtr ar);
/**
* Set all matching variables to variants, since l-dynamic value was used.
*/
void forceVariants(AnalysisResultConstPtr ar, int varClass,
bool recur = true);
/**
* Set one matching variable to be Type::Variant.
*/
void forceVariant(AnalysisResultConstPtr ar, const std::string &name,
int varClass);
/**
* Keep track of $GLOBALS['var'].
*/
void addSuperGlobal(const std::string &name);
bool isConvertibleSuperGlobal(const std::string &name) const;
/**
* Canonicalize symbol order of static globals.
*/
void canonicalizeStaticGlobals();
/**
* Generate all variable declarations for this symbol table.
*/
void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar);
/**
* Whether or not the specified jump table is empty.
*/
bool hasAllJumpTables() const {
return m_emptyJumpTables.empty();
}
bool hasJumpTable(JumpTableName name) const {
return m_emptyJumpTables.find(name) == m_emptyJumpTables.end();
}
/**
* These are static variables collected from different local scopes,
* as they have to be turned into global variables defined in
* GlobalVariables class to make ThreadLocal<GlobalVaribles> work.
* This data structure is only needed by global scope.
*/
DECLARE_BOOST_TYPES(StaticGlobalInfo);
struct StaticGlobalInfo {
Symbol *sym;
VariableTable *variables; // where this variable was from
ClassScopeRawPtr cls; // these need to be raw to avoid reference cycles
FunctionScopeRawPtr func;
// get unique identifier for this variable
static std::string GetId(ClassScopePtr cls,
FunctionScopePtr func, const std::string &name);
};
bool hasStaticLocals() const { return !m_staticLocalsVec.empty(); }
private:
enum StaticSelection {
NonStatic = 1,
Static = 2,
EitherStatic = 3
};
enum PrivateSelection {
NonPrivate = 1,
Private = 2,
EitherPrivate = 3
};
int m_attribute;
int m_nextParam;
unsigned m_hasGlobal : 1;
unsigned m_hasStatic : 1;
unsigned m_hasPrivate : 1;
unsigned m_hasNonStaticPrivate : 1;
unsigned m_forcedVariants : 4;
std::set<JumpTableName> m_emptyJumpTables;
StaticGlobalInfoPtrVec m_staticGlobalsVec;
StringToStaticGlobalInfoPtrMap m_staticGlobals;
/** static symbols local to this variable table (ie for closures) */
SymbolVec m_staticLocalsVec;
bool isGlobalTable(AnalysisResultConstPtr ar) const;
virtual TypePtr setType(AnalysisResultConstPtr ar, const std::string &name,
TypePtr type, bool coerce);
virtual TypePtr setType(AnalysisResultConstPtr ar, Symbol *sym,
TypePtr type, bool coerce);
virtual void dumpStats(std::map<std::string, int> &typeCounts);
void checkSystemGVOrder(SymbolSet &variants, unsigned int max);
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // __VARIABLE_TABLE_H__
@@ -60,21 +60,21 @@ const char *BuiltinSymbols::ExtensionFunctions[] = {
#define T(t) (const char *)Type::KindOf ## t
#define EXT_TYPE 0
#include <system/ext.inc>
NULL,
nullptr,
};
#undef EXT_TYPE
const char *BuiltinSymbols::ExtensionConsts[] = {
#define EXT_TYPE 1
#include <system/ext.inc>
NULL,
nullptr,
};
#undef EXT_TYPE
const char *BuiltinSymbols::ExtensionClasses[] = {
#define EXT_TYPE 2
#include <system/ext.inc>
NULL,
nullptr,
};
#undef EXT_TYPE
@@ -109,14 +109,14 @@ const char *BuiltinSymbols::SystemClasses[] = {
"directoryiterator",
"soapfault",
"fbmysqllexer",
NULL
nullptr
};
StringToClassScopePtrMap BuiltinSymbols::s_classes;
VariableTablePtr BuiltinSymbols::s_variables;
ConstantTablePtr BuiltinSymbols::s_constants;
StringToTypePtrMap BuiltinSymbols::s_superGlobals;
void *BuiltinSymbols::s_handle_main = NULL;
void *BuiltinSymbols::s_handle_main = nullptr;
///////////////////////////////////////////////////////////////////////////////
@@ -150,7 +150,7 @@ void BuiltinSymbols::ParseExtConsts(AnalysisResultPtr ar, const char **p,
}
TypePtr BuiltinSymbols::ParseType(const char **&p) {
const char *clsname = NULL;
const char *clsname = nullptr;
Type::KindOf ktype = (Type::KindOf)(long)(*p++);
if (ktype == CLASS_TYPE) {
clsname = *p++;
@@ -183,7 +183,7 @@ void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p,
if (sep) {
fs->setSepExtension();
}
int flags = (int)(int64)(*p++);
int flags = (int)(int64_t)(*p++);
if (flags & ClassInfo::IsAbstract) {
fs->addModifier(T_ABSTRACT);
}
@@ -209,7 +209,7 @@ void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p,
p++;
// Parse properties
while (*p) {
int flags = (int)(int64)(*p++);
int flags = (int)(int64_t)(*p++);
ModifierExpressionPtr modifiers(
new ModifierExpression(BlockScopePtr(), LocationPtr()));
if (flags & ClassInfo::IsProtected) {
@@ -233,7 +233,7 @@ void BuiltinSymbols::ParseExtClasses(AnalysisResultPtr ar, const char **p,
}
p++;
int flags = (int)(int64)(*p++);
int flags = (int)(int64_t)(*p++);
cl->setClassInfoAttribute(flags);
if (flags & ClassInfo::HasDocComment) {
cl->setDocComment(*p++);
@@ -277,7 +277,7 @@ FunctionScopePtr BuiltinSymbols::ParseExtFunction(AnalysisResultPtr ar,
}
int index = 0;
const char *paramName = NULL;
const char *paramName = nullptr;
while ((paramName = *p++ /* argName */)) {
TypePtr argType = ParseType(p);
const char *argDefault = *p++;
@@ -295,7 +295,7 @@ FunctionScopePtr BuiltinSymbols::ParseExtFunction(AnalysisResultPtr ar,
index++;
}
int flags = (int)(int64)(*p++);
int flags = (int)(int64_t)(*p++);
f->setClassInfoAttribute(flags);
if (flags & ClassInfo::HasDocComment) {
f->setDocComment(*p++);
@@ -342,11 +342,11 @@ bool BuiltinSymbols::LoadSepExtensionSymbols(AnalysisResultPtr ar,
const std::string &soname) {
string mapname = name + "_map";
const char ***symbols = NULL;
const char ***symbols = nullptr;
// If we linked with .a, the symbol is already in main program.
if (s_handle_main == NULL) {
s_handle_main = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
if (s_handle_main == nullptr) {
s_handle_main = dlopen(nullptr, RTLD_NOW | RTLD_GLOBAL);
if (!s_handle_main) {
const char *error = dlerror();
Logger::Error("Unable to load main program's symbols: %s",
@@ -358,7 +358,7 @@ bool BuiltinSymbols::LoadSepExtensionSymbols(AnalysisResultPtr ar,
}
// Otherwise, look for .so to load it.
void *handle = NULL;
void *handle = nullptr;
if (!symbols) {
handle = dlopen(soname.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
+426
Ver Arquivo
@@ -0,0 +1,426 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <stdarg.h>
#include <compiler/code_generator.h>
#include <compiler/statement/statement_list.h>
#include <compiler/option.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/analysis_result.h>
#include <compiler/analysis/variable_table.h>
#include <util/util.h>
#include <util/hash.h>
#include <boost/format.hpp>
#include <boost/scoped_array.hpp>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// statics
void CodeGenerator::BuildJumpTable(const std::vector<const char *> &strings,
MapIntToStringVec &out, int tableSize,
bool caseInsensitive) {
assert(!strings.empty());
assert(out.empty());
assert(tableSize > 0);
for (unsigned int i = 0; i < strings.size(); i++) {
const char *s = strings[i];
int hash = (caseInsensitive ? hash_string_i(s) : hash_string(s)) %
tableSize;
out[hash].push_back(s);
}
}
const char *CodeGenerator::STARTER_MARKER =
"namespace hphp_impl_starter {}";
const char *CodeGenerator::SPLITTER_MARKER =
"namespace hphp_impl_splitter {}";
const char *CodeGenerator::HASH_INCLUDE =
"#include";
///////////////////////////////////////////////////////////////////////////////
CodeGenerator::CodeGenerator(std::ostream *primary,
Output output /* = PickledPHP */,
const std::string *filename /* = NULL */)
: m_out(nullptr), m_output(output),
m_context(NoContext), m_itemIndex(-1) {
for (int i = 0; i < StreamCount; i++) {
m_streams[i] = nullptr;
m_indentation[i] = 0;
m_indentPending[i] = true;
m_lineNo[i] = 1;
m_inComments[i] = 0;
m_wrappedExpression[i] = false;
m_inExpression[i] = false;
}
setStream(PrimaryStream, primary);
useStream(PrimaryStream);
if (filename) m_filename = *filename;
m_translatePredefined = false;
m_scalarVariant = false;
m_initListFirstElem = false;
m_inFileOrClassHeader = false;
m_inNamespace = false;
}
void CodeGenerator::useStream(Stream stream) {
assert(stream >= NullStream && stream < StreamCount);
m_curStream = stream;
if (stream == NullStream) {
m_out = nullptr;
} else {
m_out = m_streams[stream];
}
}
bool CodeGenerator::usingStream(Stream stream) {
assert(stream >= 0 && stream < StreamCount);
return m_out == m_streams[stream];
}
std::ostream *CodeGenerator::getStream(Stream stream) const {
assert(stream >= 0 && stream < StreamCount);
return m_streams[stream];
}
void CodeGenerator::setStream(Stream stream, std::ostream *out) {
assert(out);
assert(stream >= 0 && stream < StreamCount);
m_streams[stream] = out;
}
int CodeGenerator::getLineNo(Stream stream) const {
assert(stream >= 0 && stream < StreamCount);
return m_lineNo[stream];
}
///////////////////////////////////////////////////////////////////////////////
void CodeGenerator::printf(const char *fmt, ...) {
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
}
void CodeGenerator::indentBegin(const char *fmt, ...) {
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
m_indentation[m_curStream]++;
}
void CodeGenerator::indentBegin() {
m_indentation[m_curStream]++;
}
void CodeGenerator::indentEnd(const char *fmt, ...) {
assert(m_indentation[m_curStream]);
m_indentation[m_curStream]--;
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
}
void CodeGenerator::indentEnd() {
assert(m_indentation[m_curStream]);
m_indentation[m_curStream]--;
}
bool CodeGenerator::inComments() const {
return m_inComments[m_curStream] > 0;
}
void CodeGenerator::startComments() {
m_inComments[m_curStream]++;
printf(" /*");
}
void CodeGenerator::endComments() {
assert(m_inComments[m_curStream] > 0);
m_inComments[m_curStream]--;
printf(" */");
}
void CodeGenerator::printSection(const char *name, bool newline /* = true */) {
if (newline) printf("\n");
printf("// %s\n", name);
}
void CodeGenerator::printSeparator() {
printf("///////////////////////////////////////"
"////////////////////////////////////////\n");
}
void CodeGenerator::namespaceBegin() {
always_assert(!m_inNamespace);
m_inNamespace = true;
printf("\n");
printf("namespace HPHP {\n");
printSeparator();
printf("\n");
}
void CodeGenerator::namespaceEnd() {
always_assert(m_inNamespace);
m_inNamespace = false;
printf("\n");
printSeparator();
printf("}\n");
}
std::string CodeGenerator::getFormattedName(const std::string &file) {
char *fn = strdup(file.c_str());
int len = strlen(fn);
always_assert(len == (int)file.size());
for (int i = 0; i < len; i++) {
if (!isalnum(fn[i])) fn[i] = '_';
}
string formatted = fn;
free(fn);
int hash = hash_string(file.data(), file.size());
formatted += boost::str(boost::format("%08x") % hash);
return formatted;
}
bool CodeGenerator::ensureInNamespace() {
if (m_inNamespace) return false;
namespaceBegin();
return true;
}
bool CodeGenerator::ensureOutOfNamespace() {
if (!m_inNamespace) return false;
namespaceEnd();
return true;
}
void CodeGenerator::ifdefBegin(bool ifdef, const char *fmt, ...) {
printf(ifdef ? "#ifdef " : "#ifndef ");
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
printf("\n");
}
void CodeGenerator::ifdefEnd(const char *fmt, ...) {
printf("#endif // ");
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
printf("\n");
}
void CodeGenerator::printDocComment(const std::string comment) {
if (comment.empty()) return;
string escaped;
escaped.reserve(comment.size() + 10);
for (unsigned int i = 0; i < comment.size(); i++) {
char ch = comment[i];
escaped += ch;
if (ch == '/' && i > 1 && comment[i+1] == '*') {
escaped += '\\'; // splitting illegal /* into /\*
}
}
print(escaped.c_str(), false);
printf("\n");
}
std::string CodeGenerator::FormatLabel(const std::string &name) {
string ret;
ret.reserve(name.size());
for (size_t i = 0; i < name.size(); i++) {
unsigned char ch = name[i];
if ((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') || ch == '_') {
ret += ch;
} else {
char buf[10];
snprintf(buf, sizeof(buf), "%s%02X", Option::LabelEscape.c_str(),
(int)ch);
ret += buf;
}
}
return ret;
}
std::string CodeGenerator::EscapeLabel(const std::string &name,
bool *binary /* = NULL */) {
return Util::escapeStringForCPP(name, binary);
}
///////////////////////////////////////////////////////////////////////////////
// helpers
void CodeGenerator::print(const char *fmt, va_list ap) {
if (!m_out) return;
boost::scoped_array<char> buf;
bool done = false;
for (int len = 1024; !done; len <<= 1) {
va_list v;
va_copy(v, ap);
buf.reset(new char[len]);
if (vsnprintf(buf.get(), len, fmt, v) < len) done = true;
va_end(v);
}
print(buf.get());
}
void CodeGenerator::print(const char *msg, bool indent /* = true */) {
const char *start = msg;
int length = 1;
for (const char *iter = msg; ; ++iter, ++length) {
if (*iter == '\n') {
if (indent) {
// Only indent if it is pending and if it is not an empty line
if (m_indentPending[m_curStream] && length > 1) printIndent();
// Printing substrings requires an additional copy operation,
// so do it only if necessary
if (iter[1] != '\0') {
printSubstring(start, length);
} else {
*m_out << start;
}
start = iter + 1;
length = 0;
}
m_lineNo[m_curStream]++;
m_indentPending[m_curStream] = true;
} else if (*iter == '\0') {
// Perform print only in case what's left is not an empty string
if (length > 1) {
if (indent && m_indentPending[m_curStream]) {
printIndent();
m_indentPending[m_curStream] = false;
}
*m_out << start;
}
break;
}
}
}
void CodeGenerator::printSubstring(const char *start, int length) {
const int BUF_LEN = 0x100;
char buf[BUF_LEN];
while (length > 0) {
int curLength = std::min(length, BUF_LEN - 1);
memcpy(buf, start, curLength);
buf[curLength] = '\0';
*m_out << buf;
length -= curLength;
start += curLength;
}
}
void CodeGenerator::printIndent() {
for (int i = 0; i < m_indentation[m_curStream]; i++) {
*m_out << Option::Tab;
}
}
///////////////////////////////////////////////////////////////////////////////
int CodeGenerator::s_idLambda = 0;
string CodeGenerator::GetNewLambda() {
return Option::LambdaPrefix + "lambda_" +
boost::lexical_cast<string>(++s_idLambda);
}
void CodeGenerator::resetIdCount(const std::string &key) {
m_idCounters[key] = 0;
}
int CodeGenerator::createNewId(const std::string &key) {
return ++m_idCounters[key];
}
int CodeGenerator::createNewId(ConstructPtr cs) {
FileScopePtr fs = cs->getFileScope();
if (fs) {
return createNewId(fs->getName());
}
return createNewId("");
}
int CodeGenerator::createNewLocalId(ConstructPtr ar) {
if (m_wrappedExpression[m_curStream]) {
return m_localId[m_curStream]++;
}
FunctionScopePtr func = ar->getFunctionScope();
if (func) {
return func->nextInlineIndex();
}
FileScopePtr fs = ar->getFileScope();
if (fs) {
return createNewId(fs->getName());
}
return createNewId("");
}
void CodeGenerator::pushBreakScope(int labelId,
bool loopCounter /* = true */) {
m_breakScopes.push_back(labelId);
if (loopCounter) {
printf("LOOP_COUNTER(%d);\n", int(labelId & ~BreakScopeBitMask));
}
}
void CodeGenerator::popBreakScope() {
m_breakScopes.pop_back();
if (m_breakScopes.size() == 0) {
m_breakLabelIds.clear();
m_contLabelIds.clear();
}
}
void CodeGenerator::pushCallInfo(int cit) {
m_callInfos.push_back(cit);
}
void CodeGenerator::popCallInfo() {
m_callInfos.pop_back();
}
int CodeGenerator::callInfoTop() {
if (m_callInfos.empty()) return -1;
return m_callInfos.back();
}
void CodeGenerator::addLabelId(const char *name, int labelId) {
if (!strcmp(name, "break")) {
m_breakLabelIds.insert(labelId);
} else if (!strcmp(name, "continue")) {
m_contLabelIds.insert(labelId);
} else {
assert(false);
}
}
bool CodeGenerator::findLabelId(const char *name, int labelId) {
if (!strcmp(name, "break")) {
return m_breakLabelIds.find(labelId) != m_breakLabelIds.end();
} else if (!strcmp(name, "continue")) {
return m_contLabelIds.find(labelId) != m_contLabelIds.end();
} else {
assert(false);
}
return false;
}
int CodeGenerator::ClassScopeCompare::cmp(const ClassScopeRawPtr &p1,
const ClassScopeRawPtr &p2) const {
int d = p1->getRedeclaringId() - p2->getRedeclaringId();
if (d) return d;
return strcasecmp(p1->getName().c_str(), p2->getName().c_str());
}
@@ -132,7 +132,7 @@ public:
public:
CodeGenerator() {} // only for creating a dummy code generator
CodeGenerator(std::ostream *primary, Output output = PickledPHP,
const std::string *filename = NULL);
const std::string *filename = nullptr);
/**
* ...if it was passed in from constructor.
@@ -163,17 +163,6 @@ public:
void indentEnd(const char *fmt, ...) ATTRIBUTE_PRINTF(2,3);
void indentEnd();
void printRaw(const char *msg) { print(msg, false);}
bool wrapExpressionBegin();
bool wrapExpressionEnd();
void genReferenceTemp(ConstructPtr scope);
void clearRefereceTemp() { m_referenceTemps[m_curStream].clear(); }
const std::string &getReferenceTemp();
bool hasReferenceTemp() const {
return !m_referenceTemps[m_curStream].empty();
}
void setReferenceTempUsed(bool flag) {
m_referenceTempsUsed[m_curStream] = flag;
}
/**
* Pre-formatted outputs.
*/
@@ -183,20 +172,12 @@ public:
void namespaceEnd();
bool ensureInNamespace();
bool ensureOutOfNamespace();
void headerBegin(const std::string &file);
void headerEnd(const std::string &file);
void ifdefBegin(bool ifdef, const char *fmt, ...) ATTRIBUTE_PRINTF(3,4);
void ifdefEnd(const char *fmt, ...) ATTRIBUTE_PRINTF(2,3);
void printInclude(const std::string &file);
void printBasicIncludes();
void printDeclareGlobals();
void printStartOfJumpTable(int tableSize);
void printDocComment(const std::string comment);
void printImplStarter(); // end of includes
void printImplSplitter(); // marker to split .cpp into smaller files
const char *getGlobals(AnalysisResultPtr ar);
static std::string FormatLabel(const std::string &name);
static std::string EscapeLabel(const std::string &name, bool *binary = NULL);
static std::string EscapeLabel(const std::string &name, bool *binary = nullptr);
/**
* Make sure PHP variables, functions and typenames are unique and
@@ -268,31 +249,13 @@ public:
m_loopStatement = loop;
}
void setInsideScalarArray(bool flag);
bool getInsideScalarArray();
void setFileOrClassHeader(bool value) { m_inFileOrClassHeader = value; }
bool isFileOrClassHeader() { return m_inFileOrClassHeader; }
void beginHoistedClasses();
void endHoistedClasses();
void collectHoistedClasses(bool flag);
void addHoistedClass(const std::string &cls);
bool checkHoistedClass(const std::string &cls);
void setScalarVariant() { m_scalarVariant = true; }
bool hasScalarVariant() { return m_scalarVariant; }
void clearScalarVariant() { m_scalarVariant = false; }
void setInitListFirstElem() { m_initListFirstElem = true; }
bool hasInitListFirstElem() { return m_initListFirstElem; }
void clearInitListFirstElem() { m_initListFirstElem = false; }
const StringToClassScopePtrVecMap &getClasses() const { return m_classes; }
void addClass(const std::string &name, ClassScopePtr cls) {
m_classes[name].push_back(cls);
}
void clearClasses() { m_classes.clear(); }
bool insertDeclaredClosure(const FunctionScope *f) {
return m_declaredClosures.insert(f).second;
}
@@ -321,8 +284,6 @@ private:
bool m_inFileOrClassHeader;
bool m_inNamespace;
int m_localId[StreamCount];
std::set<std::string, stdltistr> *m_hoistedClasses;
bool m_collectHoistedClasses;
static int s_idLambda;
std::map<std::string, int> m_idCounters;
@@ -332,7 +293,6 @@ private:
std::set<int> m_contLabelIds; // continue labels referenced
std::deque<int> m_callInfos;
LoopStatementPtr m_loopStatement;
bool m_insideScalarArray;
StringToClassScopePtrVecMap m_classes;
std::set<const FunctionScope*> m_declaredClosures;
FileScopeRawPtr m_literalScope;
@@ -38,10 +38,10 @@
#include <util/timer.h>
#include <util/hdf.h>
#include <util/async_func.h>
#include <runtime/base/program_functions.h>
#include <runtime/base/memory/smart_allocator.h>
#include <runtime/base/externals.h>
#include <runtime/base/thread_init_fini.h>
#include <runtime/base/compiler_id.h>
#include <runtime/vm/repo.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -49,6 +49,8 @@
#include <system/lib/systemlib.h>
#include <compiler/compiler.h>
#include "hhvm/process_init.h"
using namespace boost::program_options;
using std::cout;
@@ -97,9 +99,7 @@ struct CompilerOptions {
int clusterCount;
int optimizeLevel;
string filecache;
string rttiDirectory;
string javaRoot;
bool generateFFI;
bool dump;
string docjson;
bool coredump;
@@ -124,7 +124,7 @@ public:
struct stat sb;
stat(m_name, &sb);
Logger::Info("%dMB %s saved", (int64)sb.st_size/(1024*1024), m_name);
Logger::Info("%dMB %s saved", (int64_t)sb.st_size/(1024*1024), m_name);
}
private:
@@ -144,13 +144,10 @@ int phpTarget(const CompilerOptions &po, AnalysisResultPtr ar);
void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar);
int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar,
AsyncFileCacheSaver &fcThread);
int cppTarget(const CompilerOptions &po, AnalysisResultPtr ar,
AsyncFileCacheSaver &fcThread, bool allowSys = true);
int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr ar,
AsyncFileCacheSaver &fcThread);
int buildTarget(const CompilerOptions &po);
int runTarget(const CompilerOptions &po);
int generateSepExtCpp(const CompilerOptions &po, AnalysisResultPtr ar);
///////////////////////////////////////////////////////////////////////////////
@@ -186,11 +183,7 @@ int compiler_main(int argc, char **argv) {
}
}
if (ret == 0) {
if (po.target == "cpp") {
if (po.format == "exe" || po.format == "lib") {
ret = buildTarget(po);
}
} else if (po.target == "run") {
if (po.target == "run") {
ret = runTarget(po);
}
}
@@ -224,8 +217,6 @@ int prepareOptions(CompilerOptions &po, int argc, char **argv) {
"analyze | "
"php | "
"hhbc | "
"cpp | "
"sep-ext-cpp | "
"filecache | "
"run (default)")
("format,f", value<string>(&po.format),
@@ -234,7 +225,6 @@ int prepareOptions(CompilerOptions &po, int argc, char **argv) {
"php: trimmed (default) | inlined | pickled | typeinfo |"
" <any combination of them by any separator>; \n"
"hhbc: binary (default) | text; \n"
"cpp: cluster (default) | file | sys | exe | lib; \n"
"run: cluster (default) | file")
("cluster-count", value<int>(&po.clusterCount)->default_value(0),
"Cluster by file sizes and output roughly these many number of files. "
@@ -327,14 +317,6 @@ int prepareOptions(CompilerOptions &po, int argc, char **argv) {
("file-cache",
value<string>(&po.filecache),
"if specified, generate a static file cache with this file name")
("rtti-directory", value<string>(&po.rttiDirectory)->default_value(""),
"the directory of rtti profiling data")
("java-root",
value<string>(&po.javaRoot)->default_value("php"),
"the root package of generated Java FFI classes")
("generate-ffi",
value<bool>(&po.generateFFI)->default_value(false),
"generate ffi stubs")
("dump",
value<bool>(&po.dump)->default_value(false),
"dump the program graph")
@@ -361,9 +343,7 @@ int prepareOptions(CompilerOptions &po, int argc, char **argv) {
"files according to preprocessed file sizes, instead of original file "
"sizes (default). Run bin/ppp.php to generate an HDF configuration file "
"to specify here.")
#ifdef COMPILER_ID
("compiler-id", "display the git hash for the compiler id")
#endif
("repo-schema", "display the repo schema id used by this app")
("taint-status", "check if the compiler was built with taint enabled")
;
@@ -402,19 +382,15 @@ int prepareOptions(CompilerOptions &po, int argc, char **argv) {
#define HPHP_VERSION(v) cout << HPHP_COMPILER_STR #v << "\n";
#include "../version"
#ifdef COMPILER_ID
cout << "Compiler: " << COMPILER_ID << "\n";
#endif
cout << "Compiler: " << kCompilerId << "\n";
cout << "Repo schema: " << VM::Repo::kSchemaId << "\n";
return 1;
}
#ifdef COMPILER_ID
if (vm.count("compiler-id")) {
cout << COMPILER_ID << "\n";
cout << kCompilerId << "\n";
return 1;
}
#endif
if (vm.count("repo-schema")) {
cout << VM::Repo::kSchemaId << "\n";
@@ -428,8 +404,7 @@ cout << "Compiler: " << COMPILER_ID << "\n";
return 1;
}
if (hhvm &&
(po.target == "hhbc" || po.target == "run") &&
if ((po.target == "hhbc" || po.target == "run") &&
po.format.find("exe") == string::npos) {
if (po.program == "program") {
po.program = "hhvm.hhbc";
@@ -445,12 +420,6 @@ cout << "Compiler: " << COMPILER_ID << "\n";
Logger::LogLevel = Logger::LogInfo;
}
// config and system
Option::GenerateCPPMain = true;
if (po.noMetaInfo) {
Option::GenerateCPPMetaInfo = false;
Option::GenerateCPPMacros = false;
}
Option::FlAnnotate = po.fl_annotate;
Hdf config;
@@ -504,14 +473,8 @@ cout << "Compiler: " << COMPILER_ID << "\n";
(Util::format_pattern(po.excludeStaticPatterns[i], true));
}
if (po.target == "cpp" && po.format == "sys") {
BuiltinSymbols::NoSuperGlobals = true; // so to generate super globals
Option::AnalyzePerfectVirtuals = false;
}
Option::SystemGen = (po.target == "cpp" && po.format == "sys") ;
if (hhvm && (po.target == "hhbc" || po.target == "run")) {
Option::OutputHHBC = true;
Option::OutputHHBC = true;
if (po.target == "hhbc" || po.target == "run") {
Option::AnalyzePerfectVirtuals = false;
}
@@ -519,24 +482,22 @@ cout << "Compiler: " << COMPILER_ID << "\n";
Option::PreprocessedPartitionConfig = po.ppp;
if (po.format.empty()) {
if (po.target == "cpp") {
po.format = "cluster";
} else if (po.target == "php") {
if (po.target == "php") {
po.format = "trimmed";
} else if (po.target == "run") {
po.format = hhvm ? "binary" : "cluster";
} else if (hhvm && po.target == "hhbc") {
po.format = "binary";
} else if (po.target == "hhbc") {
po.format = "binary";
}
}
if (!po.docjson.empty()) {
if (po.target != "cpp" &&
po.target != "run" &&
if (po.target != "run" &&
po.target != "hhbc" &&
po.target != "analyze") {
Logger::Error(
"Cannot generate doc JSON file unless target is "
"'cpp', 'run', or 'analyze'");
"'hhbc', 'run', or 'analyze'");
} else {
Option::DocJson = po.docjson;
}
@@ -561,11 +522,6 @@ cout << "Compiler: " << COMPILER_ID << "\n";
}
}
if (po.generateFFI) {
Option::GenerateFFI = true;
Option::JavaFFIRootPackage = po.javaRoot;
}
return 0;
}
@@ -597,6 +553,7 @@ int process(const CompilerOptions &po) {
return lintTarget(po);
}
register_process_init();
init_thread_locals();
Timer timer(Timer::WallTime);
@@ -606,7 +563,7 @@ int process(const CompilerOptions &po) {
Package package(po.inputDir.c_str());
ar = package.getAnalysisResult();
if (hhvm && (po.target == "hhbc" || po.target == "run")) {
if (po.target == "hhbc" || po.target == "run") {
hhbcTargetInit(po, ar);
}
@@ -621,14 +578,10 @@ int process(const CompilerOptions &po) {
BuiltinSymbols::LoadSuperGlobals();
ClassInfo::Load();
if (po.format == "sys") ar->setSystem();
bool isPickledPHP = (po.target == "php" && po.format == "pickled");
if (!isPickledPHP) {
if (!BuiltinSymbols::Load(ar,
(po.target == "cpp" && po.format == "sys")
|| (po.target == "hhbc" && !Option::WholeProgram)
)) {
po.target == "hhbc" && !Option::WholeProgram)) {
return false;
}
if (po.target == "hhbc" && !Option::WholeProgram) {
@@ -636,6 +589,9 @@ int process(const CompilerOptions &po) {
} else {
ar->loadBuiltins();
}
if (!Option::SystemGen) {
hphp_process_init();
}
}
{
@@ -702,17 +658,12 @@ int process(const CompilerOptions &po) {
ret = analyzeTarget(po, ar);
} else if (po.target == "php") {
ret = phpTarget(po, ar);
} else if (hhvm && po.target == "hhbc") {
} else if (po.target == "hhbc") {
ret = hhbcTarget(po, ar, fileCacheThread);
} else if (po.target == "cpp") {
ret = cppTarget(po, ar, fileCacheThread);
} else if (po.target == "run") {
ret = runTargetCheck(po, ar, fileCacheThread);
} else if (po.target == "filecache") {
// do nothing
} else if (po.target == "sep-ext-cpp") {
ar->setSepExtension();
ret = generateSepExtCpp(po, ar);
} else {
Logger::Error("Unknown target: %s", po.target.c_str());
return 1;
@@ -887,11 +838,11 @@ void hhbcTargetInit(const CompilerOptions &po, AnalysisResultPtr ar) {
ar->setOutputPath(po.syncDir);
}
// Propagate relevant compiler-specific options to the runtime.
RuntimeOption::RepoLocalPath = ar->getOutputPath() + '/' + po.program;
RuntimeOption::RepoCentralPath = ar->getOutputPath() + '/' + po.program;
if (po.format.find("exe") != string::npos) {
RuntimeOption::RepoLocalPath += ".hhbc";
RuntimeOption::RepoCentralPath += ".hhbc";
}
RuntimeOption::RepoLocalMode = "rw";
RuntimeOption::RepoLocalMode = "--";
RuntimeOption::RepoDebugInfo = Option::RepoDebugInfo;
RuntimeOption::RepoJournal = "memory";
RuntimeOption::EnableHipHopSyntax = Option::EnableHipHopSyntax;
@@ -960,7 +911,7 @@ int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar,
const char *argv[] = { "objcopy", "--add-section", repo.c_str(),
buf, exe.c_str(), 0 };
string out;
ret = Process::Exec(argv[0], argv, NULL, out, NULL) ? 0 : 1;
ret = Process::Exec(argv[0], argv, nullptr, out, nullptr) ? 0 : 1;
}
return ret;
@@ -968,83 +919,19 @@ int hhbcTarget(const CompilerOptions &po, AnalysisResultPtr ar,
///////////////////////////////////////////////////////////////////////////////
int cppTarget(const CompilerOptions &po, AnalysisResultPtr ar,
AsyncFileCacheSaver &fcThread, bool allowSys /* = true */) {
int ret = 0;
int clusterCount = po.clusterCount;
// format
CodeGenerator::Output format = CodeGenerator::InvalidOutput;
if (po.format == "file") {
clusterCount = 0;
format = CodeGenerator::FileCPP;
} else if (po.format == "cluster") {
format = CodeGenerator::ClusterCPP;
} else if (po.format == "sys" && allowSys) {
clusterCount = 0;
format = CodeGenerator::SystemCPP;
ar->setSystem();
} else if (po.format == "exe" || po.format == "lib") {
format = CodeGenerator::ClusterCPP;
}
if (format == CodeGenerator::InvalidOutput) {
Logger::Error("Unknown format for CPP target: %s", po.format.c_str());
return 1;
}
if (!Option::RTTIOutputFile.empty()) {
if (!po.rttiDirectory.empty()) {
Option::UseRTTIProfileData = true;
ar->cloneRTTIFuncs(po.rttiDirectory.c_str());
} else {
Option::GenRTTIProfileData = true;
}
}
ret = analyzeTarget(po, ar);
{
Timer timer(Timer::WallTime, "creating CPP files");
if (po.syncDir.empty()) {
ar->setOutputPath(po.outputDir);
ar->outputAllCPP(format, clusterCount, NULL);
} else {
ar->setOutputPath(po.syncDir);
ar->outputAllCPP(format, clusterCount, &po.outputDir);
if (!po.filecache.empty()) {
fcThread.waitForEnd();
}
Util::syncdir(po.outputDir, po.syncDir);
boost::filesystem::remove_all(po.syncDir);
}
}
return ret;
}
///////////////////////////////////////////////////////////////////////////////
int generateSepExtCpp(const CompilerOptions &po, AnalysisResultPtr ar) {
ar->outputCPPSepExtensionImpl(po.outputFile);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
int buildTarget(const CompilerOptions &po) {
const char *HPHP_HOME = getenv("HPHP_HOME");
if (!HPHP_HOME || !*HPHP_HOME) {
throw Exception("Environment variable HPHP_HOME is not set.");
}
string cmd = string(HPHP_HOME) + "/src/legacy/run.sh";
string cmd = string(HPHP_HOME) + "/hphp/legacy/run.sh";
string flags;
if (getenv("RELEASE")) flags += "RELEASE=1 ";
if (getenv("SHOW_LINK")) flags += "SHOW_LINK=1 ";
if (getenv("SHOW_COMPILE")) flags += "SHOW_COMPILE=1 ";
if (po.format == "lib") flags += "HPHP_BUILD_LIBRARY=1 ";
if (Option::GenerateFFI) flags += "HPHP_BUILD_FFI=1 ";
const char *argv[] = {"", po.outputDir.c_str(),
po.program.c_str(), flags.c_str(), NULL};
po.program.c_str(), flags.c_str(), nullptr};
if (getenv("SHOW_COMPILE")) {
Logger::Info ("Compile command: %s %s %s", po.outputDir.c_str(),
@@ -1052,7 +939,7 @@ int buildTarget(const CompilerOptions &po) {
}
Timer timer(Timer::WallTime, "compiling and linking CPP files");
string out, err;
bool ret = Process::Exec(cmd.c_str(), argv, NULL, out, &err);
bool ret = Process::Exec(cmd.c_str(), argv, nullptr, out, &err);
if (getenv("SHOW_COMPILE")) {
Logger::Info("%s", out.c_str());
} else {
@@ -1071,11 +958,7 @@ int buildTarget(const CompilerOptions &po) {
int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr ar,
AsyncFileCacheSaver &fcThread) {
// generate code
if (po.format == "sep") return 1;
if (hhvm ?
hhbcTarget(po, ar, fcThread) :
cppTarget(po, ar, fcThread, false)) {
if (hhbcTarget(po, ar, fcThread)) {
return 1;
}
@@ -1089,10 +972,7 @@ int runTargetCheck(const CompilerOptions &po, AnalysisResultPtr ar,
}
int runTarget(const CompilerOptions &po) {
int ret = hhvm ? 0 : buildTarget(po);
if (ret) {
return ret;
}
int ret = 0;
// If there are more than one input files, we need one extra arg to run.
// If it's missing, we will stop right here, with compiled code.
@@ -1103,7 +983,7 @@ int runTarget(const CompilerOptions &po) {
// run the executable
string cmd;
if (hhvm && po.format.find("exe") == string::npos) {
if (po.format.find("exe") == string::npos) {
char buf[PATH_MAX];
if (!realpath("/proc/self/exe", buf)) return -1;
@@ -313,7 +313,7 @@ void Construct::dumpNode(int spc) {
}
std::cout << "-> 0x" << std::hex << std::setfill('0')
<< std::setw(10) << (int64)this << std::dec;
<< std::setw(10) << (int64_t)this << std::dec;
std::cout << " " << name << "(" << type << ") ";
if (id) {
@@ -322,12 +322,12 @@ void Construct::dumpNode(int spc) {
if (idPtr) {
std::cout << "idp=0x" <<
std::hex << std::setfill('0') << std::setw(10) <<
(int64)idPtr.get() << " ";
(int64_t)idPtr.get() << " ";
}
if (idCsePtr) {
std::cout << "idcsep=0x" <<
std::hex << std::setfill('0') << std::setw(10) <<
(int64)idCsePtr.get() << " ";
(int64_t)idCsePtr.get() << " ";
}
if (value != "") {
@@ -364,7 +364,7 @@ void Construct::dumpNode(int spc) {
}
string refstr;
if (dynamic_cast<SimpleVariable*>(this) != NULL) {
if (dynamic_cast<SimpleVariable*>(this) != nullptr) {
if (isReferencedValid()) {
if (isReferenced()) {
refstr += ",Referenced";
@@ -380,7 +380,7 @@ void Construct::dumpNode(int spc) {
if (refstr != "") refstr = " (" + refstr.substr(1) + ")";
string objstr;
if (dynamic_cast<SimpleVariable*>(this) != NULL) {
if (dynamic_cast<SimpleVariable*>(this) != nullptr) {
if (isNeededValid()) {
if (isNeeded()) {
objstr += "Object";
@@ -246,7 +246,6 @@ public:
* Called when generating code.
*/
virtual void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) = 0;
virtual void outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) = 0;
/**
* Implements JSON::CodeError::ISerializable.
@@ -0,0 +1,419 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/code_error.h>
#include <compiler/option.h>
#include <compiler/expression/static_member_expression.h>
#include <compiler/analysis/function_scope.h>
#include <util/parser/hphp.tab.hpp>
#include <runtime/base/complex_types.h>
#include <runtime/base/builtin_functions.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ArrayElementExpression::ArrayElementExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr variable, ExpressionPtr offset)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ArrayElementExpression)),
LocalEffectsContainer(AccessorEffect),
m_variable(variable), m_offset(offset), m_global(false),
m_dynamicGlobal(false) {
m_variable->setContext(Expression::AccessContext);
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->getName() == "GLOBALS") {
m_global = true;
m_dynamicGlobal = true;
if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr offset =
dynamic_pointer_cast<ScalarExpression>(m_offset);
if (offset->isLiteralString()) {
m_globalName = offset->getIdentifier();
if (!m_globalName.empty()) {
m_dynamicGlobal = false;
}
}
}
}
}
}
ExpressionPtr ArrayElementExpression::clone() {
ArrayElementExpressionPtr exp(new ArrayElementExpression(*this));
Expression::deepCopy(exp);
exp->m_variable = Clone(m_variable);
exp->m_offset = Clone(m_offset);
return exp;
}
void ArrayElementExpression::setContext(Context context) {
m_context |= context;
switch (context) {
case Expression::LValue:
if (!hasContext(Expression::UnsetContext)) {
m_variable->setContext(Expression::LValue);
}
if (m_variable->is(Expression::KindOfObjectPropertyExpression)) {
m_variable->clearContext(Expression::NoLValueWrapper);
}
// special case for $GLOBALS[], we do not need lvalue wrapper
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->getName() == "GLOBALS") {
m_context |= Expression::NoLValueWrapper;
}
}
break;
case Expression::DeepAssignmentLHS:
case Expression::DeepOprLValue:
case Expression::ExistContext:
case Expression::UnsetContext:
case Expression::DeepReference:
m_variable->setContext(context);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_variable->setContext(DeepReference);
break;
case Expression::InvokeArgument:
m_variable->setContext(context);
setContext(NoLValueWrapper);
default:
break;
}
}
void ArrayElementExpression::clearContext(Context context) {
m_context &= ~context;
switch (context) {
case Expression::LValue:
case Expression::DeepOprLValue:
case Expression::DeepAssignmentLHS:
case Expression::UnsetContext:
case Expression::DeepReference:
m_variable->clearContext(context);
break;
case Expression::InvokeArgument:
m_variable->clearContext(context);
clearContext(NoLValueWrapper);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_variable->clearContext(DeepReference);
break;
default:
break;
}
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
bool ArrayElementExpression::appendClass(ExpressionPtr cls,
AnalysisResultConstPtr ar,
FileScopePtr file) {
if (m_variable->is(Expression::KindOfArrayElementExpression)) {
return dynamic_pointer_cast<ArrayElementExpression>(m_variable)
->appendClass(cls, ar, file);
}
if (m_variable->is(Expression::KindOfSimpleVariable) ||
m_variable->is(Expression::KindOfDynamicVariable)) {
StaticMemberExpressionPtr sme(
new StaticMemberExpression(
m_variable->getScope(), m_variable->getLocation(),
cls, m_variable));
sme->onParse(ar, file);
m_variable = sme;
m_global = m_dynamicGlobal = false;
m_globalName.clear();
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
bool ArrayElementExpression::isTemporary() const {
return !m_global &&
!(m_context & (AccessContext|LValue|RefValue|UnsetContext));
}
void ArrayElementExpression::analyzeProgram(AnalysisResultPtr ar) {
m_variable->analyzeProgram(ar);
if (m_offset) m_offset->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (!m_global && (m_context & AccessContext) &&
!(m_context & (LValue|RefValue|DeepReference|
UnsetContext|RefParameter|InvokeArgument))) {
TypePtr type = m_variable->getActualType();
if (!type ||
(!type->is(Type::KindOfString) && !type->is(Type::KindOfArray))) {
FunctionScopePtr scope = getFunctionScope();
if (scope && !needsCSE()) scope->setNeedsRefTemp();
}
}
if (m_global) {
if (getContext() & (LValue|RefValue|DeepReference)) {
setContext(NoLValueWrapper);
} else if (!m_dynamicGlobal &&
!(getContext() &
(LValue|RefValue|RefParameter|DeepReference|
UnsetContext|ExistContext))) {
VariableTablePtr vars = ar->getVariables();
Symbol *sym = vars->getSymbol(m_globalName);
if (!sym || sym->getDeclaration().get() == this) {
Compiler::Error(Compiler::UseUndeclaredGlobalVariable,
shared_from_this());
}
}
FunctionScopePtr scope = getFunctionScope();
if (scope) scope->setNeedsCheckMem();
} else {
TypePtr at(m_variable->getActualType());
TypePtr et(m_variable->getExpectedType());
if (et &&
(et->is(Type::KindOfSequence) ||
et->is(Type::KindOfAutoSequence)) &&
at && at->isExactType()) {
// since Sequence maps to Variant in the runtime,
// using Sequence for the expected type will
// never allow the necessary casts to be generated.
m_variable->setExpectedType(at);
}
}
}
}
ConstructPtr ArrayElementExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_variable;
case 1:
return m_offset;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ArrayElementExpression::getKidCount() const {
return 2;
}
void ArrayElementExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_variable = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_offset = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
bool ArrayElementExpression::canonCompare(ExpressionPtr e) const {
return m_offset && Expression::canonCompare(e);
}
ExpressionPtr ArrayElementExpression::preOptimize(AnalysisResultConstPtr ar) {
if (!(m_context & (RefValue|LValue|UnsetContext|OprLValue|
InvokeArgument|DeepReference|DeepOprLValue))) {
if (m_offset && m_variable->isScalar()) {
Variant v, o;
if (m_variable->getScalarValue(v)) {
if (m_context & ExistContext &&
!v.isArray() &&
!v.isString() &&
!m_offset->hasEffect()) {
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(
o, hasContext(ExistContext) ?
AccessFlags::None : AccessFlags::Error);
g_context->setThrowAllErrors(false);
return replaceValue(makeScalarExpression(ar, res));
} catch (...) {
g_context->setThrowAllErrors(false);
}
}
}
}
}
return ExpressionPtr();
}
ExpressionPtr ArrayElementExpression::postOptimize(AnalysisResultConstPtr ar) {
if (!hasLocalEffect(AccessorEffect)) return ExpressionPtr();
TypePtr at(m_variable->getActualType());
if (at && (at->is(Type::KindOfString) || at->is(Type::KindOfArray))) {
clearLocalEffect(AccessorEffect);
return dynamic_pointer_cast<Expression>(shared_from_this());
}
return ExpressionPtr();
}
/**
* ArrayElementExpression comes from:
*
* reference_variable[|expr]
* ->object_dim_list[|expr]
* encaps T_VARIABLE[expr]
* encaps ${T_STRING[expr]}
*/
TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
ConstructPtr self = shared_from_this();
if (m_offset &&
!(m_context & (UnsetContext | ExistContext |
InvokeArgument | LValue | RefValue))) {
setLocalEffect(DiagnosticEffect);
}
if (m_context & (AssignmentLHS|OprLValue)) {
clearLocalEffect(AccessorEffect);
} else if (m_context & (LValue | RefValue)) {
setLocalEffect(CreateEffect);
}
// handling $GLOBALS[...]
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->getName() == "GLOBALS") {
clearLocalEffect(AccessorEffect);
m_global = true;
m_dynamicGlobal = true;
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
VariableTablePtr vars = ar->getVariables();
Lock l(ar->getMutex());
if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr offset =
dynamic_pointer_cast<ScalarExpression>(m_offset);
if (offset->isLiteralString()) {
m_globalName = offset->getIdentifier();
if (!m_globalName.empty()) {
m_dynamicGlobal = false;
clearLocalEffect(DiagnosticEffect);
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
TypePtr ret;
if (coerce) {
ret = vars->add(m_globalName, type, true, ar, self,
ModifierExpressionPtr());
} else {
ret = vars->checkVariable(m_globalName, type, coerce, ar, self);
}
getScope()->getVariables()->addSuperGlobal(m_globalName);
return ret;
}
}
} else {
vars->setAttribute(VariableTable::ContainsDynamicVariable);
}
if (hasContext(LValue) || hasContext(RefValue)) {
vars->forceVariants(ar, VariableTable::AnyVars);
vars->setAttribute(VariableTable::ContainsLDynamicVariable);
}
if (m_offset) {
m_offset->inferAndCheck(ar, Type::Primitive, false);
}
return m_implementedType = Type::Variant; // so not to lose values
}
}
if ((hasContext(LValue) || hasContext(RefValue)) &&
!hasContext(UnsetContext)) {
m_variable->setContext(LValue);
}
TypePtr varType;
if (m_offset) {
varType = m_variable->inferAndCheck(ar, coerce ? Type::AutoSequence :
Type::Sequence, coerce);
m_offset->inferAndCheck(ar, Type::Some, false);
} else {
if (hasContext(ExistContext) || hasContext(UnsetContext)) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::InvalidArrayElement, self);
}
}
m_variable->inferAndCheck(ar, Type::Array, true);
}
if (varType && Type::SameType(varType, Type::String)) {
m_implementedType.reset();
return Type::String;
}
TypePtr ret = propagateTypes(ar, Type::Variant);
m_implementedType = Type::Variant;
return ret; // so not to lose values
}
ExpressionPtr ArrayElementExpression::unneeded() {
if (m_global) {
if (m_offset) return m_offset->unneeded();
}
return Expression::unneeded();
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ArrayElementExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (Option::ConvertSuperGlobals && m_global && !m_dynamicGlobal &&
getScope() && (getScope()->is(BlockScope::ProgramScope) ||
getScope()-> getVariables()->
isConvertibleSuperGlobal(m_globalName))) {
cg_printf("$%s", m_globalName.c_str());
} else {
m_variable->outputPHP(cg, ar);
cg_printf("[");
if (m_offset) m_offset->outputPHP(cg, ar);
cg_printf("]");
}
}
@@ -56,11 +56,6 @@ public:
bool appendClass(ExpressionPtr cls,
AnalysisResultConstPtr ar, FileScopePtr file);
virtual void outputCPPExistTest(CodeGenerator &cg, AnalysisResultPtr ar,
int op);
virtual void outputCPPUnset(CodeGenerator &cg, AnalysisResultPtr ar);
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
virtual bool canonCompare(ExpressionPtr e) const;
private:
@@ -126,13 +126,6 @@ bool ArrayPairExpression::canonCompare(ExpressionPtr e) const {
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ArrayPairExpression::preOutputStash(CodeGenerator &cg,
AnalysisResultPtr ar,
int state) {
if (m_name) m_name->preOutputStash(cg, ar, state);
m_value->preOutputStash(cg, ar, state);
}
void ArrayPairExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (m_name) {
m_name->outputPHP(cg, ar);
@@ -141,40 +134,3 @@ void ArrayPairExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (m_ref) cg_printf("&");
m_value->outputPHP(cg, ar);
}
void ArrayPairExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
bool keyConverted = false;
if (m_name) {
keyConverted = outputCPPName(cg, ar);
cg_printf(", ");
}
m_value->outputCPP(cg, ar);
if (m_name && keyConverted && !m_collection) {
cg_printf(", true");
}
}
bool ArrayPairExpression::outputCPPName(CodeGenerator &cg,
AnalysisResultPtr ar) {
assert(m_name);
ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_name);
if (sc) {
if (sc->isLiteralString()) {
string s = sc->getLiteralString();
int64 res;
if (is_strictly_integer(s.c_str(), s.size(), res)) {
cg_printf("%sLL", s.c_str());
} else {
m_name->outputCPP(cg, ar);
}
return true;
}
if (sc->isLiteralInteger()) {
m_name->outputCPP(cg, ar);
return true;
}
}
m_name->outputCPP(cg, ar);
return false;
}
@@ -40,9 +40,6 @@ public:
virtual int getLocalEffects() const { return NoEffect; }
bool isScalarArrayPair() const;
void preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool isRef() const { return m_ref; }
bool canonCompare(ExpressionPtr e) const;
@@ -51,8 +48,6 @@ private:
ExpressionPtr m_value;
bool m_ref;
bool m_collection;
bool outputCPPName(CodeGenerator &cg, AnalysisResultPtr ar);
};
///////////////////////////////////////////////////////////////////////////////
@@ -0,0 +1,311 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/analysis/code_error.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/analysis/block_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/expression/unary_op_expression.h>
#include <util/parser/hphp.tab.hpp>
#include <compiler/option.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/simple_function_call.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/builtin_functions.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
AssignmentExpression::AssignmentExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr variable, ExpressionPtr value, bool ref)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(AssignmentExpression)),
m_variable(variable), m_value(value), m_ref(ref) {
m_variable->setContext(Expression::DeepAssignmentLHS);
m_variable->setContext(Expression::AssignmentLHS);
m_variable->setContext(Expression::LValue);
m_variable->setContext(Expression::NoLValueWrapper);
m_value->setContext(Expression::AssignmentRHS);
if (ref) {
m_variable->setContext(Expression::RefAssignmentLHS);
m_value->setContext(Expression::RefValue);
// we have &new special case that's handled in this class
m_value->setContext(Expression::NoRefWrapper);
}
}
ExpressionPtr AssignmentExpression::clone() {
AssignmentExpressionPtr exp(new AssignmentExpression(*this));
Expression::deepCopy(exp);
exp->m_variable = Clone(m_variable);
exp->m_value = Clone(m_value);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void AssignmentExpression::onParseRecur(AnalysisResultConstPtr ar,
ClassScopePtr scope) {
// This is that much we can do during parse phase.
TypePtr type;
if (m_value->is(Expression::KindOfScalarExpression)) {
type = static_pointer_cast<ScalarExpression>(m_value)->inferenceImpl(
ar, Type::Some, false);
} else if (m_value->is(Expression::KindOfUnaryOpExpression)) {
UnaryOpExpressionPtr uexp =
dynamic_pointer_cast<UnaryOpExpression>(m_value);
if (uexp->getOp() == T_ARRAY) {
type = Type::Array;
}
}
if (!type) type = Type::Some;
if (m_variable->is(Expression::KindOfConstantExpression)) {
// ...as in ClassConstant statement
// We are handling this one here, not in ClassConstant, purely because
// we need "value" to store in constant table.
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(m_variable);
scope->getConstants()->add(exp->getName(), type, m_value, ar, m_variable);
} else if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable);
scope->getVariables()->add(var->getName(), type, true, ar,
shared_from_this(), scope->getModifiers());
var->clearContext(Declaration); // to avoid wrong CodeError
} else {
assert(false); // parse phase shouldn't handle anything else
}
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
int AssignmentExpression::getLocalEffects() const {
return AssignEffect;
}
void AssignmentExpression::analyzeProgram(AnalysisResultPtr ar) {
m_variable->analyzeProgram(ar);
m_value->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
if (m_ref && m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
const std::string &name = var->getName();
VariableTablePtr variables = getScope()->getVariables();
variables->addUsed(name);
}
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_variable->is(Expression::KindOfConstantExpression)) {
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(m_variable);
if (!m_value->isScalar()) {
getScope()->getConstants()->setDynamic(ar, exp->getName(), false);
}
} else {
CheckNeeded(m_variable, m_value);
}
}
}
ConstructPtr AssignmentExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_variable;
case 1:
return m_value;
default:
assert(false);
break;
}
return ConstructPtr();
}
int AssignmentExpression::getKidCount() const {
return 2;
}
void AssignmentExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_variable = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_value = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
bool AssignmentExpression::isSimpleGlobalAssign(StringData **name,
TypedValue *tv) const {
if (!m_variable->is(KindOfArrayElementExpression)) return false;
ArrayElementExpressionPtr ae(
static_pointer_cast<ArrayElementExpression>(m_variable));
if (!ae->isSuperGlobal() || ae->isDynamicGlobal()) return false;
Variant v;
if (!m_value->getScalarValue(v) || v.is(KindOfArray)) return false;
if (name) {
*name = StringData::GetStaticString(ae->getGlobalName());
}
if (tv) {
if (v.isString()) {
v = StringData::GetStaticString(v.toCStrRef().get());
}
*tv = *v.asTypedValue();
}
return true;
}
ExpressionPtr AssignmentExpression::optimize(AnalysisResultConstPtr ar) {
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->checkUnused() &&
!CheckNeeded(var, m_value)) {
if (m_value->getContainedEffects() != getContainedEffects()) {
recomputeEffects();
}
return replaceValue(m_value);
}
}
return ExpressionPtr();
}
ExpressionPtr AssignmentExpression::preOptimize(AnalysisResultConstPtr ar) {
if (Option::EliminateDeadCode &&
ar->getPhase() >= AnalysisResult::FirstPreOptimize) {
// otherwise used & needed flags may not be up to date yet
ExpressionPtr rep = optimize(ar);
if (rep) return rep;
}
if (m_variable->getContainedEffects() & ~(CreateEffect|AccessorEffect)) {
return ExpressionPtr();
}
ExpressionPtr val = m_value;
while (val) {
if (val->is(KindOfExpressionList)) {
ExpressionListPtr el(static_pointer_cast<ExpressionList>(val));
val = el->listValue();
continue;
}
if (val->is(KindOfAssignmentExpression)) {
val = static_pointer_cast<AssignmentExpression>(val)->m_value;
continue;
}
break;
}
if (val && val->isScalar()) {
if (val != m_value) {
ExpressionListPtr rep(new ExpressionList(
getScope(), getLocation(),
ExpressionList::ListKindWrapped));
rep->addElement(m_value);
m_value = val->clone();
rep->addElement(static_pointer_cast<Expression>(shared_from_this()));
return replaceValue(rep);
}
if (!m_ref && m_variable->is(KindOfArrayElementExpression)) {
ArrayElementExpressionPtr ae(
static_pointer_cast<ArrayElementExpression>(m_variable));
ExpressionPtr avar(ae->getVariable());
ExpressionPtr aoff(ae->getOffset());
if (!aoff || aoff->isScalar()) {
avar = avar->getCanonLVal();
while (avar) {
if (avar->isScalar()) {
Variant v,o,r;
if (!avar->getScalarValue(v)) break;
if (!val->getScalarValue(r)) break;
try {
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);
}
g_context->setThrowAllErrors(false);
} catch (...) {
break;
}
ExpressionPtr rep(
new AssignmentExpression(
getScope(), getLocation(),
m_variable->replaceValue(Clone(ae->getVariable())),
makeScalarExpression(ar, v), false));
if (!isUnused()) {
ExpressionListPtr el(
new ExpressionList(
getScope(), getLocation(),
ExpressionList::ListKindWrapped));
el->addElement(rep);
el->addElement(val);
rep = el;
}
return replaceValue(rep);
}
avar = avar->getCanonPtr();
}
g_context->setThrowAllErrors(false);
}
}
}
return ExpressionPtr();
}
ExpressionPtr AssignmentExpression::postOptimize(AnalysisResultConstPtr ar) {
return optimize(ar);
}
TypePtr AssignmentExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
return inferAssignmentTypes(ar, type, coerce, m_variable, m_value);
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void AssignmentExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
m_variable->outputPHP(cg, ar);
cg_printf(" = ");
if (m_ref) cg_printf("&");
m_value->outputPHP(cg, ar);
}
@@ -48,13 +48,6 @@ public:
ExpressionPtr getValue() { return m_value;}
void setValue(ExpressionPtr v) { m_value = v; }
int getLocalEffects() const;
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
void preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar, int state);
static bool SpecialAssignment(CodeGenerator &cg,
AnalysisResultPtr ar,
ExpressionPtr lval,
ExpressionPtr rval,
const char *rvalStr, bool ref);
// $GLOBALS[<literal-string>] = <scalar>;
bool isSimpleGlobalAssign(StringData **name, TypedValue *tv) const;
@@ -0,0 +1,933 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/binary_op_expression.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <util/parser/hphp.tab.hpp>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/constant_expression.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/type_conversions.h>
#include <runtime/base/builtin_functions.h>
#include <runtime/base/comparisons.h>
#include <runtime/base/zend/zend_string.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/encaps_list_expression.h>
#include <compiler/expression/simple_function_call.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/statement/loop_statement.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
BinaryOpExpression::BinaryOpExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr exp1, ExpressionPtr exp2, int op)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(BinaryOpExpression)),
m_exp1(exp1), m_exp2(exp2), m_op(op), m_assign(false), m_canThrow(false) {
switch (m_op) {
case T_PLUS_EQUAL:
case T_MINUS_EQUAL:
case T_MUL_EQUAL:
case T_DIV_EQUAL:
case T_CONCAT_EQUAL:
case T_MOD_EQUAL:
case T_AND_EQUAL:
case T_OR_EQUAL:
case T_XOR_EQUAL:
case T_SL_EQUAL:
case T_SR_EQUAL:
m_assign = true;
m_exp1->setContext(Expression::LValue);
m_exp1->setContext(Expression::OprLValue);
m_exp1->setContext(Expression::DeepOprLValue);
if (m_exp1->is(Expression::KindOfObjectPropertyExpression)) {
m_exp1->setContext(Expression::NoLValueWrapper);
}
break;
case T_COLLECTION: {
std::string s = m_exp1->getLiteralString();
int cType = 0;
if (strcasecmp(s.c_str(), "vector") == 0) {
cType = Collection::VectorType;
} else if (strcasecmp(s.c_str(), "map") == 0) {
cType = Collection::MapType;
} else if (strcasecmp(s.c_str(), "stablemap") == 0) {
cType = Collection::StableMapType;
}
ExpressionListPtr el = static_pointer_cast<ExpressionList>(m_exp2);
el->setCollectionType(cType);
break;
}
default:
break;
}
}
ExpressionPtr BinaryOpExpression::clone() {
BinaryOpExpressionPtr exp(new BinaryOpExpression(*this));
Expression::deepCopy(exp);
exp->m_exp1 = Clone(m_exp1);
exp->m_exp2 = Clone(m_exp2);
return exp;
}
bool BinaryOpExpression::isTemporary() const {
switch (m_op) {
case '+':
case '-':
case '*':
case '/':
case T_SL:
case T_SR:
case T_BOOLEAN_OR:
case T_BOOLEAN_AND:
case T_LOGICAL_OR:
case T_LOGICAL_AND:
case T_INSTANCEOF:
case T_COLLECTION:
return true;
}
return false;
}
bool BinaryOpExpression::isRefable(bool checkError /* = false */) const {
return checkError && m_assign;
}
bool BinaryOpExpression::isLiteralString() const {
if (m_op == '.') {
return m_exp1->isLiteralString() && m_exp2->isLiteralString();
}
return false;
}
std::string BinaryOpExpression::getLiteralString() const {
if (m_op == '.') {
return m_exp1->getLiteralString() + m_exp2->getLiteralString();
}
return "";
}
bool BinaryOpExpression::containsDynamicConstant(AnalysisResultPtr ar) const {
switch (m_op) {
case T_COLLECTION:
return m_exp2->containsDynamicConstant(ar);
default:
break;
}
return false;
}
bool BinaryOpExpression::isShortCircuitOperator() const {
switch (m_op) {
case T_BOOLEAN_OR:
case T_BOOLEAN_AND:
case T_LOGICAL_OR:
case T_LOGICAL_AND:
return true;
default:
break;
}
return false;
}
bool BinaryOpExpression::isLogicalOrOperator() const {
switch (m_op) {
case T_BOOLEAN_OR:
case T_LOGICAL_OR:
return true;
default:
break;
}
return false;
}
ExpressionPtr BinaryOpExpression::unneededHelper() {
bool shortCircuit = isShortCircuitOperator();
if (!m_exp2->getContainedEffects() ||
(!shortCircuit && !m_exp1->getContainedEffects())) {
return Expression::unneededHelper();
}
if (shortCircuit) {
m_exp2 = m_exp2->unneeded();
m_exp2->setExpectedType(Type::Boolean);
}
return static_pointer_cast<Expression>(shared_from_this());
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
int BinaryOpExpression::getLocalEffects() const {
int effect = NoEffect;
m_canThrow = false;
switch (m_op) {
case '/':
case '%':
case T_DIV_EQUAL:
case T_MOD_EQUAL: {
Variant v2;
if (!m_exp2->getScalarValue(v2) || v2.equal(0)) {
effect = CanThrow;
m_canThrow = true;
}
break;
}
default:
break;
}
if (m_assign) effect |= AssignEffect;
return effect;
}
void BinaryOpExpression::analyzeProgram(AnalysisResultPtr ar) {
if (ar->getPhase() == AnalysisResult::AnalyzeFinal &&
m_op == T_INSTANCEOF && m_exp2->is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exp2);
addUserClass(ar, s->getString());
}
m_exp1->analyzeProgram(ar);
m_exp2->analyzeProgram(ar);
}
ExpressionPtr BinaryOpExpression::simplifyLogical(AnalysisResultConstPtr ar) {
try {
ExpressionPtr rep = foldConst(ar);
if (rep) return replaceValue(rep);
} catch (Exception e) {
}
return ExpressionPtr();
}
ConstructPtr BinaryOpExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_exp1;
case 1:
return m_exp2;
default:
assert(false);
break;
}
return ConstructPtr();
}
int BinaryOpExpression::getKidCount() const {
return 2;
}
void BinaryOpExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_exp1 = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_exp2 = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
bool BinaryOpExpression::canonCompare(ExpressionPtr e) const {
return Expression::canonCompare(e) &&
getOp() == static_cast<BinaryOpExpression*>(e.get())->getOp();
}
ExpressionPtr BinaryOpExpression::preOptimize(AnalysisResultConstPtr ar) {
if (!m_exp2->isScalar()) {
if (!m_exp1->isScalar()) {
if (m_exp1->is(KindOfBinaryOpExpression)) {
BinaryOpExpressionPtr b(
dynamic_pointer_cast<BinaryOpExpression>(m_exp1));
if (b->m_op == m_op && b->m_exp1->isScalar()) {
return foldRightAssoc(ar);
}
}
return ExpressionPtr();
}
} else if (m_canThrow && !(getLocalEffects() & CanThrow)) {
recomputeEffects();
}
ExpressionPtr optExp;
try {
optExp = foldConst(ar);
} catch (Exception &e) {
// runtime/base threw an exception, perhaps bad operands
}
if (optExp) optExp = replaceValue(optExp);
return optExp;
}
ExpressionPtr BinaryOpExpression::simplifyArithmetic(
AnalysisResultConstPtr ar) {
Variant v1;
Variant v2;
if (m_exp1->getScalarValue(v1)) {
if (v1.isInteger()) {
int64_t ival1 = v1.toInt64();
// 1 * $a => $a, 0 + $a => $a
if ((ival1 == 1 && m_op == '*') || (ival1 == 0 && m_op == '+')) {
TypePtr actType2 = m_exp2->getActualType();
TypePtr expType = getExpectedType();
if (actType2 &&
(actType2->mustBe(Type::KindOfNumeric) ||
(expType && expType->mustBe(Type::KindOfNumeric) &&
!actType2->couldBe(Type::KindOfArray) &&
Type::IsCastNeeded(ar, actType2, expType)))) {
return m_exp2;
}
}
} else if (v1.isString()) {
String sval1 = v1.toString();
if ((sval1.empty() && m_op == '.')) {
TypePtr actType2 = m_exp2->getActualType();
TypePtr expType = getExpectedType();
// '' . $a => $a
if ((expType && expType->is(Type::KindOfString)) ||
(actType2 && actType2->is(Type::KindOfString))) {
return m_exp2;
}
ExpressionPtr rep(new UnaryOpExpression(
getScope(), getLocation(),
m_exp2, T_STRING_CAST, true));
rep->setActualType(Type::String);
return rep;
}
}
}
if (m_exp2->getScalarValue(v2)) {
if (v2.isInteger()) {
int64_t ival2 = v2.toInt64();
// $a * 1 => $a, $a + 0 => $a
if ((ival2 == 1 && m_op == '*') || (ival2 == 0 && m_op == '+')) {
TypePtr actType1 = m_exp1->getActualType();
TypePtr expType = getExpectedType();
if (actType1 &&
(actType1->mustBe(Type::KindOfNumeric) ||
(expType && expType->mustBe(Type::KindOfNumeric) &&
!actType1->couldBe(Type::KindOfArray) &&
Type::IsCastNeeded(ar, actType1, expType)))) {
return m_exp1;
}
}
} else if (v2.isString()) {
String sval2 = v2.toString();
if ((sval2.empty() && m_op == '.')) {
TypePtr actType1 = m_exp1->getActualType();
TypePtr expType = getExpectedType();
// $a . '' => $a
if ((expType && expType->is(Type::KindOfString)) ||
(actType1 && actType1->is(Type::KindOfString))) {
return m_exp1;
}
ExpressionPtr rep(new UnaryOpExpression(
getScope(), getLocation(),
m_exp1, T_STRING_CAST, true));
rep->setActualType(Type::String);
return rep;
}
}
}
return ExpressionPtr();
}
void BinaryOpExpression::optimizeTypes(AnalysisResultConstPtr ar) {
switch (m_op) {
case '<':
case T_IS_SMALLER_OR_EQUAL:
case '>':
case T_IS_GREATER_OR_EQUAL:
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
case T_IS_EQUAL:
case T_IS_NOT_EQUAL:
{
// not needed for correctness, but will allow us to
// generate better code, since we can use the more
// specific runtime function
TypePtr a1(m_exp1->getActualType());
TypePtr i1(m_exp1->getImplementedType());
if (a1 && i1 &&
Type::IsMappedToVariant(i1) && Type::HasFastCastMethod(a1)) {
m_exp1->setExpectedType(a1);
}
TypePtr a2(m_exp2->getActualType());
TypePtr i2(m_exp2->getImplementedType());
if (a2 && i2 &&
Type::IsMappedToVariant(i2) && Type::HasFastCastMethod(a2)) {
m_exp2->setExpectedType(a2);
}
}
default: break;
}
}
ExpressionPtr BinaryOpExpression::postOptimize(AnalysisResultConstPtr ar) {
optimizeTypes(ar);
ExpressionPtr optExp = simplifyArithmetic(ar);
if (!optExp) {
if (isShortCircuitOperator()) optExp = simplifyLogical(ar);
}
if (optExp) optExp = replaceValue(optExp);
return optExp;
}
static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar,
LocationPtr loc, ExpressionPtr exp,
bool invert) {
/* Replace "$x === null" with an is_null call; this requires slightly
* less work at runtime. */
ExpressionListPtr expList =
ExpressionListPtr(new ExpressionList(exp->getScope(), loc));
expList->insertElement(exp);
SimpleFunctionCallPtr call
(new SimpleFunctionCall(exp->getScope(), loc,
"is_null", expList, ExpressionPtr()));
call->setValid();
call->setActualType(Type::Boolean);
call->setupScopes(ar);
ExpressionPtr result(call);
if (invert) {
result = ExpressionPtr(new UnaryOpExpression(
exp->getScope(), loc,
result, '!', true));
}
return result;
}
ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
ExpressionPtr optExp;
Variant v1;
Variant v2;
if (!m_exp2->getScalarValue(v2)) {
if (m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
switch (m_op) {
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
if (v1.isNull()) {
return makeIsNull(ar, getLocation(), m_exp2,
m_op == T_IS_NOT_IDENTICAL);
}
break;
case T_LOGICAL_AND:
case T_BOOLEAN_AND:
case T_LOGICAL_OR:
case T_BOOLEAN_OR: {
ExpressionPtr rep =
v1.toBoolean() == (m_op == T_LOGICAL_AND ||
m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1;
rep = ExpressionPtr(
new UnaryOpExpression(
getScope(), getLocation(),
rep, T_BOOL_CAST, true));
rep->setActualType(Type::Boolean);
return replaceValue(rep);
}
case '+':
case '.':
case '*':
case '&':
case '|':
case '^':
if (m_exp2->is(KindOfBinaryOpExpression)) {
BinaryOpExpressionPtr binOpExp =
dynamic_pointer_cast<BinaryOpExpression>(m_exp2);
if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) {
ExpressionPtr aExp = m_exp1;
ExpressionPtr bExp = binOpExp->m_exp1;
ExpressionPtr cExp = binOpExp->m_exp2;
m_exp1 = binOpExp = Clone(binOpExp);
m_exp2 = cExp;
binOpExp->m_exp1 = aExp;
binOpExp->m_exp2 = bExp;
if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
m_exp1 = optExp;
}
return static_pointer_cast<Expression>(shared_from_this());
}
}
break;
default:
break;
}
}
return ExpressionPtr();
}
if (m_exp1->isScalar()) {
if (!m_exp1->getScalarValue(v1)) return ExpressionPtr();
try {
if (Option::OutputHHBC &&
(!Option::WholeProgram || !Option::ParseTimeOpts)) {
// In the VM, don't optimize __CLASS__ if within a trait, since
// __CLASS__ is not resolved yet.
ClassScopeRawPtr clsScope = getOriginalClass();
if (clsScope && clsScope->isTrait()) {
ScalarExpressionPtr scalar1 =
dynamic_pointer_cast<ScalarExpression>(m_exp1);
ScalarExpressionPtr scalar2 =
dynamic_pointer_cast<ScalarExpression>(m_exp2);
if ((scalar1 && scalar1->getType() == T_CLASS_C) ||
(scalar2 && scalar2->getType() == T_CLASS_C)) {
return ExpressionPtr();
}
}
}
Variant result;
switch (m_op) {
case T_LOGICAL_XOR:
result = logical_xor(v1, v2); break;
case '|':
result = bitwise_or(v1, v2); break;
case '&':
result = bitwise_and(v1, v2); break;
case '^':
result = bitwise_xor(v1, v2); break;
case '.':
result = concat(v1, v2); break;
case T_IS_IDENTICAL:
result = same(v1, v2); break;
case T_IS_NOT_IDENTICAL:
result = !same(v1, v2); break;
case T_IS_EQUAL:
result = equal(v1, v2); break;
case T_IS_NOT_EQUAL:
result = !equal(v1, v2); break;
case '<':
result = less(v1, v2); break;
case T_IS_SMALLER_OR_EQUAL:
result = less_or_equal(v1, v2); break;
case '>':
result = more(v1, v2); break;
case T_IS_GREATER_OR_EQUAL:
result = more_or_equal(v1, v2); break;
case '+':
result = plus(v1, v2); break;
case '-':
result = minus(v1, v2); break;
case '*':
result = multiply(v1, v2); break;
case '/':
if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
return ExpressionPtr();
}
result = divide(v1, v2); break;
case '%':
if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
return ExpressionPtr();
}
result = modulo(v1, v2); break;
case T_SL:
result = shift_left(v1, v2); break;
case T_SR:
result = shift_right(v1, v2); break;
case T_BOOLEAN_OR:
result = v1 || v2; break;
case T_BOOLEAN_AND:
result = v1 && v2; break;
case T_LOGICAL_OR:
result = v1 || v2; break;
case T_LOGICAL_AND:
result = v1 && v2; break;
case T_INSTANCEOF:
result = false; break;
default:
return ExpressionPtr();
}
return makeScalarExpression(ar, result);
} catch (...) {
}
} else {
switch (m_op) {
case T_LOGICAL_AND:
case T_BOOLEAN_AND:
case T_LOGICAL_OR:
case T_BOOLEAN_OR: {
bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND ||
m_op == T_BOOLEAN_AND);
ExpressionPtr rep = useFirst ? m_exp1 : m_exp2;
rep = ExpressionPtr(
new UnaryOpExpression(
getScope(), getLocation(),
rep, T_BOOL_CAST, true));
rep->setActualType(Type::Boolean);
if (!useFirst) {
ExpressionListPtr l(
new ExpressionList(
getScope(), getLocation(),
ExpressionList::ListKindComma));
l->addElement(m_exp1);
l->addElement(rep);
l->setActualType(Type::Boolean);
rep = l;
}
rep->setExpectedType(getExpectedType());
return replaceValue(rep);
}
case T_LOGICAL_XOR:
case '|':
case '&':
case '^':
case '.':
case '+':
case '*':
optExp = foldRightAssoc(ar);
if (optExp) return optExp;
break;
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
if (v2.isNull()) {
return makeIsNull(ar, getLocation(), m_exp1,
m_op == T_IS_NOT_IDENTICAL);
}
break;
default:
break;
}
}
return ExpressionPtr();
}
ExpressionPtr
BinaryOpExpression::foldRightAssoc(AnalysisResultConstPtr ar) {
ExpressionPtr optExp1;
switch (m_op) {
case '.':
case '+':
case '*':
if (m_exp1->is(Expression::KindOfBinaryOpExpression)) {
BinaryOpExpressionPtr binOpExp =
dynamic_pointer_cast<BinaryOpExpression>(m_exp1);
if (binOpExp->m_op == m_op) {
// turn a Op b Op c, namely (a Op b) Op c into a Op (b Op c)
ExpressionPtr aExp = binOpExp->m_exp1;
ExpressionPtr bExp = binOpExp->m_exp2;
ExpressionPtr cExp = m_exp2;
m_exp1 = aExp;
m_exp2 = binOpExp = Clone(binOpExp);
binOpExp->m_exp1 = bExp;
binOpExp->m_exp2 = cExp;
if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
m_exp2 = optExp;
}
return static_pointer_cast<Expression>(shared_from_this());
}
}
break;
default:
break;
}
return ExpressionPtr();
}
TypePtr BinaryOpExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
TypePtr et1; // expected m_exp1's type
bool coerce1 = false; // whether m_exp1 needs to coerce to et1
TypePtr et2; // expected m_exp2's type
bool coerce2 = false; // whether m_exp2 needs to coerce to et2
TypePtr rt; // return type
switch (m_op) {
case '+':
case T_PLUS_EQUAL:
if (coerce && Type::SameType(type, Type::Array)) {
et1 = et2 = Type::Array;
coerce1 = coerce2 = true;
rt = Type::Array;
} else {
et1 = Type::PlusOperand;
et2 = Type::PlusOperand;
rt = Type::PlusOperand;
}
break;
case '-':
case '*':
case T_MINUS_EQUAL:
case T_MUL_EQUAL:
case '/':
case T_DIV_EQUAL:
et1 = Type::Numeric;
et2 = Type::Numeric;
rt = Type::Numeric;
break;
case '.':
et1 = et2 = rt = Type::String;
break;
case T_CONCAT_EQUAL:
et1 = et2 = Type::String;
rt = Type::Variant;
break;
case '%':
et1 = et2 = Type::Int64;
rt = Type::Numeric;
break;
case T_MOD_EQUAL:
et1 = Type::Numeric;
et2 = Type::Int64;
rt = Type::Numeric;
break;
case '|':
case '&':
case '^':
case T_AND_EQUAL:
case T_OR_EQUAL:
case T_XOR_EQUAL:
et1 = Type::Primitive;
et2 = Type::Primitive;
rt = Type::Primitive;
break;
case T_SL:
case T_SR:
case T_SL_EQUAL:
case T_SR_EQUAL:
et1 = et2 = rt = Type::Int64;
break;
case T_BOOLEAN_OR:
case T_BOOLEAN_AND:
case T_LOGICAL_OR:
case T_LOGICAL_AND:
case T_LOGICAL_XOR:
et1 = et2 = rt = Type::Boolean;
break;
case '<':
case T_IS_SMALLER_OR_EQUAL:
case '>':
case T_IS_GREATER_OR_EQUAL:
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
case T_IS_EQUAL:
case T_IS_NOT_EQUAL:
et1 = Type::Some;
et2 = Type::Some;
rt = Type::Boolean;
break;
case T_INSTANCEOF:
et1 = Type::Any;
et2 = Type::String;
rt = Type::Boolean;
break;
case T_COLLECTION:
et1 = Type::Any;
et2 = Type::Any;
rt = Type::Object;
break;
default:
assert(false);
}
switch (m_op) {
case T_PLUS_EQUAL:
{
TypePtr rhs = m_exp2->inferAndCheck(ar, et2, coerce2);
TypePtr lhs = m_exp1->inferAndCheck(ar, Type::Any, true);
if (lhs) {
if (lhs->mustBe(Type::KindOfArray)) {
TypePtr a2(m_exp2->getActualType());
if (a2 && a2->is(Type::KindOfArray)) {
m_exp2->setExpectedType(a2);
}
rt = Type::Array;
break;
}
if (lhs->mustBe(Type::KindOfNumeric)) {
if (!rhs->mustBe(lhs->getKindOf())) {
rhs = Type::combinedArithmeticType(lhs, rhs);
if (!rhs) rhs = Type::Numeric;
m_exp1->inferAndCheck(ar, rhs, true);
}
TypePtr a1(m_exp1->getCPPType());
TypePtr a2(m_exp2->getActualType());
if (a1 && a1->mustBe(Type::KindOfNumeric) &&
a2 && a2->mustBe(Type::KindOfNumeric)) {
// both LHS and RHS are numeric.
// Set the expected type of RHS to be
// the stronger type
TypePtr t = a1->getKindOf() > a2->getKindOf() ? a1 : a2;
m_exp2->setExpectedType(t);
}
rt = Type::Numeric;
break;
}
}
m_exp1->inferAndCheck(ar, rhs, true);
}
break;
case T_MINUS_EQUAL:
case T_MUL_EQUAL:
case T_DIV_EQUAL:
case T_MOD_EQUAL:
case T_AND_EQUAL:
case T_OR_EQUAL:
case T_XOR_EQUAL:
case T_SL_EQUAL:
case T_SR_EQUAL:
{
TypePtr ret = m_exp2->inferAndCheck(ar, et2, coerce2);
m_exp1->inferAndCheck(ar, ret, true);
}
break;
case T_CONCAT_EQUAL:
{
TypePtr ret = m_exp2->inferAndCheck(ar, et2, coerce2);
m_exp1->inferAndCheck(ar, Type::String, true);
TypePtr act1 = m_exp1->getActualType();
if (act1 && act1->is(Type::KindOfString)) rt = Type::String;
}
break;
case '+':
case '-':
case '*':
{
m_exp1->inferAndCheck(ar, et1, coerce1);
m_exp2->inferAndCheck(ar, et2, coerce2);
TypePtr act1 = m_exp1->getActualType();
TypePtr act2 = m_exp2->getActualType();
TypePtr combined = Type::combinedArithmeticType(act1, act2);
if (combined && combined->isSubsetOf(rt)) {
if (act1) m_exp1->setExpectedType(act1);
if (act2) m_exp2->setExpectedType(act2);
rt = combined;
} else if (m_op == '+') {
bool a1 = act1 && act1->is(Type::KindOfArray);
bool a2 = act2 && act2->is(Type::KindOfArray);
if (a1 || a2) {
m_implementedType.reset();
if (!a1) {
m_implementedType = Type::Variant;
} else if (!a2) {
m_exp1->setExpectedType(Type::Array);
// in this case, the implemented type will
// actually be Type::Array (since Array::operator+
// returns an Array)
} else {
m_exp1->setExpectedType(Type::Array);
m_exp2->setExpectedType(Type::Array);
}
rt = Type::Array;
}
}
}
break;
default:
m_exp1->inferAndCheck(ar, et1, coerce1);
m_exp2->inferAndCheck(ar, et2, coerce2);
break;
}
return rt;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void BinaryOpExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
m_exp1->outputPHP(cg, ar);
switch (m_op) {
case T_PLUS_EQUAL: cg_printf(" += "); break;
case T_MINUS_EQUAL: cg_printf(" -= "); break;
case T_MUL_EQUAL: cg_printf(" *= "); break;
case T_DIV_EQUAL: cg_printf(" /= "); break;
case T_CONCAT_EQUAL: cg_printf(" .= "); break;
case T_MOD_EQUAL: cg_printf(" %%= "); break;
case T_AND_EQUAL: cg_printf(" &= "); break;
case T_OR_EQUAL: cg_printf(" |= "); break;
case T_XOR_EQUAL: cg_printf(" ^= "); break;
case T_SL_EQUAL: cg_printf(" <<= "); break;
case T_SR_EQUAL: cg_printf(" >>= "); break;
case T_BOOLEAN_OR: cg_printf(" || "); break;
case T_BOOLEAN_AND: cg_printf(" && "); break;
case T_LOGICAL_OR: cg_printf(" or "); break;
case T_LOGICAL_AND: cg_printf(" and "); break;
case T_LOGICAL_XOR: cg_printf(" xor "); break;
case '|': cg_printf(" | "); break;
case '&': cg_printf(" & "); break;
case '^': cg_printf(" ^ "); break;
case '.': cg_printf(" . "); break;
case '+': cg_printf(" + "); break;
case '-': cg_printf(" - "); break;
case '*': cg_printf(" * "); break;
case '/': cg_printf(" / "); break;
case '%': cg_printf(" %% "); break;
case T_SL: cg_printf(" << "); break;
case T_SR: cg_printf(" >> "); break;
case T_IS_IDENTICAL: cg_printf(" === "); break;
case T_IS_NOT_IDENTICAL: cg_printf(" !== "); break;
case T_IS_EQUAL: cg_printf(" == "); break;
case T_IS_NOT_EQUAL: cg_printf(" != "); break;
case '<': cg_printf(" < "); break;
case T_IS_SMALLER_OR_EQUAL: cg_printf(" <= "); break;
case '>': cg_printf(" > "); break;
case T_IS_GREATER_OR_EQUAL: cg_printf(" >= "); break;
case T_INSTANCEOF: cg_printf(" instanceof "); break;
case T_COLLECTION: {
ExpressionListPtr el = static_pointer_cast<ExpressionList>(m_exp2);
if (el->getCount() == 0) {
cg_printf(" {}");
} else {
cg_printf(" { ");
el->outputPHP(cg, ar);
cg_printf(" }");
}
return;
}
default:
assert(false);
}
m_exp2->outputPHP(cg, ar);
}
bool BinaryOpExpression::isOpEqual() {
switch (m_op) {
case T_CONCAT_EQUAL:
case T_PLUS_EQUAL:
case T_MINUS_EQUAL:
case T_MUL_EQUAL:
case T_DIV_EQUAL:
case T_MOD_EQUAL:
case T_AND_EQUAL:
case T_OR_EQUAL:
case T_XOR_EQUAL:
case T_SL_EQUAL:
case T_SR_EQUAL:
return true;
default:
break;
}
return false;
}
@@ -51,13 +51,6 @@ public:
virtual ExpressionPtr unneededHelper();
virtual bool canonCompare(ExpressionPtr e) const;
void preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool outputCPPImplOpEqual(CodeGenerator &cg, AnalysisResultPtr ar);
static int getConcatList(ExpressionPtrVec &ev, ExpressionPtr exp,
bool &hasVoid);
bool isAssignmentOp() const { return m_assign; }
@@ -0,0 +1,227 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/class_constant_expression.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/code_error.h>
#include <util/hash.h>
#include <util/util.h>
#include <compiler/option.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/constant_expression.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ClassConstantExpression::ClassConstantExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr classExp, const std::string &varName)
: Expression(
EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ClassConstantExpression)),
StaticClassName(classExp), m_varName(varName), m_defScope(nullptr),
m_valid(false), m_depsSet(false) {
}
ExpressionPtr ClassConstantExpression::clone() {
ClassConstantExpressionPtr exp(new ClassConstantExpression(*this));
Expression::deepCopy(exp);
exp->m_class = Clone(m_class);
exp->m_depsSet = false;
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
bool ClassConstantExpression::containsDynamicConstant(AnalysisResultPtr ar)
const {
if (m_class) return true;
ClassScopePtr cls = ar->findClass(m_className);
return !cls || cls->isVolatile() ||
!cls->getConstants()->isRecursivelyDeclared(ar, m_varName);
}
void ClassConstantExpression::analyzeProgram(AnalysisResultPtr ar) {
if (m_class) {
m_class->analyzeProgram(ar);
} else if (ar->getPhase() >= AnalysisResult::AnalyzeAll) {
if (ClassScopePtr cls = resolveClass()) {
ConstructPtr decl = cls->getConstants()->
getValueRecur(ar, m_varName, cls);
cls->addUse(getScope(), BlockScope::UseKindConstRef);
m_depsSet = true;
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (!isPresent()) {
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
}
}
}
addUserClass(ar, m_className);
}
}
string ClassConstantExpression::getActualClassName() const {
if (m_defScope) {
return static_cast<ClassScope*>(m_defScope)->getId();
}
return m_className;
}
ConstructPtr ClassConstantExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_class;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ClassConstantExpression::getKidCount() const {
return 1;
}
void ClassConstantExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_class = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
ExpressionPtr ClassConstantExpression::preOptimize(AnalysisResultConstPtr ar) {
if (ar->getPhase() < AnalysisResult::FirstPreOptimize) {
return ExpressionPtr();
}
if (m_class) {
updateClassName();
if (m_class) {
return ExpressionPtr();
}
}
ClassScopePtr cls = resolveClass();
if (!cls || (cls->isVolatile() && !isPresent())) {
if (cls && !m_depsSet) {
cls->addUse(getScope(), BlockScope::UseKindConstRef);
m_depsSet = true;
}
return ExpressionPtr();
}
ConstantTablePtr constants = cls->getConstants();
ClassScopePtr defClass = cls;
ConstructPtr decl = constants->getValueRecur(ar, m_varName, defClass);
if (decl) {
BlockScope::s_constMutex.lock();
ExpressionPtr value = dynamic_pointer_cast<Expression>(decl);
BlockScope::s_constMutex.unlock();
ExpressionPtr rep = Clone(value, getScope());
bool annotate = Option::FlAnnotate;
Option::FlAnnotate = false; // avoid nested comments on getText
rep->setComment(getText());
Option::FlAnnotate = annotate;
rep->setLocation(getLocation());
return replaceValue(rep);
}
return ExpressionPtr();
}
TypePtr ClassConstantExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
m_valid = false;
ConstructPtr self = shared_from_this();
if (m_class) {
m_class->inferAndCheck(ar, Type::Any, false);
return Type::Variant;
}
ClassScopePtr cls = resolveClassWithChecks();
if (!cls) {
return Type::Variant;
}
ClassScopePtr defClass = cls;
ConstructPtr decl =
cls->getConstants()->getDeclarationRecur(ar, m_varName, defClass);
if (decl) { // No decl means an extension class or derived from redeclaring
cls = defClass;
m_valid = true;
if (cls->isUserClass()) {
cls->addUse(getScope(), BlockScope::UseKindConstRef);
}
}
BlockScope *defScope;
// checkConst grabs locks for us
TypePtr t = cls->checkConst(getScope(), m_varName, type,
coerce, ar,
shared_from_this(),
cls->getBases(), defScope);
if (defScope) {
m_valid = true;
m_defScope = defScope;
} else if (cls->derivesFromRedeclaring()) {
m_defScope = cls.get();
}
return t;
}
unsigned ClassConstantExpression::getCanonHash() const {
int64_t val =
hash_string(Util::toLower(m_varName).c_str(), m_varName.size()) -
hash_string(Util::toLower(m_className).c_str(), m_className.size());
return ~unsigned(val) ^ unsigned(val >> 32);
}
bool ClassConstantExpression::canonCompare(ExpressionPtr e) const {
return Expression::canonCompare(e) &&
m_varName == static_cast<ClassConstantExpression*>(e.get())->m_varName &&
m_className == static_cast<ClassConstantExpression*>(e.get())->m_className;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ClassConstantExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
StaticClassName::outputPHP(cg, ar);
cg_printf("::%s", m_varName.c_str());
}
bool ClassConstantExpression::isDynamic() const {
if (!m_valid) return true;
return m_defScope->getConstants()->isDynamic(m_varName);
}
@@ -19,6 +19,7 @@
#include <compiler/expression/expression_list.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/statement/function_statement.h>
#include <compiler/statement/static_statement.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/file_scope.h>
@@ -151,9 +152,6 @@ void ClosureExpression::analyzeProgram(AnalysisResultPtr ar) {
return;
}
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_func->getFileScope() != getFileScope()) {
getFileScope()->addUsedClosure(m_func->getFunctionScope());
}
// closure function's variable table (not containing function's)
VariableTablePtr variables = m_func->getFunctionScope()->getVariables();
for (int i = 0; i < m_vars->getCount(); i++) {
@@ -232,6 +230,34 @@ TypePtr ClosureExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
return s_ClosureType;
}
bool ClosureExpression::hasStaticLocals() {
ConstructPtr cons(m_func);
return hasStaticLocalsImpl(cons);
}
bool ClosureExpression::hasStaticLocalsImpl(ConstructPtr root) {
if (!root) {
return false;
}
if (root->getFunctionScope() != m_func->getFunctionScope()) {
// new scope, new statics
return false;
}
for (int i = 0; i < root->getKidCount(); i++) {
ConstructPtr cons = root->getNthKid(i);
if (StatementPtr s = dynamic_pointer_cast<Statement>(cons)) {
if (s->is(Statement::KindOfStaticStatement)) {
return true;
}
}
if (hasStaticLocalsImpl(cons)) {
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
@@ -244,75 +270,3 @@ void ClosureExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
}
m_func->outputPHPBody(cg, ar);
}
bool ClosureExpression::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state) {
FunctionScopeRawPtr cfunc(m_func->getFunctionScope());
bool output = false;
for (BlockScopePtr sc = cfunc->getOuterScope(); sc;
sc = sc->getOuterScope()) {
if (sc->is(BlockScope::ClassScope)) {
ClassScopePtr cls = boost::static_pointer_cast<ClassScope>(sc);
if (cls->isTrait()) {
output = true;
break;
}
}
}
if (!cg.inExpression()) {
return output || Expression::preOutputCPP(cg, ar, state);
}
if (output) {
cg.wrapExpressionBegin();
cfunc->outputCPPPreface(cg, ar);
}
return Expression::preOutputCPP(cg, ar, state) || output;
}
void ClosureExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
FunctionScopeRawPtr cfunc(m_func->getFunctionScope());
VariableTablePtr vt(cfunc->getVariables());
ParameterExpressionPtrIdxPairVec useVars;
bool needsAnonCls = cfunc->needsAnonClosureClass(useVars);
const string &origName = m_func->getOriginalName();
if (needsAnonCls) {
cg_printf("%sClosure$%s(NEWOBJ(%sClosure$%s)(&%s%s, \"%s\"",
Option::SmartPtrPrefix, origName.c_str(),
Option::ClassPrefix, origName.c_str(),
Option::CallInfoPrefix, origName.c_str(),
origName.c_str());
} else {
// no use vars, so can use the generic closure
cg_printf("%sClosure(NEWOBJ(%sClosure)(&%s%s, \"%s\"",
Option::SmartPtrPrefix, Option::ClassPrefix,
Option::CallInfoPrefix, origName.c_str(),
origName.c_str());
}
bool hasEmit = false;
if (needsAnonCls) {
if (!useVars.empty()) cg_printf(", ");
BOOST_FOREACH(ParameterExpressionPtrIdxPair paramPair, useVars) {
ParameterExpressionPtr param(paramPair.first);
ExpressionPtr value((*m_values)[paramPair.second]);
if (!hasEmit) hasEmit = true;
else cg_printf(", ");
bool ref = param->isRef() && value->isRefable();
if (ref) {
value->setContext(NoRefWrapper);
cg_printf("strongBind(");
}
value->outputCPP(cg, ar);
if (ref) cg_printf(")");
}
}
cg_printf("))");
}
@@ -32,7 +32,6 @@ public:
FunctionStatementPtr func, ExpressionListPtr vars);
DECLARE_BASE_EXPRESSION_VIRTUAL_FUNCTIONS;
virtual bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
virtual ConstructPtr getNthKid(int n) const;
virtual void setNthKid(int n, ConstructPtr cp);
@@ -41,12 +40,16 @@ public:
FunctionStatementPtr getClosureFunction() { return m_func; }
ExpressionListPtr getClosureVariables() { return m_vars; }
ExpressionListPtr getClosureValues() { return m_values; }
bool hasStaticLocals();
private:
FunctionStatementPtr m_func;
ExpressionListPtr m_vars;
ExpressionListPtr m_values;
static TypePtr s_ClosureType;
bool hasStaticLocalsImpl(ConstructPtr root);
};
///////////////////////////////////////////////////////////////////////////////
@@ -100,7 +100,7 @@ bool ConstantExpression::getScalarValue(Variant &value) {
}
unsigned ConstantExpression::getCanonHash() const {
int64 val = hash_string(Util::toLower(m_name).c_str(), m_name.size());
int64_t val = hash_string(Util::toLower(m_name).c_str(), m_name.size());
return ~unsigned(val) ^ unsigned(val >> 32);
}
@@ -255,7 +255,7 @@ TypePtr ConstantExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
// read value and dynamic-ness together + check() atomically
value = constants->getValue(m_name);
isDynamic = constants->isDynamic(m_name);
BlockScope *defScope = NULL;
BlockScope *defScope = nullptr;
std::vector<std::string> bases;
actualType = constants->check(getScope(), m_name, type, coerce,
ar, self, bases, defScope);
@@ -285,57 +285,3 @@ TypePtr ConstantExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
void ConstantExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("%s", m_name.c_str());
}
void ConstantExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
// special cases: STDIN, STDOUT, STDERR, INF, and NAN
if (m_name == "STDIN" || m_name == "STDOUT" || m_name == "STDERR") {
cg_printf("%s%s", Option::ConstantPrefix, m_name.c_str());
return;
}
if (m_name == "INF" || m_name == "NAN") {
if (cg.hasScalarVariant() && Option::UseScalarVariant) {
cg_printf("%s_varNR", m_name.c_str());
} else {
cg_printf("%s%s", Option::ConstantPrefix, m_name.c_str());
}
return;
}
string lower = Util::toLower(m_name);
bool requireFwDeclaration = false;
if (lower == "true" || lower == "false" || lower == "null") {
if (cg.hasScalarVariant()) {
cg_printf((Option::UseScalarVariant ? "%s_varNR" : "%s"), lower.c_str());
} else {
cg_printf("%s", lower.c_str());
}
} else if (m_valid) {
if (m_dynamic) {
cg_printf("getDynamicConstant(%s->%s%s, ",
cg.getGlobals(ar), Option::ConstantPrefix,
CodeGenerator::FormatLabel(m_name).c_str());
cg_printString(m_name, ar, shared_from_this());
cg_printf(")");
} else {
cg_printf("%s%s", Option::ConstantPrefix,
CodeGenerator::FormatLabel(m_name).c_str());
requireFwDeclaration = true;
}
} else {
cg_printf("getUndefinedConstant(");
cg_printString(CodeGenerator::FormatLabel(m_name).c_str(), ar,
shared_from_this());
cg_printf(")");
requireFwDeclaration = true;
}
if (requireFwDeclaration && cg.isFileOrClassHeader()) {
if (getClassScope()) {
getClassScope()->addUsedConstHeader(m_name);
} else {
getFileScope()->addUsedConstHeader(m_name);
}
}
}
@@ -53,6 +53,7 @@ public:
const std::string &getDocComment() const {
return m_docComment;
}
bool isNull() const;
bool isBoolean() const;
bool isDouble() const;
@@ -60,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:
@@ -0,0 +1,144 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/dynamic_function_call.h>
#include <compiler/analysis/code_error.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/simple_function_call.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/class_scope.h>
#include <util/util.h>
#include <compiler/option.h>
#include <compiler/analysis/variable_table.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
DynamicFunctionCall::DynamicFunctionCall
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr name, ExpressionListPtr params, ExpressionPtr cls)
: FunctionCall(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(DynamicFunctionCall),
name, "", params, cls) {
}
ExpressionPtr DynamicFunctionCall::clone() {
DynamicFunctionCallPtr exp(new DynamicFunctionCall(*this));
FunctionCall::deepCopy(exp);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void DynamicFunctionCall::analyzeProgram(AnalysisResultPtr ar) {
FunctionCall::analyzeProgram(ar);
if (ar->getPhase() >= AnalysisResult::AnalyzeAll) {
if (!m_className.empty()) {
resolveClass();
}
if (!m_class) {
addUserClass(ar, m_className);
}
if (m_params) {
m_params->markParams(canInvokeFewArgs());
}
if (!m_class && m_className.empty()) {
FunctionScopePtr fs = getFunctionScope();
VariableTablePtr vt = fs->getVariables();
vt->setAttribute(VariableTable::ContainsDynamicFunctionCall);
}
}
}
ExpressionPtr DynamicFunctionCall::preOptimize(AnalysisResultConstPtr ar) {
if (ExpressionPtr rep = FunctionCall::preOptimize(ar)) return rep;
if (m_nameExp->isScalar()) {
Variant v;
if (m_nameExp->getScalarValue(v) &&
v.isString()) {
string name = v.toString().c_str();
ExpressionPtr cls = m_class;
if (!cls && !m_className.empty()) {
cls = makeScalarExpression(ar, m_className);
}
return ExpressionPtr(NewSimpleFunctionCall(
getScope(), getLocation(),
name, m_params, cls));
}
}
return ExpressionPtr();
}
TypePtr DynamicFunctionCall::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
reset();
ConstructPtr self = shared_from_this();
if (m_class) {
m_class->inferAndCheck(ar, Type::Any, false);
} else if (!m_className.empty()) {
ClassScopePtr cls = resolveClassWithChecks();
if (cls) {
m_classScope = cls;
}
}
if (!m_class && m_className.empty()) {
m_nameExp->inferAndCheck(ar, Type::Variant, false);
} else {
m_nameExp->inferAndCheck(ar, Type::String, false);
}
if (m_params) {
for (int i = 0; i < m_params->getCount(); i++) {
(*m_params)[i]->inferAndCheck(ar, Type::Variant, true);
}
}
return Type::Variant;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void DynamicFunctionCall::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (m_class || !m_className.empty()) {
StaticClassName::outputPHP(cg, ar);
cg_printf("::");
m_nameExp->outputPHP(cg, ar);
} else {
const char *prefix = Option::IdPrefix.c_str();
if (cg.getOutput() == CodeGenerator::TrimmedPHP &&
cg.usingStream(CodeGenerator::PrimaryStream) &&
!m_nameExp->is(Expression::KindOfScalarExpression)) {
cg_printf("${%sdynamic_load($%stmp = (", prefix, prefix);
m_nameExp->outputPHP(cg, ar);
cg_printf("), '%stmp'", prefix);
cg_printf(")}");
} else {
m_nameExp->outputPHP(cg, ar);
}
}
cg_printf("(");
if (m_params) m_params->outputPHP(cg, ar);
cg_printf(")");
}
@@ -32,7 +32,6 @@ public:
DECLARE_BASE_EXPRESSION_VIRTUAL_FUNCTIONS;
ExpressionPtr preOptimize(AnalysisResultConstPtr ar);
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
};
///////////////////////////////////////////////////////////////////////////////
@@ -95,9 +95,3 @@ void DynamicVariable::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
m_exp->outputPHP(cg, ar);
cg_printf("}");
}
void DynamicVariable::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("variables->get(");
m_exp->outputCPP(cg, ar);
cg_printf(")");
}
@@ -136,55 +136,3 @@ void EncapsListExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (m_type == '`') cg_printf(")");
}
static void outputListElement(ExpressionPtr exp, CodeGenerator &cg,
AnalysisResultPtr ar) {
if (exp->is(Expression::KindOfScalarExpression)) {
TypePtr actType = exp->getActualType();
bool str = actType && actType->is(Type::KindOfString);
if (!str) cg_printf("toString(");
exp->outputCPP(cg, ar);
if (!str) cg_printf(")");
} else {
exp->outputCPP(cg, ar);
}
}
void EncapsListExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (m_type == '`') cg_printf("f_shell_exec(");
if (m_exps) {
int n = m_exps->getCount();
always_assert(n > 0);
if (n == 1) {
ExpressionPtr exp = (*m_exps)[0];
outputListElement(exp, cg, ar);
} else if (n <= MAX_CONCAT_ARGS) {
if (n == 2) {
cg_printf("concat(");
} else {
cg_printf("concat%d(", n);
}
for (int i = 0; i < n; i++) {
ExpressionPtr exp = (*m_exps)[i];
if (i > 0) cg_printf(", ");
outputListElement(exp, cg, ar);
}
cg_printf(")");
} else {
cg_printf("StringBuffer()");
for (int i = 0; i < n; i++) {
ExpressionPtr exp = (*m_exps)[i];
cg_printf(".addWithTaint(");
outputListElement(exp, cg, ar);
cg_printf(")");
}
cg_printf(".detachWithTaint()");
}
} else {
cg_printf("\"\"");
}
if (m_type == '`') cg_printf(")");
}
+916
Ver Arquivo
@@ -0,0 +1,916 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/expression.h>
#include <compiler/analysis/code_error.h>
#include <compiler/parser/parser.h>
#include <util/parser/hphp.tab.hpp>
#include <util/util.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/array_pair_expression.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/expression/function_call.h>
#include <compiler/analysis/file_scope.h>
#include <util/hash.h>
#include <runtime/base/array/array_iterator.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
#define DEC_EXPR_NAMES(x,t) #x
const char *Expression::Names[] = {
DECLARE_EXPRESSION_TYPES(DEC_EXPR_NAMES)
};
#define DEC_EXPR_CLASSES(x,t) Expression::t
Expression::ExprClass Expression::Classes[] = {
DECLARE_EXPRESSION_TYPES(DEC_EXPR_CLASSES)
};
Expression::Expression(EXPRESSION_CONSTRUCTOR_BASE_PARAMETERS)
: Construct(scope, loc), m_context(RValue), m_kindOf(kindOf),
m_originalScopeSet(false), m_unused(false), m_canon_id(0), m_error(0),
m_canonPtr() {
}
ExpressionPtr Expression::replaceValue(ExpressionPtr rep) {
if (hasContext(Expression::RefValue) &&
isRefable(true) && !rep->isRefable(true)) {
/*
An assignment isRefable, but the rhs may not be. Need this to
prevent "bad pass by reference" errors.
*/
ExpressionListPtr el(new ExpressionList(getScope(), getLocation(),
ExpressionList::ListKindWrapped));
el->addElement(rep);
rep->clearContext(AssignmentRHS);
rep = el;
}
if (rep->is(KindOfSimpleVariable) && !is(KindOfSimpleVariable)) {
static_pointer_cast<SimpleVariable>(rep)->setAlwaysStash();
}
rep->copyContext(m_context & ~(DeadStore|AccessContext));
if (TypePtr t1 = getType()) {
if (TypePtr t2 = rep->getType()) {
if (!Type::SameType(t1, t2)) {
rep->setExpectedType(t1);
}
}
}
if (rep->getScope() != getScope()) {
rep->resetScope(getScope());
}
return rep;
}
void Expression::copyContext(int contexts) {
unsigned val = contexts;
while (val) {
unsigned next = val & (val - 1);
unsigned low = val ^ next; // lowest set bit
setContext((Context)low);
val = next;
}
}
void Expression::clearContext() {
unsigned val = m_context;
while (val) {
unsigned next = val & (val - 1);
unsigned low = val ^ next; // lowest set bit
clearContext((Context)low);
val = next;
}
}
void Expression::setArgNum(int n) {
m_argNum = n;
int kc = getKidCount();
for (int i=0; i < kc; i++) {
ExpressionPtr kid = getNthExpr(i);
if (kid) {
kid->setArgNum(n);
}
}
}
void Expression::deepCopy(ExpressionPtr exp) {
exp->m_actualType = m_actualType;
exp->m_expectedType = m_expectedType;
exp->m_implementedType = m_implementedType;
exp->m_assertedType = m_assertedType;
exp->m_canon_id = 0;
exp->m_unused = false;
exp->m_canonPtr.reset();
exp->m_replacement.reset();
exp->clearVisited();
};
bool Expression::hasSubExpr(ExpressionPtr sub) const {
if (this == sub.get()) return true;
for (int i = getKidCount(); i--; ) {
ExpressionPtr kid = getNthExpr(i);
if (kid && kid->hasSubExpr(sub)) return true;
}
return false;
}
Expression::ExprClass Expression::getExprClass() const {
ExprClass cls = Classes[m_kindOf];
if (cls == Update) {
ExpressionPtr k = getNthExpr(0);
if (!k || !(k->hasContext(OprLValue))) cls = Expression::None;
}
return cls;
}
FileScopeRawPtr Expression::getUsedScalarScope(CodeGenerator& cg) {
return cg.getLiteralScope() ?
cg.getLiteralScope() : getFileScope();
}
bool Expression::getEffectiveScalar(Variant &v) {
if (is(KindOfExpressionList)) {
ExpressionRawPtr sub = static_cast<ExpressionList*>(this)->listValue();
if (!sub) return false;
return sub->getEffectiveScalar(v);
}
return getScalarValue(v);
}
void Expression::addElement(ExpressionPtr exp) {
assert(false);
}
void Expression::insertElement(ExpressionPtr exp, int index /* = 0 */) {
assert(false);
}
ExpressionPtr Expression::unneededHelper() {
ExpressionListPtr elist = ExpressionListPtr
(new ExpressionList(getScope(), getLocation(),
ExpressionList::ListKindWrapped));
bool change = false;
for (int i=0, n = getKidCount(); i < n; i++) {
ExpressionPtr kid = getNthExpr(i);
if (kid && kid->getContainedEffects()) {
ExpressionPtr rep = kid->unneeded();
if (rep != kid) change = true;
if (rep->is(Expression::KindOfExpressionList)) {
for (int j=0, m = rep->getKidCount(); j < m; j++) {
elist->addElement(rep->getNthExpr(j));
}
} else {
elist->addElement(rep);
}
}
}
if (change) {
getScope()->addUpdates(BlockScope::UseKindCaller);
}
int n = elist->getCount();
assert(n);
if (n == 1) {
return elist->getNthExpr(0);
} else {
return elist;
}
}
ExpressionPtr Expression::unneeded() {
if (getLocalEffects() || is(KindOfScalarExpression) || isNoRemove()) {
return static_pointer_cast<Expression>(shared_from_this());
}
if (!getContainedEffects()) {
getScope()->addUpdates(BlockScope::UseKindCaller);
return ScalarExpressionPtr
(new ScalarExpression(getScope(), getLocation(),
T_LNUMBER, string("0")));
}
return unneededHelper();
}
///////////////////////////////////////////////////////////////////////////////
bool Expression::IsIdentifier(const string &value) {
if (value.empty()) {
return false;
}
unsigned char ch = value[0];
if ((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') &&
ch < '\x7f' && ch != '_') {
return false;
}
for (unsigned int i = 1; i < value.size(); i++) {
unsigned char ch = value[i];
if (((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') &&
(ch < '0' || ch > '9') && ch < '\x7f' && ch != '_')) {
if (ch == '\\' && i < value.size() - 1 && value[i+1] != '\\') {
continue;
}
return false;
}
}
return true;
}
TypePtr Expression::getType() {
if (m_expectedType) return m_expectedType;
if (m_actualType) return m_actualType;
return Type::Any;
}
TypePtr Expression::getGenType() {
if (m_expectedType) return m_expectedType;
if (m_implementedType) return m_implementedType;
if (m_actualType) return m_actualType;
return Type::Any;
}
TypePtr Expression::getCPPType() {
if (m_implementedType) return m_implementedType;
if (m_actualType) return m_actualType;
return Type::Variant;
}
TypePtr Expression::propagateTypes(AnalysisResultConstPtr ar, TypePtr inType) {
ExpressionPtr e = getCanonTypeInfPtr();
TypePtr ret = inType;
while (e) {
if (e->getAssertedType() && !getAssertedType()) {
setAssertedType(e->getAssertedType());
}
TypePtr inferred = Type::Inferred(ar, ret, e->m_actualType);
if (!inferred) {
break;
}
ret = inferred;
e = e->getCanonTypeInfPtr();
}
return ret;
}
void Expression::analyzeProgram(AnalysisResultPtr ar) {
}
BlockScopeRawPtr Expression::getOriginalScope() {
if (!m_originalScopeSet) {
m_originalScopeSet = true;
m_originalScope = getScope();
}
return m_originalScope;
}
void Expression::setOriginalScope(BlockScopeRawPtr scope) {
m_originalScope = scope;
m_originalScopeSet = true;
}
ClassScopeRawPtr Expression::getOriginalClass() {
BlockScopeRawPtr scope = getOriginalScope();
return scope ? scope->getContainingClass() : ClassScopeRawPtr();
}
FunctionScopeRawPtr Expression::getOriginalFunction() {
BlockScopeRawPtr scope = getOriginalScope();
return scope ? scope->getContainingFunction() : FunctionScopeRawPtr();
}
void Expression::resetTypes() {
m_actualType .reset();
m_expectedType .reset();
m_implementedType.reset();
}
TypePtr Expression::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
assert(type);
resetTypes();
TypePtr actualType = inferTypes(ar, type, coerce);
if (type->is(Type::KindOfSome) || type->is(Type::KindOfAny)) {
m_actualType = actualType;
m_expectedType.reset();
return actualType;
}
return checkTypesImpl(ar, type, actualType, coerce);
}
TypePtr Expression::checkTypesImpl(AnalysisResultConstPtr ar,
TypePtr expectedType,
TypePtr actualType, bool coerce) {
TypePtr ret;
actualType = propagateTypes(ar, actualType);
assert(actualType);
if (coerce) {
ret = Type::Coerce(ar, expectedType, actualType);
setTypes(ar, actualType, expectedType);
} else {
ret = Type::Intersection(ar, actualType, expectedType);
setTypes(ar, actualType, ret);
}
assert(ret);
return ret;
}
void Expression::setTypes(AnalysisResultConstPtr ar, TypePtr actualType,
TypePtr expectedType) {
assert(actualType);
assert(expectedType);
m_actualType = actualType;
if (!expectedType->is(Type::KindOfAny) &&
!expectedType->is(Type::KindOfSome)) {
// store the expected type if it is not Any nor Some,
// regardless of the actual type
m_expectedType = expectedType;
} else {
m_expectedType.reset();
}
// This is a special case where Type::KindOfObject means any object.
if (m_expectedType && m_expectedType->is(Type::KindOfObject) &&
!m_expectedType->isSpecificObject() &&
m_actualType->isSpecificObject()) {
m_expectedType.reset();
}
if (m_actualType->isSpecificObject()) {
boost::const_pointer_cast<AnalysisResult>(ar)->
addClassDependency(getFileScope(), m_actualType->getName());
}
}
void Expression::setDynamicByIdentifier(AnalysisResultPtr ar,
const std::string &value) {
string id = Util::toLower(value);
size_t c = id.find("::");
FunctionScopePtr fi;
ClassScopePtr ci;
if (c != 0 && c != string::npos && c+2 < id.size()) {
string cl = id.substr(0, c);
string fn = id.substr(c+2);
if (IsIdentifier(cl) && IsIdentifier(fn)) {
ci = ar->findClass(cl);
if (ci) {
fi = ci->findFunction(ar, fn, false);
if (fi) fi->setDynamic();
}
}
} else if (IsIdentifier(id)) {
fi = ar->findFunction(id);
if (fi) fi->setDynamic();
ClassScopePtr ci = ar->findClass(id, AnalysisResult::MethodName);
if (ci) {
fi = ci->findFunction(ar, id, false);
if (fi) fi->setDynamic();
}
}
}
bool Expression::CheckNeededRHS(ExpressionPtr value) {
bool needed = true;
always_assert(value);
while (value->is(KindOfAssignmentExpression)) {
value = dynamic_pointer_cast<AssignmentExpression>(value)->getValue();
}
if (value->isScalar()) {
needed = false;
} else {
TypePtr type = value->getType();
if (type && (type->is(Type::KindOfSome) || type->is(Type::KindOfAny))) {
type = value->getActualType();
}
if (type && type->isNoObjectInvolved()) needed = false;
}
return needed;
}
bool Expression::CheckNeeded(ExpressionPtr variable, ExpressionPtr value) {
// if the value may involve object, consider the variable as "needed"
// so that objects are not destructed prematurely.
bool needed = true;
if (value) needed = CheckNeededRHS(value);
if (variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(variable);
const std::string &name = var->getName();
VariableTablePtr variables = var->getScope()->getVariables();
if (needed) {
variables->addNeeded(name);
} else {
needed = variables->isNeeded(name);
}
}
return needed;
}
bool Expression::CheckVarNR(ExpressionPtr value,
TypePtr expectedType /* = TypePtr */) {
if (!expectedType) expectedType = value->getExpectedType();
return (!value->hasContext(Expression::RefValue) &&
expectedType && expectedType->is(Type::KindOfVariant) &&
(value->getCPPType()->is(Type::KindOfArray) ||
value->getCPPType()->is(Type::KindOfString) ||
value->getCPPType()->is(Type::KindOfObject) ||
value->getCPPType()->isPrimitive() ||
value->isScalar()));
}
TypePtr Expression::inferAssignmentTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce, ExpressionPtr variable,
ExpressionPtr
value /* =ExpressionPtr() */) {
assert(type);
TypePtr ret = type;
if (value) {
ret = value->inferAndCheck(ar, Type::Some, false);
if (value->isLiteralNull()) {
ret = Type::Null;
}
assert(ret);
}
BlockScopePtr scope = getScope();
if (variable->is(Expression::KindOfConstantExpression)) {
// ...as in ClassConstant statement
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(variable);
BlockScope *defScope = nullptr;
std::vector<std::string> bases;
scope->getConstants()->check(getScope(), exp->getName(), ret,
true, ar, variable,
bases, defScope);
}
m_implementedType.reset();
TypePtr vt = variable->inferAndCheck(ar, ret, true);
if (!coerce && type->is(Type::KindOfAny)) {
ret = vt;
} else {
TypePtr it = variable->getCPPType();
if (!Type::SameType(it, ret)) {
m_implementedType = it;
}
}
if (value) {
TypePtr vat(value->getActualType());
TypePtr vet(value->getExpectedType());
TypePtr vit(value->getImplementedType());
if (vat && !vet && vit &&
Type::IsMappedToVariant(vit) &&
Type::HasFastCastMethod(vat)) {
value->setExpectedType(vat);
}
}
return ret;
}
ExpressionPtr Expression::MakeConstant(AnalysisResultConstPtr ar,
BlockScopePtr scope,
LocationPtr loc,
const std::string &value) {
ConstantExpressionPtr exp(new ConstantExpression(
scope, loc,
value));
if (value == "true" || value == "false") {
if (ar->getPhase() >= AnalysisResult::PostOptimize) {
exp->m_actualType = Type::Boolean;
}
} else if (value == "null") {
if (ar->getPhase() >= AnalysisResult::PostOptimize) {
exp->m_actualType = Type::Variant;
}
} else {
assert(false);
}
return exp;
}
void Expression::CheckPassByReference(AnalysisResultPtr ar,
ExpressionPtr param) {
if (param->hasContext(Expression::RefValue) &&
!param->isRefable(true)) {
param->setError(Expression::BadPassByRef);
Compiler::Error(Compiler::BadPassByReference, param);
}
}
unsigned Expression::getCanonHash() const {
int64_t val = hash_int64(getKindOf());
for (int i = getKidCount(); i--; ) {
ExpressionPtr k = getNthExpr(i);
if (k) {
val = hash_int64(val ^ (((int64_t)k->getKindOf()<<32)+k->getCanonID()));
}
}
return (unsigned)val ^ (unsigned)(val >> 32);
}
bool Expression::canonCompare(ExpressionPtr e) const {
if (e->getKindOf() != getKindOf()) {
return false;
}
int kk = getKidCount();
if (kk != e->getKidCount()) {
return false;
}
for (int i = kk; i--; ) {
ExpressionPtr k1 = getNthExpr(i);
ExpressionPtr k2 = e->getNthExpr(i);
if (k1 != k2) {
if (!k1 || !k2) {
return false;
}
if (k1->getCanonID() != k2->getCanonID()) {
return false;
}
}
}
return true;
}
bool Expression::equals(ExpressionPtr other) {
if (!other) return false;
// So that we can leverage canonCompare()
setCanonID(0);
other->setCanonID(0);
if (other->getKindOf() != getKindOf()) {
return false;
}
int nKids = getKidCount();
if (nKids != other->getKidCount()) {
return false;
}
for (int i = 0; i < nKids; i++) {
ExpressionPtr thisKid = getNthExpr(i);
ExpressionPtr otherKid = other->getNthExpr(i);
if (!thisKid || !otherKid) {
if (thisKid == otherKid) continue;
return false;
}
if (!thisKid->equals(otherKid)) {
return false;
}
}
return canonCompare(other);
}
ExpressionPtr Expression::getCanonTypeInfPtr() const {
if (!m_canonPtr) return ExpressionPtr();
if (!(m_context & (LValue|RefValue|UnsetContext|DeepReference))) {
return m_canonPtr;
}
if (!hasAnyContext(AccessContext|ObjectContext) ||
!m_canonPtr->getActualType()) {
return ExpressionPtr();
}
switch (m_canonPtr->getActualType()->getKindOf()) {
case Type::KindOfArray:
{
if (!hasContext(AccessContext)) break;
if (m_canonPtr->getAssertedType()) return m_canonPtr;
if (!is(Expression::KindOfSimpleVariable)) break;
SimpleVariableConstPtr sv(
static_pointer_cast<const SimpleVariable>(shared_from_this()));
if (sv->couldBeAliased()) return ExpressionPtr();
if (hasContext(LValue) &&
!(m_context & (RefValue | UnsetContext | DeepReference))) {
return m_canonPtr;
}
}
break;
case Type::KindOfObject:
{
if (!hasContext(ObjectContext)) break;
if (m_canonPtr->getAssertedType()) return m_canonPtr;
if (!is(Expression::KindOfSimpleVariable)) break;
SimpleVariableConstPtr sv(
static_pointer_cast<const SimpleVariable>(shared_from_this()));
if (sv->couldBeAliased()) return ExpressionPtr();
if (hasContext(LValue) &&
!(m_context & (RefValue | UnsetContext | DeepReference))) {
return m_canonPtr;
}
}
default:
break;
}
return ExpressionPtr();
}
ExpressionPtr Expression::fetchReplacement() {
ExpressionPtr t = m_replacement;
m_replacement.reset();
return t;
}
void Expression::computeLocalExprAltered() {
// if no kids, do nothing
if (getKidCount() == 0) return;
bool res = false;
for (int i = 0; i < getKidCount(); i++) {
ExpressionPtr k = getNthExpr(i);
if (k) {
k->computeLocalExprAltered();
res |= k->isLocalExprAltered();
}
}
if (res) {
setLocalExprAltered();
}
}
bool Expression::isArray() const {
if (is(KindOfUnaryOpExpression)) {
return static_cast<const UnaryOpExpression*>(this)->getOp() == T_ARRAY;
}
return false;
}
bool Expression::isUnquotedScalar() const {
if (!is(KindOfScalarExpression)) return false;
return !((ScalarExpression*)this)->isQuoted();
}
ExpressionPtr Expression::MakeScalarExpression(AnalysisResultConstPtr ar,
BlockScopePtr scope,
LocationPtr loc,
CVarRef value) {
if (value.isArray()) {
ExpressionListPtr el(new ExpressionList(scope, loc,
ExpressionList::ListKindParam));
for (ArrayIter iter(value); iter; ++iter) {
ExpressionPtr k(MakeScalarExpression(ar, scope, loc, iter.first()));
ExpressionPtr v(MakeScalarExpression(ar, scope, loc, iter.second()));
if (!k || !v) return ExpressionPtr();
ArrayPairExpressionPtr ap(
new ArrayPairExpression(scope, loc, k, v, false));
el->addElement(ap);
}
if (!el->getCount()) el.reset();
return ExpressionPtr(
new UnaryOpExpression(scope, loc, el, T_ARRAY, true));
} else if (value.isNull()) {
return MakeConstant(ar, scope, loc, "null");
} else if (value.isBoolean()) {
return MakeConstant(ar, scope, loc, value ? "true" : "false");
} else {
return ScalarExpressionPtr
(new ScalarExpression(scope, loc, value));
}
}
///////////////////////////////////////////////////////////////////////////////
void Expression::collectCPPTemps(ExpressionPtrVec &collection) {
if (isChainRoot()) {
collection.push_back(static_pointer_cast<Expression>(shared_from_this()));
} else {
for (int i = 0; i < getKidCount(); i++) {
ExpressionPtr kid = getNthExpr(i);
if (kid) kid->collectCPPTemps(collection);
}
}
}
void Expression::disableCSE() {
ExpressionPtrVec v;
collectCPPTemps(v);
ExpressionPtrVec::iterator it(v.begin());
for (; it != v.end(); ++it) {
ExpressionPtr p(*it);
p->clearChainRoot();
}
}
bool Expression::hasChainRoots() {
ExpressionPtrVec v;
collectCPPTemps(v);
return !v.empty();
}
bool Expression::GetCseTempInfo(
AnalysisResultPtr ar,
ExpressionPtr p,
TypePtr &t) {
assert(p);
switch (p->getKindOf()) {
case Expression::KindOfArrayElementExpression:
{
ArrayElementExpressionPtr ap(
static_pointer_cast<ArrayElementExpression>(p));
ExpressionPtr var(ap->getVariable());
TypePtr srcType, dstType;
bool needsCast =
var->getTypeCastPtrs(ar, srcType, dstType);
TypePtr testType(needsCast ? dstType : srcType);
if (testType) {
t = testType;
return !testType->is(Type::KindOfArray);
}
return true;
}
break;
default:
break;
}
return true;
}
ExpressionPtr Expression::getNextCanonCsePtr() const {
bool dAccessCtx =
hasContext(AccessContext);
bool dLval =
hasContext(LValue);
bool dExistCtx =
hasContext(ExistContext);
bool dUnsetCtx =
hasContext(UnsetContext);
bool dGlobals = false;
if (is(KindOfArrayElementExpression)) {
ArrayElementExpressionConstPtr a(
static_pointer_cast<const ArrayElementExpression>(
shared_from_this()));
dGlobals = a->isSuperGlobal() || a->isDynamicGlobal();
}
// see rules below - no hope to find CSE candidate
if (dExistCtx || dUnsetCtx || dGlobals || (!dAccessCtx && dLval)) {
return ExpressionPtr();
}
KindOf dKindOf = getKindOf();
ExpressionPtr match;
ExpressionPtr p(getCanonLVal());
for (; p; p = p->getCanonLVal()) {
// check if p is a suitable candidate for CSE of
// downstream. the rules are:
// A) rvals can always be CSE-ed regardless of access context,
// except for unset context, which it never can be CSE-ed for
// B) lvals can only be CSE-ed if in AccessContext
// C) rvals and lvals cannot be CSE-ed for each other
// D) for now, ExistContext is not optimized
// E) no CSE for $GLOBALS[...]
// F) node types need to match
bool pLval = p->hasContext(LValue);
KindOf pKindOf = p->getKindOf();
if (dKindOf != pKindOf) continue;
if (dLval) {
assert(dAccessCtx);
bool pAccessCtx = p->hasContext(AccessContext);
if (pLval && pAccessCtx) {
// match found
match = p;
break;
}
} else {
bool pExistCtx = p->hasContext(ExistContext);
bool pUnsetCtx = p->hasContext(UnsetContext);
if (!pLval && !pExistCtx && !pUnsetCtx) {
// match found
match = p;
break;
}
}
}
return match;
}
ExpressionPtr Expression::getCanonCsePtr() const {
ExpressionPtr next(getNextCanonCsePtr());
if (next) {
if (next->isChainRoot()) return next;
return next->getCanonCsePtr();
}
return ExpressionPtr();
}
bool Expression::getTypeCastPtrs(
AnalysisResultPtr ar, TypePtr &srcType, TypePtr &dstType) {
srcType = m_actualType;
dstType = m_expectedType;
if (m_implementedType && srcType &&
!Type::SameType(m_implementedType, srcType)) {
srcType = m_implementedType;
}
if (!srcType && dstType && Type::IsCastNeeded(ar, Type::Variant, dstType)) {
srcType = Type::Variant;
return true;
}
return dstType && srcType && ((m_context & LValue) == 0) &&
Type::IsCastNeeded(ar, srcType, dstType);
}
bool Expression::needsFastCastTemp(AnalysisResultPtr ar) {
if (is(KindOfSimpleVariable)) return false;
if (!canUseFastCast(ar)) return false;
if (hasAnyContext(ExistContext|AccessContext) &&
(is(KindOfObjectPropertyExpression) ||
is(KindOfArrayElementExpression))) {
return false;
}
assert(m_actualType);
return !m_actualType->isPrimitive();
}
bool Expression::couldCppTypeBeReferenced() {
if (is(KindOfDynamicVariable)) return true;
SimpleVariablePtr p(
dynamic_pointer_cast<SimpleVariable>(
shared_from_this()));
BlockScopeRawPtr scope(getScope());
VariableTablePtr vt(scope ? scope->getVariables() : VariableTablePtr());
// a simple variable could have its CPP type referenced if:
// it could be aliased or,
// it is a non-lval parameter or,
// the scope it lives in has a dynamic variable or contains extract()
// note that we default to true (the conservative case) if no symbol
// or variable table is found
return p ?
(p->couldBeAliased() ||
(!p->getSymbol() ||
(p->getSymbol()->isParameter() && !p->getSymbol()->isLvalParam())) ||
(!vt || (vt->getAttribute(VariableTable::ContainsDynamicVariable) ||
vt->getAttribute(VariableTable::ContainsExtract)))) :
!isTemporary();
}
bool Expression::canUseFastCast(AnalysisResultPtr ar) {
TypePtr srcType, dstType;
getTypeCastPtrs(ar, srcType, dstType);
// if the impl type is Variant and the actual type is known
// with a fast cast method, and we have a dst type that
// is not Variant (in CPP), then we have something to benefit
// from doing a fast cast and should emit one.
if (m_implementedType &&
Type::IsMappedToVariant(m_implementedType) &&
m_actualType &&
Type::HasFastCastMethod(m_actualType) &&
dstType &&
!Type::IsMappedToVariant(dstType)) {
if (m_assertedType) return true;
if (is(KindOfSimpleVariable) &&
static_cast<SimpleVariable*>(this)->isGuarded()) {
return true;
}
}
return false;
}
@@ -36,8 +36,7 @@
virtual ExpressionPtr clone(); \
virtual TypePtr inferTypes(AnalysisResultPtr ar, TypePtr type, \
bool coerce); \
virtual void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar); \
virtual void outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar)
virtual void outputPHP(CodeGenerator &cg, AnalysisResultPtr ar);
#define DECLARE_EXPRESSION_VIRTUAL_FUNCTIONS \
DECLARE_BASE_EXPRESSION_VIRTUAL_FUNCTIONS; \
virtual ConstructPtr getNthKid(int n) const; \
@@ -172,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.
*/
@@ -186,28 +186,9 @@ public:
/**
* Implementing Construct.
*/
virtual void outputCPP(CodeGenerator &cg, AnalysisResultPtr ar);
virtual void outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) = 0;
void outputCPPCast(CodeGenerator &cg, AnalysisResultPtr ar);
virtual void outputCPPDecl(CodeGenerator &cg, AnalysisResultPtr ar);
virtual void outputCPPExistTest(CodeGenerator &cg, AnalysisResultPtr ar,
int op);
virtual void outputCPPUnset(CodeGenerator &cg, AnalysisResultPtr ar);
virtual void preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
virtual bool outputCPPUnneeded(CodeGenerator &cg, AnalysisResultPtr ar);
bool outputCPPBegin(CodeGenerator &cg, AnalysisResultPtr ar);
bool outputCPPEnd(CodeGenerator &cg, AnalysisResultPtr ar);
void collectCPPTemps(ExpressionPtrVec &collection);
void disableCSE();
bool hasChainRoots();
bool preOutputCPPTemp(CodeGenerator &cg, AnalysisResultPtr ar,
bool emitTemps);
virtual bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool preOutputOffsetLHS(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool hasCPPTemp() const { return !m_cppTemp.empty(); }
const std::string &cppTemp() const { return m_cppTemp; }
std::string genCPPTemp(CodeGenerator &cg, AnalysisResultPtr ar);
@@ -216,7 +197,6 @@ public:
void setOriginalScope(BlockScopeRawPtr scope);
ClassScopeRawPtr getOriginalClass();
FunctionScopeRawPtr getOriginalFunction();
std::string originalClassName(CodeGenerator &cg, bool withComma);
/**
* For generic walks
@@ -390,7 +370,6 @@ public:
static bool GetCseTempInfo(
AnalysisResultPtr ar, ExpressionPtr p, TypePtr &t);
bool outputCPPArithArg(CodeGenerator &cg, AnalysisResultPtr ar, bool arrayOk);
bool isUnused() const { return m_unused; }
void setUnused(bool u) { m_unused = u; }
ExpressionPtr fetchReplacement();
@@ -400,8 +379,6 @@ public:
* Correctly compute the local expression altered bit
*/
void computeLocalExprAltered();
bool outputCPPGuardedObjectPtr(CodeGenerator &cg);
protected:
static bool IsIdentifier(const std::string &value);
@@ -432,13 +409,9 @@ protected:
TypePtr expectedType);
void setDynamicByIdentifier(AnalysisResultPtr ar,
const std::string &value);
bool outputLineMap(CodeGenerator &cg, AnalysisResultPtr ar,
bool force = false);
void resetTypes();
private:
static ExprClass Classes[];
void outputCPPInternal(CodeGenerator &cg, AnalysisResultPtr ar);
/**
* Returns true if a type cast is needed, and sets src/dst type
+516
Ver Arquivo
@@ -0,0 +1,516 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/expression_list.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/expression/binary_op_expression.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/expression/array_pair_expression.h>
#include <compiler/analysis/function_scope.h>
#include <runtime/base/array/array_init.h>
#include <compiler/parser/parser.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ExpressionList::ExpressionList(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ListKind kind)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ExpressionList)),
m_outputCount(-1),
m_arrayElements(false), m_collectionType(0), m_kind(kind) {
}
ExpressionPtr ExpressionList::clone() {
ExpressionListPtr exp(new ExpressionList(*this));
Expression::deepCopy(exp);
exp->m_exps.clear();
for (unsigned int i = 0; i < m_exps.size(); i++) {
exp->m_exps.push_back(Clone(m_exps[i]));
}
return exp;
}
void ExpressionList::toLower() {
for (unsigned int i = 0; i < m_exps.size(); i++) {
ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exps[i]);
s->toLower();
}
}
void ExpressionList::setContext(Context context) {
Expression::setContext(context);
if (m_kind == ListKindParam && context & UnsetContext) {
for (unsigned int i = m_exps.size(); i--; ) {
if (m_exps[i]) {
m_exps[i]->setContext(UnsetContext);
m_exps[i]->setContext(LValue);
m_exps[i]->setContext(NoLValueWrapper);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void ExpressionList::addElement(ExpressionPtr exp) {
ArrayPairExpressionPtr ap = dynamic_pointer_cast<ArrayPairExpression>(exp);
if (ap) {
m_arrayElements = true;
} else {
assert(!m_arrayElements);
}
m_exps.push_back(exp);
}
void ExpressionList::insertElement(ExpressionPtr exp, int index /* = 0 */) {
m_exps.insert(m_exps.begin() + index, exp);
}
void ExpressionList::removeElement(int index) {
m_exps.erase(m_exps.begin() + index, m_exps.begin() + index + 1);
}
void ExpressionList::clearElements() {
m_exps.clear();
}
bool ExpressionList::isRefable(bool checkError /* = false */) const {
if (m_kind == ListKindWrapped || m_kind == ListKindLeft) {
// Its legal to ref a list...
if (checkError) return true;
// ...but we shouldnt apply ref() to it unless the corresponding
// arg is refable
int ix = m_kind == ListKindLeft ? 0 : m_exps.size() - 1;
return m_exps[ix]->isRefable(false);
}
return false;
}
bool ExpressionList::isScalar() const {
if (m_arrayElements) {
return isScalarArrayPairs();
}
if (m_kind == ListKindParam) {
for (unsigned int i = m_exps.size(); i--; ) {
if (m_exps[i] && !m_exps[i]->isScalar()) return false;
}
return true;
} else if (!hasEffect()) {
ExpressionPtr v(listValue());
return v ? v->isScalar() : false;
}
return false;
}
bool ExpressionList::isNoObjectInvolved() const {
for (unsigned int i = 0; i < m_exps.size(); i++) {
TypePtr t = m_exps[i]->getActualType();
if (t == nullptr || !t->isNoObjectInvolved())
return false;
}
return true;
}
bool ExpressionList::containsDynamicConstant(AnalysisResultPtr ar) const {
for (unsigned int i = 0; i < m_exps.size(); i++) {
if (m_exps[i]->containsDynamicConstant(ar)) return true;
}
return false;
}
bool ExpressionList::isScalarArrayPairs() const {
if (!m_arrayElements || m_collectionType) return false;
for (unsigned int i = 0; i < m_exps.size(); i++) {
ArrayPairExpressionPtr exp =
dynamic_pointer_cast<ArrayPairExpression>(m_exps[i]);
if (!exp || !exp->isScalarArrayPair()) {
return false;
}
}
return true;
}
void ExpressionList::getStrings(std::vector<std::string> &strings) {
for (unsigned int i = 0; i < m_exps.size(); i++) {
ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exps[i]);
strings.push_back(s->getString());
}
}
void ExpressionList::getOriginalStrings(std::vector<std::string> &strings) {
for (unsigned int i = 0; i < m_exps.size(); i++) {
ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exps[i]);
strings.push_back(s->getOriginalString());
}
}
bool
ExpressionList::flattenLiteralStrings(vector<ExpressionPtr> &literals) const {
for (unsigned i = 0; i < m_exps.size(); i++) {
ExpressionPtr e = m_exps[i];
if (e->is(Expression::KindOfArrayPairExpression)) {
ArrayPairExpressionPtr ap = dynamic_pointer_cast<ArrayPairExpression>(e);
if (ap->getName()) return false;
e = ap->getValue();
}
if (e->is(Expression::KindOfUnaryOpExpression)) {
UnaryOpExpressionPtr unary = dynamic_pointer_cast<UnaryOpExpression>(e);
if (unary->getOp() == T_ARRAY) {
ExpressionListPtr el =
dynamic_pointer_cast<ExpressionList>(unary->getExpression());
if (!el->flattenLiteralStrings(literals)) {
return false;
}
}
}
else if (e->isLiteralString()) {
literals.push_back(e);
} else {
return false;
}
}
return true;
}
bool ExpressionList::getScalarValue(Variant &value) {
if (m_arrayElements) {
if (isScalarArrayPairs()) {
ArrayInit init(m_exps.size());
for (unsigned int i = 0; i < m_exps.size(); i++) {
ArrayPairExpressionPtr exp =
dynamic_pointer_cast<ArrayPairExpression>(m_exps[i]);
ExpressionPtr name = exp->getName();
ExpressionPtr val = exp->getValue();
if (!name) {
Variant v;
bool ret = val->getScalarValue(v);
if (!ret) assert(false);
init.set(v);
} else {
Variant n;
Variant v;
bool ret1 = name->getScalarValue(n);
bool ret2 = val->getScalarValue(v);
if (!(ret1 && ret2)) return ExpressionPtr();
init.set(n, v);
}
}
value = Array(init.create());
return true;
}
return false;
}
if (m_kind != ListKindParam && !hasEffect()) {
ExpressionPtr v(listValue());
return v ? v->getScalarValue(value) : false;
}
return false;
}
void ExpressionList::stripConcat() {
ExpressionList &el = *this;
for (int i = 0; i < el.getCount(); ) {
ExpressionPtr &e = el[i];
if (e->is(Expression::KindOfUnaryOpExpression)) {
UnaryOpExpressionPtr u(boost::static_pointer_cast<UnaryOpExpression>(e));
if (u->getOp() == '(') {
e = u->getExpression();
}
}
if (e->is(Expression::KindOfBinaryOpExpression)) {
BinaryOpExpressionPtr b
(boost::static_pointer_cast<BinaryOpExpression>(e));
if (b->getOp() == '.') {
e = b->getExp1();
el.insertElement(b->getExp2(), i + 1);
continue;
}
}
i++;
}
}
void ExpressionList::setOutputCount(int count) {
assert(count >= 0 && count <= (int)m_exps.size());
m_outputCount = count;
}
int ExpressionList::getOutputCount() const {
return m_outputCount < 0 ? m_exps.size() : m_outputCount;
}
void ExpressionList::resetOutputCount() {
m_outputCount = -1;
}
void ExpressionList::markParam(int p, bool noRefWrapper) {
ExpressionPtr param = (*this)[p];
if (param->hasContext(Expression::InvokeArgument)) {
if (noRefWrapper) {
param->setContext(Expression::NoRefWrapper);
} else {
param->clearContext(Expression::NoRefWrapper);
}
} else if (!param->hasContext(Expression::RefParameter)) {
param->setContext(Expression::InvokeArgument);
param->setContext(Expression::RefValue);
if (noRefWrapper) {
param->setContext(Expression::NoRefWrapper);
}
}
param->setArgNum(p);
}
void ExpressionList::markParams(bool noRefWrapper) {
for (int i = 0; i < getCount(); i++) {
markParam(i, noRefWrapper);
}
}
void ExpressionList::setCollectionType(int cType) {
m_arrayElements = true;
m_collectionType = cType;
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
ExpressionPtr &ExpressionList::operator[](int index) {
assert(index >= 0 && index < getCount());
return m_exps[index];
}
void ExpressionList::analyzeProgram(AnalysisResultPtr ar) {
for (unsigned int i = 0; i < m_exps.size(); i++) {
if (m_exps[i]) m_exps[i]->analyzeProgram(ar);
}
}
bool ExpressionList::kidUnused(int i) const {
if (m_kind == ListKindParam) {
return false;
}
if (isUnused()) return true;
if (m_kind == ListKindLeft) {
return i != 0;
}
return (i + 1u) != m_exps.size();
}
ConstructPtr ExpressionList::getNthKid(int n) const {
if (n < (int)m_exps.size()) {
return m_exps[n];
}
return ConstructPtr();
}
int ExpressionList::getKidCount() const {
return m_exps.size();
}
void ExpressionList::setNthKid(int n, ConstructPtr cp) {
int m = m_exps.size();
if (n >= m) {
assert(false);
} else {
m_exps[n] = boost::dynamic_pointer_cast<Expression>(cp);
}
}
ExpressionPtr ExpressionList::listValue() const {
if (size_t i = m_exps.size()) {
if (m_kind == ListKindComma || m_kind == ListKindWrapped) {
return m_exps[i-1];
} else if (m_kind == ListKindLeft) {
return m_exps[0];
}
}
return ExpressionPtr();
}
bool ExpressionList::isLiteralString() const {
ExpressionPtr v(listValue());
return v ? v->isLiteralString() : false;
}
string ExpressionList::getLiteralString() const {
ExpressionPtr v(listValue());
return v ? v->getLiteralString() : string("");
}
void ExpressionList::optimize(AnalysisResultConstPtr ar) {
bool changed = false;
size_t i = m_exps.size();
if (m_kind != ListKindParam) {
size_t skip = m_kind == ListKindLeft ? 0 : i - 1;
while (i--) {
if (i != skip) {
ExpressionPtr &e = m_exps[i];
if (!e || (e->getContainedEffects() == NoEffect && !e->isNoRemove())) {
removeElement(i);
changed = true;
} else if (e->is(KindOfExpressionList)) {
ExpressionListPtr el(static_pointer_cast<ExpressionList>(e));
removeElement(i);
for (size_t j = el->getCount(); j--; ) {
insertElement((*el)[j], i);
}
changed = true;
} else if (e->getLocalEffects() == NoEffect) {
e = e->unneeded();
// changed already handled by unneeded
}
}
}
if (m_exps.size() == 1) {
// don't convert an exp-list with type assertions to
// a ListKindWrapped
if (!isNoRemove()) {
m_kind = ListKindWrapped;
}
} else if (m_kind == ListKindLeft && m_exps[0]->isScalar()) {
ExpressionPtr e = m_exps[0];
removeElement(0);
addElement(e);
m_kind = ListKindWrapped;
}
} else {
bool isUnset = hasContext(UnsetContext) &&
ar->getPhase() >= AnalysisResult::PostOptimize;
int isGlobal = -1;
while (i--) {
ExpressionPtr &e = m_exps[i];
if (isUnset) {
if (e->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(e);
if (var->checkUnused()) {
const std::string &name = var->getName();
VariableTablePtr variables = getScope()->getVariables();
if (!variables->isNeeded(name)) {
removeElement(i);
changed = true;
}
}
}
} else {
bool global = e && (e->getContext() & Declaration) == Declaration;
if (isGlobal < 0) {
isGlobal = global;
} else {
always_assert(isGlobal == global);
}
if (isGlobal && e->isScalar()) {
removeElement(i);
changed = true;
}
}
}
}
if (changed) {
getScope()->addUpdates(BlockScope::UseKindCaller);
}
}
ExpressionPtr ExpressionList::preOptimize(AnalysisResultConstPtr ar) {
optimize(ar);
return ExpressionPtr();
}
ExpressionPtr ExpressionList::postOptimize(AnalysisResultConstPtr ar) {
optimize(ar);
return ExpressionPtr();
}
TypePtr ExpressionList::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
size_t size = m_exps.size();
bool commaList = size && (m_kind != ListKindParam);
size_t ix = m_kind == ListKindLeft ? 0 : size - 1;
TypePtr tmp = commaList ? Type::Some : type;
TypePtr ret = type;
for (size_t i = 0; i < size; i++) {
TypePtr t = i != ix ? tmp : type;
bool c = coerce && (!commaList || i == ix);
if (ExpressionPtr e = m_exps[i]) {
e->inferAndCheck(ar, t, c);
if (commaList && i == ix) {
e->setExpectedType(TypePtr());
ret = e->getActualType();
if (e->getImplementedType()) {
m_implementedType = e->getImplementedType();
}
if (!ret) ret = Type::Variant;
}
}
}
return ret;
}
bool ExpressionList::canonCompare(ExpressionPtr e) const {
if (!Expression::canonCompare(e)) return false;
ExpressionListPtr l =
static_pointer_cast<ExpressionList>(e);
return m_arrayElements == l->m_arrayElements &&
m_collectionType == l->m_collectionType &&
m_kind == l->m_kind;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ExpressionList::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
for (unsigned int i = 0; i < m_exps.size(); i++) {
if (i > 0) cg_printf(", ");
ExpressionPtr exp = m_exps[i];
if (exp) {
if (exp->hasContext(RefParameter)) {
cg_printf("&");
}
exp->outputPHP(cg, ar);
}
}
}
unsigned int ExpressionList::checkLitstrKeys() const {
assert(m_arrayElements && !m_collectionType);
std::set<string> keys;
for (unsigned int i = 0; i < m_exps.size(); i++) {
ArrayPairExpressionPtr ap =
dynamic_pointer_cast<ArrayPairExpression>(m_exps[i]);
ExpressionPtr name = ap->getName();
if (!name) return 0;
Variant value;
bool ret = name->getScalarValue(value);
if (!ret) return 0;
if (!value.isString()) return 0;
String str = value.toString();
if (str->isInteger()) return 0;
string s(str.data(), str.size());
keys.insert(s);
}
return keys.size();
}
@@ -85,19 +85,6 @@ public:
virtual bool canonCompare(ExpressionPtr e) const;
void preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
bool outputCPPUnneeded(CodeGenerator &cg, AnalysisResultPtr ar);
bool hasNonArrayCreateValue(bool arrayElements = true,
unsigned int start = 0) const;
void outputCPPUniqLitKeyArrayInit(CodeGenerator &cg,
AnalysisResultPtr ar,
bool litstrKeys,
int64 max,
bool arrayElements = true,
unsigned int start = 0);
/**
* Checks whether the expression list contains only literal strings and
* (recursive) arrays of literal strings. Also returns the list of strings
@@ -108,10 +95,6 @@ public:
private:
void optimize(AnalysisResultConstPtr ar);
unsigned int checkLitstrKeys() const;
bool outputCPPArrayCreate(CodeGenerator &cg, AnalysisResultPtr ar,
bool isVector, bool pre);
bool outputCPPInternal(CodeGenerator &cg,
AnalysisResultPtr ar, bool needed, bool pre);
ExpressionPtrVec m_exps;
int m_outputCount;
@@ -59,10 +59,9 @@ FunctionCall::FunctionCall
m_nameExp->getKindOf() == Expression::KindOfScalarExpression) {
assert(m_name.empty());
ScalarExpressionPtr c = dynamic_pointer_cast<ScalarExpression>(m_nameExp);
m_origName = c->getString();
m_origName = c->getOriginalLiteralString();
c->toLower(true /* func call*/);
m_name = c->getString();
assert(!m_name.empty());
m_name = c->getLiteralString();
} else {
m_origName = name;
m_name = Util::toLower(name);
@@ -585,183 +584,3 @@ TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar,
return type;
}
bool FunctionCall::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state) {
optimizeArgArray(ar);
bool noLSB = m_noStatic || (!m_class && m_className.empty()) ||
isSelf() || isParent();
if (!noLSB) {
if (m_funcScope && !m_funcScope->usesLSB()) {
noLSB = true;
}
}
if (noLSB || isUnused()) {
return Expression::preOutputCPP(cg, ar, state);
}
if (!cg.inExpression()) {
return true;
}
Expression::preOutputCPP(cg, ar, state & ~FixOrder);
cg.wrapExpressionBegin();
if (m_classScope) {
string className = m_classScope->getId();
cg_printf("fi.setStaticClassName(%s%s::s_class_name);\n",
Option::ClassPrefix, className.c_str());
} else {
m_clsNameTemp = cg.createNewLocalId(shared_from_this());
cg_printf("CStrRef clsName%d(", m_clsNameTemp);
cg_printString(m_origClassName, ar, shared_from_this());
cg_printf(");\n");
cg_printf("fi.setStaticClassName(clsName%d);\n", m_clsNameTemp);
}
m_noStatic = true;
preOutputStash(cg, ar, FixOrder);
m_noStatic = false;
cg_printf("fi.resetStaticClassName();\n");
if (!(state & FixOrder)) {
cg_printf("id(%s);\n", cppTemp().c_str());
}
return true;
}
void FunctionCall::outputDynamicCall(CodeGenerator &cg,
AnalysisResultPtr ar, bool method) {
const char *kind = method ? "Meth" : "Func";
const char *var = method ? "mcp" : "vt";
int pcount = m_params ? m_params->getCount() : 0;
if (canInvokeFewArgs()) {
if (Option::InvokeWithSpecificArgs) {
cg_printf("get%s%dArgs())(%s%d, ", kind, pcount, var, m_ciTemp);
} else {
cg_printf("get%sFewArgs())(%s%d, ", kind, var, m_ciTemp);
}
if (pcount) {
for (int i = 0; i < pcount; i++) {
(*m_params)[i]->setContext(NoRefWrapper);
}
cg_printf("%d, ", pcount);
cg.pushCallInfo(m_ciTemp);
FunctionScope::OutputCPPArguments(m_params, FunctionScopePtr(),
cg, ar, 0, false);
cg.popCallInfo();
} else {
cg_printf("0");
}
if (!Option::InvokeWithSpecificArgs) {
for (int i = pcount; i < Option::InvokeFewArgsCount; i++) {
cg_printf(", null_variant");
}
}
} else {
cg_printf("get%s())(%s%d, ", kind, var, m_ciTemp);
if (pcount) {
cg.pushCallInfo(m_ciTemp);
FunctionScope::OutputCPPArguments(m_params, FunctionScopePtr(),
cg, ar, -1, false);
cg.popCallInfo();
} else {
cg_printf("Array()");
}
}
cg_printf(")");
}
void FunctionCall::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) {
optimizeArgArray(ar);
bool staticClassName = false;
bool noLSB = !m_cppTemp.empty() || m_noStatic ||
(!m_class && m_className.empty()) ||
isSelf() || isParent() || isStatic();
if (!noLSB) {
if (m_funcScope && !m_funcScope->usesLSB()) {
noLSB = true;
}
}
if (!noLSB) {
if (!m_className.empty()) {
cg_printf("STATIC_CLASS_NAME_CALL(");
if (m_classScope) {
string className = m_classScope->getId();
cg_printf("%s%s::s_class_name, ",
Option::ClassPrefix, className.c_str());
} else {
assert(m_clsNameTemp >= 0);
cg_printf("clsName%d, ", m_clsNameTemp);
}
} else {
cg_printf("STATIC_CLASS_INVOKE_CALL(mcp%d.getClassName(), ",
m_ciTemp);
}
if (m_voidReturn) m_voidWrapper = true;
staticClassName = true;
}
if (m_voidReturn) clearContext(RefValue);
TypePtr eType = getExpectedType();
bool wrap = m_voidWrapper && m_cppTemp.empty() && !isUnused();
if (wrap) {
cg_printf("(");
setExpectedType(TypePtr());
}
Expression::outputCPP(cg, ar);
if (wrap) {
setExpectedType(eType);
if (eType && !Type::IsMappedToVariant(eType)) {
if (eType->is(Type::KindOfBoolean)) {
cg_printf(", false)");
} else if (eType->isInteger()) {
cg_printf(", 0)");
} else if (eType->is(Type::KindOfDouble)) {
cg_printf(", 0.0)");
} else if (eType->is(Type::KindOfString)) {
cg_printf(", empty_string)");
} else {
cg_printf(", ");
eType->outputCPPCast(cg, ar, getScope());
cg_printf("(null))");
}
} else {
cg_printf(", null)");
}
}
if (staticClassName) {
cg_printf(")");
}
}
void FunctionCall::optimizeArgArray(AnalysisResultPtr ar) {
if (m_extraArg <= 0 || m_argArrayId >= 0) return;
int paramCount = m_params->getOutputCount();
int iMax = paramCount - m_extraArg;
bool isScalar = true;
for (int i = iMax; i < paramCount; i++) {
ExpressionPtr param = (*m_params)[i];
if (!param->isScalar()) {
isScalar = false;
break;
}
}
if (isScalar) {
ExpressionPtr argArrayPairs =
ExpressionListPtr(new ExpressionList(getScope(), getLocation()));
for (int i = iMax; i < paramCount; i++) {
ExpressionPtr param = (*m_params)[i];
argArrayPairs->addElement(
ArrayPairExpressionPtr(new ArrayPairExpression(
getScope(), param->getLocation(),
ExpressionPtr(), param, false)));
}
string text;
m_argArrayId =
ar->registerScalarArray(false, getFileScope(), argArrayPairs,
m_argArrayHash, m_argArrayIndex, text);
}
}
@@ -35,8 +35,6 @@ protected:
public:
void analyzeProgram(AnalysisResultPtr ar);
// overriding Expression::outputCPP to implement void wrapper
virtual void outputCPP(CodeGenerator &cg, AnalysisResultPtr ar);
virtual bool isRefable(bool checkError = false) const { return true;}
virtual bool isTemporary() const;
@@ -52,8 +50,6 @@ public:
ExpressionPtr getNameExp() const { return m_nameExp; }
const ExpressionListPtr& getParams() const { return m_params; }
void setNoInline() { m_noInline = true; }
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
void deepCopy(FunctionCallPtr exp);
FunctionScopeRawPtr getFuncScope() const { return m_funcScope; }
@@ -62,8 +58,6 @@ public:
bool isValid() const { return m_valid; }
protected:
void outputDynamicCall(CodeGenerator &cg,
AnalysisResultPtr ar, bool method);
ExpressionPtr m_nameExp;
std::string m_name;
std::string m_origName;
@@ -286,7 +286,7 @@ ExpressionPtr IncludeExpression::postOptimize(AnalysisResultConstPtr ar) {
return replaceValue(rep->clone());
}
}
if (!hhvm || !Option::OutputHHBC) {
if (!Option::OutputHHBC) {
m_exp.reset();
}
} else {
@@ -307,53 +307,3 @@ TypePtr IncludeExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
void IncludeExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
UnaryOpExpression::outputPHP(cg, ar);
}
void IncludeExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
// Includes aren't really supported in system mode
if (cg.getOutput() == CodeGenerator::SystemCPP) {
cg_printf("true");
return;
}
const char *vars = m_privateScope ?
"lvar_ptr(LVariableTable())" : "variables";
bool require = (m_op == T_REQUIRE || m_op == T_REQUIRE_ONCE);
bool once = (m_op == T_INCLUDE_ONCE || m_op == T_REQUIRE_ONCE);
if (!m_include.empty()) {
FileScopePtr fs = ar->findFileScope(m_include);
if (fs) {
if (!fs->canUseDummyPseudoMain(ar)) {
if (!fs->needPseudoMainVariables()) {
vars = "NULL";
}
cg_printf("%s%s(%s, %s, %s)", Option::PseudoMainPrefix,
fs->pseudoMainName().c_str(),
once ? "true" : "false",
vars, cg.getGlobals(ar));
} else {
cg_printf("true");
}
return;
}
}
// include() and require() need containing file's directory
string currentDir = "\"\"";
if (m_loc && m_loc->file && *m_loc->file) {
string file = m_loc->file;
size_t pos = file.rfind('/');
if (pos != string::npos) {
currentDir = '"';
currentDir += file.substr(0, pos + 1);
currentDir += '"';
}
}
// fallback to dynamic include
cg_printf("%s(", require ? "require" : "include");
m_exp->outputCPP(cg, ar);
cg_printf(", %s, %s, %s)",
once ? "true" : "false",
vars,
currentDir.c_str());
}
+265
Ver Arquivo
@@ -0,0 +1,265 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/list_assignment.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/expression/binary_op_expression.h>
#include <util/parser/hphp.tab.hpp>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
/*
Determine whether the rhs behaves normall, or abnormally.
1) If the expression is the silence operator, recurse on the inner expression.
2) If the expression is a list assignment expression, recurse on the
RHS of the expression.
3) If the expression is one of the following, then E behaves normally:
Simple/Dynamic variable (including $this and superglobals)
Array element expression
Property expression
Static variable expression
Function call expression
Preinc/predec expression (but not postinc/postdec)
Assignment expression
Assignment op expression
Binding assignment expression
Include/require expression
Eval expression
Array expression
Array cast expression
4) For all other expressions, E behaves abnormally. This includes:
All binary operator expressions
All unary operator expressions except silence and preinc/predec
Scalar expression of type null, bool, int, double, or string
Qop expression (?:)
Constant expression
Class constant expression
Isset or empty expression
Exit expression
Instanceof expression
*/
static ListAssignment::RHSKind GetRHSKind(ExpressionPtr rhs) {
switch (rhs->getKindOf()) {
case Expression::KindOfSimpleVariable:
case Expression::KindOfDynamicVariable:
case Expression::KindOfArrayElementExpression:
case Expression::KindOfObjectPropertyExpression:
case Expression::KindOfStaticMemberExpression:
case Expression::KindOfSimpleFunctionCall:
case Expression::KindOfDynamicFunctionCall:
case Expression::KindOfObjectMethodExpression:
case Expression::KindOfNewObjectExpression:
case Expression::KindOfAssignmentExpression:
case Expression::KindOfIncludeExpression:
return ListAssignment::Regular;
case Expression::KindOfListAssignment:
return GetRHSKind(static_pointer_cast<ListAssignment>(rhs)->getArray());
case Expression::KindOfUnaryOpExpression: {
UnaryOpExpressionPtr u(static_pointer_cast<UnaryOpExpression>(rhs));
switch (u->getOp()) {
case '@':
return GetRHSKind(u->getExpression());
case T_INC:
case T_DEC:
return u->getFront() ?
ListAssignment::Regular : ListAssignment::Checked;
case T_EVAL:
case T_ARRAY:
case T_ARRAY_CAST:
return ListAssignment::Regular;
default:
return ListAssignment::Null;
}
break;
}
case Expression::KindOfBinaryOpExpression: {
BinaryOpExpressionPtr b(static_pointer_cast<BinaryOpExpression>(rhs));
return b->isAssignmentOp() || b->getOp() == '+' ?
ListAssignment::Regular : ListAssignment::Null;
}
case Expression::KindOfQOpExpression:
return ListAssignment::Checked;
default: break;
}
return ListAssignment::Null;
}
static bool AssignmentCouldSet(ExpressionListPtr vars, ExpressionPtr var) {
for (int i = 0; i < vars->getCount(); i++) {
ExpressionPtr v = (*vars)[i];
if (!v) continue;
if (v->is(Expression::KindOfSimpleVariable) &&
v->canonCompare(var)) {
return true;
}
if (v->is(Expression::KindOfDynamicVariable)) return true;
if (v->is(Expression::KindOfListAssignment) &&
AssignmentCouldSet(static_pointer_cast<ListAssignment>(v)->
getVariables(), var)) {
return true;
}
}
return false;
}
ListAssignment::ListAssignment
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionListPtr variables, ExpressionPtr array)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ListAssignment)),
m_variables(variables), m_array(array), m_rhsKind(Regular) {
setLValue();
if (m_array) {
m_rhsKind = GetRHSKind(m_array);
if (m_array->is(KindOfSimpleVariable)) {
if (AssignmentCouldSet(m_variables, m_array)) {
m_array->setContext(LValue);
}
}
}
}
ExpressionPtr ListAssignment::clone() {
ListAssignmentPtr exp(new ListAssignment(*this));
Expression::deepCopy(exp);
exp->m_variables = Clone(m_variables);
exp->m_array = Clone(m_array);
return exp;
}
void ListAssignment::setLValue() {
if (m_variables) {
for (int i = 0; i < m_variables->getCount(); i++) {
ExpressionPtr exp = (*m_variables)[i];
if (exp) {
if (exp->is(Expression::KindOfListAssignment)) {
ListAssignmentPtr sublist =
dynamic_pointer_cast<ListAssignment>(exp);
sublist->setLValue();
} else {
// Magic contexts I took from assignment expression
exp->setContext(Expression::DeepAssignmentLHS);
exp->setContext(Expression::AssignmentLHS);
exp->setContext(Expression::LValue);
exp->setContext(Expression::NoLValueWrapper);
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void ListAssignment::analyzeProgram(AnalysisResultPtr ar) {
if (m_variables) m_variables->analyzeProgram(ar);
if (m_array) m_array->analyzeProgram(ar);
FunctionScopePtr func = getFunctionScope();
if (func) func->disableInline();
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_variables) {
for (int i = 0; i < m_variables->getCount(); i++) {
ExpressionPtr exp = (*m_variables)[i];
if (exp) {
if (!exp->is(Expression::KindOfListAssignment)) {
CheckNeeded(exp, ExpressionPtr());
}
}
}
}
}
}
ConstructPtr ListAssignment::getNthKid(int n) const {
switch (n) {
case 0:
return m_variables;
case 1:
return m_array;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ListAssignment::getKidCount() const {
return 2;
}
void ListAssignment::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_variables = boost::dynamic_pointer_cast<ExpressionList>(cp);
break;
case 1:
m_array = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
TypePtr ListAssignment::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
if (m_variables) {
for (int i = m_variables->getCount(); i--; ) {
ExpressionPtr exp = (*m_variables)[i];
if (exp) {
if (exp->is(Expression::KindOfListAssignment)) {
exp->inferAndCheck(ar, Type::Any, false);
} else {
inferAssignmentTypes(ar, Type::Variant, true, exp);
}
}
}
}
if (!m_array) return TypePtr();
return m_array->inferAndCheck(ar, Type::Variant, false);
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ListAssignment::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("list(");
if (m_variables) m_variables->outputPHP(cg, ar);
if (m_array) {
cg_printf(") = ");
m_array->outputPHP(cg, ar);
} else {
cg_printf(")");
}
}
@@ -49,11 +49,6 @@ private:
RHSKind m_rhsKind;
void setLValue();
bool outputCPPAssignment(CodeGenerator &cg, AnalysisResultPtr ar,
const std::string &arrTmp, bool subRef);
void preOutputVariables(CodeGenerator &cg, AnalysisResultPtr ar, int state);
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
};
///////////////////////////////////////////////////////////////////////////////
@@ -134,20 +134,3 @@ void ModifierExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
}
}
}
void ModifierExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
/**
* Not sure if we can bar access coding errors...
if (isPublic()) {
cg_printf("public: ");
} else if (isProtected()) {
cg_printf("protected: ");
} else {
cg_printf("private: ");
}
*/
cg_printf("public: ");
if (isStatic()) cg_printf("static ");
}
@@ -0,0 +1,165 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/new_object_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/option.h>
#include <compiler/analysis/variable_table.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
NewObjectExpression::NewObjectExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr variable, ExpressionListPtr params)
: FunctionCall(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(NewObjectExpression),
variable, "", params, variable),
m_dynamic(false) {
/*
StaticClassName takes care of parent & self properly, so
we pass in variable as the class name.
But NewObjectExpression is written to use the class as the
function name, so clear it here, to take care of the dynamic
case.
Also set m_noStatic, to prevent errors in code gen due to
m_className being set.
*/
m_class.reset();
m_noStatic = true;
}
ExpressionPtr NewObjectExpression::clone() {
NewObjectExpressionPtr exp(new NewObjectExpression(*this));
FunctionCall::deepCopy(exp);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void NewObjectExpression::analyzeProgram(AnalysisResultPtr ar) {
FunctionCall::analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeAll ||
ar->getPhase() == AnalysisResult::AnalyzeFinal) {
FunctionScopePtr func;
if (!m_name.empty()) {
addUserClass(ar, m_name);
if (ClassScopePtr cls = resolveClass()) {
m_name = m_className;
func = cls->findConstructor(ar, true);
if (func) func->addNewObjCaller(getScope());
}
}
if (m_params) {
markRefParams(func, "", canInvokeFewArgs());
}
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
TypePtr at(getActualType());
if (at && at->isSpecificObject() && !getExpectedType()) {
setExpectedType(at);
}
}
}
}
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
reset();
m_classScope.reset();
FunctionScopePtr prev = m_funcScope;
m_funcScope.reset();
ConstructPtr self = shared_from_this();
if (!m_name.empty() && !isStatic()) {
ClassScopePtr cls = resolveClassWithChecks();
m_name = m_className;
if (!cls) {
if (m_params) m_params->inferAndCheck(ar, Type::Any, false);
return Type::Object;
}
if (getScope()->isFirstPass() &&
(cls->isTrait() ?
!isSelf() && !isParent() :
cls->isInterface() || cls->isAbstract())) {
Compiler::Error(Compiler::InvalidInstantiation, self);
}
if (cls->isVolatile() && !isPresent()) {
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
}
m_dynamic = cls->derivesFromRedeclaring();
bool valid = true;
FunctionScopePtr func = cls->findConstructor(ar, true);
if (!func) {
if (m_params) {
if (!m_dynamic && m_params->getCount()) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::BadConstructorCall, self);
}
m_params->setOutputCount(0);
}
m_params->inferAndCheck(ar, Type::Some, false);
}
} else {
if (func != prev) func->addNewObjCaller(getScope());
m_extraArg = func->inferParamTypes(ar, self, m_params, valid);
m_variableArgument = func->isVariableArgument();
}
if (valid) {
m_classScope = cls;
m_funcScope = func;
}
if (!valid || m_dynamic) {
m_implementedType = Type::Object;
} else {
m_implementedType.reset();
}
return Type::CreateObjectType(m_name);
} else {
if (m_params) {
m_params->markParams(canInvokeFewArgs());
}
}
m_implementedType.reset();
m_nameExp->inferAndCheck(ar, Type::String, false);
if (m_params) m_params->inferAndCheck(ar, Type::Any, false);
return Type::Object;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void NewObjectExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("new ");
m_nameExp->outputPHP(cg, ar);
cg_printf("(");
if (m_params) m_params->outputPHP(cg, ar);
cg_printf(")");
}
@@ -31,9 +31,7 @@ public:
DECLARE_BASE_EXPRESSION_VIRTUAL_FUNCTIONS;
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
virtual bool isRefable(bool checkError = false) const { return checkError; }
void preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar, int state);
bool isTemporary() const { return true; }
private:
bool m_dynamic;
@@ -0,0 +1,298 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/object_method_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/statement/statement.h>
#include <util/util.h>
#include <util/hash.h>
#include <compiler/option.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/parser/parser.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ObjectMethodExpression::ObjectMethodExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr object, ExpressionPtr method, ExpressionListPtr params)
: FunctionCall(
EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ObjectMethodExpression),
method, "", params, ExpressionPtr()),
m_object(object), m_bindClass(true) {
m_object->setContext(Expression::ObjectContext);
m_object->clearContext(Expression::LValue);
m_object->clearContext(Expression::AccessContext);
}
ExpressionPtr ObjectMethodExpression::clone() {
ObjectMethodExpressionPtr exp(new ObjectMethodExpression(*this));
FunctionCall::deepCopy(exp);
exp->m_object = Clone(m_object);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void ObjectMethodExpression::analyzeProgram(AnalysisResultPtr ar) {
FunctionCall::analyzeProgram(ar);
m_object->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
FunctionScopePtr func = m_funcScope;
if (!func && m_object->isThis() && !m_name.empty()) {
ClassScopePtr cls = getClassScope();
if (cls) {
m_classScope = cls;
func = cls->findFunction(ar, m_name, true, true);
if (func &&
!cls->isInterface() &&
!(func->isVirtual() &&
(func->isAbstract() ||
(func->hasOverride() &&
cls->getAttribute(ClassScope::NotFinal))) &&
!func->isPerfectVirtual())) {
m_funcScope = func;
func->addCaller(getScope());
}
}
}
markRefParams(func, m_name, canInvokeFewArgs());
}
// This is OK because AnalyzeFinal is guaranteed to run for a CPP
// target, regardless of opts (and we only need the following
// for CPP targets)
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
// necessary because we set the expected type of m_object to
// Type::Some during type inference.
TypePtr at(m_object->getActualType());
TypePtr it(m_object->getImplementedType());
if (!m_object->isThis() && at && at->is(Type::KindOfObject)) {
if (at->isSpecificObject() && it && Type::IsMappedToVariant(it)) {
// fast-cast inference
ClassScopePtr scope(ar->findClass(at->getName()));
if (scope) {
// add a dependency to m_object's class type
// to allow the fast cast to succeed
addUserClass(ar, at->getName());
}
}
m_object->setExpectedType(at);
}
}
}
ConstructPtr ObjectMethodExpression::getNthKid(int n) const {
if (!n) return m_object;
return FunctionCall::getNthKid(n);
}
void ObjectMethodExpression::setNthKid(int n, ConstructPtr cp) {
if (!n) {
m_object = boost::dynamic_pointer_cast<Expression>(cp);
} else {
FunctionCall::setNthKid(n, cp);
}
}
TypePtr ObjectMethodExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
assert(false);
return TypePtr();
}
void ObjectMethodExpression::setInvokeParams(AnalysisResultPtr ar) {
FunctionScope::FunctionInfoPtr info;
if (Option::WholeProgram) {
info = FunctionScope::GetFunctionInfo(m_name);
}
if (!Option::WholeProgram || info || m_name.empty()) {
for (int i = m_params->getCount(); i--; ) {
if (!info || info->isRefParam(i)) {
m_params->markParam(i, canInvokeFewArgs());
}
}
}
// If we cannot find information of the so-named function, it might not
// exist, or it might go through __call(), either of which cannot have
// reference parameters.
for (int i = 0; i < m_params->getCount(); i++) {
(*m_params)[i]->inferAndCheck(ar, Type::Variant, false);
}
m_params->resetOutputCount();
}
ExpressionPtr ObjectMethodExpression::preOptimize(AnalysisResultConstPtr ar) {
if (ar->getPhase() < AnalysisResult::FirstPreOptimize) {
return ExpressionPtr();
}
if (m_classScope && m_funcScope &&
(!m_funcScope->isVirtual() ||
(Option::WholeProgram && !m_funcScope->hasOverride()))) {
if (Option::DynamicInvokeFunctions.size()) {
if (Option::DynamicInvokeFunctions.find(
m_classScope->getName() + "::" + m_funcScope->getName()) !=
Option::DynamicInvokeFunctions.end()) {
setNoInline();
}
}
return inliner(ar, m_object, "");
}
return ExpressionPtr();
}
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
assert(type);
IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
resetTypes();
reset();
ConstructPtr self = shared_from_this();
TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);
m_valid = true;
m_bindClass = true;
if (m_name.empty()) {
m_nameExp->inferAndCheck(ar, Type::String, false);
setInvokeParams(ar);
// we have to use a variant to hold dynamic value
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
ClassScopePtr cls;
if (objectType && !objectType->getName().empty()) {
if (m_classScope && !strcasecmp(objectType->getName().c_str(),
m_classScope->getName().c_str())) {
cls = m_classScope;
} else {
cls = ar->findExactClass(shared_from_this(), objectType->getName());
}
}
if (!cls) {
m_classScope.reset();
m_funcScope.reset();
m_valid = false;
setInvokeParams(ar);
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
if (m_classScope != cls) {
m_classScope = cls;
m_funcScope.reset();
}
FunctionScopePtr func = m_funcScope;
if (!func) {
func = cls->findFunction(ar, m_name, true, true);
if (!func) {
if (!cls->isTrait() &&
!cls->getAttribute(ClassScope::MayHaveUnknownMethodHandler) &&
!cls->getAttribute(ClassScope::HasUnknownMethodHandler) &&
!cls->getAttribute(ClassScope::InheritsUnknownMethodHandler)) {
if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
if (!Option::AllDynamic) {
setDynamicByIdentifier(ar, m_name);
}
} else {
Compiler::Error(Compiler::UnknownObjectMethod, self);
}
}
m_valid = false;
setInvokeParams(ar);
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
m_funcScope = func;
func->addCaller(getScope(), !type->is(Type::KindOfAny));
}
bool valid = true;
m_bindClass = func->isStatic();
// use $this inside a static function
if (m_object->isThis()) {
FunctionScopePtr localfunc = getFunctionScope();
if (localfunc->isStatic()) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::MissingObjectContext, self);
}
valid = false;
}
}
// invoke() will return Variant
if (cls->isInterface() ||
(func->isVirtual() &&
(!Option::WholeProgram || func->isAbstract() ||
(func->hasOverride() && cls->getAttribute(ClassScope::NotFinal))) &&
!func->isPerfectVirtual())) {
valid = false;
}
if (!valid) {
setInvokeParams(ar);
checkTypesImpl(ar, type, Type::Variant, coerce);
m_valid = false; // so we use invoke() syntax
if (!Option::AllDynamic) {
func->setDynamic();
}
assert(m_actualType);
return m_actualType;
}
assert(func);
return checkParamsAndReturn(ar, type, coerce, func, false);
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ObjectMethodExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
m_object->outputPHP(cg, ar);
cg_printf("->");
if (m_nameExp->getKindOf() == Expression::KindOfScalarExpression) {
m_nameExp->outputPHP(cg, ar);
} else {
cg_printf("{");
m_nameExp->outputPHP(cg, ar);
cg_printf("}");
}
cg_printf("(");
m_params->outputPHP(cg, ar);
cg_printf(")");
}
@@ -38,9 +38,6 @@ public:
virtual TypePtr inferAndCheck(AnalysisResultPtr ar, TypePtr type,
bool coerce);
virtual bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state);
ExpressionPtr getObject() const { return m_object; }
private:
ExpressionPtr m_object;
@@ -50,9 +47,6 @@ private:
// for avoiding code generate toObject(Variant)
bool directVariantProxy(AnalysisResultPtr ar);
bool m_bindClass;
void outputCPPObject(CodeGenerator &cg, AnalysisResultPtr ar);
void outputCPPObjectCall(CodeGenerator &cg, AnalysisResultPtr ar);
};
///////////////////////////////////////////////////////////////////////////////
@@ -0,0 +1,359 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/object_property_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/option.h>
#include <compiler/expression/simple_variable.h>
#include <util/hash.h>
#include <util/parser/hphp.tab.hpp>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ObjectPropertyExpression::ObjectPropertyExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr object, ExpressionPtr property)
: Expression(
EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ObjectPropertyExpression)),
LocalEffectsContainer(AccessorEffect),
m_object(object), m_property(property), m_propSym(nullptr) {
m_valid = false;
m_propSymValid = false;
m_object->setContext(Expression::ObjectContext);
m_object->setContext(Expression::AccessContext);
}
ExpressionPtr ObjectPropertyExpression::clone() {
ObjectPropertyExpressionPtr exp(new ObjectPropertyExpression(*this));
Expression::deepCopy(exp);
exp->m_object = Clone(m_object);
exp->m_property = Clone(m_property);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
bool ObjectPropertyExpression::isTemporary() const {
return !m_valid && !(m_context & (LValue | RefValue | UnsetContext));
}
bool ObjectPropertyExpression::isNonPrivate(AnalysisResultPtr ar) {
// To tell whether a property is declared as private in the context
ClassScopePtr cls = getOriginalClass();
if (!cls || !cls->getVariables()->hasNonStaticPrivate()) return true;
if (m_property->getKindOf() != Expression::KindOfScalarExpression) {
return false;
}
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>(m_property);
string propName = name->getLiteralString();
if (propName.empty()) {
return false;
}
Symbol *sym = cls->getVariables()->getSymbol(propName);
if (!sym || sym->isStatic() || !sym->isPrivate()) return true;
return false;
}
void ObjectPropertyExpression::setContext(Context context) {
m_context |= context;
switch (context) {
case Expression::LValue:
if (!hasContext(Expression::UnsetContext)) {
m_object->setContext(Expression::LValue);
}
break;
case Expression::DeepAssignmentLHS:
case Expression::DeepOprLValue:
case Expression::ExistContext:
case Expression::UnsetContext:
case Expression::DeepReference:
case Expression::InvokeArgument:
m_object->setContext(context);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_object->setContext(DeepReference);
break;
default:
break;
}
if (!m_valid &&
(m_context & (LValue|RefValue)) &&
!(m_context & AssignmentLHS)) {
setLocalEffect(CreateEffect);
}
if (context == InvokeArgument) {
setContext(NoLValueWrapper);
}
}
void ObjectPropertyExpression::clearContext(Context context) {
m_context &= ~context;
switch (context) {
case Expression::LValue:
case Expression::DeepOprLValue:
case Expression::DeepAssignmentLHS:
case Expression::UnsetContext:
case Expression::DeepReference:
case Expression::InvokeArgument:
m_object->clearContext(context);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_object->clearContext(DeepReference);
break;
default:
break;
}
if (!(m_context & (LValue|RefValue))) {
clearLocalEffect(CreateEffect);
}
if (context == InvokeArgument) {
clearContext(NoLValueWrapper);
}
}
void ObjectPropertyExpression::analyzeProgram(AnalysisResultPtr ar) {
m_object->analyzeProgram(ar);
m_property->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_valid && !hasLocalEffect(UnknownEffect) &&
!m_object->isThis() &&
(!m_object->is(KindOfSimpleVariable) ||
!static_pointer_cast<SimpleVariable>(m_object)->isGuarded())) {
setLocalEffect(DiagnosticEffect);
}
if (FunctionScopePtr func = getFunctionScope()) {
if (!m_valid &&
m_context & (LValue|RefValue|DeepReference|UnsetContext)) {
func->setNeedsRefTemp();
}
if (getContext() & (RefValue | AssignmentLHS | OprLValue)) {
func->setNeedsCheckMem();
}
if (m_valid) func->setNeedsObjTemp();
}
}
}
ConstructPtr ObjectPropertyExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_object;
case 1:
return m_property;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ObjectPropertyExpression::getKidCount() const {
return 2;
}
void ObjectPropertyExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_object = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_property = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
m_valid = false;
ConstructPtr self = shared_from_this();
TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);
if (!m_property->is(Expression::KindOfScalarExpression)) {
m_property->inferAndCheck(ar, Type::String, false);
// we also lost track of which class variable an expression is about, hence
// any type inference could be wrong. Instead, we just force variants on
// all class variables.
if (m_context & (LValue | RefValue)) {
ar->forceClassVariants(getOriginalClass(), false, true);
}
return Type::Variant; // we have to use a variant to hold dynamic value
}
ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
const string &name = exp->getLiteralString();
if (name.empty()) {
m_property->inferAndCheck(ar, Type::String, false);
if (m_context & (LValue | RefValue)) {
ar->forceClassVariants(getOriginalClass(), false, true);
}
return Type::Variant; // we have to use a variant to hold dynamic value
}
m_property->inferAndCheck(ar, Type::String, false);
ClassScopePtr cls;
if (objectType && !objectType->getName().empty()) {
// what object-> has told us
cls = ar->findExactClass(shared_from_this(), objectType->getName());
} else {
if ((m_context & LValue) && objectType &&
!objectType->is(Type::KindOfObject) &&
!objectType->is(Type::KindOfVariant) &&
!objectType->is(Type::KindOfSome) &&
!objectType->is(Type::KindOfAny)) {
m_object->inferAndCheck(ar, Type::Object, true);
}
}
if (!cls) {
if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
ar->forceClassVariants(name, getOriginalClass(), false, true);
}
return Type::Variant;
}
// resolved to this class
if (m_context & RefValue) {
type = Type::Variant;
coerce = true;
}
// use $this inside a static function
if (m_object->isThis()) {
FunctionScopePtr func = m_object->getOriginalFunction();
if (!func || func->isStatic()) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::MissingObjectContext, self);
}
m_actualType = Type::Variant;
return m_actualType;
}
}
assert(cls);
if (!m_propSym || cls != m_objectClass.lock()) {
m_objectClass = cls;
ClassScopePtr parent;
m_propSym = cls->findProperty(parent, name, ar);
if (m_propSym) {
if (!parent) {
parent = cls;
}
m_symOwner = parent;
always_assert(m_propSym->isPresent());
m_propSymValid =
(!m_propSym->isPrivate() || getOriginalClass() == parent) &&
!m_propSym->isStatic();
if (m_propSymValid) {
m_symOwner->addUse(getScope(),
BlockScope::GetNonStaticRefUseKind(
m_propSym->getHash()));
}
}
}
TypePtr ret;
if (m_propSymValid && (!cls->derivesFromRedeclaring() ||
m_propSym->isPrivate())) {
always_assert(m_symOwner);
TypePtr t(m_propSym->getType());
if (t && t->is(Type::KindOfVariant)) {
// only check property if we could possibly do some work
ret = t;
} else {
if (coerce && type->is(Type::KindOfAutoSequence) &&
(!t || t->is(Type::KindOfVoid) ||
t->is(Type::KindOfSome) || t->is(Type::KindOfArray))) {
type = Type::Array;
}
assert(getScope()->is(BlockScope::FunctionScope));
GET_LOCK(m_symOwner);
ret = m_symOwner->checkProperty(getScope(), m_propSym, type, coerce, ar);
}
always_assert(m_object->getActualType() &&
m_object->getActualType()->isSpecificObject());
m_valid = true;
return ret;
} else {
m_actualType = Type::Variant;
return m_actualType;
}
}
ExpressionPtr
ObjectPropertyExpression::postOptimize(AnalysisResultConstPtr ar) {
bool changed = false;
if (m_objectClass && hasLocalEffect(AccessorEffect)) {
int prop = hasContext(AssignmentLHS) ?
ClassScope::MayHaveUnknownPropSetter :
hasContext(ExistContext) ?
ClassScope::MayHaveUnknownPropTester :
hasContext(UnsetContext) && hasContext(LValue) ?
ClassScope::MayHavePropUnsetter :
ClassScope::MayHaveUnknownPropGetter;
if ((m_context & (AssignmentLHS|OprLValue)) ||
!m_objectClass->implementsAccessor(prop)) {
clearLocalEffect(AccessorEffect);
changed = true;
}
}
if (m_valid &&
(hasLocalEffect(AccessorEffect) || hasLocalEffect(CreateEffect))) {
clearLocalEffect(AccessorEffect);
clearLocalEffect(CreateEffect);
changed = true;
}
return changed ?
dynamic_pointer_cast<Expression>(shared_from_this()) :
ExpressionPtr();
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ObjectPropertyExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
m_object->outputPHP(cg, ar);
cg_printf("->");
if (m_property->getKindOf() == Expression::KindOfScalarExpression) {
m_property->outputPHP(cg, ar);
} else {
cg_printf("{");
m_property->outputPHP(cg, ar);
cg_printf("}");
}
}

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