| 1254 | } |
| 1255 | |
| 1256 | GDScriptDecomp::BytecodeTestResult GDScriptDecomp::_test_bytecode(Vector<uint8_t> p_buffer, int &r_tok_max, int &r_func_max, bool print_verbosely) { |
| 1257 | #define ERR_TEST_FAILED(x) \ |
| 1258 | error_message = "Line " + String::num_int64(line) + ": " + String(x); \ |
| 1259 | if (print_verbosely) { \ |
| 1260 | print_failed_verbose_func(x); \ |
| 1261 | } \ |
| 1262 | return BytecodeTestResult::BYTECODE_TEST_FAIL; |
| 1263 | |
| 1264 | #define SIZE_CHECK(x) \ |
| 1265 | if (i + x >= tokens.size()) { \ |
| 1266 | ERR_TEST_FAILED("Size check failed: " + itos(i) + " " + itos(x)); \ |
| 1267 | } |
| 1268 | |
| 1269 | error_message = ""; |
| 1270 | |
| 1271 | int line = 0; |
| 1272 | auto print_failed_verbose_func = [&](const String &p_str) { |
| 1273 | String prefix = vformat("Bytecode test for %s (%07x) failed on line %d: ", get_engine_version(), get_bytecode_rev(), line); |
| 1274 | print_line(prefix + p_str); |
| 1275 | }; |
| 1276 | |
| 1277 | auto get_builtin_name = [&](int p_id) -> String { |
| 1278 | return p_id == -1 ? "preload" : get_function_name(p_id); |
| 1279 | }; |
| 1280 | |
| 1281 | ScriptState script_state; |
| 1282 | //Load bytecode |
| 1283 | Error err = get_script_state(p_buffer, script_state); |
| 1284 | if (err) { |
| 1285 | if (print_verbosely) { |
| 1286 | print_failed_verbose_func("Failed to get identifiers, constants, and tokens"); |
| 1287 | } |
| 1288 | return BytecodeTestResult::BYTECODE_TEST_CORRUPT; |
| 1289 | } |
| 1290 | Vector<uint32_t> &tokens = script_state.tokens; |
| 1291 | HashMap<uint32_t, uint32_t> &lines = script_state.lines; |
| 1292 | int version = script_state.bytecode_version; |
| 1293 | int bytecode_version = get_bytecode_version(); |
| 1294 | int FUNC_MAX = get_function_count(); |
| 1295 | |
| 1296 | if (version != bytecode_version) { |
| 1297 | ERR_TEST_FAILED("Bytecode version mismatch: " + itos(version) + " != " + itos(bytecode_version)); |
| 1298 | } |
| 1299 | |
| 1300 | ERR_FAIL_COND_V_MSG(err != OK, BYTECODE_TEST_CORRUPT, "Failed to get identifiers, constants, and tokens"); |
| 1301 | auto get_line_func([&](int i) { |
| 1302 | if (lines.has(i)) { |
| 1303 | return lines[i]; |
| 1304 | } |
| 1305 | if (script_state.end_lines.has(i)) { |
| 1306 | return script_state.end_lines[i]; |
| 1307 | } |
| 1308 | return 0U; |
| 1309 | }); |
| 1310 | |
| 1311 | // reserved words can be used as member accessors in all versions of GDScript, and used as function names in GDScript 1.0 |
| 1312 | auto is_not_actually_reserved_word = [&](int i) { |
| 1313 | return (check_prev_token(i, tokens, G_TK_PERIOD) || |