Index: /branches/1.3/ChangeLog
===================================================================
--- /branches/1.3/ChangeLog	(revision 765)
+++ /branches/1.3/ChangeLog	(revision 766)
@@ -1,4 +1,6 @@
 1.3.2 2011-??-??
 ========
+ * adds 30 seconds timeout to "compiling" flag
+ * decompiler: improves decompiling
  * disassembler: DECLARE_INHERITED_CLASS/DELAYED class not found
  * disassembler: don't dump builtin functions
Index: /branches/1.3/Decompiler.class.php
===================================================================
--- /branches/1.3/Decompiler.class.php	(revision 765)
+++ /branches/1.3/Decompiler.class.php	(revision 766)
@@ -11,9 +11,14 @@
 function str($code, $indent = '') // {{{
 {
+	if (is_array($code)) {
+		$array = array();
+		foreach ($code as $key => $value) {
+			$array[$key] = str($value, $indent);
+		}
+		return $array;
+	}
 	if (is_object($code)) {
-		if (get_class($code) != 'Decompiler_Code') {
-			$code = toCode($code, $indent);
-		}
-		return $code->__toString();
+		$code = foldToCode($code, $indent);
+		return $code->toCode($indent);
 	}
 
@@ -21,5 +26,5 @@
 }
 // }}}
-function toCode($src, $indent = '') // {{{
+function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
 {
 	if (is_array($indent)) {
@@ -27,13 +32,18 @@
 	}
 
-	if (is_object($src)) {
-		if (!method_exists($src, 'toCode')) {
-			var_dump($src);
-			exit('no toCode');
-		}
-		return new Decompiler_Code($src->toCode($indent));
-	}
-
-	return new Decompiler_Code($src);
+	if (!is_object($src)) {
+		return new Decompiler_Code($src);
+	}
+
+	if (!method_exists($src, 'toCode')) {
+		var_dump($src);
+		exit('no toCode');
+	}
+	if (get_class($src) != 'Decompiler_Code') {
+		// rewrap it
+		$src = new Decompiler_Code($src->toCode($indent));
+	}
+
+	return $src;
 }
 // }}}
@@ -61,4 +71,28 @@
 }
 // }}}
+function unquoteName_($str, $asVariableName, $indent = '') // {{{
+{
+	$str = str($str, $indent);
+	if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
+		return str_replace('\\\\', '\\', substr($str, 1, -1));
+	}
+	else if ($asVariableName) {
+		return "{" . $str . "}";
+	}
+	else {
+		return $str;
+	}
+}
+// }}}
+function unquoteVariableName($str, $indent = '') // {{{
+{
+	return unquoteName_($str, true, $indent);
+}
+// }}}
+function unquoteName($str, $indent = '') // {{{
+{
+	return unquoteName_($str, false, $indent);
+}
+// }}}
 class Decompiler_Object // {{{
 {
@@ -76,5 +110,19 @@
 	function toCode($indent)
 	{
-		return var_export($this->value, true);
+		$code = var_export($this->value, true);
+		if (gettype($this->value) == 'string') {
+			switch ($this->value) {
+			case "\r":
+				return '"\\r"';
+			case "\n":
+				return '"\\n"';
+			case "\r\n":
+				return '"\\r\\n"';
+			}
+			$code = str_replace("\r\n", '\' . "\\r\\n" . \'', $code);
+			$code = str_replace("\r", '\' . "\\r" . \'', $code);
+			$code = str_replace("\n", '\' . "\\n" . \'', $code);
+		}
+		return $code;
 	}
 }
@@ -86,13 +134,9 @@
 	function Decompiler_Code($src)
 	{
+		assert('isset($src)');
 		$this->src = $src;
 	}
 
 	function toCode($indent)
-	{
-		return $this;
-	}
-
-	function __toString()
 	{
 		return $this->src;
@@ -118,13 +162,20 @@
 	function toCode($indent)
 	{
-		$op1 = toCode($this->op1, $indent);
+		$opstr = $this->parent->binops[$this->opc];
+
+		$op1 = foldToCode($this->op1, $indent);
 		if (is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
-			$op1 = "($op1)";
-		}
-		$opstr = $this->parent->binops[$this->opc];
-		if ($op1 == '0' && $this->opc == XC_SUB) {
-			return $opstr . toCode($this->op2, $indent);
-		}
-		return $op1 . ' ' . $opstr . ' ' . toCode($this->op2, $indent);
+			$op1 = "(" . str($op1, $indent) . ")";
+		}
+		$op2 = foldToCode($this->op2, $indent);
+		if (is_a($this->op2, 'Decompiler_Binop') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
+			$op2 = "(" . str($op2, $indent) . ")";
+		}
+
+		if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
+			return $opstr . str($op2, $indent);
+		}
+
+		return str($op1) . ' ' . $opstr . ' ' . str($op2);
 	}
 }
@@ -148,4 +199,8 @@
 			return '$' . substr($this->src, 1, -1);
 		case ZEND_FETCH_STATIC:
+			if (ZEND_ENGINE_2_3) {
+				// closure local variable?
+				return str($this->src);
+			}
 			die('static fetch cant to string');
 		case ZEND_FETCH_GLOBAL:
@@ -178,4 +233,5 @@
 	var $offsets = array();
 	var $isLast = false;
+	var $isObject = false;
 	var $assign = null;
 
@@ -183,11 +239,17 @@
 	{
 		if (is_a($this->value, 'Decompiler_ListBox')) {
-			$exp = toCode($this->value->obj->src, $indent);
+			$exp = str($this->value->obj->src, $indent);
 		}
 		else {
-			$exp = toCode($this->value, $indent);
-		}
-		foreach ($this->offsets as $dim) {
-			$exp .= '[' . toCode($dim, $indent) . ']';
+			$exp = str($this->value, $indent);
+		}
+		$last = count($this->offsets) - 1;
+		foreach ($this->offsets as $i => $dim) {
+			if ($this->isObject && $i == $last) {
+				$exp .= '->' . unquoteVariableName($dim, $indent);
+			}
+			else {
+				$exp .= '[' . str($dim, $indent) . ']';
+			}
 		}
 		return $exp;
@@ -212,7 +274,7 @@
 			$dim->value = $this->src;
 			if (!isset($dim->assign)) {
-				return toCode($dim, $indent);
-			}
-			return toCode($this->dims[0]->assign, $indent) . ' = ' . toCode($dim, $indent);
+				return str($dim, $indent);
+			}
+			return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
 		}
 		/* flatten dims */
@@ -223,7 +285,7 @@
 				$assign = &$assign[$offset];
 			}
-			$assign = toCode($dim->assign, $indent);
-		}
-		return $this->toList($assigns) . ' = ' . toCode($this->src, $indent);
+			$assign = foldToCode($dim->assign, $indent);
+		}
+		return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
 	}
 
@@ -370,9 +432,23 @@
 class Decompiler
 {
-	var $rName = '!^[\\w_][\\w\\d_]*$!';
-	var $rQuotedName = "!^'[\\w_][\\w\\d_]*'\$!";
+	var $namespace;
+	var $namespaceDecided;
 
 	function Decompiler()
 	{
+		// {{{ testing
+		// XC_UNDEF XC_OP_DATA
+		$this->test = !empty($_ENV['XCACHE_DECOMPILER_TEST']);
+		$this->usedOps = array();
+
+		if ($this->test) {
+			$content = file_get_contents(__FILE__);
+			for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
+				if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
+					echo "not recognized opcode ", $opname, "\n";
+				}
+			}
+		}
+		// }}}
 		// {{{ opinfo
 		$this->unaryops = array(
@@ -421,4 +497,29 @@
 				// }}}
 	}
