Arquivos
hhvm/hphp/idl/base.php
T
Sara Golemon 9381ab39e7 Improve idl.php generator
Output constants and class implementations in .cpp file
Include base.php via __DIR__ rather than cwd for calling from outside hphp/idl
2013-04-09 15:33:09 -07:00

1170 linhas
36 KiB
PHP

<?php
if (file_exists('../system/globals/constants.php')) {
@require_once '../system/globals/constants.php';
}
///////////////////////////////////////////////////////////////////////////////
// types
define('Boolean', 1);
define('Int32', 4);
define('Int64', 5);
define('Double', 6);
define('String', 7);
define('Int64Vec', 8);
define('StringVec', 9);
define('VariantVec', 10);
define('Int64Map', 11);
define('StringMap', 12);
define('VariantMap', 13);
define('Object', 14);
define('Resource', 15);
define('Variant', 16);
define('Numeric', 17);
define('Primitive', 18);
define('PlusOperand', 19);
define('Sequence', 20);
define('Any', 21);
define('TypeMask', 0x00FF);
define('Reference', 0x0100);
$TYPENAMES = array
(Boolean => array('name' => 'bool', 'enum' => 'Boolean',
'idlname' => 'Boolean', 'phpname' => 'bool'),
Int32 => array('name' => 'int', 'enum' => 'Int32',
'idlname' => 'Int32', 'phpname' => 'int'),
Int64 => array('name' => 'int64_t', 'enum' => 'Int64',
'idlname' => 'Int64', 'phpname' => 'int'),
Double => array('name' => 'double', 'enum' => 'Double',
'idlname' => 'Double', 'phpname' => 'float'),
String => array('name' => 'String', 'enum' => 'String',
'idlname' => 'String', 'phpname' => 'string'),
Int64Vec => array('name' => 'Array', 'enum' => 'Array',
'idlname' => 'Int64Vec', 'phpname' => 'vector'),
StringVec => array('name' => 'Array', 'enum' => 'Array',
'idlname' => 'StringVec', 'phpname' => 'vector'),
VariantVec => array('name' => 'Array', 'enum' => 'Array',
'idlname' => 'VariantVec', 'phpname' => 'vector'),
Int64Map => array('name' => 'Array', 'enum' => 'Array',
'idlname' => 'Int64Map', 'phpname' => 'map'),
StringMap => array('name' => 'Array', 'enum' => 'Array',
'idlname' => 'StringMap', 'phpname' => 'map'),
VariantMap => array('name' => 'Array', 'enum' => 'Array',
'idlname' => 'VariantMap', 'phpname' => 'map'),
Object => array('name' => 'Object', 'enum' => 'Object',
'idlname' => 'Object', 'phpname' => 'object'),
Resource => array('name' => 'Object', 'enum' => 'Object',
'idlname' => 'Resource', 'phpname' => 'resource'),
Variant => array('name' => 'Variant', 'enum' => 'Variant',
'idlname' => 'Variant', 'phpname' => 'mixed'),
Numeric => array('name' => 'Numeric', 'enum' => 'Numeric',
'idlname' => 'Numeric', 'phpname' => 'number'),
Primitive => array('name' => 'Primitive', 'enum' => 'Primitive',
'idlname' => 'Primitive', 'phpname' => 'num|string'),
PlusOperand => array('name' => 'PlusOperand', 'enum' => 'PlusOperand',
'idlname' => 'PlusOperand','phpname' => 'num|array'),
Sequence => array('name' => 'Sequence', 'enum' => 'Sequence',
'idlname' => 'Sequence', 'phpname' => 'string|array'),
Any => array('name' => 'Variant', 'enum' => 'Some',
'idlname' => 'Any', 'phpname' => 'mixed'),
);
$REFNAMES = array('String' => 'CStrRef',
'Array' => 'CArrRef',
'Object' => 'CObjRef',
'Variant' => 'CVarRef',
'Numeric' => 'CVarRef',
'Primitive' => 'CVarRef',
'PlusOperand' => 'CVarRef',
'Sequence' => 'CVarRef',
);
$MAGIC_METHODS = array('__get' => 'ObjectData::UseGet',
'__set' => 'ObjectData::UseSet',
'__isset' => 'ObjectData::UseIsset',
'__unset' => 'ObjectData::UseUnset',
'__call' => 'ObjectData::HasCall',
'__callStatic' => 'ObjectData::HasCallStatic');
function get_idl_name($type, $null = '') {
global $TYPENAMES;
if ($type === null) {
return $null;
}
if (is_string($type)) {
return "'$type'";
}
if ($type & Reference) {
return $TYPENAMES[$type & ~Reference]['idlname'] . ' | Reference';
}
return $TYPENAMES[$type]['idlname'];
}
function get_php_name($type, $null = 'mixed') {
global $TYPENAMES;
if ($type === null) {
return $null;
}
if (is_string($type)) {
return $type;
}
if ($type & Reference) {
return $TYPENAMES[$type & ~Reference]['phpname'];
}
return $TYPENAMES[$type]['phpname'];
}
///////////////////////////////////////////////////////////////////////////////
// flags
// ClassInfo attributes, and these numbers need to be consistent with them!
define('IsAbstract', 1 << 4);
define('IsFinal', 1 << 5);
define('IsPublic', 1 << 6);
define('IsProtected', 1 << 7);
define('IsPrivate', 1 << 8);
define('IgnoreRedefinition', 1 << 8);
define('IsStatic', 1 << 9);
// FIXME (#2163116): IsInherited = (1 << 10) in base_class.h
define('IsCppAbstract', 1 << 10);
define('IsReference', 1 << 11);
define('IsConstructor', 1 << 12);
define('IsNothing', 1 << 13);
define('HasDocComment', 1 << 14);
define('HipHopSpecific', 1 << 16);
define('VariableArguments', 1 << 17);
define('RefVariableArguments', 1 << 18);
define('MixedVariableArguments', 1 << 19);
define('FunctionIsFoldable', 1 << 20);
define('NoEffect', 1 << 21);
define('NoInjection', 1 << 22);
define('HasOptFunction', 1 << 23);
define('AllowIntercept', 1 << 24);
define('NoProfile', 1 << 25);
define('ContextSensitive', 1 << 26);
define('NoDefaultSweep', 1 << 27);
define('IsSystem', 1 << 28);
define('IsTrait', 1 << 29);
define('NeedsActRec', 1 << 31);
// Mask for checking the flags related to variable arguments
define('VarArgsMask', (VariableArguments | RefVariableArguments |
MixedVariableArguments));
function get_flag_names($arr, $name, $global_func) {
$flag = 0;
if (!empty($arr[$name])) {
$flag |= $arr[$name];
}
if ($flag == 0) return '';
$ret = '';
if ($flag & IsAbstract ) $ret .= ' | IsAbstract' ;
if ($flag & IsFinal ) $ret .= ' | IsFinal' ;
if ($flag & IsPublic ) $ret .= ' | IsPublic' ;
if ($flag & IsProtected ) $ret .= ' | IsProtected' ;
if ($global_func) {
if ($flag & IgnoreRedefinition ) $ret .= ' | IgnoreRedefinition' ;
} else {
if ($flag & IsPrivate ) $ret .= ' | IsPrivate' ;
}
if ($flag & IsStatic ) $ret .= ' | IsStatic' ;
if ($flag & HasDocComment ) $ret .= ' | HasDocComment' ;
if ($flag & HipHopSpecific ) $ret .= ' | HipHopSpecific' ;
if ($flag & VariableArguments ) $ret .= ' | VariableArguments' ;
if ($flag & RefVariableArguments ) $ret .= ' | RefVariableArguments' ;
if ($flag & MixedVariableArguments) $ret .= ' | MixedVariableArguments';
if ($flag & FunctionIsFoldable ) $ret .= ' | FunctionIsFoldable' ;
if ($flag & NoEffect ) $ret .= ' | NoEffect' ;
if ($flag & NoInjection ) $ret .= ' | NoInjection' ;
if ($flag & HasOptFunction ) $ret .= ' | HasOptFunction' ;
if ($flag & AllowIntercept ) $ret .= ' | AllowIntercept' ;
if ($flag & NoProfile ) $ret .= ' | NoProfile' ;
if ($flag & ContextSensitive ) $ret .= ' | ContextSensitive' ;
if ($flag & NoDefaultSweep ) $ret .= ' | NoDefaultSweep' ;
if ($flag & IsTrait ) $ret .= ' | IsTrait' ;
if ($flag & NeedsActRec ) $ret .= ' | NeedsActRec' ;
if ($ret == '') {
throw new Exception("invalid flag $flag");
}
return substr($ret, 2);
}
///////////////////////////////////////////////////////////////////////////////
// schema functions that will be used (and only used) by schemas
function ResetSchema() {
global $current_class, $preamble, $funcs, $classes, $constants;
$current_class = '';
$preamble = '';
$funcs = array();
$classes = array();
$constants = array();
}
ResetSchema();
function DefinePreamble($p) {
global $preamble;
$preamble .= $p;
}
function BeginClass($class) {
global $classes, $current_class;
$current_class = $class['name'];
if (!isset($class['parent'])) $class['parent'] = null;
if (!isset($class['ifaces'])) $class['ifaces'] = array();
if (!isset($class['bases'])) $class['bases'] = array();
$class['methods'] = array();
$class['properties'] = array();
$class['consts'] = array();
if (empty($class['flags'])) {
$class['flags'] = 0;
}
$doc = get_class_doc_comments($class);
if (!empty($doc)) {
$class['flags'] |= HasDocComment;
$class['doc'] = $doc;
} else {
$class['flags'] &= ~HasDocComment;
$class['doc'] = null;
}
$classes[$current_class] = $class;
}
function EndClass() {
global $classes, $current_class;
$have_ctor = false;
foreach ($classes[$current_class]['methods'] as $method) {
if ($method['name'] == '__construct') $have_ctor = true;
}
// We don't have the information to autogenerate a ctor def,
// so make the user do it.
if (!$have_ctor) {
throw new Exception("No constructor defined for class $current_class");
}
$current_class = '';
}
function DefineProperty($property) {
global $classes, $current_class;
$classes[$current_class]['properties'][] = $property;
}
function DefineConstant($const) {
global $constants, $classes, $current_class;
if (empty($current_class)) {
$constants[] = $const;
} else {
$classes[$current_class]['consts'][] = $const;
}
}
function DefineFunction($func) {
global $classes, $current_class;
if (empty($func['flags'])) {
$func['flags'] = 0;
}
if ($current_class && $classes[$current_class]['flags'] & HipHopSpecific) {
$func['flags'] |= HipHopSpecific;
}
if (!isset($func['return'])) $func['return'] = array();
$func['ret_desc'] = idx($func['return'], 'desc');
$func['ret_hint'] = idx($func['return'], 'hint');
$func['return'] = idx($func['return'], 'type');
if ($func['return'] & Reference) {
$func['ref'] = true;
$func['return'] = Variant | ($func['return'] & (~TypeMask));
}
$args = array();
if (!empty($func['args'])) {
foreach ($func['args'] as $arg) {
if (array_key_exists('value', $arg)) {
if (!is_string($arg['value']) || $arg['value'] === '') {
throw new Exception('default value has to be non-empty string for '.
$func['name'].'(..'.$arg['name'].'..)');
}
if (preg_match('/^q_([A-Za-z]+)_(\w+)$/', $arg['value'], $m)) {
$class = $m[1];
$constant = $m[2];
$arg['default'] = "q_${class}_${constant}";
} else {
$arg['default'] = $arg['value'];
}
$arg['defaultSerialized'] = get_serialized_default($arg['value']);
$arg['defaultText'] = get_default_text($arg['value']);
}
if (idx($arg, 'type') & Reference) {
$arg['ref'] = true;
$arg['type'] = Variant | ($arg['type'] & (~TypeMask));
}
$args[] = $arg;
}
$func['args'] = $args;
} else {
$func['args'] = array();
}
$doc = get_function_doc_comments($func, $current_class);
if (!empty($doc)) {
$func['flags'] |= HasDocComment;
$func['doc'] = $doc;
} else {
$func['flags'] &= ~HasDocComment;
$func['doc'] = null;
}
if (!empty($func['opt'])) {
$func['flags'] |= HasOptFunction;
} else {
$func['flags'] &= ~HasOptFunction;
$func['opt'] = null;
}
global $funcs, $classes, $current_class;
if (empty($current_class)) {
$funcs[] = $func;
} else {
$classes[$current_class]['methods'][] = $func;
}
}
///////////////////////////////////////////////////////////////////////////////
// code generation
function typename($type, $prefix = true) {
if (is_string($type)) {
if ($prefix) return 'p_' . $type;
return $type;
}
global $TYPENAMES;
$type = $type & TypeMask;
if ($type !== 0) {
if (!isset($TYPENAMES[$type])) {
exit("Unknown type $type\n");
}
return $TYPENAMES[$type]['name'];
}
return 'void';
}
function typeidlname($type, $null = '') {
global $TYPENAMES;
if ($type === null) {
return $null;
}
if (is_string($type)) {
return "'$type'";
}
if ($type & Reference) {
return $TYPENAMES[$type & ~Reference]['idlname'];
}
return $TYPENAMES[$type]['idlname'];
}
function param_typename($arg, $forceRef = false) {
$type = $arg['type'];
if (is_string($type)) {
return 'p_' . $type;
}
global $REFNAMES;
$name = typename($type);
$ref = idx($arg, 'ref');
if (idx($arg, 'ref')) {
return ($name === "Variant") ? "VRefParam" : $name;
}
if ($forceRef) {
return $name;
}
return isset($REFNAMES[$name]) ? $REFNAMES[$name] : $name;
}
function typeenum($type) {
if (is_string($type)) {
return 'Void';
}
global $TYPENAMES;
$type = $type & TypeMask;
if ($type !== 0) {
return $TYPENAMES[$type]['enum'];
}
return 'Void';
}
function fprintType($f, $type) {
if (is_string($type)) {
fprintf($f, 'S(999), "%s"', $type);
} else {
fprintf($f, 'T(%s)', typeenum($type));
}
}
function get_serialized_default($s) {
// These values are special and cannot be returned by
// ReflectionParameter::getDefaultValue().
if ($s == 'TimeStamp::Current()' ||
preg_match('/^k_SQLITE3_/', $s)) {
return "\x01";
}
if (preg_match('/^".*"$/', $s) ||
preg_match('/^[\-0-9.]+$/', $s) ||
preg_match('/^0x[0-9a-fA-F]+$/', $s) ||
preg_match('/^(true|false|null)$/', $s) ||
$s == 'Array()'
) {
return serialize(eval("return $s;"));
}
if (preg_match('/^null_(string|array|object|variant)$/', $s)) {
return serialize(null);
}
if (preg_match('/^k_\w+( ?\| ?k_\w+)*$/', $s, $m)) {
$s = preg_replace('/k_/', '', $s);
return serialize(eval("return $s;"));
}
if (preg_match('/^q_([A-Za-z]+)\$\$(\w+)$/', $s, $m)) {
$class = $m[1];
$constant = $m[2];
return serialize(eval("return $class::$constant;"));
}
if ($s == 'RAND_MAX') {
return serialize(getrandmax());
}
if ($s == 'INT_MAX') {
return serialize((1 << 31) - 1);
}
throw new Exception("Unable to serialize default value: [$s]");
}
function get_default_text($s) {
if (preg_match('/^null_(string|array|object|variant)$/', $s)) {
return 'null';
}
if (preg_match('/^k_\w+( ?\| ?k_\w+)*$/', $s, $m)) {
return preg_replace('/k_/', '', $s);
}
if (preg_match('/^q_([A-Za-z]+)_(\w+)$/', $s, $m)) {
$class = $m[1];
$constant = $m[2];
return "$class::$constant";
}
return $s;
}
function generateFuncCPPInclude($func, $f, $newline = true) {
fprintf($f, '"%s", ', $func['name']);
fprintType($f, $func['return']);
fprintf($f, ', S(%d), ', idx($func, 'ref') ? 1 : 0);
for ($i = 0; $i < count($func['args']); $i++) {
$arg = $func['args'][$i];
fprintf($f, '"%s", ', $arg['name']);
fprintType($f, $arg['type']);
fprintf($f, ', ');
if (isset($arg['default'])) {
$serialized = escape_cpp($arg['defaultSerialized']);
fprintf($f, '"%s", S(%d), ',
$serialized, strlen($arg['defaultSerialized']));
fprintf($f, '"%s", ', escape_cpp($arg['defaultText']));
} else {
fprintf($f, 'NULL, S(0), NULL, ');
}
fprintf($f, 'S(%d), ', idx($arg, 'ref') ? 1 : 0);
}
fprintf($f, "NULL, ");
fprintf($f, 'S(%d), ', $func['flags']);
if (!empty($func['doc'])) {
fprintf($f, '"%s", ', escape_cpp($func['doc']));
}
if (!empty($func['opt'])) {
fprintf($f, 'S(%s), ', $func['opt']);
}
if ($newline) fprintf($f, "\n");
}
function generateFuncOptDecls($func, $f) {
if ($func['opt']) {
fprintf($f, "extern ExpressionPtr ".
"%s(CodeGenerator *cg, AnalysisResultConstPtr ar, ".
"SimpleFunctionCallPtr call, int mode);\n",
$func['opt']);
}
}
function generateConstCPPInclude($const, $f) {
fprintf($f, '"%s", T(%s),'. "\n", $const['name'], typeenum($const['type']));
}
function generateClassCPPInclude($class, $f) {
fprintf($f, '"%s", "%s", ', $class['name'], strtolower($class['parent']));
foreach ($class['ifaces'] as $if) {
fprintf($f, '"%s",', strtolower($if));
}
fprintf($f, 'NULL, ');
foreach ($class['methods'] as $m) {
generateMethodCPPInclude($m, $f);
fprintf($f, ",");
}
fprintf($f, "NULL,");
foreach ($class['properties'] as $p) {
generatePropertyCPPInclude($p, $f);
fprintf($f, ",");
}
fprintf($f, "NULL,");
foreach ($class['consts'] as $k) {
fprintf($f, '"%s", T(%s),', $k['name'], typeenum($k['type']));
}
fprintf($f, "NULL,\n");
fprintf($f, 'S(%d), ', $class['flags']);
if (!empty($class['doc'])) {
fprintf($f, '"%s", ', escape_cpp($class['doc']));
}
}
function generateMethodCPPInclude($method, $f) {
generateFuncCPPInclude($method, $f, false, 'G');
fprintf($f, "S(%d)", $method['flags']);
}
function generatePropertyCPPInclude($property, $f) {
fprintf($f, "S(%d), \"%s\", ", $property['flags'], $property['name']);
fprintType($f, $property['type']);
}
function generateFuncArgsCPPHeader($func, $f, $forceRef = false,
$static = false) {
$var_arg = ($func['flags'] & VarArgsMask);
$args = $func['args'];
fprintf($f, "(");
if ($static) {
fprintf($f, "const char* cls ");
}
if ($var_arg) fprintf($f, 'int _argc');
if ($var_arg && count($args) > 0) fprintf($f, ', ');
for ($i = 0; $i < count($args); $i++) {
$arg = $args[$i];
if ($static || $i > 0) fprintf($f, ', ');
fprintf($f, '%s %s', param_typename($arg, $forceRef),
$arg['name']);
if (isset($arg['default'])) {
fprintf($f, ' = %s', $arg['default']);
}
}
if ($var_arg) {
fprintf($f, ', CArrRef _argv = null_array');
}
fprintf($f, ")");
}
function generateFuncArgsCall($func, $f) {
$var_arg = ($func['flags'] & VarArgsMask);
$args = $func['args'];
if ($var_arg) fprintf($f, '_argc');
if ($var_arg && count($args) > 0) fprintf($f, ', ');
for ($i = 0; $i < count($args); $i++) {
$arg = $args[$i];
fprintf($f, ', ');
fprintf($f, '%s', $arg['name']);
}
if ($var_arg) {
fprintf($f, ', _argv');
}
}
function generateFuncCPPForwardDeclarations($func, $f) {
if (is_string($func['return'])) {
fprintf($f, "FORWARD_DECLARE_CLASS_BUILTIN(%s);\n",
typename($func['return'], false));
}
foreach ($func['args'] as $arg) {
if (is_string($arg['type'])) {
fprintf($f, "FORWARD_DECLARE_CLASS_BUILTIN(%s);\n",
typename($arg['type'], false));
}
}
}
function generateFuncCPPHeader($func, $f, $method = false, $forceRef = false,
$static = false, $class = false) {
if ($method) {
fprintf($f, '%s%s %s_%s', $static ? 'static ' : '',
typename($func['return']), $static ? "ti" : "t",
strtolower($func['name']));
} else {
generateFuncCPPForwardDeclarations($func, $f);
fprintf($f, '%s f_%s', typename($func['return']), $func['name']);
}
generateFuncArgsCPPHeader($func, $f, $forceRef, $static);
fprintf($f, ";\n");
if ($static && $method) {
fprintf($f, ' public: static %s t_%s', typename($func['return']),
strtolower($func['name']));
// for the actual static call there is no class name needed
generateFuncArgsCPPHeader($func, $f, $forceRef, false);
fprintf($f, " {\n return ti_%s(\"%s\"", strtolower($func['name']),
strtolower($class['name']));
generateFuncArgsCall($func, $f);
fprintf($f, ");\n }\n");
}
}
function generateConstCPPHeader($const, $f) {
$name = typename($const['type']);
if ($name == 'String') {
$name = 'StaticString';
}
fprintf($f, "extern const %s k_%s;\n", $name, $const['name']);
}
function generateConstCPPImplementation($const, $f, $prefix = 'k_') {
$name = typename($const['type']);
if ($name == 'String') {
$name = 'StaticString';
}
$def = '';
if (isset($const['value'])) {
if ($name == 'StaticString') {
$def = '"' . addslashes($const['value']) . '"';
} else if ($name == 'bool') {
$def = $const['value'] ? 'true' : 'false';
} else {
$def = $const['value'];
}
$def = " = $def";
}
fprintf($f, "const %s %s%s%s;\n", $name, $prefix, $const['name'], $def);
}
function generateClassCPPHeader($class, $f) {
global $MAGIC_METHODS;
$clsname = $class['name'];
foreach ($class['consts'] as $k) {
$name = typename($k['type']);
if ($name == 'String') {
$name = 'StaticString';
}
fprintf($f, "extern const %s q_%s\$\$%s;\n", $name, $clsname, $k['name']);
}
fprintf($f,
<<<EOT
///////////////////////////////////////////////////////////////////////////////
// class ${class['name']}
EOT
);
fprintf($f, "FORWARD_DECLARE_CLASS_BUILTIN(%s);\n", $clsname);
foreach ($class['properties'] as $p) {
generatePropertyCPPForwardDeclarations($p, $f);
}
foreach ($class['methods'] as $m) {
generateFuncCPPForwardDeclarations($m, $f);
}
fprintf($f, "class c_%s", $clsname);
$flags = array();
foreach ($class['methods'] as $m) {
$name = $m['name'];
if (isset($MAGIC_METHODS[$name]) && $MAGIC_METHODS[$name]) {
$flags[$name] = $MAGIC_METHODS[$name];
}
}
if ($class['parent']) {
global $classes;
$pclass = $class;
while ($flags && $pclass['parent'] && isset($classes[$class['parent']])) {
$pclass = $classes[$class['parent']];
foreach ($pclass['methods'] as $m) {
unset($flags[$m['name']]);
}
}
fprintf($f, " : public c_" . $class['parent']);
} else {
fprintf($f, " : public ExtObjectData");
if ($flags) {
fprintf($f, "Flags<%s>", implode('|', $flags));
$flags = false;
}
}
foreach ($class['bases'] as $p) {
fprintf($f, ", public $p");
}
$parents = array();
fprintf($f, " {\n public:\n");
fprintf($f, " DECLARE_CLASS(%s, %s, %s)\n", $clsname, $clsname,
$class['parent'] ? $class['parent'] : 'ObjectData');
fprintf($f, "\n");
if (!empty($class['properties'])) {
fprintf($f, " // properties\n");
foreach ($class['properties'] as $p) {
generatePropertyCPPHeader($p, $f);
}
fprintf($f, "\n");
}
fprintf($f, " // need to implement\n");
if ($flags) {
fprintf($f, " // constructor must call setAttributes(%s)\n",
implode('|', $flags));
}
fprintf($f, " public: c_%s(VM::Class* cls = c_%s::s_cls);\n",
$class['name'], $class['name']);
fprintf($f, " public: ~c_%s();\n", $class['name']);
foreach ($class['methods'] as $m) {
generateMethodCPPHeader($m, $class, $f);
}
fprintf($f, "\n");
fprintf($f, " // implemented by HPHP\n");
foreach ($class['methods'] as $m) {
generatePreImplemented($m, $class, $f);
}
if (!empty($class['footer'])) {
fprintf($f, $class['footer']);
}
fprintf($f, "\n};\n");
}
function generateClassCPPImplementation($class, $f) {
foreach ($class['consts'] as $k) {
generateConstCPPImplementation($k, $f, "q_{$class['name']}$$");
}
foreach ($class['methods'] as $m) {
generateMethodCPPImplementation($m, $class, $f);
}
}
function generateMethodCPPHeader($method, $class, $f) {
global $MAGIC_METHODS;
fprintf($f, " public: ");
generateFuncCPPHeader($method, $f, true,
isset($MAGIC_METHODS[$method['name']]),
$method['flags'] & IsStatic, $class);
}
function generateMethodCPPImplementation($method, $class, $f) {
if ($method['flags'] & IsStatic) {
$prefix = "c_{$class['name']}::ti_";
$sprop = 'const char *cls';
} else {
$prefix = "c_{$class['name']}::t_";
$sprop = '';
}
generateFuncCPPImplementation($method, $f, $prefix, $sprop);
}
function generatePropertyCPPHeader($property, $f) {
fprintf($f, " public: ");
fprintf($f, "%s m_%s;\n", typename($property['type']),
$property['name']);
}
function generatePropertyCPPForwardDeclarations($property, $f) {
if (is_string($property['type'])) {
fprintf($f, "FORWARD_DECLARE_CLASS_BUILTIN(%s);\n",
typename($property['type'], false));
}
}
function generatePreImplemented($method, $class, $f) {
if ($method['name'] == '__construct') {
fprintf($f, " public: c_%s *create", $class['name']);
generateFuncArgsCPPHeader($method, $f, true);
fprintf($f, ";\n");
}
}
function generateFuncCPPImplementation($func, $f, $prefix = 'f_', $sprop = '') {
$schema = "";
$schema_no = 0;
if ($func['return'] == Object || $func['return'] == Resource) {
$schema .= '.set(' . ($schema_no++) . ', -1, "OO")';
}
$output = '';
$need_ret = false;
fprintf($f, '%s %s%s(', typename($func['return']), $prefix, $func['name']);
$var_arg = ($func['flags'] & VarArgsMask);
if ($var_arg) fprintf($f, 'int _argc');
if ($var_arg && count($func['args']) > 0) fprintf($f, ', ');
$params = "";
$params_no = 0;
if ($sprop) {
// For static class methods to inject class name
fprintf($f, $sprop);
}
for ($i = 0; $i < count($func['args']); $i++) {
$arg = $func['args'][$i];
if ($sprop || ($i > 0)) fprintf($f, ', ');
fprintf($f, '%s %s', param_typename($arg),
$arg['name']);
if (isset($arg['default'])) {
fprintf($f, ' /* = %s */', $arg['default']);
}
if ($arg['type'] == Object || $arg['type'] == Resource) {
$params .= '.set(' . ($params_no++) .
', (OpaqueObject::GetIndex(' . $arg['name'] . '))';
} else {
$params .= '.set(' . ($params_no++) . ', ' . $arg['name'] . ')';
}
if ($arg['type'] == Object || $arg['type'] == Resource) {
if (idx($arg, 'ref')) {
$schema .= '.set(' . ($schema_no++) . ', ' . $i . ', "OO")';
} else {
$schema .= '.set(' . ($schema_no++) . ', ' . $i . ', "O")';
}
} else if (idx($arg, 'ref')) {
$schema .= '.set(' . ($schema_no++) . ', ' . $i . ', "R")';
}
if (idx($arg, 'ref')) {
$need_ret = true;
$output .= ' '.$arg['name'].' = ((Variant)_ret[1])['.$i.'];'."\n";
}
}
if ($var_arg) {
fprintf($f, ', CArrRef _argv /* = null_array */');
}
fprintf($f, ") {\n");
fprintf($f, " throw NotImplementedException(__func__);\n");
fprintf($f, "}\n\n");
}
function replaceParams($filename, $header) {
global $funcs;
$orig = $file = file_get_contents($filename);
foreach ($funcs as &$func) {
$var_arg = ($func['flags'] & VarArgsMask);
$args = $func['args'];
$search = '(?!return\s)\b\w+\s+f_'.$func['name'].'\s*\(\s*';
if ($var_arg) $search .= '\w+\s+\w+';
if ($var_arg && count($args) > 0) $search .= ',\s*';
for ($i = 0; $i < count($args); $i++) {
$arg = $args[$i];
$search .= '\w+\s+\w+\s*';
if (isset($arg['default'])) {
if ($header) {
$search .= '=\s*(?:'.preg_quote($arg['default'], '/').'|\d+)\s*';
} else {
$search .= '(?:\/\*\s*=\s*(?:'.preg_quote($arg['default'], '/').
'|\d+)\s*\*\/\s*)?';
}
}
if ($i < count($args) - 1) {
$search .= ',(\s*)';
}
}
if ($var_arg) {
if ($header) {
$search .= ',\s*\w+\s+\w+\s*=\s*null_array\s*';
} else {
$search .= ',(\s*)\w+\s+\w+\s*(?:\/\*\s*=\s*null_array\s*\*\/\s*)?';
}
}
$search .= '\)';
$replace = typename($func['return']).' f_'.$func['name'].'(';
if ($var_arg) $replace .= 'int _argc, ';
for ($i = 0; $i < count($args); $i++) {
$arg = $args[$i];
$replace .= param_typename($arg).' '.$arg['name'];
if (isset($arg['default'])) {
if ($header) {
$replace .= ' = '.addcslashes($arg['default'], '\\');
} else {
$replace .= ' /* = '.addcslashes($arg['default'], '\\').' */';
}
}
if ($i < count($args) - 1) {
$replace .= ',${'.($i+1).'}';
}
}
if ($var_arg) {
if ($header) {
$replace .= ', CArrRef _argv = null_array';
} else {
$replace .= ',${'.($i).'}';
$replace .= 'CArrRef _argv /* = null_array */';
}
}
$replace .= ')';
if ($header && preg_match("/inline\s+$search/ms", $file)) {
$func['inlined'] = true;
}
//var_dump($search, $replace);
$count = preg_match_all("/$search/ms", $file, $m);
if ($count == 0) {
if ($header || !isset($func['inlined'])) {
var_dump($search, $replace);
print $func['name']." not found in $filename\n";
}
} else if ($count == 1) {
$file = preg_replace("/$search/ms", $replace, $file);
} else {
print "skipped ".$func['name']." in $filename\n";
}
}
if ($orig != $file) {
file_put_contents($filename, $file);
}
}
///////////////////////////////////////////////////////////////////////////////
// helpers
function php_escape_val($val) {
if (is_string($val)) {
return '"'.escape_cpp($val).'"';
} else if ($val === true) {
return 'true';
} else if ($val === false) {
return 'false';
} else if ($val === null) {
return 'uninit_null()';
} else {
return var_export($val, true);
}
}
function escape_php($val) {
$val = preg_replace("/\\\\/", "\\\\\\\\", $val);
$val = preg_replace("/\\\"/", "\\\\\"", $val);
$val = preg_replace("/\\$/", "\\\\$", $val);
$val = preg_replace("/\n/", "\\\\n", $val); // optional
return $val;
}
function escape_cpp($val) {
$len = strlen($val);
$ret = '';
for ($i = 0; $i < $len; $i++) {
$ch = $val[$i];
switch ($ch) {
case "\n": $ret .= "\\n"; break;
case "\r": $ret .= "\\r"; break;
case "\t": $ret .= "\\t"; break;
case "\a": $ret .= "\\a"; break;
case "\b": $ret .= "\\b"; break;
case "\f": $ret .= "\\f"; break;
case "\v": $ret .= "\\v"; break;
case "\0": $ret .= "\\000";break;
case "\"": $ret .= "\\\""; break;
case "\\": $ret .= "\\\\"; break;
case "?": $ret .= "\\?"; break; // avoiding trigraph errors
default:
if (ord($ch) >= 0x20 && ord($ch) <= 0x7F) {
$ret .= $ch;
} else {
$ret .= sprintf("\\%03o", ord($ch));
}
break;
}
}
return $ret;
}
function idx($arr, $idx, $default=null) {
if ($idx === null) {
return $default;
}
if (isset($arr[$idx])) {
return $arr[$idx];
}
return $default;
}
function format_doc_desc($arr, $clsname) {
if ($arr['flags'] & HipHopSpecific) {
$desc = "( HipHop specific )\n";
} else {
$clsname = preg_replace('#_#', '-', strtolower($clsname));
$name = preg_replace('#_#', '-', strtolower($arr['name']));
$name = preg_replace('#^--#', '', $name);
$url = "http://php.net/manual/en/$clsname.$name.php";
$desc = "( excerpt from $url )\n";
}
$details = idx($arr, 'desc', '');
if ($details) {
$desc .= "\n$details";
}
return wordwrap($desc, 72)."\n\n";
}
function format_doc_arg($name, $type, $desc) {
$width1 = 12;
$width2 = 8;
if (!$desc) $desc = ' ';
$lines = explode("\n", wordwrap($desc, 72 - $width1 - $width2));
$col = str_pad('@'.$name, $width1 - 1);
$ret = $col;
if (strlen($col) >= $width1) {
$ret .= "\n".str_repeat(' ', $width1 - 1);
}
$col = str_pad(get_php_name($type), $width2 - 1);
$ret .= ' '.$col;
if (strlen($col) >= $width2) {
$ret .= "\n".str_repeat(' ', $width1 + $width2 - 1);
}
$ret .= ' '.$lines[0]."\n";
for ($i = 1; $i < count($lines); $i++) {
$ret .= rtrim(str_repeat(' ', $width1 + $width2) . $lines[$i])."\n";
}
return $ret;
}
function format_doc_comment($text) {
$lines = explode("\n", $text);
$ret = "/**\n";
for ($i = 0; $i < count($lines) - 1; $i++) {
$line = $lines[$i];
$ret .= rtrim(" * $line")."\n";
}
$ret .= " */";
return $ret;
}
function get_function_doc_comments($func, $clsname) {
$text = format_doc_desc($func, empty($clsname) ? 'function' : $clsname);
if ($func['args']) {
foreach ($func['args'] as $arg) {
$desc = idx($arg, 'desc', '');
if (idx($func, 'ref')) {
$desc = '(output) ' . $desc;
}
$text .= format_doc_arg($arg['name'], idx($arg, 'type'), $desc);
}
}
$ret = ($func['return'] !== null || !empty($func['ret_desc']));
if ($func['args'] && $ret) {
$text .= "\n";
}
if ($ret) {
$text .= format_doc_arg('return', $func['return'], $func['ret_desc']);
}
return format_doc_comment($text);
}
function get_class_doc_comments($class) {
return format_doc_comment(format_doc_desc($class, 'class'));
}
///////////////////////////////////////////////////////////////////////////////
// phpnet
function phpnet_clean($text) {
$text = preg_replace('#<!--UdmComment.*?/UdmComment-->#s', '', $text);
$text = preg_replace('#<div class="example-contents">.*?</div>#s',
'<>', $text);
$text = preg_replace('#<p class="para">#', '<>', $text);
$text = preg_replace('#<b class="note">Note</b>:#', '', $text);
$text = preg_replace('#<.+?>#', '', $text);
$text = preg_replace('#[ \t\n]+#s', ' ', $text);
$text = preg_replace('# ?<> ?#', "\n\n", $text);
$text = preg_replace('/&#039;/', "'", $text);
$text = trim(html_entity_decode($text));
$text = preg_replace('/[^\t\n -~]/', '', $text);
return $text;
}
function phpnet_get_function_info($name, $clsname = 'function') {
$clsname = preg_replace('#_#', '-', strtolower($clsname));
$name = preg_replace('#_#', '-', strtolower($name));
$doc = @file_get_contents("http://php.net/manual/en/$clsname.$name.php");
if ($doc === false) {
return array();
}
$ret = array();
if (preg_match('#<div class="refsect1 description">(.*?)'.
'<div class="refsect1 #s', $doc, $m)) {
$desc = $m[1];
if (preg_match('#<p class="para rdfs-comment">(.*)</div>#s', $desc, $m)) {
$ret['desc'] = phpnet_clean($m[1]);
}
}
if (preg_match('#<div class="refsect1 parameters">(.*?)'.
'<div class="refsect1 #s', $doc, $m)) {
$desc = $m[1];
if (preg_match_all('#<tt class="parameter">(.*?)</tt>#s', $desc, $m)) {
foreach ($m[1] as $param) {
$ret['param_names'][] = phpnet_clean($param);
}
}
if (preg_match_all('#<dd>(.*?)</dd>#s', $desc, $m)) {
foreach ($m[1] as $param) {
$ret['params'][] = phpnet_clean($param);
}
}
$desc = preg_replace('#<h3.*</h3>#', '', $desc);
$desc = preg_replace('#<dl>.*</dl>#s', '', $desc);
$desc = phpnet_clean($desc);
if (!empty($desc)) {
$ret['desc'] .= "\n$desc";
}
}
if (preg_match('#<div class="refsect1 returnvalues">(.*?)'.
'(<div class="refsect1 |<div id="usernotes">)#s', $doc, $m)) {
$desc = $m[1];
$desc = preg_replace('#<h3.*</h3>#', '', $desc);
$ret['ret'] = phpnet_clean($desc);
}
return $ret;
}
function phpnet_get_class_desc($name) {
$name = preg_replace('#_#', '-', strtolower($name));
$doc = @file_get_contents("http://php.net/manual/en/class.$name.php");
if ($doc !== false &&
preg_match('#<h2 class="title">Introduction</h2>(.*?)'.
'<div class="section"#s', $doc, $m)) {
return phpnet_clean($m[1]);
}
return false;
}
function phpnet_get_extension_functions($name) {
$doc = @file_get_contents("http://www.php.net/manual/en/ref.$name.php");
if ($doc === false) {
return false;
}
preg_match_all('#<li><a href="function\..*?\.php">(.*?)</a>.*?</li>#',
$doc, $m);
return $m[1];
}