Comparar commits
347 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| a3803ffa8a | |||
| a85369c7ce | |||
| 1cea03f3f7 | |||
| 52db7956bb | |||
| f6f1655ca5 | |||
| 3fa077dd6f | |||
| 4caa51283a | |||
| 4f70af91f7 | |||
| 8a0dd4fae4 | |||
| afd5f72964 | |||
| ec6617f6bd | |||
| 964de35b10 | |||
| 819058d081 | |||
| e6542d070a | |||
| 83bd8ff445 | |||
| cf10678119 | |||
| 5226f80d05 | |||
| a2ffdae2cf | |||
| 785cf27e00 | |||
| b32b2ec879 | |||
| 1f638e631c | |||
| 1ab14f87af | |||
| fefd56e9d7 | |||
| 17049b3a42 | |||
| be4c7698eb | |||
| 0dcbc83f74 | |||
| d660972153 | |||
| ca437add64 | |||
| 3611bf3d58 | |||
| 565c8477f8 | |||
| 8f3512f6d3 | |||
| 7241c247dd | |||
| f06575d4e2 | |||
| cc897749f8 | |||
| b597e3a753 | |||
| 8ec621676d | |||
| a134b25671 | |||
| 15fa1ebfde | |||
| ce75713972 | |||
| adc56c6d8c | |||
| 3fb0fe8a6a | |||
| 38306b527f | |||
| e4e3bee5d3 | |||
| d25adc550b | |||
| 8b23419a37 | |||
| af5623b4fc | |||
| b51003b5aa | |||
| 5f019f5b43 | |||
| 35e347efa5 | |||
| 6169f73d80 | |||
| 96350d1a66 | |||
| f51b3c302b | |||
| 6366959956 | |||
| a7a3c2e8cc | |||
| 9df492da6a | |||
| 8b36e4b067 | |||
| 518ae943da | |||
| f5b168643d | |||
| 4b075be2ee | |||
| 51f986a0ae | |||
| e8d372f813 | |||
| 6ddef58d56 | |||
| 6434422e85 | |||
| aa3124d64d | |||
| b4085ff17b | |||
| 1bcb5de881 | |||
| b5bebbb8b7 | |||
| 20ac715ec2 | |||
| a95a84390e | |||
| e0d098ef70 | |||
| e3c7403906 | |||
| dd83c93a8a | |||
| 9c470aa8ed | |||
| 1569061e8c | |||
| e10f38e7fa | |||
| ba0cc6be65 | |||
| 79af6e51fa | |||
| 5ba5cb3761 | |||
| 089caca5ea | |||
| 23b681e872 | |||
| f1a3410eb5 | |||
| 4a3a5ccc6a | |||
| 2776ebd221 | |||
| 21dab1f587 | |||
| a06a7e07af | |||
| c1c68516c1 | |||
| 477f8330d4 | |||
| 71ee3ea273 | |||
| 60c61ee3e1 | |||
| f10ecaf20d | |||
| ea782cbd3e | |||
| 7fe1c94a7c | |||
| 085040d70f | |||
| 3ea0d8b8e0 | |||
| fd2f07d1f1 | |||
| 3e8cbc356f | |||
| 10f9f6b239 | |||
| a611ac930d | |||
| 94f8158ca5 | |||
| 3c20163337 | |||
| a9926b46d2 | |||
| 26178124a4 | |||
| c7bb7d8eb6 | |||
| 05f5808d81 | |||
| 8e30d1c315 | |||
| 3887bec93f | |||
| 2fb7b0e279 | |||
| fea70b0391 | |||
| a4c6fec8e9 | |||
| f6ac148c09 | |||
| 95fc3dc1c9 | |||
| 2ae5ebf3f5 | |||
| c9f6d8aeb9 | |||
| 6aa91b97c8 | |||
| 830c48eb8a | |||
| e7a57f89c8 | |||
| e5469c9b19 | |||
| 96235149fb | |||
| c59f2ae71f | |||
| 4eaf40d7aa | |||
| 73ebbc2160 | |||
| 181f341d00 | |||
| b9e14c448c | |||
| 2fe9a69916 | |||
| 34ba5330d5 | |||
| 01b29a87df | |||
| cf41a82f42 | |||
| 65119c99a0 | |||
| 10c79adeba | |||
| 35e456c966 | |||
| fd49414b4d | |||
| 6850f3fa89 | |||
| 4286a730c5 | |||
| 6c87ecb74e | |||
| 7eeb594316 | |||
| ef70ffc4e0 | |||
| 1811ef243f | |||
| bdb5a88268 | |||
| 8be4799db9 | |||
| 2f3f5f3c5a | |||
| 3b3f9aa449 | |||
| 7e321f8d5a | |||
| 951d7c0adf | |||
| 7ff582616f | |||
| c4c4404aa9 | |||
| a8e7668189 | |||
| 18a45ba0d0 | |||
| a6104b9d6e | |||
| e355d3de17 | |||
| 99bfd8e9ee | |||
| 30fcc6a0d1 | |||
| 61d3e28392 | |||
| 21c4fbdbf3 | |||
| 33e81ef7bb | |||
| e518130c7b | |||
| 5848c633c3 | |||
| 9be42ffc84 | |||
| e150671d40 | |||
| c9ae7340cf | |||
| a7c629366f | |||
| 45486a934f | |||
| 92c74b141a | |||
| 45a794d7dd | |||
| 883a11fbef | |||
| c6475efbec | |||
| a1baf2d540 | |||
| 0562656c5b | |||
| c1e71c1917 | |||
| 668a9c1da2 | |||
| f4551719a9 | |||
| 3f0a4f7ece | |||
| ecdd06cebe | |||
| 5744173e90 | |||
| 0f3ba86d84 | |||
| 37d974ef83 | |||
| 7f43d78955 | |||
| 1f68fd307f | |||
| 3e12490127 | |||
| 334a83f7b6 | |||
| 8c2a7af8d4 | |||
| b40c724281 | |||
| 157b59cdac | |||
| 7969b8f2ee | |||
| addb01eea4 | |||
| 5eac64e8bd | |||
| 193ddf4a60 | |||
| 3f61cabef2 | |||
| bffeb65b84 | |||
| 6aa9f21425 | |||
| de1249af6e | |||
| f5c0cd21ae | |||
| 9f21844eb5 | |||
| 8c6d77deef | |||
| e5663bd9ec | |||
| 7917779c18 | |||
| a395e9e3dd | |||
| 27d247e976 | |||
| af3c7ac8df | |||
| 7c1503e778 | |||
| ab71559f5a | |||
| aac3f2bfd5 | |||
| 296b2baede | |||
| 2ef2187778 | |||
| e30755ddbe | |||
| bc085bbcf6 | |||
| 09b97f8336 | |||
| 991ca4e231 | |||
| 449ebdc43f | |||
| 6ab2ea2218 | |||
| 0b27530c65 | |||
| cd7b28ad6a | |||
| 943b945e1b | |||
| ab6a6508fb | |||
| c1b7da6a4c | |||
| 1d6df52d09 | |||
| 4507c61901 | |||
| 7f6b1532dc | |||
| 12471034e5 | |||
| 28fc7a3a45 | |||
| a9a021e739 | |||
| 812070e82d | |||
| 8d86116117 | |||
| 520ef90a75 | |||
| c3cd188c6f | |||
| c1a8c937f1 | |||
| 35842e9141 | |||
| fc5b95110f | |||
| 9cb2d7af85 | |||
| 6a311f2fab | |||
| b3b41e08bb | |||
| 3e24e506f4 | |||
| ef1d4c9c1e | |||
| cee31f4412 | |||
| 65d2487ab7 | |||
| 67013476b5 | |||
| 2b3ae234fb | |||
| 6fed85fbb4 | |||
| bd2e014e8f | |||
| c8cc07a61c | |||
| 26c4d57cfa | |||
| f58323820c | |||
| 2f9f1a705b | |||
| 387a3555e2 | |||
| 8a119496e1 | |||
| 5662a67f12 | |||
| ac63ae02c6 | |||
| f3a6aaee37 | |||
| bbdf9729fb | |||
| 09e01875a0 | |||
| 26a6669575 | |||
| 162b46fb99 | |||
| f5188b8fe9 | |||
| 0f9c93f9f9 | |||
| 5ed74f1e03 | |||
| d9943ae3c5 | |||
| f109b02c7d | |||
| ac0ec5696a | |||
| 64b6d99ef2 | |||
| e300bc8cce | |||
| bc6703d03f | |||
| aee58ac19e | |||
| c375ba70b0 | |||
| 1643cb1d17 | |||
| 51f0a5bd3b | |||
| 8572bdeb7f | |||
| cf7133b406 | |||
| a44b190095 | |||
| e69e05711a | |||
| 3cf77e2df9 | |||
| 7fd841e6c1 | |||
| f3ddbe63f1 | |||
| 1e4b0ed05b | |||
| 2406e11d44 | |||
| 1e54607680 | |||
| 28116234fb | |||
| 337f15cc8d | |||
| a18dd73d06 | |||
| 067e38c23d | |||
| 2db86bd721 | |||
| 527a155239 | |||
| 1bc06a26cf | |||
| 76681bfc14 | |||
| 740868989c | |||
| 26c2654528 | |||
| bab632f35a | |||
| 51ffd22f03 | |||
| 03c8d47acb | |||
| a4903eb14e | |||
| 9efb3c8e25 | |||
| c21f2c3966 | |||
| 3a5087077f | |||
| dc8cc4656b | |||
| 00b2101db0 | |||
| 3248a83461 | |||
| 14c9e841c8 | |||
| 839bb3103e | |||
| dec99505a6 | |||
| f3cf268bb1 | |||
| 5455370de0 | |||
| 116bb2c662 | |||
| cc5cc6d5cb | |||
| cc863bf301 | |||
| e09dbb6f89 | |||
| 5acac417e2 | |||
| b17ec4702a | |||
| f6efec4d7c | |||
| 75948e48f7 | |||
| 3286e1d7c0 | |||
| d6be84e026 | |||
| 0763e566f4 | |||
| 8c57d016d5 | |||
| 44dc3b58f4 | |||
| b24236e602 | |||
| dbab820ff4 | |||
| 441fb578ec | |||
| 755d38157f | |||
| 4196354f4f | |||
| f7dd399bd6 | |||
| 0b586489c9 | |||
| 31222e2c84 | |||
| d87811fd9b | |||
| 6492f650f5 | |||
| d09fd3e421 | |||
| 5baef2646d | |||
| 22541f491c | |||
| a4b2eea173 | |||
| e3d0c97679 | |||
| 795400d899 | |||
| 5483751a25 | |||
| 4797536f9d | |||
| c67d35c50a | |||
| 453c9938c9 | |||
| 3e19c90b93 | |||
| bed8c0e6fe | |||
| 1b83a11ee8 | |||
| 94081c2cbc | |||
| c7959bbe6a | |||
| 94aed72d26 | |||
| 6ac06a50eb | |||
| 5e7c9d3457 | |||
| 1eeb100510 | |||
| 6a4ec5ae0e | |||
| 52697c84a0 | |||
| c4956fac10 | |||
| ddd8720bf4 | |||
| 84d9aa7236 | |||
| 363d1bb20f |
+37
-78
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
}
|
||||
|
||||
@@ -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 ()
|
||||
+18
-17
@@ -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
@@ -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
@@ -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;
|
||||
@@ -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,
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
+10
-105
@@ -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;
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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("]");
|
||||
}
|
||||
}
|
||||
-5
@@ -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:
|
||||
-44
@@ -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;
|
||||
}
|
||||
-5
@@ -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);
|
||||
}
|
||||
-7
@@ -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;
|
||||
}
|
||||
|
||||
-7
@@ -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);
|
||||
}
|
||||
+29
-75
@@ -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("))");
|
||||
}
|
||||
+4
-1
@@ -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);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
+2
-56
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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(")");
|
||||
}
|
||||
-1
@@ -32,7 +32,6 @@ public:
|
||||
|
||||
DECLARE_BASE_EXPRESSION_VIRTUAL_FUNCTIONS;
|
||||
ExpressionPtr preOptimize(AnalysisResultConstPtr ar);
|
||||
bool preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
-6
@@ -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(")");
|
||||
}
|
||||
-52
@@ -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(")");
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
-17
@@ -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;
|
||||
+2
-183
@@ -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;
|
||||
+1
-51
@@ -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());
|
||||
}
|
||||
@@ -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(")");
|
||||
}
|
||||
}
|
||||
-5
@@ -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);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
-17
@@ -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(")");
|
||||
}
|
||||
-2
@@ -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(")");
|
||||
}
|
||||
-6
@@ -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
Referência em uma Nova Issue
Bloquear um usuário