+	function detectNamespace($name) // {{{
+	{
+		if ($this->namespaceDecided) {
+			return;
+		}
+
+		if (strpos($name, '\\') !== false) {
+			$this->namespace = strtok($name, '\\');
+			echo 'namespace ', $this->namespace, ";\n\n";
+		}
+
+		$this->namespaceDecided = true;
+	}
+	// }}}
+	function stripNamespace($name) // {{{
+	{
+		$len = strlen($this->namespace) + 1;
+		if (substr($name, 0, $len) == $this->namespace . '\\') {
+			return substr($name, $len);
+		}
+		else {
+			return $name;
+		}
+	}
+	// }}}
 	function outputPhp(&$opcodes, $opline, $last, $indent) // {{{
 	{
@@ -428,22 +529,23 @@
 			$op = $opcodes[$i];
 			if (isset($op['php'])) {
-				$toticks = isset($op['ticks']) ? $op['ticks'] : 0;
+				$toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
 				if ($curticks != $toticks) {
-					if (!$toticks) {
-						echo $origindent, "}\n";
+					$oldticks = $curticks;
+					$curticks = $toticks;
+					if (!$curticks) {
+						echo $origindent, "}\n\n";
 						$indent = $origindent;
 					}
 					else {
-						if ($curticks) {
-							echo $origindent, "}\n";
-						}
-						else if (!$curticks) {
+						if ($oldticks) {
+							echo $origindent, "}\n\n";
+						}
+						else if (!$oldticks) {
 							$indent .= INDENT;
 						}
-						echo $origindent, "declare(ticks=$curticks) {\n";
-					}
-					$curticks = $toticks;
-				}
-				echo $indent, toCode($op['php'], $indent), ";\n";
+						echo $origindent, "declare (ticks=$curticks) {\n";
+					}
+				}
+				echo $indent, str($op['php'], $indent), ";\n";
 			}
 		}
@@ -457,5 +559,5 @@
 		switch ($op['op_type']) {
 		case XC_IS_CONST:
-			return toCode(value($op['constant']), $EX);
+			return foldToCode(value($op['constant']), $EX);
 
 		case XC_IS_VAR:
@@ -464,5 +566,5 @@
 			$ret = $T[$op['var']];
 			if ($tostr) {
-				$ret = toCode($ret, $EX);
+				$ret = foldToCode($ret, $EX);
 			}
 			if ($free) {
@@ -531,5 +633,6 @@
 			$last = count($opcodes) - 1;
 			if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
-				unset($opcodes[$last]);
+				$this->usedOps[XC_HANDLE_EXCEPTION] = true;
+				$opcodes[$last]['opcode'] = XC_NOP;
 				--$last;
 			}
@@ -537,5 +640,5 @@
 				$op1 = $opcodes[$last]['op1'];
 				if ($op1['op_type'] == XC_IS_CONST && array_key_exists('constant', $op1) && $op1['constant'] === $defaultReturnValue) {
-					unset($opcodes[$last]);
+					$opcodes[$last]['opcode'] = XC_NOP;
 					--$last;
 				}
@@ -562,4 +665,10 @@
 			$op['line'] = $i;
 			switch ($op['opcode']) {
+			case XC_CONT:
+			case XC_BRK:
+				$op['jmpouts'] = array();
+				break;
+
+			case XC_GOTO:
 			case XC_JMP:
 				$target = $op['op1']['var'];
@@ -580,4 +689,5 @@
 			case XC_JMPZ_EX:
 			case XC_JMPNZ_EX:
+			case XC_JMP_SET:
 			// case XC_FE_RESET:
 			case XC_FE_FETCH:
@@ -678,9 +788,9 @@
 			$body = ob_get_clean();
 
-			$as = toCode($op['fe_as'], $EX);
+			$as = foldToCode($op['fe_as'], $EX);
 			if (isset($op['fe_key'])) {
-				$as = toCode($op['fe_key'], $EX) . ' => ' . $as;
-			}
-			echo "{$indent}foreach (" . toCode($op['fe_src'], $EX) . " as $as) {\n";
+				$as = str($op['fe_key'], $EX) . ' => ' . str($as);
+			}
+			echo "{$indent}foreach (" . str($op['fe_src'], $EX) . " as $as) {\n";
 			echo $body;
 			echo "{$indent}}";
@@ -711,17 +821,28 @@
 			return;
 		}
-		if (!empty($op['jmpouts']) && isset($op['isjmp'])) {
+		if (isset($op['jmpouts']) && isset($op['isjmp'])) {
 			if (isset($op['cond'])) {
-				echo "{$indent}check ($op[cond]) {\n";
+				echo "{$indent}check (" . str($op["cond"]) . ") {\n";
 				echo INDENT;
 			}
-			echo $indent;
-			echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0];
-			if (isset($op['jmpouts'][1])) {
-				echo ', line', $op['jmpouts'][1];
-			}
-			echo ";";
-			// echo ' // <- line', $op['line'];
-			echo "\n";
+			switch ($op['opcode']) {
+			case XC_CONT:
+			case XC_BRK:
+				break;
+
+			case XC_GOTO:
+				echo $indent, 'goto', ' line', $op['jmpouts'][0], ';', "\n";
+				break;
+
+			default:
+				echo $indent;
+				echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0];
+				if (isset($op['jmpouts'][1])) {
+					echo ', line', $op['jmpouts'][1];
+				}
+				echo ";";
+				// echo ' // <- line', $op['line'];
+				echo "\n";
+			}
 			if (isset($op['cond'])) echo "$indent}\n";
 		}
@@ -751,5 +872,5 @@
 			// $this->dumpop($op, $EX);
 			// any true comes here, so it's a "or"
