Example #1
0
function gen_vm($def, $skel)
{
    global $definition_file, $skeleton_file, $executor_file, $op_types, $list, $opcodes, $helpers, $params, $opnames, $vm_op_flags;
    // Load definition file
    $in = @file($def);
    if (!$in) {
        die("ERROR: Can not open definition file '{$def}'\n");
    }
    // We need absolute path to definition file to use it in #line directives
    $definition_file = realpath($def);
    // Load skeleton file
    $skl = @file($skel);
    if (!$skl) {
        die("ERROR: Can not open skeleton file '{$skel}'\n");
    }
    // We need absolute path to skeleton file to use it in #line directives
    $skeleton_file = realpath($skel);
    // Parse definition file into tree
    $lineno = 0;
    $handler = null;
    $helper = null;
    $max_opcode_len = 0;
    $max_opcode = 0;
    $export = array();
    foreach ($in as $line) {
        ++$lineno;
        if (strpos($line, "ZEND_VM_HANDLER(") === 0) {
            // Parsing opcode handler's definition
            if (preg_match("/^ZEND_VM_HANDLER\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(,\\s*([A-Z_|]+)\\s*)?\\)/", $line, $m) == 0) {
                die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HANDLER definition.\n");
            }
            $code = (int) $m[1];
            $op = $m[2];
            $len = strlen($op);
            $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1);
            $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2);
            $flags = $flags1 | $flags2 << 8;
            if (isset($m[6])) {
                $flags |= parse_ext_spec($def, $lineno, $m[6]);
            }
            if ($len > $max_opcode_len) {
                $max_opcode_len = $len;
            }
            if ($code > $max_opcode) {
                $max_opcode = $code;
            }
            if (isset($opcodes[$code])) {
                die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n");
            }
            if (isset($opnames[$op])) {
                die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n");
            }
            $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "", "flags" => $flags);
            $opnames[$op] = $code;
            $handler = $code;
            $helper = null;
            $list[$lineno] = array("handler" => $handler);
        } else {
            if (strpos($line, "ZEND_VM_HELPER(") === 0) {
                // Parsing helper's definition
                if (preg_match("/^ZEND_VM_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*\\)/", $line, $m) == 0) {
                    die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n");
                }
                $helper = $m[1];
                $op1 = parse_operand_spec($def, $lineno, $m[2], $flags1);
                $op2 = parse_operand_spec($def, $lineno, $m[3], $flags2);
                if (isset($helpers[$helper])) {
                    die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n");
                }
                $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => null, "code" => "");
                $handler = null;
                $list[$lineno] = array("helper" => $helper);
            } else {
                if (strpos($line, "ZEND_VM_HELPER_EX(") === 0) {
                    // Parsing helper with parameter definition
                    if (preg_match("/^ZEND_VM_HELPER_EX\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*(.*)\\s*\\)/", $line, $m) == 0) {
                        die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n");
                    }
                    $helper = $m[1];
                    $op1 = parse_operand_spec($def, $lineno, $m[2], $flags1);
                    $op2 = parse_operand_spec($def, $lineno, $m[3], $flags2);
                    $param = $m[4];
                    if (isset($helpers[$helper])) {
                        die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n");
                    }
                    // Store parameter
                    $params[$param] = 1;
                    $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => $param, "code" => "");
                    $handler = null;
                    $list[$lineno] = array("helper" => $helper);
                } else {
                    if (strpos($line, "ZEND_VM_EXPORT_HANDLER(") === 0) {
                        if (preg_match("/^ZEND_VM_EXPORT_HANDLER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_]+)\\s*\\)/", $line, $m) == 0) {
                            die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HANDLER definition.\n");
                        }
                        if (!isset($opnames[$m[2]])) {
                            die("ERROR ({$def}:{$lineno}): opcode '{$m[2]}' is not defined.\n");
                        }
                        $export[] = array("handler", $m[1], $m[2]);
                    } else {
                        if (strpos($line, "ZEND_VM_EXPORT_HELPER(") === 0) {
                            if (preg_match("/^ZEND_VM_EXPORT_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Za-z_]+)\\s*\\)/", $line, $m) == 0) {
                                die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HELPER definition.\n");
                            }
                            if (!isset($helpers[$m[2]])) {
                                die("ERROR ({$def}:{$lineno}): helper '{$m[2]}' is not defined.\n");
                            }
                            $export[] = array("helper", $m[1], $m[2]);
                        } else {
                            if (strpos($line, "ZEND_VM_DEFINE_OP(") === 0) {
                                if (preg_match("/^ZEND_VM_DEFINE_OP\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*\\);/", $line, $m) == 0) {
                                    die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_DEFINE_OP definition.\n");
                                }
                                $code = (int) $m[1];
                                $op = $m[2];
                                $len = strlen($op);
                                if ($len > $max_opcode_len) {
                                    $max_opcode_len = $len;
                                }
                                if ($code > $max_opcode) {
                                    $max_opcode = $code;
                                }
                                if (isset($opcodes[$code])) {
                                    die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n");
                                }
                                if (isset($opnames[$op])) {
                                    die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n");
                                }
                                $opcodes[$code] = array("op" => $op, "code" => "");
                                $opnames[$op] = $code;
                            } else {
                                if ($handler !== null) {
                                    // Add line of code to current opcode handler
                                    $opcodes[$handler]["code"] .= $line;
                                } else {
                                    if ($helper !== null) {
                                        // Add line of code to current helper
                                        $helpers[$helper]["code"] .= $line;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    ksort($opcodes);
    // Search for opcode handlers those are used by other opcode handlers
    foreach ($opcodes as $dsc) {
        if (preg_match("/ZEND_VM_DISPATCH_TO_HANDLER\\(\\s*([A-Z_]*)\\s*\\)/m", $dsc["code"], $m)) {
            $op = $m[1];
            if (!isset($opnames[$op])) {
                die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is not defined.\n");
            }
            $code = $opnames[$op];
            $opcodes[$code]['use'] = 1;
        }
    }
    // Generate opcode #defines (zend_vm_opcodes.h)
    $code_len = strlen((string) $max_opcode);
    $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n");
    // Insert header
    out($f, $GLOBALS['header_text']);
    fputs($f, "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n");
    fputs($f, "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n");
    fputs($f, "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n");
    fputs($f, "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n");
    fputs($f, "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n");
    fputs($f, "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n");
    fputs($f, "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n");
    fputs($f, "\n");
    foreach ($vm_op_flags as $name => $val) {
        fprintf($f, "#define %-24s 0x%08x\n", $name, $val);
    }
    fputs($f, "\n");
    fputs($f, "BEGIN_EXTERN_C()\n\n");
    fputs($f, "ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);\n");
    fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode);\n\n");
    fputs($f, "END_EXTERN_C()\n\n");
    foreach ($opcodes as $code => $dsc) {
        $code = str_pad((string) $code, $code_len, " ", STR_PAD_LEFT);
        $op = str_pad($dsc["op"], $max_opcode_len);
        fputs($f, "#define {$op} {$code}\n");
    }
    $code = str_pad((string) $max_opcode, $code_len, " ", STR_PAD_LEFT);
    $op = str_pad("ZEND_VM_LAST_OPCODE", $max_opcode_len);
    fputs($f, "\n#define {$op} {$code}\n");
    fputs($f, "\n#endif\n");
    fclose($f);
    echo "zend_vm_opcodes.h generated successfully.\n";
    // zend_vm_opcodes.c
    $f = fopen(__DIR__ . "/zend_vm_opcodes.c", "w+") or die("ERROR: Cannot create zend_vm_opcodes.c\n");
    // Insert header
    out($f, $GLOBALS['header_text']);
    fputs($f, "#include <stdio.h>\n");
    fputs($f, "#include <zend.h>\n\n");
    fputs($f, "static const char *zend_vm_opcodes_names[" . ($max_opcode + 1) . "] = {\n");
    for ($i = 0; $i <= $max_opcode; $i++) {
        fputs($f, "\t" . (isset($opcodes[$i]["op"]) ? '"' . $opcodes[$i]["op"] . '"' : "NULL") . ",\n");
    }
    fputs($f, "};\n\n");
    fputs($f, "static uint32_t zend_vm_opcodes_flags[" . ($max_opcode + 1) . "] = {\n");
    for ($i = 0; $i <= $max_opcode; $i++) {
        fprintf($f, "\t0x%08x,\n", isset($opcodes[$i]["flags"]) ? $opcodes[$i]["flags"] : 0);
    }
    fputs($f, "};\n\n");
    fputs($f, "ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {\n");
    fputs($f, "\treturn zend_vm_opcodes_names[opcode];\n");
    fputs($f, "}\n");
    fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode) {\n");
    fputs($f, "\treturn zend_vm_opcodes_flags[opcode];\n");
    fputs($f, "}\n");
    fclose($f);
    echo "zend_vm_opcodes.c generated successfully.\n";
    // Generate zend_vm_execute.h
    $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n");
    $executor_file = realpath(__DIR__ . "/zend_vm_execute.h");
    // Insert header
    out($f, $GLOBALS['header_text']);
    out($f, "#ifdef ZEND_WIN32\n");
    // Suppress free_op1 warnings on Windows
    out($f, "# pragma warning(once : 4101)\n");
    if (ZEND_VM_SPEC) {
        // Suppress (<non-zero constant> || <expression>) warnings on windows
        out($f, "# pragma warning(once : 6235)\n");
        // Suppress (<zero> && <expression>) warnings on windows
        out($f, "# pragma warning(once : 6237)\n");
        // Suppress (<non-zero constant> && <expression>) warnings on windows
        out($f, "# pragma warning(once : 6239)\n");
        // Suppress (<expression> && <non-zero constant>) warnings on windows
        out($f, "# pragma warning(once : 6240)\n");
        // Suppress (<non-zero constant> || <non-zero constant>) warnings on windows
        out($f, "# pragma warning(once : 6285)\n");
        // Suppress (<non-zero constant> || <expression>) warnings on windows
        out($f, "# pragma warning(once : 6286)\n");
        // Suppress constant with constant comparison warnings on windows
        out($f, "# pragma warning(once : 6326)\n");
    }
    out($f, "#endif\n");
    // Support for ZEND_USER_OPCODE
    out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n");
    for ($i = 0; $i < 255; ++$i) {
        out($f, "\t(user_opcode_handler_t)NULL,\n");
    }
    out($f, "\t(user_opcode_handler_t)NULL\n};\n\n");
    out($f, "static zend_uchar zend_user_opcodes[256] = {");
    for ($i = 0; $i < 255; ++$i) {
        if ($i % 16 == 1) {
            out($f, "\n\t");
        }
        out($f, "{$i},");
    }
    out($f, "255\n};\n\n");
    // Generate specialized executor
    gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers");
    // Generate zend_vm_get_opcode_handler() function
    out($f, "static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)\n");
    out($f, "{\n");
    if (!ZEND_VM_SPEC) {
        out($f, "\treturn zend_opcode_handlers[opcode];\n");
    } else {
        out($f, "\t\tstatic const int zend_vm_decode[] = {\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 0              */\n");
        out($f, "\t\t\t_CONST_CODE,  /* 1 = IS_CONST   */\n");
        out($f, "\t\t\t_TMP_CODE,    /* 2 = IS_TMP_VAR */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 3              */\n");
        out($f, "\t\t\t_VAR_CODE,    /* 4 = IS_VAR     */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 5              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 6              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 7              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 8 = IS_UNUSED  */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 9              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 10             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 11             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 12             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 13             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 14             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 15             */\n");
        out($f, "\t\t\t_CV_CODE      /* 16 = IS_CV     */\n");
        out($f, "\t\t};\n");
        out($f, "\t\treturn zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];\n");
    }
    out($f, "}\n\n");
    // Generate zend_vm_get_opcode_handler() function
    out($f, "ZEND_API void zend_vm_set_opcode_handler(zend_op* op)\n");
    out($f, "{\n");
    out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n");
    out($f, "}\n\n");
    // Generate zend_vm_call_opcode_handler() function
    if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) {
        out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
        out($f, "{\n");
        out($f, "\tint ret;\n");
        out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
        out($f, "\tconst zend_op *orig_opline = opline;\n");
        out($f, "#endif\n");
        out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
        out($f, "\tzend_execute_data *orig_execute_data = execute_data;\n");
        out($f, "\texecute_data = ex;\n");
        out($f, "#else\n");
        out($f, "\tzend_execute_data *execute_data = ex;\n");
        out($f, "#endif\n");
        out($f, "\n");
        out($f, "\tLOAD_OPLINE();\n");
        out($f, "#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
        out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
        out($f, "\tif (EXPECTED(opline)) {\n");
        out($f, "\t\tret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0;\n");
        out($f, "\t\tSAVE_OPLINE();\n");
        out($f, "\t} else {\n");
        out($f, "\t\tret = -1;\n");
        out($f, "\t}\n");
        out($f, "#else\n");
        out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
        out($f, "\tSAVE_OPLINE();\n");
        out($f, "#endif\n");
        out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
        out($f, "\texecute_data = orig_execute_data;\n");
        out($f, "#endif\n");
        out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
        out($f, "\topline = orig_opline;\n");
        out($f, "#endif\n");
        out($f, "\treturn ret;\n");
        out($f, "}\n\n");
    } else {
        out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
        out($f, "{\n");
        out($f, "\tzend_error_noreturn(E_CORE_ERROR, \"zend_vm_call_opcode_handler() is not supported\");\n");
        out($f, "\treturn 0;\n");
        out($f, "}\n\n");
    }
    // Export handlers and helpers
    if (count($export) > 0 && ZEND_VM_KIND != ZEND_VM_KIND_CALL) {
        out($f, "#undef OPLINE\n");
        out($f, "#undef DCL_OPLINE\n");
        out($f, "#undef USE_OPLINE\n");
        out($f, "#undef LOAD_OPLINE\n");
        out($f, "#undef LOAD_NEXT_OPLINE\n");
        out($f, "#undef SAVE_OPLINE\n");
        out($f, "#define OPLINE EX(opline)\n");
        out($f, "#define DCL_OPLINE\n");
        out($f, "#define USE_OPLINE const zend_op *opline = EX(opline);\n");
        out($f, "#define LOAD_OPLINE()\n");
        out($f, "#define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()\n");
        out($f, "#define SAVE_OPLINE()\n");
        out($f, "#undef HANDLE_EXCEPTION\n");
        out($f, "#undef HANDLE_EXCEPTION_LEAVE\n");
        out($f, "#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
        out($f, "#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
        out($f, "#undef ZEND_VM_CONTINUE\n");
        out($f, "#undef ZEND_VM_RETURN\n");
        out($f, "#undef ZEND_VM_ENTER\n");
        out($f, "#undef ZEND_VM_LEAVE\n");
        out($f, "#undef ZEND_VM_DISPATCH\n");
        out($f, "#define ZEND_VM_CONTINUE()   return  0\n");
        out($f, "#define ZEND_VM_RETURN()     return -1\n");
        out($f, "#define ZEND_VM_ENTER()      return  1\n");
        out($f, "#define ZEND_VM_LEAVE()      return  2\n");
        out($f, "#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n\n");
        out($f, "\n");
    }
    foreach ($export as $dsk) {
        list($kind, $func, $name) = $dsk;
        out($f, "ZEND_API int {$func}(");
        if ($kind == "handler") {
            out($f, "ZEND_OPCODE_HANDLER_ARGS)\n");
            $code = $opcodes[$opnames[$name]]['code'];
        } else {
            $h = $helpers[$name];
            if ($h['param'] == null) {
                out($f, "ZEND_OPCODE_HANDLER_ARGS)\n");
            } else {
                out($f, $h['param'] . " ZEND_OPCODE_HANDLER_ARGS_DC)\n");
            }
            $code = $h['code'];
        }
        $done = 0;
        if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) {
            if ($kind == "handler") {
                $op = $opcodes[$opnames[$name]];
                if (isset($op['op1']["ANY"]) && isset($op['op2']["ANY"])) {
                    out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                    $done = 1;
                }
            } else {
                if ($helpers[$name]["param"] == null) {
                    $h = $helpers[$name];
                    if (isset($h['op1']["ANY"]) && isset($h['op2']["ANY"])) {
                        out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                        $done = 1;
                    }
                }
            }
        }
        if (!$done) {
            gen_code($f, 0, ZEND_VM_KIND_CALL, 1, $code, 'ANY', 'ANY', $name);
        }
    }
    fclose($f);
    echo "zend_vm_execute.h generated successfully.\n";
}
Example #2
0
function gen_vm($def, $skel)
{
    global $definition_file, $skeleton_file, $executor_file, $op_types, $list, $opcodes, $helpers, $params, $opnames;
    // Load definition file
    $in = @file($def);
    if (!$in) {
        die("ERROR: Can not open definition file '{$def}'\n");
    }
    // We need absolute path to definition file to use it in #line directives
    $definition_file = realpath($def);
    // Load skeleton file
    $skl = @file($skel);
    if (!$skl) {
        die("ERROR: Can not open skeleton file '{$skel}'\n");
    }
    // We need absolute path to skeleton file to use it in #line directives
    $skeleton_file = realpath($skel);
    // Parse definition file into tree
    $lineno = 0;
    $handler = null;
    $helper = null;
    $max_opcode_len = 0;
    $max_opcode = 0;
    $export = array();
    foreach ($in as $line) {
        ++$lineno;
        if (strpos($line, "ZEND_VM_HANDLER(") === 0) {
            // Parsing opcode handler's definition
            if (preg_match("/^ZEND_VM_HANDLER\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*([A-Z|]+)\\s*\\)/", $line, $m) == 0) {
                die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HANDLER definition.\n");
            }
            $code = (int) $m[1];
            $op = $m[2];
            $len = strlen($op);
            $op1 = array_flip(explode("|", $m[3]));
            $op2 = array_flip(explode("|", $m[4]));
            if ($len > $max_opcode_len) {
                $max_opcode_len = $len;
            }
            if ($code > $max_opcode) {
                $max_opcode = $code;
            }
            if (isset($opcodes[$code])) {
                die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n");
            }
            if (isset($opnames[$op])) {
                die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n");
            }
            $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "");
            $opnames[$op] = $code;
            $handler = $code;
            $helper = null;
            $list[$lineno] = array("handler" => $handler);
        } else {
            if (strpos($line, "ZEND_VM_HELPER(") === 0) {
                // Parsing helper's definition
                if (preg_match("/^ZEND_VM_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*([A-Z|]+)\\s*\\)/", $line, $m) == 0) {
                    die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n");
                }
                $helper = $m[1];
                $op1 = array_flip(explode("|", $m[2]));
                $op2 = array_flip(explode("|", $m[3]));
                if (isset($helpers[$helper])) {
                    die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n");
                }
                $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => null, "code" => "");
                $handler = null;
                $list[$lineno] = array("helper" => $helper);
            } else {
                if (strpos($line, "ZEND_VM_HELPER_EX(") === 0) {
                    // Parsing helper with parameter definition
                    if (preg_match("/^ZEND_VM_HELPER_EX\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*(.*)\\s*\\)/", $line, $m) == 0) {
                        die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n");
                    }
                    $helper = $m[1];
                    $op1 = array_flip(explode("|", $m[2]));
                    $op2 = array_flip(explode("|", $m[3]));
                    $param = $m[4];
                    if (isset($helpers[$helper])) {
                        die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n");
                    }
                    // Store parameter
                    $params[$param] = 1;
                    $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => $param, "code" => "");
                    $handler = null;
                    $list[$lineno] = array("helper" => $helper);
                } else {
                    if (strpos($line, "ZEND_VM_EXPORT_HANDLER(") === 0) {
                        if (preg_match("/^ZEND_VM_EXPORT_HANDLER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_]+)\\s*\\)/", $line, $m) == 0) {
                            die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HANDLER definition.\n");
                        }
                        if (!isset($opnames[$m[2]])) {
                            die("ERROR ({$def}:{$lineno}): opcode '{$m[2]}' is not defined.\n");
                        }
                        $export[] = array("handler", $m[1], $m[2]);
                    } else {
                        if (strpos($line, "ZEND_VM_EXPORT_HELPER(") === 0) {
                            if (preg_match("/^ZEND_VM_EXPORT_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Za-z_]+)\\s*\\)/", $line, $m) == 0) {
                                die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HELPER definition.\n");
                            }
                            if (!isset($helpers[$m[2]])) {
                                die("ERROR ({$def}:{$lineno}): helper '{$m[2]}' is not defined.\n");
                            }
                            $export[] = array("helper", $m[1], $m[2]);
                        } else {
                            if ($handler !== null) {
                                // Add line of code to current opcode handler
                                $opcodes[$handler]["code"] .= $line;
                            } else {
                                if ($helper !== null) {
                                    // Add line of code to current helper
                                    $helpers[$helper]["code"] .= $line;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    ksort($opcodes);
    // Search for opcode handlers those are used by other opcode handlers
    foreach ($opcodes as $dsc) {
        if (preg_match("/ZEND_VM_DISPATCH_TO_HANDLER\\(\\s*([A-Z_]*)\\s*\\)/m", $dsc["code"], $m)) {
            $op = $m[1];
            if (!isset($opnames[$op])) {
                die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is not defined.\n");
            }
            $code = $opnames[$op];
            $opcodes[$code]['use'] = 1;
        }
    }
    // Generate opcode #defines (zend_vm_opcodes.h)
    $code_len = strlen((string) $max_opcode);
    $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n");
    // Insert header
    out($f, $GLOBALS['header_text']);
    foreach ($opcodes as $code => $dsc) {
        $code = str_pad((string) $code, $code_len, " ", STR_PAD_LEFT);
        $op = str_pad($dsc["op"], $max_opcode_len);
        fputs($f, "#define {$op} {$code}\n");
    }
    fclose($f);
    echo "zend_vm_opcodes.h generated successfully.\n";
    // Generate zend_vm_execute.h
    $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n");
    $executor_file = realpath(__DIR__ . "/zend_vm_execute.h");
    // Insert header
    out($f, $GLOBALS['header_text']);
    // Suppress free_op1 warnings on Windows
    out($f, "#ifdef ZEND_WIN32\n# pragma warning(once : 4101)\n#endif\n");
    // Support for ZEND_USER_OPCODE
    out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n");
    for ($i = 0; $i < 255; ++$i) {
        out($f, "\t(user_opcode_handler_t)NULL,\n");
    }
    out($f, "\t(user_opcode_handler_t)NULL\n};\n\n");
    out($f, "static zend_uchar zend_user_opcodes[256] = {");
    for ($i = 0; $i < 255; ++$i) {
        if ($i % 16 == 1) {
            out($f, "\n\t");
        }
        out($f, "{$i},");
    }
    out($f, "255\n};\n\n");
    // Generate specialized executor
    gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers", 0);
    // Generate un-specialized executor
    if (ZEND_VM_OLD_EXECUTOR) {
        out($f, "\n/* Old executor */\n\n");
        out($f, "#undef ZEND_VM_CONTINUE\n\n");
        out($f, "#undef ZEND_VM_RETURN\n\n");
        out($f, "#undef ZEND_VM_ENTER\n\n");
        out($f, "#undef ZEND_VM_LEAVE\n\n");
        out($f, "#undef ZEND_VM_DISPATCH\n\n");
        out($f, "#undef ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL\n\n");
        gen_executor($f, $skl, 0, ZEND_VM_KIND_CALL, "old_execute", "zend_vm_use_old_executor", 1);
    }
    // Generate zend_vm_get_opcode_handler() function
    out($f, "static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)\n");
    out($f, "{\n");
    if (!ZEND_VM_SPEC) {
        out($f, "\treturn zend_opcode_handlers[opcode];\n");
    } else {
        if (ZEND_VM_OLD_EXECUTOR) {
            out($f, "\tif (zend_vm_old_executor) {\n");
            out($f, "\t\treturn zend_opcode_handlers[opcode];\n");
            out($f, "\t} else {\n");
        }
        out($f, "\t\tstatic const int zend_vm_decode[] = {\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 0              */\n");
        out($f, "\t\t\t_CONST_CODE,  /* 1 = IS_CONST   */\n");
        out($f, "\t\t\t_TMP_CODE,    /* 2 = IS_TMP_VAR */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 3              */\n");
        out($f, "\t\t\t_VAR_CODE,    /* 4 = IS_VAR     */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 5              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 6              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 7              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 8 = IS_UNUSED  */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 9              */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 10             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 11             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 12             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 13             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 14             */\n");
        out($f, "\t\t\t_UNUSED_CODE, /* 15             */\n");
        out($f, "\t\t\t_CV_CODE      /* 16 = IS_CV     */\n");
        out($f, "\t\t};\n");
        out($f, "\t\treturn zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];\n");
        if (ZEND_VM_OLD_EXECUTOR) {
            out($f, "\t}\n");
        }
    }
    out($f, "}\n\n");
    // Generate zend_vm_get_opcode_handler() function
    out($f, "ZEND_API void zend_vm_set_opcode_handler(zend_op* op)\n");
    out($f, "{\n");
    out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n");
    out($f, "}\n\n");
    // Export handlers and helpers
    if (count($export) > 0 && !ZEND_VM_OLD_EXECUTOR && ZEND_VM_KIND != ZEND_VM_KIND_CALL) {
        out($f, "#undef OPLINE\n");
        out($f, "#undef DCL_OPLINE\n");
        out($f, "#undef USE_OPLINE\n");
        out($f, "#undef LOAD_OPLINE\n");
        out($f, "#undef SAVE_OPLINE\n");
        out($f, "#define OPLINE EX(opline)\n");
        out($f, "#define DCL_OPLINE\n");
        out($f, "#define USE_OPLINE zend_op *opline = EX(opline);\n");
        out($f, "#define LOAD_OPLINE()\n");
        out($f, "#define SAVE_OPLINE()\n");
        out($f, "#undef CHECK_EXCEPTION\n");
        out($f, "#undef HANDLE_EXCEPTION\n");
        out($f, "#undef HANDLE_EXCEPTION_LEAVE\n");
        out($f, "#define CHECK_EXCEPTION() LOAD_OPLINE()\n");
        out($f, "#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
        out($f, "#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
        out($f, "#undef ZEND_VM_CONTINUE\n");
        out($f, "#undef ZEND_VM_RETURN\n");
        out($f, "#undef ZEND_VM_ENTER\n");
        out($f, "#undef ZEND_VM_LEAVE\n");
        out($f, "#undef ZEND_VM_DISPATCH\n");
        out($f, "#undef ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL\n\n");
        out($f, "#define ZEND_VM_CONTINUE()   return 0\n");
        out($f, "#define ZEND_VM_RETURN()     return 1\n");
        out($f, "#define ZEND_VM_ENTER()      return 2\n");
        out($f, "#define ZEND_VM_LEAVE()      return 3\n");
        out($f, "#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n\n");
        out($f, "#define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL execute_data TSRMLS_CC\n\n");
    }
    foreach ($export as $dsk) {
        list($kind, $func, $name) = $dsk;
        out($f, "ZEND_API int {$func}(");
        if ($kind == "handler") {
            out($f, "ZEND_OPCODE_HANDLER_ARGS)\n");
            $code = $opcodes[$opnames[$name]]['code'];
        } else {
            $h = $helpers[$name];
            if ($h['param'] == null) {
                out($f, "ZEND_OPCODE_HANDLER_ARGS)\n");
            } else {
                out($f, $h['param'] . ", ZEND_OPCODE_HANDLER_ARGS)\n");
            }
            $code = $h['code'];
        }
        $done = 0;
        if (ZEND_VM_OLD_EXECUTOR) {
            if ($kind == "handler") {
                out($f, "{\n\treturn " . $name . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                $done = 1;
            } else {
                if ($helpers[$name]["param"] == null) {
                    out($f, "{\n\treturn " . $name . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                    $done = 1;
                }
            }
        } else {
            if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) {
                if ($kind == "handler") {
                    $op = $opcodes[$opnames[$name]];
                    if (isset($op['op1']["ANY"]) && isset($op['op2']["ANY"])) {
                        out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                        $done = 1;
                    }
                } else {
                    if ($helpers[$name]["param"] == null) {
                        $h = $helpers[$name];
                        if (isset($h['op1']["ANY"]) && isset($h['op2']["ANY"])) {
                            out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                            $done = 1;
                        }
                    }
                }
            }
        }
        if (!$done) {
            gen_code($f, 0, ZEND_VM_KIND_CALL, 1, $code, 'ANY', 'ANY', $name);
        }
    }
    fclose($f);
    echo "zend_vm_execute.h generated successfully.\n";
}
Example #3
0
function gen_vm($def, $skel)
{
    global $definition_file, $skeleton_file, $executor_file, $op_types, $list, $opcodes, $helpers, $params, $opnames, $vm_op_flags, $used_extra_spec;
    // Load definition file
    $in = @file($def);
    if (!$in) {
        die("ERROR: Can not open definition file '{$def}'\n");
    }
    // We need absolute path to definition file to use it in #line directives
    $definition_file = realpath($def);
    // Load skeleton file
    $skl = @file($skel);
    if (!$skl) {
        die("ERROR: Can not open skeleton file '{$skel}'\n");
    }
    // We need absolute path to skeleton file to use it in #line directives
    $skeleton_file = realpath($skel);
    // Parse definition file into tree
    $lineno = 0;
    $handler = null;
    $helper = null;
    $max_opcode_len = 0;
    $max_opcode = 0;
    $extra_num = 256;
    $export = array();
    foreach ($in as $line) {
        ++$lineno;
        if (strpos($line, "ZEND_VM_HANDLER(") === 0) {
            // Parsing opcode handler's definition
            if (preg_match("/^ZEND_VM_HANDLER\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(,\\s*([A-Z_|]+)\\s*)?(,\\s*SPEC\\(([A-Z_|=,]+)\\)\\s*)?\\)/", $line, $m) == 0) {
                die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HANDLER definition.\n");
            }
            $code = (int) $m[1];
            $op = $m[2];
            $len = strlen($op);
            $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1);
            $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2);
            $flags = $flags1 | $flags2 << 8;
            if (!empty($m[6])) {
                $flags |= parse_ext_spec($def, $lineno, $m[6]);
            }
            if ($len > $max_opcode_len) {
                $max_opcode_len = $len;
            }
            if ($code > $max_opcode) {
                $max_opcode = $code;
            }
            if (isset($opcodes[$code])) {
                die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n");
            }
            if (isset($opnames[$op])) {
                die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n");
            }
            $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "", "flags" => $flags);
            if (isset($m[8])) {
                $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[8]);
                if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
                    $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
                }
                if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
                    $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
                }
            }
            $opnames[$op] = $code;
            $handler = $code;
            $helper = null;
            $list[$lineno] = array("handler" => $handler);
        } else {
            if (strpos($line, "ZEND_VM_TYPE_SPEC_HANDLER(") === 0) {
                // Parsing opcode handler's definition
                if (preg_match("/^ZEND_VM_TYPE_SPEC_HANDLER\\(\\s*([A-Z_]+)\\s*,\\s*([^,]+),\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(,\\s*([A-Z_|]+)\\s*)?(,\\s*SPEC\\(([A-Z_|=,]+)\\)\\s*)?\\)/", $line, $m) == 0) {
                    die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_TYPE_HANDLER_HANDLER definition.\n");
                }
                $orig_op = $m[1];
                if (!isset($opnames[$orig_op])) {
                    die("ERROR ({$def}:{$lineno}): Opcode with name '{$orig_op}' is not defined.\n");
                }
                $orig_code = $opnames[$orig_op];
                $condition = $m[2];
                $code = $extra_num++;
                $op = $m[3];
                $op1 = parse_operand_spec($def, $lineno, $m[4], $flags1);
                $op2 = parse_operand_spec($def, $lineno, $m[5], $flags2);
                $flags = $flags1 | $flags2 << 8;
                if (!empty($m[7])) {
                    $flags |= parse_ext_spec($def, $lineno, $m[7]);
                }
                if (isset($opcodes[$code])) {
                    die("ERROR ({$def}:{$lineno}): Opcode with name '{$code}' is already defined.\n");
                }
                $opcodes[$orig_code]['type_spec'][$code] = $condition;
                $used_extra_spec["TYPE"] = 1;
                $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "", "flags" => $flags);
                if (isset($m[9])) {
                    $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[9]);
                    if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
                        $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
                    }
                    if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
                        $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
                    }
                }
                $opnames[$op] = $code;
                $handler = $code;
                $helper = null;
                $list[$lineno] = array("handler" => $handler);
            } else {
                if (strpos($line, "ZEND_VM_HELPER(") === 0 || strpos($line, "ZEND_VM_INLINE_HELPER(") === 0) {
                    // Parsing helper's definition
                    if (preg_match("/^ZEND_VM(_INLINE)?_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(?:,\\s*SPEC\\(([A-Z_|=,]+)\\)\\s*)?(?:,\\s*([^)]*)\\s*)?\\)/", $line, $m) == 0) {
                        die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n");
                    }
                    $inline = !empty($m[1]);
                    $helper = $m[2];
                    $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1);
                    $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2);
                    $param = isset($m[6]) ? $m[6] : null;
                    if (isset($helpers[$helper])) {
                        die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n");
                    }
                    // Store parameters
                    foreach (explode(",", $param) as $p) {
                        $p = trim($p);
                        if ($p !== "") {
                            $params[$p] = 1;
                        }
                    }
                    $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => $param, "code" => "", "inline" => $inline);
                    if (!empty($m[5])) {
                        $helpers[$helper]["spec"] = parse_spec_rules($def, $lineno, $m[5]);
                    }
                    $handler = null;
                    $list[$lineno] = array("helper" => $helper);
                } else {
                    if (strpos($line, "ZEND_VM_EXPORT_HANDLER(") === 0) {
                        if (preg_match("/^ZEND_VM_EXPORT_HANDLER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_]+)\\s*\\)/", $line, $m) == 0) {
                            die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HANDLER definition.\n");
                        }
                        if (!isset($opnames[$m[2]])) {
                            die("ERROR ({$def}:{$lineno}): opcode '{$m[2]}' is not defined.\n");
                        }
                        $export[] = array("handler", $m[1], $m[2]);
                    } else {
                        if (strpos($line, "ZEND_VM_EXPORT_HELPER(") === 0) {
                            if (preg_match("/^ZEND_VM_EXPORT_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Za-z_]+)\\s*\\)/", $line, $m) == 0) {
                                die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HELPER definition.\n");
                            }
                            if (!isset($helpers[$m[2]])) {
                                die("ERROR ({$def}:{$lineno}): helper '{$m[2]}' is not defined.\n");
                            }
                            $export[] = array("helper", $m[1], $m[2]);
                        } else {
                            if (strpos($line, "ZEND_VM_DEFINE_OP(") === 0) {
                                if (preg_match("/^ZEND_VM_DEFINE_OP\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*\\);/", $line, $m) == 0) {
                                    die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_DEFINE_OP definition.\n");
                                }
                                $code = (int) $m[1];
                                $op = $m[2];
                                $len = strlen($op);
                                if ($len > $max_opcode_len) {
                                    $max_opcode_len = $len;
                                }
                                if ($code > $max_opcode) {
                                    $max_opcode = $code;
                                }
                                if (isset($opcodes[$code])) {
                                    die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n");
                                }
                                if (isset($opnames[$op])) {
                                    die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n");
                                }
                                $opcodes[$code] = array("op" => $op, "code" => "");
                                $opnames[$op] = $code;
                            } else {
                                if ($handler !== null) {
                                    // Add line of code to current opcode handler
                                    $opcodes[$handler]["code"] .= $line;
                                } else {
                                    if ($helper !== null) {
                                        // Add line of code to current helper
                                        $helpers[$helper]["code"] .= $line;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    ksort($opcodes);
    // Search for opcode handlers those are used by other opcode handlers
    foreach ($opcodes as $dsc) {
        if (preg_match("/ZEND_VM_DISPATCH_TO_HANDLER\\(\\s*([A-Z_]*)\\s*\\)/m", $dsc["code"], $m)) {
            $op = $m[1];
            if (!isset($opnames[$op])) {
                die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is not defined.\n");
            }
            $code = $opnames[$op];
            $opcodes[$code]['use'] = 1;
        }
    }
    // Generate opcode #defines (zend_vm_opcodes.h)
    $code_len = strlen((string) $max_opcode);
    $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n");
    // Insert header
    out($f, HEADER_TEXT);
    fputs($f, "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n");
    fputs($f, "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n");
    fputs($f, "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n");
    fputs($f, "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n");
    fputs($f, "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n");
    fputs($f, "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n");
    fputs($f, "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n");
    fputs($f, "\n");
    foreach ($vm_op_flags as $name => $val) {
        fprintf($f, "#define %-24s 0x%08x\n", $name, $val);
    }
    fputs($f, "#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)\n");
    fputs($f, "#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)\n");
    fputs($f, "\n");
    fputs($f, "BEGIN_EXTERN_C()\n\n");
    fputs($f, "ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);\n");
    fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode);\n\n");
    fputs($f, "END_EXTERN_C()\n\n");
    foreach ($opcodes as $code => $dsc) {
        $code = str_pad((string) $code, $code_len, " ", STR_PAD_LEFT);
        $op = str_pad($dsc["op"], $max_opcode_len);
        if ($code <= $max_opcode) {
            fputs($f, "#define {$op} {$code}\n");
        }
    }
    $code = str_pad((string) $max_opcode, $code_len, " ", STR_PAD_LEFT);
    $op = str_pad("ZEND_VM_LAST_OPCODE", $max_opcode_len);
    fputs($f, "\n#define {$op} {$code}\n");
    fputs($f, "\n#endif\n");
    fclose($f);
    echo "zend_vm_opcodes.h generated successfully.\n";
    // zend_vm_opcodes.c
    $f = fopen(__DIR__ . "/zend_vm_opcodes.c", "w+") or die("ERROR: Cannot create zend_vm_opcodes.c\n");
    // Insert header
    out($f, HEADER_TEXT);
    fputs($f, "#include <stdio.h>\n");
    fputs($f, "#include <zend.h>\n\n");
    fputs($f, "static const char *zend_vm_opcodes_names[" . ($max_opcode + 1) . "] = {\n");
    for ($i = 0; $i <= $max_opcode; $i++) {
        fputs($f, "\t" . (isset($opcodes[$i]["op"]) ? '"' . $opcodes[$i]["op"] . '"' : "NULL") . ",\n");
    }
    fputs($f, "};\n\n");
    fputs($f, "static uint32_t zend_vm_opcodes_flags[" . ($max_opcode + 1) . "] = {\n");
    for ($i = 0; $i <= $max_opcode; $i++) {
        fprintf($f, "\t0x%08x,\n", isset($opcodes[$i]["flags"]) ? $opcodes[$i]["flags"] : 0);
    }
    fputs($f, "};\n\n");
    fputs($f, "ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {\n");
    fputs($f, "\treturn zend_vm_opcodes_names[opcode];\n");
    fputs($f, "}\n");
    fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode) {\n");
    fputs($f, "\treturn zend_vm_opcodes_flags[opcode];\n");
    fputs($f, "}\n");
    fclose($f);
    echo "zend_vm_opcodes.c generated successfully.\n";
    // Generate zend_vm_execute.h
    $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n");
    $executor_file = realpath(__DIR__ . "/zend_vm_execute.h");
    // Insert header
    out($f, HEADER_TEXT);
    out($f, "#ifdef ZEND_WIN32\n");
    // Suppress free_op1 warnings on Windows
    out($f, "# pragma warning(disable : 4101)\n");
    if (ZEND_VM_SPEC) {
        // Suppress (<non-zero constant> || <expression>) warnings on windows
        out($f, "# pragma warning(once : 6235)\n");
        // Suppress (<zero> && <expression>) warnings on windows
        out($f, "# pragma warning(once : 6237)\n");
        // Suppress (<non-zero constant> && <expression>) warnings on windows
        out($f, "# pragma warning(once : 6239)\n");
        // Suppress (<expression> && <non-zero constant>) warnings on windows
        out($f, "# pragma warning(once : 6240)\n");
        // Suppress (<non-zero constant> || <non-zero constant>) warnings on windows
        out($f, "# pragma warning(once : 6285)\n");
        // Suppress (<non-zero constant> || <expression>) warnings on windows
        out($f, "# pragma warning(once : 6286)\n");
        // Suppress constant with constant comparison warnings on windows
        out($f, "# pragma warning(once : 6326)\n");
    }
    out($f, "#endif\n");
    // Support for ZEND_USER_OPCODE
    out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n");
    for ($i = 0; $i < 255; ++$i) {
        out($f, "\t(user_opcode_handler_t)NULL,\n");
    }
    out($f, "\t(user_opcode_handler_t)NULL\n};\n\n");
    out($f, "static zend_uchar zend_user_opcodes[256] = {");
    for ($i = 0; $i < 255; ++$i) {
        if ($i % 16 == 1) {
            out($f, "\n\t");
        }
        out($f, "{$i},");
    }
    out($f, "255\n};\n\n");
    // Generate specialized executor
    gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers");
    // Generate zend_vm_get_opcode_handler() function
    out($f, "static const void *zend_vm_get_opcode_handler_ex(uint32_t spec, const zend_op* op)\n");
    out($f, "{\n");
    if (!ZEND_VM_SPEC) {
        out($f, "\treturn zend_opcode_handlers[spec];\n");
    } else {
        out($f, "\tstatic const int zend_vm_decode[] = {\n");
        out($f, "\t\t_UNUSED_CODE, /* 0              */\n");
        out($f, "\t\t_CONST_CODE,  /* 1 = IS_CONST   */\n");
        out($f, "\t\t_TMP_CODE,    /* 2 = IS_TMP_VAR */\n");
        out($f, "\t\t_UNUSED_CODE, /* 3              */\n");
        out($f, "\t\t_VAR_CODE,    /* 4 = IS_VAR     */\n");
        out($f, "\t\t_UNUSED_CODE, /* 5              */\n");
        out($f, "\t\t_UNUSED_CODE, /* 6              */\n");
        out($f, "\t\t_UNUSED_CODE, /* 7              */\n");
        out($f, "\t\t_UNUSED_CODE, /* 8 = IS_UNUSED  */\n");
        out($f, "\t\t_UNUSED_CODE, /* 9              */\n");
        out($f, "\t\t_UNUSED_CODE, /* 10             */\n");
        out($f, "\t\t_UNUSED_CODE, /* 11             */\n");
        out($f, "\t\t_UNUSED_CODE, /* 12             */\n");
        out($f, "\t\t_UNUSED_CODE, /* 13             */\n");
        out($f, "\t\t_UNUSED_CODE, /* 14             */\n");
        out($f, "\t\t_UNUSED_CODE, /* 15             */\n");
        out($f, "\t\t_CV_CODE      /* 16 = IS_CV     */\n");
        out($f, "\t};\n");
        out($f, "\tuint32_t offset = 0;\n");
        out($f, "\tif (spec & SPEC_RULE_OP1) offset = offset * 5 + zend_vm_decode[op->op1_type];\n");
        out($f, "\tif (spec & SPEC_RULE_OP2) offset = offset * 5 + zend_vm_decode[op->op2_type];\n");
        if (isset($used_extra_spec["OP_DATA"])) {
            out($f, "\tif (spec & SPEC_RULE_OP_DATA) offset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];\n");
        }
        if (isset($used_extra_spec["RETVAL"])) {
            out($f, "\tif (spec & SPEC_RULE_RETVAL) offset = offset * 2 + (op->result_type != IS_UNUSED);\n");
        }
        if (isset($used_extra_spec["QUICK_ARG"])) {
            out($f, "\tif (spec & SPEC_RULE_QUICK_ARG) offset = offset * 2 + (op->op2.num < MAX_ARG_FLAG_NUM);\n");
        }
        if (isset($used_extra_spec["SMART_BRANCH"])) {
            out($f, "\tif (spec & SPEC_RULE_SMART_BRANCH) {\n");
            out($f, "\t\toffset = offset * 3;\n");
            out($f, "\t\tif ((op+1)->opcode == ZEND_JMPZ) {\n");
            out($f, "\t\t\toffset += 1;\n");
            out($f, "\t\t} else if ((op+1)->opcode == ZEND_JMPNZ) {\n");
            out($f, "\t\t\toffset += 2;\n");
            out($f, "\t\t}\n");
            out($f, "\t}\n");
        }
        if (isset($used_extra_spec["DIM_OBJ"])) {
            out($f, "\tif (spec & SPEC_RULE_DIM_OBJ) {\n");
            out($f, "\t\toffset = offset * 3;\n");
            out($f, "\t\tif (op->extended_value == ZEND_ASSIGN_DIM) {\n");
            out($f, "\t\t\toffset += 1;\n");
            out($f, "\t\t} else if (op->extended_value == ZEND_ASSIGN_OBJ) {\n");
            out($f, "\t\t\toffset += 2;\n");
            out($f, "\t\t}\n");
            out($f, "\t}\n");
        }
        out($f, "\treturn zend_opcode_handlers[(spec & SPEC_START_MASK) + offset];\n");
    }
    out($f, "}\n\n");
    out($f, "static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)\n");
    out($f, "{\n");
    if (!ZEND_VM_SPEC) {
        out($f, "\treturn zend_vm_get_opcode_handler_ex(opcode, op);\n");
    } else {
        out($f, "\treturn zend_vm_get_opcode_handler_ex(zend_spec_handlers[opcode], op);\n");
    }
    out($f, "}\n\n");
    // Generate zend_vm_get_opcode_handler() function
    out($f, "ZEND_API void zend_vm_set_opcode_handler(zend_op* op)\n");
    out($f, "{\n");
    out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n");
    out($f, "}\n\n");
    // Generate zend_vm_set_opcode_handler_ex() function
    out($f, "ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint32_t op2_info, uint32_t res_info)\n");
    out($f, "{\n");
    out($f, "\tzend_uchar opcode = zend_user_opcodes[op->opcode];\n");
    if (!ZEND_VM_SPEC) {
        out($f, "\top->handler = zend_vm_get_opcode_handler_ex(opcode, op);\n");
    } else {
        out($f, "\tuint32_t spec = zend_spec_handlers[opcode];\n");
        if (isset($used_extra_spec["TYPE"])) {
            out($f, "\tswitch (opcode) {\n");
            foreach ($opcodes as $code => $dsc) {
                if (isset($dsc['type_spec'])) {
                    $orig_op = $dsc['op'];
                    out($f, "\t\tcase {$orig_op}:\n");
                    $first = true;
                    foreach ($dsc['type_spec'] as $code => $condition) {
                        if ($first) {
                            out($f, "\t\t\tif ({$condition}) {\n");
                            $first = false;
                        } else {
                            out($f, "\t\t\t} else if ({$condition}) {\n");
                        }
                        $spec_dsc = $opcodes[$code];
                        if (isset($spec_dsc["spec"]["NO_CONST_CONST"])) {
                            out($f, "\t\t\t\tif (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {\n");
                            out($f, "\t\t\t\t\tbreak;\n");
                            out($f, "\t\t\t\t}\n");
                        }
                        out($f, "\t\t\t\tspec = {$spec_dsc['spec_code']};\n");
                        if (isset($spec_dsc["spec"]["COMMUTATIVE"])) {
                            out($f, "\t\t\t\tif (op->op1_type > op->op2_type) {\n");
                            out($f, "\t\t\t\t\tzend_swap_operands(op);\n");
                            out($f, "\t\t\t\t}\n");
                        }
                    }
                    if (!$first) {
                        out($f, "\t\t\t}\n");
                    }
                    out($f, "\t\t\tbreak;\n");
                }
            }
            out($f, "\t\tdefault:\n");
            out($f, "\t\t\tbreak;\n");
            out($f, "\t}\n");
        }
        out($f, "\top->handler = zend_vm_get_opcode_handler_ex(spec, op);\n");
    }
    out($f, "}\n\n");
    // Generate zend_vm_call_opcode_handler() function
    if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) {
        out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
        out($f, "{\n");
        out($f, "\tint ret;\n");
        out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
        out($f, "\tconst zend_op *orig_opline = opline;\n");
        out($f, "#endif\n");
        out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
        out($f, "\tzend_execute_data *orig_execute_data = execute_data;\n");
        out($f, "\texecute_data = ex;\n");
        out($f, "#else\n");
        out($f, "\tzend_execute_data *execute_data = ex;\n");
        out($f, "#endif\n");
        out($f, "\n");
        out($f, "\tLOAD_OPLINE();\n");
        out($f, "#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n");
        out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
        out($f, "\tif (EXPECTED(opline)) {\n");
        out($f, "\t\tret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0;\n");
        out($f, "\t\tSAVE_OPLINE();\n");
        out($f, "\t} else {\n");
        out($f, "\t\tret = -1;\n");
        out($f, "\t}\n");
        out($f, "#else\n");
        out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
        out($f, "\tSAVE_OPLINE();\n");
        out($f, "#endif\n");
        out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n");
        out($f, "\texecute_data = orig_execute_data;\n");
        out($f, "#endif\n");
        out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n");
        out($f, "\topline = orig_opline;\n");
        out($f, "#endif\n");
        out($f, "\treturn ret;\n");
        out($f, "}\n\n");
    } else {
        out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n");
        out($f, "{\n");
        out($f, "\tzend_error_noreturn(E_CORE_ERROR, \"zend_vm_call_opcode_handler() is not supported\");\n");
        out($f, "\treturn 0;\n");
        out($f, "}\n\n");
    }
    // Export handlers and helpers
    if (count($export) > 0 && ZEND_VM_KIND != ZEND_VM_KIND_CALL) {
        out($f, "#undef OPLINE\n");
        out($f, "#undef DCL_OPLINE\n");
        out($f, "#undef USE_OPLINE\n");
        out($f, "#undef LOAD_OPLINE\n");
        out($f, "#undef LOAD_NEXT_OPLINE\n");
        out($f, "#undef SAVE_OPLINE\n");
        out($f, "#define OPLINE EX(opline)\n");
        out($f, "#define DCL_OPLINE\n");
        out($f, "#define USE_OPLINE const zend_op *opline = EX(opline);\n");
        out($f, "#define LOAD_OPLINE()\n");
        out($f, "#define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()\n");
        out($f, "#define SAVE_OPLINE()\n");
        out($f, "#undef HANDLE_EXCEPTION\n");
        out($f, "#undef HANDLE_EXCEPTION_LEAVE\n");
        out($f, "#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
        out($f, "#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
        out($f, "#undef ZEND_VM_CONTINUE\n");
        out($f, "#undef ZEND_VM_RETURN\n");
        out($f, "#undef ZEND_VM_ENTER\n");
        out($f, "#undef ZEND_VM_LEAVE\n");
        out($f, "#undef ZEND_VM_DISPATCH\n");
        out($f, "#define ZEND_VM_CONTINUE()   return  0\n");
        out($f, "#define ZEND_VM_RETURN()     return -1\n");
        out($f, "#define ZEND_VM_ENTER()      return  1\n");
        out($f, "#define ZEND_VM_LEAVE()      return  2\n");
        out($f, "#define ZEND_VM_INTERRUPT()  return zend_interrupt_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n");
        out($f, "#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n\n");
        out($f, "\n");
    }
    foreach ($export as $dsk) {
        list($kind, $func, $name) = $dsk;
        out($f, "ZEND_API int {$func}(");
        if ($kind == "handler") {
            out($f, "ZEND_OPCODE_HANDLER_ARGS)\n");
            $code = $opcodes[$opnames[$name]]['code'];
        } else {
            $h = $helpers[$name];
            if ($h['param'] == null) {
                out($f, "ZEND_OPCODE_HANDLER_ARGS)\n");
            } else {
                out($f, $h['param'] . " ZEND_OPCODE_HANDLER_ARGS_DC)\n");
            }
            $code = $h['code'];
        }
        $done = 0;
        if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) {
            if ($kind == "handler") {
                $op = $opcodes[$opnames[$name]];
                if (isset($op['op1']["ANY"]) && isset($op['op2']["ANY"])) {
                    out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                    $done = 1;
                }
            } else {
                if ($helpers[$name]["param"] == null) {
                    $h = $helpers[$name];
                    if (isset($h['op1']["ANY"]) && isset($h['op2']["ANY"])) {
                        out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n");
                        $done = 1;
                    }
                }
            }
        }
        if (!$done) {
            gen_code($f, 0, ZEND_VM_KIND_CALL, 1, $code, 'ANY', 'ANY', $name);
        }
    }
    fclose($f);
    echo "zend_vm_execute.h generated successfully.\n";
}