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:
Jordan DeLong
2013-05-25 11:07:16 -07:00
commit de Sara Golemon
commit fa6f3f7e12
60 arquivos alterados com 211 adições e 1 exclusões
+50
Ver Arquivo
@@ -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);
}
+17
Ver Arquivo
@@ -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.
+15
Ver Arquivo
@@ -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;
+30 -1
Ver Arquivo
@@ -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": [
]
}
}
+14
Ver Arquivo
@@ -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);
}
+1
Ver Arquivo
@@ -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);
+20
Ver Arquivo
@@ -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];
+1
Ver Arquivo
@@ -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);
+8
Ver Arquivo
@@ -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,
+1
Ver Arquivo
@@ -0,0 +1 @@
-vErrorHandling.NoticeFrequency=1 -vErrorHandling.WarningFrequency=1 -vLog.RuntimeErrorReportingLevel=10
+4
Ver Arquivo
@@ -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
+1
Ver Arquivo
@@ -0,0 +1 @@
../warnings.opts
+18
Ver Arquivo
@@ -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
+1
Ver Arquivo
@@ -0,0 +1 @@
../warnings.opts