-			$cond = implode(' and ', $op['cond_false']);
+			$cond = implode(' and ', str($op['cond_false']));
 			// var_dump($op['cond'] = $cond);
 			/*
@@ -765,12 +886,4 @@
 	}
 	// }}}
-	function unquoteName($str) // {{{
-	{
-		if (preg_match($this->rQuotedName, $str)) {
-			$str = substr($str, 1, -1);
-		}
-		return $str;
-	}
-	// }}}
 	function dasmBasicBlock(&$EX, $opline, $last) // {{{
 	{
@@ -784,4 +897,5 @@
 			$opc = $op['opcode'];
 			if ($opc == XC_NOP) {
+				$this->usedOps[$opc] = true;
 				continue;
 			}
@@ -799,5 +913,5 @@
 				continue;
 			}
-			// $this->dumpop($op, $EX); //var_dump($op);
+			// echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op);
 
 			$resvar = null;
@@ -813,7 +927,9 @@
 			$call = array(&$this, $opname);
 			if (is_callable($call)) {
+				$this->usedOps[$opc] = true;
 				$this->{$opname}($op, $EX);
 			}
 			else if (isset($this->binops[$opc])) { // {{{
+				$this->usedOps[$opc] = true;
 				$op1val = $this->getOpVal($op1, $EX, false);
 				$op2val = $this->getOpVal($op2, $EX, false);
@@ -823,11 +939,13 @@
 			}
 			else if (isset($this->unaryops[$opc])) { // {{{
+				$this->usedOps[$opc] = true;
 				$op1val = $this->getOpVal($op1, $EX);
 				$myop = $this->unaryops[$opc];
-				$rvalue = "$myop$op1val";
+				$rvalue = $myop . str($op1val);
 				$resvar = $rvalue;
 				// }}}
 			}
 			else {
+				$covered = true;
 				switch ($opc) {
 				case XC_NEW: // {{{
@@ -835,8 +953,24 @@
 					$EX['object'] = (int) $res['var'];
 					$EX['called_scope'] = null;
-					$EX['fbc'] = 'new ' . $this->unquoteName($this->getOpVal($op1, $EX));
+					$EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX);
 					if (!ZEND_ENGINE_2) {
 						$resvar = '$new object$';
 					}
+					break;
+					// }}}
+				case XC_THROW: // {{{
+					$resvar = 'throw ' . str($this->getOpVal($op1, $EX));
+					break;
+					// }}}
+				case XC_CLONE: // {{{
+					$resvar = 'clone ' . str($this->getOpVal($op1, $EX));
+					break;
+					// }}}
+				case XC_CATCH: // {{{
+					$resvar = 'catch (' . str($this->getOpVal($op1, $EX)) . ' ' . str($this->getOpVal($op2, $EX)) . ')';
+					break;
+					// }}}
+				case XC_INSTANCEOF: // {{{
+					$resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . str($this->getOpVal($op2, $EX));
 					break;
 					// }}}
@@ -857,7 +991,7 @@
 					}
 					else {
-						$class = $op2['constant'];
-						if (is_object($class)) {
-							$class = get_class($class);
+						$class = $this->getOpVal($op2, $EX);
+						if (isset($op2['constant'])) {
+							$class = $this->stripNamespace(unquoteName($class));
 						}
 					}
@@ -866,15 +1000,17 @@
 					// }}}
 				case XC_FETCH_CONSTANT: // {{{
+					if ($op1['op_type'] == XC_IS_UNUSED) {
+						$resvar = $this->stripNamespace($op2['constant']);
+						break;
+					}
+
 					if ($op1['op_type'] == XC_IS_CONST) {
-						$resvar = $op1['constant'];
-					}
-					else if ($op1['op_type'] == XC_IS_UNUSED) {
-						$resvar = $op2['constant'];
+						$resvar = $this->stripNamespace($op1['constant']);
 					}
 					else {
-						$class = $T[$op1['var']];
-						assert($class[0] == 'class');
-						$resvar = $class[1] . '::' . $op2['constant'];
-					}
+						$resvar = $this->getOpVal($op1, $EX);
+					}
+
+					$resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX));
 					break;
 					// }}}
@@ -897,14 +1033,14 @@
 					case ZEND_FETCH_STATIC_MEMBER:
 						$class = $this->getOpVal($op2, $EX);
-						$rvalue = $class . '::$' . $this->unquoteName($rvalue);
+						$rvalue = str($class) . '::$' . unquoteName($rvalue, $EX);
 						break;
 					default:
-						$name = $this->unquoteName($rvalue);
-						$globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[$rvalue]";
+						$name = unquoteName($rvalue, $EX);
+						$globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]";
 						$rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
 						break;
 					}
 					if ($opc == XC_UNSET_VAR) {
-						$op['php'] = "unset(" . toCode($rvalue, $EX) . ")";
+						$op['php'] = "unset(" . str($rvalue, $EX) . ")";
 						$lastphpop = &$op;
 					}
@@ -923,6 +1059,7 @@
 				case XC_FETCH_DIM_IS:
 				case XC_ASSIGN_DIM:
+				case XC_UNSET_DIM_OBJ: // PHP 4 only
 				case XC_UNSET_DIM:
-				case XC_UNSET_DIM_OBJ:
+				case XC_UNSET_OBJ:
 					$src = $this->getOpVal($op1, $EX, false);
 					if (is_a($src, "Decompiler_ForeachBox")) {
@@ -931,10 +1068,12 @@
 						break;
 					}
-					else if (is_a($src, "Decompiler_DimBox")) {
+
+					if (is_a($src, "Decompiler_DimBox")) {
 						$dimbox = $src;
 					}
 					else {
 						if (!is_a($src, "Decompiler_ListBox")) {
-							$list = new Decompiler_List($this->getOpVal($op1, $EX, false));
+							$op1val = $this->getOpVal($op1, $EX, false);
+							$list = new Decompiler_List(isset($op1val) ? $op1val : '$this');
 
 							$src = new Decompiler_ListBox($list);
@@ -960,6 +1099,10 @@
 						$dim->isLast = true;
 					}
+					if ($opc == XC_UNSET_OBJ) {
+						$dim->isObject = true;
+					}
 					unset($dim);
 					$rvalue = $dimbox;
+					unset($dimbox);
 
 					if ($opc == XC_ASSIGN_DIM) {
@@ -967,8 +1110,8 @@
 						++ $i;
 						$rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
-						$resvar = toCode($lvalue, $EX) . ' = ' . $rvalue;
-					}
-					else if ($opc == XC_UNSET_DIM) {
-						$op['php'] = "unset(" . toCode($rvalue, $EX) . ")";
+						$resvar = str($lvalue, $EX) . ' = ' . str($rvalue);
+					}
+					else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) {
+						$op['php'] = "unset(" . str($rvalue, $EX) . ")";
 						$lastphpop = &$op;
 					}
@@ -991,10 +1134,10 @@
 						$dim->assign = $lvalue;
 						if ($dim->isLast) {
-							$resvar = toCode($dim->value, $EX);
+							$resvar = foldToCode($dim->value, $EX);
 						}
 						unset($dim);
 						break;
 					}
-					$resvar = "$lvalue = " . toCode($rvalue, $EX);
+					$resvar = "$lvalue = " . str($rvalue, $EX);
 					break;
 					// }}}
@@ -1003,6 +1146,6 @@
 					$rvalue = $this->getOpVal($op2, $EX, false);
 					if (is_a($rvalue, 'Decompiler_Fetch')) {
-						$src = toCode($rvalue->src, $EX);
-						if (substr($src, 1, -1) == substr($lvalue, 1)) {
+						$src = str($rvalue->src, $EX);
+						if ('$' . unquoteName($src) == $lvalue) {
 							switch ($rvalue->fetchType) {
 							case ZEND_FETCH_GLOBAL:
@@ -1013,9 +1156,9 @@
 								$statics = &$EX['op_array']['static_variables'];
 								$resvar = 'static ' . $lvalue;
-								$name = substr($src, 1, -1);
+								$name = unquoteName($src);
 								if (isset($statics[$name])) {
 									$var = $statics[$name];
 									$resvar .= ' = ';
-									$resvar .= toCode(value($var), $EX);
+									$resvar .= str(value($var), $EX);
 								}
 								unset($statics);
@@ -1026,5 +1169,5 @@
 					}
 					// TODO: PHP_6 global
-					$rvalue = toCode($rvalue, $EX);
+					$rvalue = str($rvalue, $EX);
 					$resvar = "$lvalue = &$rvalue";
 					break;
@@ -1042,12 +1185,5 @@
 						$obj = '$this';
 					}
-					$prop = $this->getOpVal($op2, $EX);
-					if (preg_match($this->rQuotedName, $prop)) {
-						$prop = substr($prop, 1, -1);;
-						$rvalue = "{$obj}->$prop";
-					}
-					else {
-						$rvalue = "{$obj}->{" . "$prop}";
-					}
+					$rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
 					if ($res['op_type'] != XC_IS_UNUSED) {
 						$resvar = $rvalue;
@@ -1057,5 +1193,5 @@
 						$lvalue = $rvalue;
 						$rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
-						$resvar = "$lvalue = $rvalue";
+						$resvar = "$lvalue = " . str($rvalue);
 					}
 					break;
@@ -1066,11 +1202,5 @@
 				case XC_ISSET_ISEMPTY_VAR: // {{{
 					if ($opc == XC_ISSET_ISEMPTY_VAR) {
-						$rvalue = $this->getOpVal($op1, $EX);;
-						if (preg_match($this->rQuotedName, $rvalue)) {
-							$rvalue = '$' . substr($rvalue, 1, -1);
-						}
-						else {
-							$rvalue = '${' . $rvalue . '}';
-						}
+						$rvalue = $this->getOpVal($op1, $EX);
 						if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
 							$class = $this->getOpVal($op2, $EX);
@@ -1085,13 +1215,11 @@
 						$dim = $this->getOpVal($op2, $EX);
 						if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
-							if (preg_match($this->rQuotedName, $dim)) {
-								$rvalue = $container . "->" . substr($dim, 1, -1);
+							if (!isset($container)) {
+								$container = '$this';
 							}
-							else {
-								$rvalue = $container . "->{" . $dim . "}";
-							}
+							$rvalue = $container . "->" . unquoteVariableName($dim);
 						}
 						else {
-							$rvalue = $container . "[$dim]";
+							$rvalue = $container . '[' . str($dim) .']';
 						}
 					}
@@ -1113,14 +1241,9 @@
 				case XC_SEND_VAR: // {{{
 					$ref = ($opc == XC_SEND_REF ? '&' : '');
-					$EX['argstack'][] = $ref . $this->getOpVal($op1, $EX);
+					$EX['argstack'][] = $ref . str($this->getOpVal($op1, $EX));
 					break;
 					// }}}
 				case XC_INIT_STATIC_METHOD_CALL:
-				case XC_INIT_METHOD_CALL:
-				case XC_INIT_FCALL_BY_FUNC:
-				case XC_INIT_FCALL_BY_NAME: // {{{
-					if (($ext & ZEND_CTOR_CALL)) {
-						break;
-					}
+				case XC_INIT_METHOD_CALL: // {{{
 					array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
 					if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
@@ -1131,5 +1254,5 @@
 						if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) {
 							$EX['object'] = null;
-							$EX['called_scope'] = $this->unquoteName($obj);
+							$EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX));
 						}
 						else {
@@ -1146,11 +1269,26 @@
 					}
 
-					if ($opc == XC_INIT_FCALL_BY_FUNC) {
-						$which = $op1['var'];
-						$EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
-					}
-					else {
-						$EX['fbc'] = $this->getOpVal($op2, $EX, false);
-					}
+					$EX['fbc'] = $this->getOpVal($op2, $EX, false);
+					if (($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL) && !isset($EX['fbc'])) {
+						$EX['fbc'] = '__construct';
+					}
+					break;
+					// }}}
+				case XC_INIT_NS_FCALL_BY_NAME:
+				case XC_INIT_FCALL_BY_NAME: // {{{
+					array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
+					if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
+						break;
+					}
+					$EX['object'] = null;
+					$EX['called_scope'] = null;
+					$EX['fbc'] = $this->getOpVal($op2, $EX);
+					break;
+					// }}}
+				case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
+					$EX['object'] = null;
+					$EX['called_scope'] = null;
+					$which = $op1['var'];
+					$EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
 					break;
 					// }}}
@@ -1162,5 +1300,5 @@
 					break;
 				case XC_DO_FCALL:
-					$fname = $this->unquoteName($this->getOpVal($op1, $EX, false));
+					$fname = unquoteName($this->getOpVal($op1, $EX, false), $EX);
 					$args = $this->popargs($EX, $ext);
 					$resvar = $fname . "($args)";
@@ -1169,5 +1307,5 @@
 					$object = null;
 
-					$fname = $this->unquoteName($EX['fbc']);
+					$fname = unquoteName($EX['fbc'], $EX);
 					if (!is_int($EX['object'])) {
 						$object = $EX['object'];
@@ -1176,8 +1314,9 @@
 					$args = $this->popargs($EX, $ext);
 
-					$resvar =
-						(isset($object) ? $object . '->' : '' )
-						. (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' )
-						. $fname . "($args)";
+					$prefix = (isset($object) ? $object . '->' : '' )
+						. (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' );
+					$resvar = $prefix
+						. (!$prefix ? $this->stripNamespace($fname) : $fname)
+						. "($args)";
 					unset($args);
 
@@ -1203,5 +1342,7 @@
 					}
 					$class = &$this->dc['class_table'][$key];
-					$class['name'] = $this->unquoteName($this->getOpVal($op2, $EX));
+					if (!isset($class['name'])) {
+						$class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX);
+					}
 					if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
 						$ext /= XC_SIZEOF_TEMP_VARIABLE;
@@ -1213,16 +1354,30 @@
 					}
 
-					while ($i + 2 < $ic
-					 && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
-					 && $opcodes[$i + 2]['op1']['var'] == $res['var']
-					 && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
+					for (;;) {
+						if ($i + 1 < $ic
+						 && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
+						 && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
+							// continue
+						}
+						else if ($i + 2 < $ic
+						 && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
+						 && $opcodes[$i + 2]['op1']['var'] == $res['var']
+						 && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
+							// continue
+						}
+						else {
+							break;
+						}
+						$this->usedOps[XC_ADD_INTERFACE] = true;
+
 						$fetchop = &$opcodes[$i + 1];
-						$impl = $this->unquoteName($this->getOpVal($fetchop['op2'], $EX));
+						$interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX));
 						$addop = &$opcodes[$i + 2];
-						$class['interfaces'][$addop['extended_value']] = $impl;
+						$class['interfaces'][$addop['extended_value']] = $interface;
 						unset($fetchop, $addop);
 						$i += 2;
 					}
 					$this->dclass($class);
+					echo "\n";
 					unset($class);
 					break;
@@ -1239,20 +1394,19 @@
 					switch ($opc) {
 					case XC_ADD_CHAR:
-						$op2val = toCode(chr($op2val), $EX);
+						$op2val = value(chr(str($op2val)));
 						break;
 					case XC_ADD_STRING:
-						$op2val = toCode($op2val, $EX);
 						break;
 					case XC_ADD_VAR:
 						break;
 					}
-					if ($op1val == "''") {
+					if (str($op1val) == "''") {
 						$rvalue = $op2val;
 					}
-					else if ($op2val == "''") {
+					else if (str($op2val) == "''") {
 						$rvalue = $op1val;
 					}
 					else {
-						$rvalue = $op1val . ' . ' . $op2val;
+						$rvalue = str($op1val) . ' . ' . str($op2val);
 					}
 					$resvar = $rvalue;
@@ -1261,10 +1415,10 @@
 				case XC_PRINT: // {{{
 					$op1val = $this->getOpVal($op1, $EX);
-					$resvar = "print($op1val)";
+					$resvar = "print(" . str($op1val) . ")";
 					break;
 					// }}}
 				case XC_ECHO: // {{{
 					$op1val = $this->getOpVal($op1, $EX);
-					$resvar = "echo $op1val";
+					$resvar = "echo " . str($op1val);
 					break;
 					// }}}
@@ -1314,5 +1468,5 @@
 					// }}}
 				case XC_RETURN: // {{{
-					$resvar = "return " . $this->getOpVal($op1, $EX);
+					$resvar = "return " . str($this->getOpVal($op1, $EX));
 					break;
 					// }}}
@@ -1320,5 +1474,5 @@
 					$type = $op2['var']; // hack
 					$keyword = $this->includeTypes[$type];
-					$resvar = "$keyword(" . $this->getOpVal($op1, $EX) . ")";
+					$resvar = "$keyword " . str($this->getOpVal($op1, $EX));
 					break;
 					// }}}
@@ -1357,4 +1511,9 @@
 					// }}}
 				case XC_JMP_NO_CTOR:
+					break;
+				case XC_JMP_SET: // ?:
+					$resvar = $this->getOpVal($op1, $EX);
+					$op['cond'] = $resvar; 
+					$op['isjmp'] = true;
 					break;
 				case XC_JMPNZ: // while
@@ -1379,11 +1538,11 @@
 						var_dump($op);// exit;
 					}
-					if ($opc == XC_JMPZ_EX || $opc == XC_JMPNZ_EX || $opc == XC_JMPZ) {
+					if ($opc == XC_JMPZ_EX || $opc == XC_JMPNZ_EX) {
 						$targetop = &$EX['opcodes'][$op2['opline_num']];
 						if ($opc == XC_JMPNZ_EX) {
-							$targetop['cond_true'][] = toCode($rvalue, $EX);
+							$targetop['cond_true'][] = foldToCode($rvalue, $EX);
 						}
 						else {
-							$targetop['cond_false'][] = toCode($rvalue, $EX);
+							$targetop['cond_false'][] = foldToCode($rvalue, $EX);
 						}
 						unset($targetop);
@@ -1395,4 +1554,15 @@
 					break;
 					// }}}
+				case XC_CONT:
+				case XC_BRK:
+					$op['cond'] = null;
+					$op['isjmp'] = true;
+					$resvar = $opc == XC_CONT ? 'continue' : 'break';
+					$count = str($this->getOpVal($op2, $EX));
+					if ($count != '1') {
+						$resvar .= ' ' . $count;
+					}
+					break;
+				case XC_GOTO:
 				case XC_JMP: // {{{
 					$op['cond'] = null;
@@ -1401,5 +1571,7 @@
 					// }}}
 				case XC_CASE:
-				case XC_BRK:
+					$switchValue = $this->getOpVal($op1, $EX);
+					$caseValue = $this->getOpVal($op2, $EX);
+					$resvar = str($switchValue) . ' == ' . str($caseValue);
 					break;
 				case XC_RECV_INIT:
@@ -1425,12 +1597,5 @@
 					$flags = array_flip(explode('_', $opname));
 					if (isset($flags['OBJ'])) {
-						$resvar = $this->getOpVal($op1, $EX);
-						$prop = $this->unquoteName($this->getOpVal($op2, $EX));
-						if ($prop{0} == '$') {
-							$resvar = $resvar . "{" . $prop . "}";
-						}
-						else {
-							$resvar = $resvar . "->" . $prop;
-						}
+						$resvar = $this->getOpVal($op1, $EX) . '->' . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
 					}
 					else {
@@ -1439,8 +1604,8 @@
 					$opstr = isset($flags['DEC']) ? '--' : '++';
 					if (isset($flags['POST'])) {
-						$resvar .= ' ' . $opstr;
+						$resvar .= $opstr;
 					}
 					else {
-						$resvar = "$opstr $resvar";
+						$resvar = "$opstr$resvar";
 					}
 					break;
@@ -1453,8 +1618,5 @@
 				case XC_END_SILENCE: // {{{
 					$EX['silence'] --;
-					$lastresvar = '@' . toCode($lastresvar, $EX);
-					break;
-					// }}}
-				case XC_CONT: // {{{
+					$lastresvar = '@' . str($lastresvar, $EX);
 					break;
 					// }}}
@@ -1480,4 +1642,19 @@
 				case XC_EXT_NOP:
 					break;
+				case XC_DECLARE_FUNCTION:
+					$this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
+					break;
+				case XC_DECLARE_LAMBDA_FUNCTION: // {{{
+					ob_start();
+					$this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
+					$resvar = ob_get_clean();
+					$istmpres = true;
+					break;
+					// }}}
+				case XC_DECLARE_CONST:
+					$name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX));
+					$value = str($this->getOpVal($op2, $EX));
+					$resvar = 'const ' . $name . ' = ' . $value;
+					break;
 				case XC_DECLARE_FUNCTION_OR_CLASS:
 					/* always removed by compiler */
