Implement most of class_alias
Throws the aliased class into a target cache slot for the new
name. Handles errors when you try to re-alias a class, but doesn't
restrict a few other cases zend does:
- If you implement an interface twice, zend complains (one of the
alias tests checks this). I tried turning it on, but we violate
it in systemlib currently so I left it off.
- class_alias_014.php does some namespace stuff I don't quite grok.
(@ptarjan let me know what to do if it's easy).
- inter_007.php uses class_alias, but is testing a warning that
happens even with out it. (We don't raise this warning.)
- zend raises a warning if you try to class_alias a non-user-defined
class; I left this out.
Esse commit está contido em:
@@ -408,7 +408,9 @@ static bool by_source(const BlockScopePtr &b1, const BlockScopePtr &b2) {
|
||||
void AnalysisResult::canonicalizeSymbolOrder() {
|
||||
getConstants()->canonicalizeSymbolOrder();
|
||||
getVariables()->canonicalizeSymbolOrder();
|
||||
}
|
||||
|
||||
void AnalysisResult::markRedeclaringClasses() {
|
||||
AnalysisResultPtr ar = shared_from_this();
|
||||
for (StringToClassScopePtrVecMap::iterator iter = m_classDecs.begin();
|
||||
iter != m_classDecs.end(); ++iter) {
|
||||
@@ -420,6 +422,43 @@ void AnalysisResult::canonicalizeSymbolOrder() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In WholeProgram mode, during parse time we collected all
|
||||
* class_alias calls so we can mark the targets of such calls
|
||||
* redeclaring if necessary.
|
||||
*
|
||||
* Two cases here that definitely require this:
|
||||
*
|
||||
* - If an alias name has the same name as another class, we need
|
||||
* to mark *that* class as redeclaring, since it may mean
|
||||
* different things in different requests now.
|
||||
*
|
||||
* - If an alias name can refer to more than one class, each of
|
||||
* those classes must be marked redeclaring.
|
||||
*
|
||||
* In the simple case of a unique alias name and a unique target
|
||||
* name, we might be able to get away with manipulating the target
|
||||
* classes' volatility.
|
||||
*
|
||||
* Rather than work through the various cases here, though, we've
|
||||
* just decided to just play it safe and mark all the names involved
|
||||
* as redeclaring for now.
|
||||
*/
|
||||
for (auto& kv : m_classAliases) {
|
||||
auto markRedeclaring = [&] (const std::string& name) {
|
||||
auto it = m_classDecs.find(name);
|
||||
if (it != m_classDecs.end()) {
|
||||
auto& classes = it->second;
|
||||
for (unsigned int i = 0; i < classes.size(); ++i) {
|
||||
classes[i]->setRedeclaring(ar, i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
markRedeclaring(Util::toLower(kv.first));
|
||||
markRedeclaring(Util::toLower(kv.second));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -606,6 +645,9 @@ void AnalysisResult::collectFunctionsAndClasses(FileScopePtr fs) {
|
||||
ClassScopePtrVec &clsVec = m_classDecs[iter->first];
|
||||
clsVec.insert(clsVec.end(), iter->second.begin(), iter->second.end());
|
||||
}
|
||||
|
||||
m_classAliases.insert(fs->getClassAliases().begin(),
|
||||
fs->getClassAliases().end());
|
||||
}
|
||||
|
||||
static bool by_filename(const FileScopePtr &f1, const FileScopePtr &f2) {
|
||||
@@ -631,6 +673,8 @@ void AnalysisResult::analyzeProgram(bool system /* = false */) {
|
||||
// Keep generated code identical without randomness
|
||||
canonicalizeSymbolOrder();
|
||||
|
||||
markRedeclaringClasses();
|
||||
|
||||
// Analyze some special cases
|
||||
for (set<string>::const_iterator it = Option::VolatileClasses.begin();
|
||||
it != Option::VolatileClasses.end(); ++it) {
|
||||
@@ -800,8 +844,14 @@ void AnalysisResult::analyzeProgramFinal() {
|
||||
for (uint i = 0; i < m_fileScopes.size(); i++) {
|
||||
m_fileScopes[i]->analyzeProgram(ar);
|
||||
}
|
||||
|
||||
// Keep generated code identical without randomness
|
||||
canonicalizeSymbolOrder();
|
||||
|
||||
// XXX: this is only here because canonicalizeSymbolOrder used to do
|
||||
// it---is it necessary to repeat at this phase? (Probably not ...)
|
||||
markRedeclaringClasses();
|
||||
|
||||
setPhase(AnalysisResult::CodeGen);
|
||||
}
|
||||
|
||||
|
||||
@@ -300,6 +300,7 @@ public:
|
||||
void addNamedScalarVarArray(const std::string &s);
|
||||
StringToClassScopePtrVecMap getExtensionClasses();
|
||||
void addInteger(int64_t n);
|
||||
|
||||
private:
|
||||
Package *m_package;
|
||||
bool m_parseOnDemand;
|
||||
@@ -320,6 +321,10 @@ private:
|
||||
StringToFileScopePtrMap m_constDecs;
|
||||
std::set<std::string> m_constRedeclared;
|
||||
|
||||
// Map names of class aliases to the class names they will alias.
|
||||
// Only in WholeProgram mode. See markRedeclaringClasses.
|
||||
std::multimap<std::string,std::string> m_classAliases;
|
||||
|
||||
bool m_classForcedVariants[2];
|
||||
|
||||
StatementPtrVec m_stmts;
|
||||
@@ -353,6 +358,11 @@ private:
|
||||
*/
|
||||
bool inParseOnDemandDirs(const std::string &filename) const;
|
||||
|
||||
/*
|
||||
* Find the names of all functions and classes in the program; mark
|
||||
* functions with duplicate names as redeclaring, but duplicate
|
||||
* classes aren't yet marked. See markRedeclaringClasses.
|
||||
*/
|
||||
void collectFunctionsAndClasses(FileScopePtr fs);
|
||||
|
||||
/**
|
||||
@@ -361,6 +371,13 @@ private:
|
||||
*/
|
||||
void canonicalizeSymbolOrder();
|
||||
|
||||
/*
|
||||
* After all the class names have been collected and symbol order is
|
||||
* canonicalized, this passes through and marks duplicate class
|
||||
* names as redeclaring.
|
||||
*/
|
||||
void markRedeclaringClasses();
|
||||
|
||||
/**
|
||||
* Checks circular class derivations that can cause stack overflows for
|
||||
* subsequent analysis. Also checks to make sure no two redundant parents.
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef incl_HPHP_FILE_SCOPE_H_
|
||||
#define incl_HPHP_FILE_SCOPE_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "hphp/compiler/analysis/block_scope.h"
|
||||
#include "hphp/compiler/analysis/function_container.h"
|
||||
#include "hphp/compiler/analysis/code_error.h"
|
||||
@@ -135,6 +137,14 @@ public:
|
||||
void addConstantDependency(AnalysisResultPtr ar,
|
||||
const std::string &decname);
|
||||
|
||||
void addClassAlias(const std::string& target, const std::string& alias) {
|
||||
m_classAliasMap.insert(std::make_pair(target, alias));
|
||||
}
|
||||
|
||||
std::multimap<std::string,std::string> const& getClassAliases() const {
|
||||
return m_classAliasMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only by World
|
||||
*/
|
||||
@@ -180,6 +190,7 @@ public:
|
||||
return boost::static_pointer_cast<FileScope>
|
||||
(BlockScope::shared_from_this());
|
||||
}
|
||||
|
||||
private:
|
||||
int m_size;
|
||||
MD5 m_md5;
|
||||
@@ -202,6 +213,10 @@ private:
|
||||
BlockScopeSet m_providedDefs;
|
||||
std::set<std::string> m_redecBases;
|
||||
|
||||
// Map from class alias names to the class they are aliased to.
|
||||
// This is only needed in WholeProgram mode.
|
||||
std::multimap<std::string,std::string> m_classAliasMap;
|
||||
|
||||
FunctionScopePtr createPseudoMain(AnalysisResultConstPtr ar);
|
||||
void setFileLevel(StatementListPtr stmt);
|
||||
};
|
||||
|
||||
@@ -53,6 +53,7 @@ void SimpleFunctionCall::InitFunctionTypeMap() {
|
||||
if (FunctionTypeMap.empty()) {
|
||||
FunctionTypeMap["define"] = FunType::Define;
|
||||
FunctionTypeMap["create_function"] = FunType::Create;
|
||||
FunctionTypeMap["class_alias"] = FunType::ClassAlias;
|
||||
|
||||
FunctionTypeMap["func_get_arg"] = FunType::VariableArgument;
|
||||
FunctionTypeMap["func_get_args"] = FunType::VariableArgument;
|
||||
@@ -183,6 +184,7 @@ void SimpleFunctionCall::mungeIfSpecialFunction(AnalysisResultConstPtr ar,
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FunType::Create:
|
||||
if (Option::ParseTimeOpts &&
|
||||
m_params->getCount() == 2 &&
|
||||
@@ -197,6 +199,25 @@ void SimpleFunctionCall::mungeIfSpecialFunction(AnalysisResultConstPtr ar,
|
||||
ar->appendExtraCode(fs->getName(), code);
|
||||
}
|
||||
break;
|
||||
|
||||
// The class_alias builtin can create new names for other classes;
|
||||
// we need to mark some of these classes redeclaring to avoid
|
||||
// making incorrect assumptions during WholeProgram mode. See
|
||||
// AnalysisResult::collectFunctionsAndClasses.
|
||||
case FunType::ClassAlias:
|
||||
if ((m_params->getCount() == 2 || m_params->getCount() == 3) &&
|
||||
Option::WholeProgram) {
|
||||
if (!(*m_params)[0]->isLiteralString() ||
|
||||
!(*m_params)[1]->isLiteralString()) {
|
||||
parseTimeFatal(Compiler::NoError,
|
||||
"class_alias with non-literal parameters is not allowed when "
|
||||
"WholeProgram optimizations are turned on");
|
||||
}
|
||||
fs->addClassAlias((*m_params)[0]->getLiteralString(),
|
||||
(*m_params)[1]->getLiteralString());
|
||||
}
|
||||
break;
|
||||
|
||||
case FunType::VariableArgument:
|
||||
/*
|
||||
Note:
|
||||
@@ -209,10 +230,12 @@ void SimpleFunctionCall::mungeIfSpecialFunction(AnalysisResultConstPtr ar,
|
||||
*/
|
||||
fs->setAttribute(FileScope::VariableArgument);
|
||||
break;
|
||||
|
||||
case FunType::Extract:
|
||||
fs->setAttribute(FileScope::ContainsLDynamicVariable);
|
||||
fs->setAttribute(FileScope::ContainsExtract);
|
||||
break;
|
||||
|
||||
case FunType::Compact: {
|
||||
// If all the parameters in the compact() call are statically known,
|
||||
// there is no need to create a variable table.
|
||||
@@ -229,13 +252,16 @@ void SimpleFunctionCall::mungeIfSpecialFunction(AnalysisResultConstPtr ar,
|
||||
fs->setAttribute(FileScope::ContainsCompact);
|
||||
break;
|
||||
}
|
||||
|
||||
case FunType::GetDefinedVars:
|
||||
fs->setAttribute(FileScope::ContainsDynamicVariable);
|
||||
fs->setAttribute(FileScope::ContainsGetDefinedVars);
|
||||
fs->setAttribute(FileScope::ContainsCompact);
|
||||
break;
|
||||
|
||||
case FunType::Unknown:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ protected:
|
||||
GetDefinedVars,
|
||||
FBCallUserFuncSafe,
|
||||
ThrowFatal,
|
||||
ClassAlias,
|
||||
};
|
||||
|
||||
static std::map<std::string,FunType> FunctionTypeMap;
|
||||
|
||||
@@ -42,6 +42,35 @@
|
||||
"args": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "class_alias",
|
||||
"desc": "Creates an alias named alias based on the defined class original. The aliased class is exactly the same as the original class.",
|
||||
"flags": [
|
||||
"HasDocComment"
|
||||
],
|
||||
"return": {
|
||||
"type": "Boolean",
|
||||
"desc": "Returns TRUE on success or FALSE on failure."
|
||||
},
|
||||
"args": [
|
||||
{
|
||||
"name": "original",
|
||||
"type": "String",
|
||||
"desc": "The original class."
|
||||
},
|
||||
{
|
||||
"name": "alias",
|
||||
"type": "String",
|
||||
"desc": "The alias name for the class."
|
||||
},
|
||||
{
|
||||
"name": "autoload",
|
||||
"type": "Boolean",
|
||||
"value": "true",
|
||||
"desc": "Whether do autoload if the original class is not found."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "class_exists",
|
||||
"desc": "This function checks whether or not the given class has been defined.",
|
||||
@@ -383,4 +412,4 @@
|
||||
],
|
||||
"classes": [
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "hphp/runtime/base/class_info.h"
|
||||
#include "hphp/runtime/vm/jit/translator.h"
|
||||
#include "hphp/runtime/vm/jit/translator-inline.h"
|
||||
#include "hphp/runtime/vm/unit.h"
|
||||
#include "hphp/util/util.h"
|
||||
|
||||
namespace HPHP {
|
||||
@@ -66,6 +67,19 @@ Array f_get_declared_traits() {
|
||||
return ClassInfo::GetTraits();
|
||||
}
|
||||
|
||||
bool f_class_alias(CStrRef original,
|
||||
CStrRef alias,
|
||||
bool autoload /* = true */) {
|
||||
auto const origClass =
|
||||
autoload ? Unit::loadClass(original.get())
|
||||
: Unit::lookupClass(original.get());
|
||||
if (!origClass) {
|
||||
raise_warning("Class %s not found", original->data());
|
||||
return false;
|
||||
}
|
||||
return Unit::aliasClass(origClass, alias.get());
|
||||
}
|
||||
|
||||
bool f_class_exists(CStrRef class_name, bool autoload /* = true */) {
|
||||
return Unit::classExists(class_name.get(), autoload, AttrNone);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace HPHP {
|
||||
Array f_get_declared_classes();
|
||||
Array f_get_declared_interfaces();
|
||||
Array f_get_declared_traits();
|
||||
bool f_class_alias(CStrRef original, CStrRef alias, bool autoload = true);
|
||||
bool f_class_exists(CStrRef class_name, bool autoload = true);
|
||||
bool f_interface_exists(CStrRef interface_name, bool autoload = true);
|
||||
bool f_trait_exists(CStrRef trait_name, bool autoload = true);
|
||||
|
||||
@@ -628,6 +628,26 @@ Class* Unit::defClass(const PreClass* preClass,
|
||||
}
|
||||
}
|
||||
|
||||
bool Unit::aliasClass(Class* original, const StringData* alias) {
|
||||
auto const aliasNe = Unit::GetNamedEntity(alias);
|
||||
|
||||
if (!aliasNe->m_cachedClassOffset) {
|
||||
Lock lk(s_classesMutex);
|
||||
if (!aliasNe->m_cachedClassOffset) {
|
||||
aliasNe->m_cachedClassOffset =
|
||||
Transl::TargetCache::allocKnownClass(alias);
|
||||
}
|
||||
}
|
||||
|
||||
auto const aliasClass = aliasNe->getCachedClass();
|
||||
if (aliasClass) {
|
||||
raise_warning("Cannot redeclare class %s", alias->data());
|
||||
return false;
|
||||
}
|
||||
aliasNe->setCachedClass(original);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unit::defTypedef(Id id) {
|
||||
assert(id < m_typedefs.size());
|
||||
auto thisType = &m_typedefs[id];
|
||||
|
||||
@@ -470,6 +470,7 @@ struct Unit {
|
||||
|
||||
static Class* defClass(const HPHP::PreClass* preClass,
|
||||
bool failIsFatal = true);
|
||||
static bool aliasClass(Class* original, const StringData* alias);
|
||||
void defTypedef(Id id);
|
||||
|
||||
static TypedValue* lookupCns(const StringData* cnsName);
|
||||
|
||||
@@ -12198,6 +12198,14 @@ const char *g_class_map[] = {
|
||||
(const char *)0x20 /* KindOfArray */, NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
(const char *)0x10006040, "class_alias", "", (const char*)0, (const char*)0,
|
||||
"/**\n * ( excerpt from http://php.net/manual/en/function.class-alias.php )\n *\n * Creates an alias named alias based on the defined class original. The\n * aliased class is exactly the same as the original class.\n *\n * @original string The original class.\n * @alias string The alias name for the class.\n * @autoload bool Whether do autoload if the original class is not\n * found.\n *\n * @return bool Returns TRUE on success or FALSE on failure.\n */",
|
||||
(const char *)0x9 /* KindOfBoolean */, (const char *)0x2000, "original", "", (const char *)0x14 /* KindOfString */, "", (const char *)0, "", (const char *)0, NULL,
|
||||
(const char *)0x2000, "alias", "", (const char *)0x14 /* KindOfString */, "", (const char *)0, "", (const char *)0, NULL,
|
||||
(const char *)0x2000, "autoload", "", (const char *)0x9 /* KindOfBoolean */, "b:1;", (const char *)4, "true", (const char *)4, NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
(const char *)0x10006040, "class_exists", "", (const char*)0, (const char*)0,
|
||||
"/**\n * ( excerpt from http://php.net/manual/en/function.class-exists.php )\n *\n * This function checks whether or not the given class has been defined.\n *\n * @class_name string The class name. The name is matched in a\n * case-insensitive manner.\n * @autoload bool Whether or not to call __autoload by default.\n *\n * @return bool Returns TRUE if class_name is a defined class, FALSE\n * otherwise.\n */",
|
||||
(const char *)0x9 /* KindOfBoolean */, (const char *)0x2000, "class_name", "", (const char *)0x14 /* KindOfString */, "", (const char *)0, "", (const char *)0, NULL,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-vErrorHandling.NoticeFrequency=1 -vErrorHandling.WarningFrequency=1 -vLog.RuntimeErrorReportingLevel=10
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
// Class alias of undefined class:
|
||||
class_alias('a', 'b');
|
||||
@@ -0,0 +1 @@
|
||||
HipHop Warning: Class a not found in %s on line 4
|
||||
Link simbólico
+1
@@ -0,0 +1 @@
|
||||
../warnings.opts
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
// With autoload, no warning.
|
||||
$i = 0;
|
||||
function __autoload($k) {
|
||||
if ($GLOBALS['i'] == 0) {
|
||||
class a {}
|
||||
}
|
||||
++$GLOBALS['i'];
|
||||
}
|
||||
class_alias('a', 'b');
|
||||
new b();
|
||||
|
||||
// With autoload failing to add it.
|
||||
class_alias('c', 'd');
|
||||
|
||||
// With autoload disabled.
|
||||
class_alias('e', 'f', false);
|
||||
@@ -0,0 +1,2 @@
|
||||
HipHop Warning: Class c not found in %s on line 15
|
||||
HipHop Warning: Class e not found in %s on line 18
|
||||
Link simbólico
+1
@@ -0,0 +1 @@
|
||||
../warnings.opts
|
||||
Referência em uma Nova Issue
Bloquear um usuário