@@ -1487,7 +1664,19 @@
 					// $EX['tickschanged'] = true;
 					break;
+				case XC_RAISE_ABSTRACT_ERROR:
+					// abstract function body is empty, don't need this code
+					break;
+				case XC_USER_OPCODE:
+					echo '// ZEND_USER_OPCODE, impossible to decompile';
+					break;
+				case XC_OP_DATA:
+					break;
 				default: // {{{
 					echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n";
-					// }}}
+					$covered = false;
+					// }}}
+				}
+				if ($covered) {
+					$this->usedOps[$opc] = true;
 				}
 			}
@@ -1523,5 +1712,5 @@
 			$a = array_pop($EX['argstack']);
 			if (is_array($a)) {
-				array_unshift($args, toCode($a, $EX));
+				array_unshift($args, foldToCode($a, $EX));
 			}
 			else {
@@ -1536,16 +1725,16 @@
 		$op1 = $op['op1'];
 		$op2 = $op['op2'];
-		$d = array('opname' => xcache_get_opcode($op['opcode']), 'opcode' => $op['opcode']);
-
-		foreach (array('op1' => 'op1', 'op2' => 'op2', 'result' => 'res') as $k => $kk) {
+		$d = array(xcache_get_opcode($op['opcode']), $op['opcode']);
+
+		foreach (array('op1' => '1:', 'op2' => '2:', 'result' => '>') as $k => $kk) {
 			switch ($op[$k]['op_type']) {
 			case XC_IS_UNUSED:
-				$d[$kk] = '*UNUSED* ' . $op[$k]['opline_num'];
+				$d[$kk] = 'U:' . $op[$k]['opline_num'];
 				break;
 
 			case XC_IS_VAR:
 				$d[$kk] = '$' . $op[$k]['var'];
-				if ($kk != 'res') {
-					$d[$kk] .= ':' . $this->getOpVal($op[$k], $EX);
+				if ($k != 'result') {
+					$d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
 				}
 				break;
@@ -1553,6 +1742,6 @@
 			case XC_IS_TMP_VAR:
 				$d[$kk] = '#' . $op[$k]['var'];
-				if ($kk != 'res') {
-					$d[$kk] .= ':' . $this->getOpVal($op[$k], $EX);
+				if ($k != 'result') {
+					$d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
 				}
 				break;
@@ -1563,5 +1752,5 @@
 
 			default:
-				if ($kk == 'res') {
+				if ($k == 'result') {
 					var_dump($op);
 					exit;
@@ -1573,7 +1762,10 @@
 			}
 		}
-		$d['ext'] = $op['extended_value'];
-
-		var_dump($d);
+		$d[';'] = $op['extended_value'];
+
+		foreach ($d as $k => $v) {
+			echo is_int($k) ? '' : $k, str($v), "\t";
+		}
+		echo PHP_EOL;
 	}
 	// }}}
@@ -1602,5 +1794,5 @@
 				$ai = $op_array['arg_info'][$i];
 				if (!empty($ai['class_name'])) {
-					echo $ai['class_name'], ' ';
+					echo $this->stripNamespace($ai['class_name']), ' ';
 					if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
 						echo 'or NULL ';
@@ -1638,8 +1830,8 @@
 					}
 				}
-				echo toCode($arg[0], $indent);
+				echo str($arg[0], $indent);
 			}
 			if (isset($arg[1])) {
-				echo ' = ', toCode($arg[1], $indent);
+				echo ' = ', str($arg[1], $indent);
 			}
 		}
@@ -1648,6 +1840,7 @@
 	function dfunction($func, $indent = '', $nobody = false) // {{{
 	{
+		$this->detectNamespace($func['op_array']['function_name']);
+
 		if ($nobody) {
-			$body = ";\n";
 			$EX = array();
 			$EX['op_array'] = &$func['op_array'];
@@ -1664,14 +1857,35 @@
 		}
 
-		echo 'function ', $func['op_array']['function_name'], '(';
+		$functionName = $this->stripNamespace($func['op_array']['function_name']);
+		if ($functionName == '{closure}') {
+			$functionName = '';
+		}
+		echo 'function ', $functionName, '(';
 		$this->dargs($EX, $indent);
-		echo ")\n";
-		echo $indent, "{\n";
-		echo $body;
-		echo "$indent}\n";
+		echo ")";
+		if ($nobody) {
+			echo ";\n";
+		}
+		else {
+			if ($functionName !== '') {
+				echo "\n";
+				echo $indent, "{\n";
+			}
+			else {
+				echo " {\n";
+			}
+
+			echo $body;
+			echo "$indent}";
+			if ($functionName !== '') {
+				echo "\n";
+			}
+		}
 	}
 	// }}}
 	function dclass($class, $indent = '') // {{{
 	{
+		$this->detectNamespace($class['name']);
+
 		// {{{ class decl
 		if (!empty($class['doc_comment'])) {
@@ -1683,17 +1897,16 @@
 		if (!empty($class['ce_flags'])) {
 			if ($class['ce_flags'] & ZEND_ACC_INTERFACE) {
-				echo 'interface ';
 				$isinterface = true;
 			}
 			else {
-				if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT) {
+				if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
 					echo "abstract ";
 				}
-				if ($class['ce_flags'] & ZEND_ACC_FINAL) {
+				if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) {
 					echo "final ";
 				}
 			}
 		}
-		echo 'class ', $class['name'];
+		echo $isinterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
 		if ($class['parent']) {
 			echo ' extends ', $class['parent'];
@@ -1717,5 +1930,5 @@
 					echo $newindent;
 					echo $prefix, $name, ' = ';
-					echo toCode(value($v), $newindent);
+					echo str(value($v), $newindent);
 					echo ";\n";
 				}
@@ -1791,5 +2004,5 @@
 				if (isset($value)) {
 					echo ' = ';
-					echo toCode(value($value), $newindent);
+					echo str(value($value), $newindent);
 				}
 				echo ";\n";
@@ -1810,7 +2023,9 @@
 					}
 					echo $newindent;
+					$isAbstractMethod = false;
 					if (isset($opa['fn_flags'])) {
-						if ($opa['fn_flags'] & ZEND_ACC_ABSTRACT) {
+						if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isinterface) {
 							echo "abstract ";
+							$isAbstractMethod = true;
 						}
 						if ($opa['fn_flags'] & ZEND_ACC_FINAL) {
@@ -1836,5 +2051,5 @@
 						}
 					}
-					$this->dfunction($func, $newindent, $isinterface);
+					$this->dfunction($func, $newindent, $isinterface || $isAbstractMethod);
 					if ($opa['function_name'] == 'Decompiler') {
 						//exit;
@@ -1867,9 +2082,9 @@
 	function output() // {{{
 	{
-		echo "<?". "php\n";
+		echo "<?". "php\n\n";
 		foreach ($this->dc['class_table'] as $key => $class) {
 			if ($key{0} != "\0") {
+				$this->dclass($class);
 				echo "\n";
-				$this->dclass($class);
 			}
 		}
@@ -1877,43 +2092,34 @@
 		foreach ($this->dc['function_table'] as $key => $func) {
 			if ($key{0} != "\0") {
+				$this->dfunction($func);
 				echo "\n";
-				$this->dfunction($func);
-			}
-		}
-
-		echo "\n";
+			}
+		}
+
 		$this->dop_array($this->dc['op_array']);
 		echo "\n?" . ">\n";
+
+		if (!empty($this->test)) {
+			$this->outputUnusedOp();
+		}
 		return true;
 	}
 	// }}}
+	function outputUnusedOp() // {{{
+	{
+		for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
+			if ($opname == 'UNDEF')  {
+				continue;
+			}
+
+			if (!isset($this->usedOps[$i])) {
+				echo "not covered opcode ", $opname, "\n";
+			}
+		}
+	}
+	// }}}
 }
 
 // {{{ defines
-define('ZEND_ACC_STATIC',         0x01);
-define('ZEND_ACC_ABSTRACT',       0x02);
-define('ZEND_ACC_FINAL',          0x04);
-define('ZEND_ACC_IMPLEMENTED_ABSTRACT',       0x08);
-
-define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS',    0x10);
-define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS',    0x20);
-define('ZEND_ACC_FINAL_CLASS',                0x40);
-define('ZEND_ACC_INTERFACE',                  0x80);
-define('ZEND_ACC_PUBLIC',     0x100);
-define('ZEND_ACC_PROTECTED',  0x200);
-define('ZEND_ACC_PRIVATE',    0x400);
-define('ZEND_ACC_PPP_MASK',  (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
-
-define('ZEND_ACC_CHANGED',    0x800);
-define('ZEND_ACC_IMPLICIT_PUBLIC',    0x1000);
-
-define('ZEND_ACC_CTOR',       0x2000);
-define('ZEND_ACC_DTOR',       0x4000);
-define('ZEND_ACC_CLONE',      0x8000);
-
-define('ZEND_ACC_ALLOW_STATIC',   0x10000);
-
-define('ZEND_ACC_SHADOW', 0x2000);
-
 define('ZEND_ENGINE_2_4', PHP_VERSION >= "5.3.99");
 define('ZEND_ENGINE_2_3', ZEND_ENGINE_2_4 || PHP_VERSION >= "5.3.");
@@ -1921,4 +2127,32 @@
 define('ZEND_ENGINE_2_1', ZEND_ENGINE_2_2 || PHP_VERSION >= "5.1.");
 define('ZEND_ENGINE_2',   ZEND_ENGINE_2_1 || PHP_VERSION >= "5.0.");
+
+define('ZEND_ACC_STATIC',         0x01);
+define('ZEND_ACC_ABSTRACT',       0x02);
+define('ZEND_ACC_FINAL',          0x04);
+define('ZEND_ACC_IMPLEMENTED_ABSTRACT',       0x08);
+
+define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS',    0x10);
+define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS',    0x20);
+define('ZEND_ACC_FINAL_CLASS',                0x40);
+define('ZEND_ACC_INTERFACE',                  0x80);
+if (ZEND_ENGINE_2_4) {
+	define('ZEND_ACC_TRAIT',                  0x120);
+}
+define('ZEND_ACC_PUBLIC',     0x100);
+define('ZEND_ACC_PROTECTED',  0x200);
+define('ZEND_ACC_PRIVATE',    0x400);
+define('ZEND_ACC_PPP_MASK',  (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
+
+define('ZEND_ACC_CHANGED',    0x800);
+define('ZEND_ACC_IMPLICIT_PUBLIC',    0x1000);
+
+define('ZEND_ACC_CTOR',       0x2000);
+define('ZEND_ACC_DTOR',       0x4000);
+define('ZEND_ACC_CLONE',      0x8000);
+
+define('ZEND_ACC_ALLOW_STATIC',   0x10000);
+
+define('ZEND_ACC_SHADOW', 0x2000);
 
 if (ZEND_ENGINE_2_4) {
@@ -1990,8 +2224,8 @@
 define('IS_LONG',     1);
 define('IS_DOUBLE',   2);
-define('IS_STRING',   3);
+define('IS_BOOL',     ZEND_ENGINE_2 ? 3 : 6);
 define('IS_ARRAY',    4);
 define('IS_OBJECT',   5);
-define('IS_BOOL',     6);
+define('IS_STRING',   ZEND_ENGINE_2 ? 6 : 3);
 define('IS_RESOURCE', 7);
 define('IS_CONSTANT', 8);
@@ -2019,5 +2253,5 @@
 	'XC_ASSIGN_DIM' => -1,
 	'XC_UNSET_DIM' => -1,
-	'XC_FETCH_OBJ_' => -1,
+	'XC_UNSET_OBJ' => -1,
 	'XC_ASSIGN_OBJ' => -1,
 	'XC_ISSET_ISEMPTY_DIM_OBJ' => -1,
@@ -2040,9 +2274,19 @@
 	'XC_FETCH_DIM_' => -1,
 	'XC_UNSET_DIM_OBJ' => -1,
-	'XC_FETCH_OBJ_' => -1,
 	'XC_ISSET_ISEMPTY' => -1,
 	'XC_INIT_FCALL_BY_FUNC' => -1,
 	'XC_DO_FCALL_BY_FUNC' => -1,
 	'XC_DECLARE_FUNCTION_OR_CLASS' => -1,
+	'XC_INIT_NS_FCALL_BY_NAME' => -1,
+	'XC_GOTO' => -1,
+	'XC_CATCH' => -1,
+	'XC_THROW' => -1,
+	'XC_INSTANCEOF' => -1,
+	'XC_DECLARE_FUNCTION' => -1,
+	'XC_RAISE_ABSTRACT_ERROR' => -1,
+	'XC_DECLARE_CONST' => -1,
+	'XC_USER_OPCODE' => -1,
+	'XC_JMP_SET' => -1,
+	'XC_DECLARE_LAMBDA_FUNCTION' => -1,
 ) as $k => $v) {
 	if (!defined($k)) {
@@ -2050,13 +2294,4 @@
 	}
 }
-
-/* XC_UNDEF XC_OP_DATA
-$content = file_get_contents(__FILE__);
-for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
-	if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
-		echo "not done ", $opname, "\n";
-	}
-}
-// */
 // }}}
 
Index: /branches/1.3/NEWS
===================================================================
--- /branches/1.3/NEWS	(revision 765)
+++ /branches/1.3/NEWS	(revision 766)
@@ -1,4 +1,6 @@
 1.3.2 2011-??-??
 ========
+ * adds 30 seconds timeout to "compiling" flag
+ * improves decompiling
  * memory leak on recompile
  * disassembler fixes and updates for new PHP
Index: /branches/1.3/decompilesample.php
===================================================================
--- /branches/1.3/decompilesample.php	(revision 765)
+++ /branches/1.3/decompilesample.php	(revision 766)
@@ -1,6 +1,12 @@
 <?php
 
-class ClassName
-{
+//* >= PHP 5.3
+namespace ns;
+// */
+
+abstract class ClassName
+{
+	const CONST_VALUE = 'A constant value';
+
 	/** doc */
 	static public $static = array(
@@ -29,5 +35,21 @@
 	public function __construct($a, $b)
 	{
-	}
+		echo CONST_VALUE;
+		echo ClassName::CONST_VALUE;
+		unset(ClassName::$classProp);
+		unset($obj->objProp);
+		unset($this->thisProp);
+		unset($array['index']->valueProp);
+		unset($obj->array['index']);
+		unset($this->array['index']);
+		$obj->objProp = 1;
+		$this->thisProp = 1;
+		$array['index']->valueProp = 1;
+		$array['index'] = 1;
+		$array[1] = 1;
+	}
+
+	/** doc */
+	abstract public function abastractMethod();
 
 	/** doc */
@@ -65,4 +87,285 @@
 }
 
+interface IInterface
+{
+	public function nothing();
+}
+
+function f1($f)
+{
+	echo __FUNCTION__;
+	echo $f;
+}
+
+final class Child extends ClassName implements IInterface
+{
+	public function __construct()
+	{
+		parent::__construct();
+		ClassName::__construct();
+		echo __CLASS__;
+		echo __METHOD__;
+		throw new Exception();
+		$this->methodCall();
+	}
+
+	public function __destruct()
+	{
+		parent::__destruct();
+		functionCall();
+	}
+
+	static public function __callStatic($name, $args)
+	{
+	}
+
+	public function __toString()
+	{
+	}
+
+	public function __set($name, $value)
+	{
+	}
+
+	public function __get($name)
+	{
+	}
+
+	public function __isset($name)
+	{
+	}
+
+	public function __unset($name)
+	{
+	}
+
+	public function __sleep()
+	{
+	}
+
+	public function __wakeup()
+	{
+	}
+
+	public function __clone()
+	{
+		return array();
+	}
+}
+
+if ($late) {
+class LateBindingClass
+{}
+
+function lateBindingFunction($arg)
+{
+	echo 'lateFunction';
+}
+}
+
+echo "\r\n";
+echo "\r";
+echo "\n";
+echo str_replace(array('a' => 'a', 'b' => 'c'), 'b');
+$object = new ClassName();
+$object = new $className();
+$result = $object instanceof ClassName;
+$cloned = clone $object;
+$a = 1;
+$a = $b + $c;
+$a = $b + 1;
+$a = 1 + $b;
+$a = $b - $c;
+$a = $b * $c;
+$a = $b / $c;
+$a = $b % $c;
+$a = $b . $c;
+$a = $b = $c;
+$a = $b & $c;
+$a = $b | $c;
+$a = $b ^ $c;
+$a = ~$b;
+$a = -$b;
+$a = +$b;
+$a = $b >> $c;
+$a = $b >> $c;
+$a = $b == $c;
+$a = $b === $c;
+$a = $b != $c;
+$a = $b < $c;
+$a = $b <= $c;
+$a = $b <= $c;
+$a = $b++;
+$a = ++$b;
+$a = $obj->b++;
+$a = ++$obj->b;
+$a = $b--;
+$a = --$b;
+$a = $obj->b--;
+$a = --$obj->b;
+$a = $b xor $c;
+$a = !$b;
+$a = $b === $c;
+$a = $b !== $c;
+$a = $b << 2;
+$a = $b >> 3;
+$a += $b;
+$a -= $b;
+$a *= $b;
+$a /= $b;
+$a <<= $b;
+$a >>= $b;
+$a &= $b;
+$a |= $b;
+$a .= $b;
+$a %= $b;
+$a ^= $b;
+$a = 'a' . 'b';
+$a = 'a' . 'abc';
+@f1();
+print('1');
+ref(&$a);
+$a = $array['index'];
+$a = $object->prop;
+$a = $this->prop;
+$array['index'] = 1;
+$object->prop = 1;
+$this->prop = 1;
+$a = isset($b);
+$a = empty($b);
+$a = isset($array['index']);
+$a = empty($array['index']);
+$a = isset($object->prop);
+$a = empty($object->prop);
+$a = isset($this->prop);
+$a = empty($this->prop);
+$a = (int) $b;
+$a = (double) $b;
+$a = (string) $b;
+$a = (array) $b;
+$a = (object) $b;
+$a = (bool) $b;
+$a = (unset) $b;
+$a = (array) $b;
+$a = (object) $b;
+// PHP6+ $a = (scalar) $b;
+$a = $b ? $c : $d;
+$a = f1() ? f2() : f3();
+$a = $b and $c;
+$a = $b or $c;
+$a = $b && $c;
+$a = $b || $c;
+
+try {
+	echo 'outer try 1';
+	try {
+		echo 'inner try';
+	}
+	catch (InnerException $e) {
+		echo $e;
+	}
+	echo 'outer try 2';
+}
+catch (OuterException $e) {
+	echo $e;
+}
+
+if ($a) {
+	echo 'if ($a)';
+}
+else if ($b) {
+	echo 'else if ($b)';
+}
+else {
+	echo 'else';
+}
+
+while (false) {
+	echo 'while';
+}
+
+do {
+	echo 'do/while';
+} while (false);
+
+for ($i = 1; $i < 10; ++$i) {
+	echo $i;
+	break;
+}
+
+foreach ($array as $key => $value) {
+	foreach ($array as $key => $value) {
+		echo $key . ' = ' . $value . "\n";
+		break 2;
+		continue;
+	}
+}
+
+switch ($switch) {
+case 'case1':
+	echo 'case1';
+	break;
+
+case 'case2':
+	echo 'case2';
+	break;
+
+default:
+	echo 'default';
+	break;
+}
+
+declare (ticks=1) {
+	echo 1;
+	echo 2;
+}
+
+require 'require.php';
+require_once 'require_once.php';
+include 'include.php';
+include_once 'include_once.php';
+echo __FILE__;
+echo __LINE__;
+
+//*
+echo 'PHP 5.3+ code testing';
+const CONST_VALUE = 1;
+echo $this::CONST_VALUE;
+echo $a::CONST_VALUE;
+echo CONST_VALUE;
+$this::__construct();
+$obj::__construct();
+
+$a = $b ?: $d;
+$a = ($b ?: $d) + $c;
+$a = f1() ?: f2();
+
+echo 'goto a';
+goto a;
+
+for ($i = 1; $i <= 2; ++$i) {
+	goto a;
+}
+
+a:
+echo 'label a';
+echo preg_replace_callback('~-([a-z])~', function ($match) {
+	return strtoupper($match[1]);
+}, 'hello-world');
+$greet = function ($name) {
+	printf("Hello %s\r\n", $name);
+};
+$greet('World');
+$greet('PHP');
+$total = 0;
+$tax = 1;
+$callback = function ($quantity, $product) use ($tax, &$total) {
+	static $static = array(1);
+	$tax = 'tax';
+	$pricePerItem = constant('PRICE_' . strtoupper($product));
+	$total += $pricePerItem * $quantity * ($tax + 1);
+};
+// */
+exit();
 
 ?>
Index: /branches/1.3/disassembler.c
===================================================================
--- /branches/1.3/disassembler.c	(revision 765)
+++ /branches/1.3/disassembler.c	(revision 766)
@@ -37,26 +37,15 @@
 	add_assoc_zval_ex(dst, ZEND_STRS("op_array"), zv);
 
+	buf = emalloc(bufsize);
+
 	ALLOC_INIT_ZVAL(list);
 	array_init(list);
 	b = TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead;
 	for (; b; b = b->pListNext) {
+		int keysize, keyLength;
+
 		ALLOC_INIT_ZVAL(zv);
 		array_init(zv);
 		xc_dasm_zend_function(zv, b->pData TSRMLS_CC);
-
-		add_u_assoc_zval_ex(list, BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, zv);
-	}
-	add_assoc_zval_ex(dst, ZEND_STRS("function_table"), list);
-	
-	buf = emalloc(bufsize);
-	ALLOC_INIT_ZVAL(list);
-	array_init(list);
-	b = TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead;
-	for (; b; b = b->pListNext) {
-		int keysize, keyLength;
-
-		ALLOC_INIT_ZVAL(zv);
-		array_init(zv);
-		xc_dasm_zend_class_entry(zv, CestToCePtr(*(xc_cest_t *)b->pData) TSRMLS_CC);
 
 		keysize = BUCKET_KEY_SIZE(b) + 2;
@@ -82,4 +71,41 @@
 			}
 		}
+
+		add_u_assoc_zval_ex(list, BUCKET_KEY_TYPE(b), ZSTR(buf), keyLength, zv);
+	}
+	add_assoc_zval_ex(dst, ZEND_STRS("function_table"), list);
+	
+	ALLOC_INIT_ZVAL(list);
+	array_init(list);
+	b = TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead;
+	for (; b; b = b->pListNext) {
+		int keysize, keyLength;
+
+		ALLOC_INIT_ZVAL(zv);
+		array_init(zv);
+		xc_dasm_zend_class_entry(zv, CestToCePtr(*(xc_cest_t *)b->pData) TSRMLS_CC);
+
+		keysize = BUCKET_KEY_SIZE(b) + 2;
+		if (keysize > bufsize) {
+			do {
+				bufsize *= 2;
+			} while (keysize > bufsize);
+			buf = erealloc(buf, bufsize);
+		}
+		memcpy(buf, BUCKET_KEY_S(b), keysize);
+		buf[keysize - 2] = buf[keysize - 1] = ""[0];
+		keyLength = b->nKeyLength;
+#ifdef IS_UNICODE
+		if (BUCKET_KEY_TYPE(b) == IS_UNICODE) {
+			if (buf[0] == ""[0] && buf[1] == ""[0]) {
+				keyLength ++;
+			}
+		} else
+#endif
+		{
+			if (buf[0] == ""[0]) {
+				keyLength ++;
+			}
+		}
 		add_u_assoc_zval_ex(list, BUCKET_KEY_TYPE(b), ZSTR(buf), keyLength, zv);
 	}
Index: /branches/1.3/phpdop.phpr
===================================================================
--- /branches/1.3/phpdop.phpr	(revision 765)
+++ /branches/1.3/phpdop.phpr	(revision 766)
@@ -110,6 +110,8 @@
 }
 var_dump($pk);
-if (isset($op_array)) {
-	dump_opcodes($op_array['opcodes']);
+if (isset($classes)) {
+	foreach ($classes as $name => $class) {
+		dump_class($name, $class);
+	}
 }
 if (isset($funcs)) {
@@ -118,8 +120,6 @@
 	}
 }
-if (isset($classes)) {
-	foreach ($classes as $name => $class) {
-		dump_class($name, $class);
-	}
+if (isset($op_array)) {
+	dump_opcodes($op_array['opcodes']);
 }
 
Index: /branches/1.3/processor/struct.m4
===================================================================
--- /branches/1.3/processor/struct.m4	(revision 765)
+++ /branches/1.3/processor/struct.m4	(revision 766)
@@ -33,5 +33,5 @@
 			/* {{{ init assert */
 			ifdef(`SIZEOF_$1', , `m4_errprint(`missing SIZEOF_$1, safe to ignore')')
-			ifdef(`COUNTOF_$1', , `m4_errprint(`missing COUNTOF_$1, safe to ignore'))')
+			ifdef(`COUNTOF_$1', , `m4_errprint(`missing COUNTOF_$1, safe to ignore')')
 			dnl SIZEOF_x COUNTOF_x can be both defined or both not
 			ifdef(`SIZEOF_$1', `
Index: /branches/1.3/utils.c
===================================================================
--- /branches/1.3/utils.c	(revision 765)
+++ /branches/1.3/utils.c	(revision 766)
@@ -244,4 +244,10 @@
 			case IS_TMP_VAR:
 				break;
+
+			case IS_CONST:
+				if (spec == OPSPEC_UCLASS) {
+					break;
+				}
+				/* fall */
 
 			default:
Index: /branches/1.3/xcache.c
===================================================================
--- /branches/1.3/xcache.c	(revision 765)
+++ /branches/1.3/xcache.c	(revision 766)
@@ -1001,5 +1001,5 @@
 	/* {{{ restore */
 	/* stale precheck */
-	if (cache->compiling) {
+	if (XG(request_time) - cache->compiling < 30) {
 		cache->clogs ++; /* is it safe here? */
 		return old_compile_file(h, type TSRMLS_CC);
@@ -1010,5 +1010,5 @@
 	ENTER_LOCK_EX(cache) {
 		/* clogged */
-		if (cache->compiling) {
+		if (XG(request_time) - cache->compiling < 30) {
 			cache->clogs ++;
 			op_array = NULL;
